Neoteroi / BlackSheep

Fast ASGI web framework for Python
https://www.neoteroi.dev/blacksheep/
MIT License
1.86k stars 78 forks source link

Startup error #438

Closed FilterBody closed 9 months ago

FilterBody commented 10 months ago
from datetime import datetime
from blacksheep import Application, get

app = Application()

@get("/")
def home():
    return f"Hello, World! {datetime.now().isoformat()}"

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app="main:app", port=8887, reload=True)

result

blacksheep.server.routing.RouteDuplicate: Cannot register route pattern `/` for `GET` more than once. This pattern is already registered for handler home.

env

blacksheep         2.0.0
certifi            2023.11.17
charset-normalizer 3.1.0
click              8.1.7
essentials         1.1.5
essentials-openapi 1.0.9
guardpost          1.0.2
h11                0.14.0
httptools          0.6.1
idna               3.6
itsdangerous       2.1.2
MarkupSafe         2.1.3
pip                23.0.1
python-dateutil    2.8.2
PyYAML             6.0.1
requests           2.31.0
rodi               2.0.5
setuptools         65.5.0
six                1.16.0
typing_extensions  4.8.0
urllib3            2.1.0
uvicorn            0.24.0.post1
RobertoPrevato commented 10 months ago

Hi @FilterBody Thank you for reporting this with reproduction steps. I didn´t notice this because in such cases I was running with the uvicorn CLI: the following works.

uvicorn main:app --reload --port 8887

The project templates I worked on work, too, because they don't try to configure routes in the same file used to run uvicorn programmatically.

I will take a look.

RobertoPrevato commented 10 months ago

Ok this has to do with the new singleton router that was added as default one for the reasons explained here:

https://www.neoteroi.dev/blacksheep/versions/migrating-to-v2/#automatic-import-of-routes-and-controllers

In this case, the singleton router is initialized before uvicorn forks the process for its dynamic import, thus causing the issue. The application code gets executed more than once in the context of the same router (the router was already instantiated).

It is funny I didn't notice because the same does not happen when you run uvicorn from the command line, or when you don´t import blacksheep in the files used to run uvicorn programmatically (like in my templates, as I wrote earlier - see this example)

The following works - just for the sake of example:

from datetime import datetime
from blacksheep import Application
from blacksheep.server.routing import Router

router = Router()
get = router.get

app = Application(router=router)

@get("/")
def home():
    return f"Hello, World! {datetime.now().isoformat()}"

if __name__ == "__main__":
    import uvicorn

    uvicorn.run(app="main:app", port=8887, reload=True)
FilterBody commented 10 months ago

Okay, thank you very much

RobertoPrevato commented 10 months ago

Let's keep this open, I want to fix this issue.