-
-
Notifications
You must be signed in to change notification settings - Fork 902
lazy imports using lazy-imports library
#5303
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as resolved.
This comment was marked as resolved.
|
f415575 fixed the double-access on Now the basic code works: from nicegui import ui
ui.label('First label')
ui.label('Second label. Did not break!')
ui.run()As always pipeline will be our guiding star. Let's see tomorrow. |
|
Pipeline's less dramatic than I think. 2 things are broken, mainly:
Actually point 2 can theoretically affect someone who's
Gist is: with |
801bf46 to
425cb4f
Compare
|
Well, as of 425cb4f the only failing tests is the @falkoschindler This is getting somewhere, somewhere farther than anywhere we had been. Consider closing: |
|
If in #4564 we swapped a function for a Class, may we in this case swap a module for a Class and then we can solve issue 1. |
|
@evnchn I added the other PRs to the description of this PR. If this PR converges and is merged eventually, we can close them. |
|
Actually this PR is more mergable if we incorporate https://github.com/evnchn/nicegui/tree/feature/lazy-imports-clipboard-class @falkoschindler can you review the changes there and then if it looks good we can merge that into this PR |
…ance - Add lazy-imports dependency to pyproject.toml - Convert ui.py to use LazyModule for deferred loading of UI elements - Achieve 85% reduction in import time (from ~4.3s to ~0.66s) - Maintain full backward compatibility with existing API - Preserve TYPE_CHECKING branch for static type checkers The lazy loading implementation defers the import of heavy UI element classes until they are first accessed, significantly improving the initial import time of NiceGUI while maintaining all functionality.
The previous implementation using the __getattr__ pattern had a critical bug where accessing the same attribute twice would fail with AttributeError. Root cause: When using __getattr__ pattern, the LazyModule instance is not registered in sys.modules. When LazyModule.__getattr__ calls setattr(self, name, value), it sets the attribute on the LazyModule instance, not on the actual module in sys.modules. On second access, the attribute is no longer in __deferred_attrs (it was popped), but it's also not on the module, causing AttributeError. Solution: Use load(_mod) to register the LazyModule instance in sys.modules, ensuring setattr() actually sets attributes on the module Python sees. This fix: - Resolves the double-access bug - Maintains the same performance (610ms vs 655ms - actually slightly better) - Passes all comprehensive tests including multiple re-accesses - Properly caches accessed attributes on the module
5de423b to
231bacc
Compare
231bacc to
e720b60
Compare
|
@falkoschindler I chose to rebase because the merge conflict is unreal. Also nobody remember what happened those 98 days ago, so rebase over merge is more similar to how our memory works 🧠 Nevertheless, I want to report: Despite what the original description says, this PR is working!
from nicegui import ui
@ui.page('/')
def page():
ui.json_editor({})
ui.run(show=False, reload=False)
#!/bin/bash
source /workspaces/nicegui/.venv/bin/activate
start=$(date +%s.%N)
coproc python test.py
while read -r line <&${COPROC[0]}; do
echo "$line"
if [[ "$line" == *"NiceGUI ready to go on"* ]]; then
end=$(date +%s.%N)
elapsed=$(awk "BEGIN {print $end - $start}")
echo "Time taken: $elapsed seconds"
kill $COPROC_PID
break
fi
done
|
|
Would consider that this is not draft. If you agree, you can mark it ready for review and review it. |

Motivation
Observing how we basically re-import NiceGUI again if we want to do
run.cpu_bound, lazy imports which defer heavy imports until actually needed is a promising approach to make it faster (ref: #5684)Implementation Strategy
It began with my rapid iteration with Manus to use https://github.com/bachorp/lazy-imports, and we iterate until
from nicegui import uiworks.Implementation (this part by Copilot)
Refactoring and performance improvements:
Clipboard API modernization:
Clipboardclass with asyncread,write, andread_imagemethods, and provided a singletonclipboardinstance for use throughout the codebase. [1] [2] [3] [4]lazy-importslibrary #5303 (comment) for additional context.UI lazy loading:
ui.pyusing thelazy_importslibrary, significantly improving startup performance by loading modules only when accessed. Registered the lazy module insys.modulesfor proper caching. [1] [2] [3] [4]lazy-importsas a required dependency inpyproject.toml.Import handling fixes:
client.pyto resolve issues with lazy imports and ensure they are available when needed.lazy-importslibrary #5303 (comment) for additional context.Progress