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

CLI pattern if __name__ == "__main__": main() does not work with misleading message #181

Closed
nazarewk opened this issue Nov 25, 2022 · 6 comments

Comments

@nazarewk
Copy link

nazarewk commented Nov 25, 2022

related to #143

One of the patterns for running CLI programs is:

from nicegui import ui

def main():
    ui.label('Hello NiceGUI!')
    ui.button('BUTTON', on_click=lambda: ui.notify('button was pressed'))
    ui.run(port=8082)

if __name__ == "__main__":
    main()

This does not work with reload=True, because the reloadable __name__ is __mp_main__ instead.

So I made it work using second entry:

from nicegui import ui

def main():
    ui.label('Hello NiceGUI!')
    ui.button('BUTTON', on_click=lambda: ui.notify('button was pressed'))
    ui.run(port=8082)

if __name__ in ("__main__", "__mp_main__"):
    main()

Any idea what causes this and/or whether it can be fixed in nicegui itself?

@nazarewk nazarewk changed the title CLI pattern if __name__ == "__main__": main() does not work CLI pattern if __name__ == "__main__": main() does not work with misleading message Nov 25, 2022
@falkoschindler
Copy link
Contributor

falkoschindler commented Nov 28, 2022

When Uvicorn is reloading the app (which happens for the first time right after starting the script), it re-evaluates main.py while searching for the nicegui:app object. In contrast to the original process, this "reloader" process has __name__ "__mp_main__" with "mp" indicating "multi-processing". The main guard (successfully...) prevents the new process from evaluating main(), running ui.run and initializing globals.config. This is not what you want in this case.

Technically I don't see an easy solution for supporting the default main guard if __name__ == '__main__': for auto-reloading NiceGUI apps. Sure, you can extend the guard with __mp_main__. The question is, what is you motivation for the guard in the first place - besides it being a common pattern?

If you want to avoid the code in main() being evaluated a second time when starting with reload=True, you can wrap it in a page function:

from nicegui import ui

@ui.page('/')
def main_page():
    ui.label('Hello NiceGUI!')
    ui.button('BUTTON', on_click=lambda: ui.notify('button was pressed'))

ui.run(port=8082)

Personally I'm not sure if you really need a main guard. Ideally your main script is short and concise, so that it does not need to be imported from anywhere.

@nazarewk
Copy link
Author

I want my main script to serve as a CLI primarily and be able to spin up a web gui as an option.

@falkoschindler
Copy link
Contributor

I'll close this issue, since I don't see how we can avoid the conflict with the main guard. Using NiceGUI as a secondary mode in an otherwise headless CLI application in combination with reload=True seems to be a rather tricky setup. But if including __mp_main__ in the main guard helps, I'd leave it this way.

@denravonska
Copy link

Is this supposed to work when you invoke your code as a module? I get the exact same error message when using a __main__.py consisting of only

from nicegui import ui

ui.run()
$ python -m apto
ERROR:    Traceback (most recent call last):
  File "env/lib/python3.10/site-packages/starlette/routing.py", line 677, in lifespan
    async with self.lifespan_context(app) as maybe_state:
  File "env/lib/python3.10/site-packages/starlette/routing.py", line 566, in __aenter__
    await self._router.startup()
  File "env/lib/python3.10/site-packages/starlette/routing.py", line 656, in startup
    handler()
  File "env/lib/python3.10/site-packages/nicegui/nicegui.py", line 62, in handle_startup
    raise RuntimeError('\n\n'
RuntimeError: 

You must call ui.run() to start the server.
If ui.run() is behind a main guard
   if __name__ == "__main__":
remove the guard or replace it with
   if __name__ in {"__main__", "__mp_main__"}:
to allow for multiprocessing.

ERROR:    Application startup failed. Exiting.

@falkoschindler
Copy link
Contributor

Hi @denravonska!
Can you, please, open a new discussion for this matter? And can you give a bit more information about your file and directory structure so that we can reproduce the issue? Thanks!

@denravonska
Copy link

@falkoschindler Done, #1111 :)

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