Skip to content
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

NiceGUI runs python code twice #794

Closed
JS-Aibel opened this issue Apr 19, 2023 · 2 comments
Closed

NiceGUI runs python code twice #794

JS-Aibel opened this issue Apr 19, 2023 · 2 comments
Assignees

Comments

@JS-Aibel
Copy link

JS-Aibel commented Apr 19, 2023

Description

When running the code below the python code get executed twice.
When running scripts that takes a while, it is bad for performance that it has to run twice.

from nicegui import ui

print("Start")
print("End")

ui.label("test")
ui.run()

Output from terminal:

> python test.py
Start
End
Start
End
NiceGUI ready to go on http://0.0.0.0:8080
@falkoschindler falkoschindler self-assigned this Apr 19, 2023
@falkoschindler
Copy link
Contributor

Thanks for bringing this up, @JS-Aibel!

This is known behavior. When running with ui.run(reload=True) (the default) there are two processes:

  • The main process evaluates the script, runs ui.run and starts the uvicorn server.
  • Because reload=True, it starts another process that also evaluates the script. Whenever the code is changed, this child process is replaced with a new one that evaluates the script again.

Note that the script is evaluated twice only when starting it. The auto-reload on code change will re-evaluate only once.

To avoid the re-evaluation, you have several options:

  • Use ui.run(reload=False). Of course, you loose the handy auto-reload feature. But for production this might be the way to go.

  • Use some kind of main guard:

    from nicegui import ui
    
    if __name__ == '__mp_main__':
        print("Start")
        print("End")
        ui.label("test")
    
    ui.run()

    This avoids evaluating the code in the "__main__" process and restricts it to the child process "__mp_main__".

  • Use a page decorator:

    @ui.page('/')
    def main():
        print("Start")
        print("End")
        ui.label("test")

    This evaluates the UI only when accessed. But if you have an expensive initialization to do once when starting the script, this might not be the way to go. Page decorators also change the visibility, since it generates a new page per client, so the state is not shared anymore.

  • Move expensive initialization into a startup callback:

    def startup():
        print("Start")
        print("End")
    
    app.on_startup(startup)
    
    ui.label("test")

    This way startup is only evaluated once in the child process, since the app doesn't start in the main process (unless reload=False).

@itd-fsc
Copy link

itd-fsc commented Oct 2, 2024

We solved this by creating an empty UI if reload is enabled and if it is the process that will just start the script again

RELOAD = True


def main():
    # If reload is enabled, then it has to run the main program as a subprocess (https://github.com/zauberzeug/nicegui/issues/794)
    # => it runs the file again as a subprocess causing everything to be executed twice
    # => This reload runner doesn't need to run the actual code
    if RELOAD and __name__ == '__main__':
        ui.run(host='127.0.0.1', port=8080, reload=RELOAD)
        return
    
    print("Building UI")
    ui.label('Test')
    ui.run(host='127.0.0.1', port=8080, reload=RELOAD)


if __name__ in ['__main__', '__mp_main__']:
    main()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants