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

Allow dependency injection Config and Server #1248

Closed
wants to merge 10 commits into from

Conversation

abersheeran
Copy link
Member

@abersheeran abersheeran commented Nov 12, 2021

Related links:

To solve #742 , a short and simple code:

config = Config("example:app", host="127.0.0.1", port=5000, log_level="info")
server = Server(config=config)

thread = threading.Thread(target=uvicorn.run_server, args=(server,))
thread.start()

To solve #1151

async with app_factory() as app:
    config = Config(app)
    server = Server(config=config)
    uvicorn.run_server(server)

To solve #451 and #1091

should_exit = False

class CustomServer(uvicorn.Server):
    def handle_exit(self, sig, frame):
        global should_exit
        should_exit = True
        return super().handle_exit(sig, frame)

class App:
    ...

    async def http(self, receive, send):
        """Handle http messages"""

        message = await receive()
        assert message['type'] == 'http.request'

        while message['more_body']:
            message = await receive()

        await send({
            'type': 'http.response.start',
            'status': 200,
            'headers': [
                (b'cache-control', b'no-cache'),
                (b'content-type', b'text/event-stream'),
                (b'connection', b'keep-alive')
            ]
        })

        while not should_exit:
            ............ # send some message

if __name__ == '__main__':
    config = Config(App())
    server = CustomServer(config=config)
    uvicorn.run_server(server)

Copy link
Member

@Kludex Kludex left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The examples on the description shows uvicorn.serve, but that is not available yet. We need to add from uvicorn.main import serve on the __init__.py.

@abersheeran abersheeran marked this pull request as ready for review November 15, 2021 03:10
@Kludex
Copy link
Member

Kludex commented Nov 22, 2021

@euri10 Do you have something against this or can we we merge it?

@euri10
Copy link
Member

euri10 commented Nov 23, 2021

@euri10 Do you have something against this or can we we merge it?

nothing against, that may be a breaking change though for some people, @florimondmanca will know for sure, so maybe hold it onto a minor bump if patches need to be released in between

uvicorn/main.py Outdated Show resolved Hide resolved
@florimondmanca
Copy link
Member

Well I think the general idea is quite nice.

Tho as @HansBrende pointed out, seems there’s a distinction and decision to take about whether the serve() utility should include the multiprocessing/reload code branches or not.

k don’t have much more background to tell which would best address the issues this aims at, though. Just thought I’d raise that distinction as something that should be discussed before merging.

In a all cases, one of this or #1012 should end up merged and the other one closed, right?

@florimondmanca
Copy link
Member

florimondmanca commented Nov 27, 2021

I also agree with @HansBrende that the uvicorn.serve naming would be a bit confusing, as eg Server.serve() is while this is not, and this is actually calling into server.run(). I’d also be in favor of something like uvicorn.run_server() like in #1012.

@abersheeran abersheeran requested a review from Kludex November 29, 2021 11:51
@@ -444,6 +444,13 @@ def load(self) -> None:

self.lifespan_class = import_from_string(LIFESPAN[self.lifespan])

if (self.reload or self.workers > 1) and not isinstance(self.app, str):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this check go in the constructor? By the time we get here, we are already in the subprocess I believe...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this check should be combined with import_from_string(self.app).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? Isn't it preferable to "fail fast" in the main process rather than failing later in possibly N subprocesses?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the user provides a string, checks the type in one place, but tries to import it in another far away, don't you think this is unreasonable?

If it is for this purpose, then import_from_string(self.app) should also "fail fast".

@Kludex
Copy link
Member

Kludex commented May 10, 2022

Let's continue on #1012. It's easier, yep.

@Kludex Kludex closed this May 10, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants