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

Document how to run uvicorn programatically #1525

Merged
merged 5 commits into from Jun 20, 2022
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
44 changes: 41 additions & 3 deletions docs/index.md
Expand Up @@ -196,9 +196,25 @@ For more information, see the [settings documentation](settings.md).

### Running programmatically

To run uvicorn directly from your application...
There are many ways to run uvicorn directly from your application.
Kludex marked this conversation as resolved.
Show resolved Hide resolved

**example.py**:
#### `uvicorn.run` function
Kludex marked this conversation as resolved.
Show resolved Hide resolved

The easiest way is to run via `uvicorn.run` function:
Kludex marked this conversation as resolved.
Show resolved Hide resolved

```python
Kludex marked this conversation as resolved.
Show resolved Hide resolved
import uvicorn

async def app(scope, receive, send):
...

if __name__ == "__main__":
uvicorn.run("main:app", host="127.0.0.1", port=5000, log_level="info")
```
Copy link
Member

Choose a reason for hiding this comment

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

`uvicorn.run` accepts the following arguments:

* `app`: Either an ASGI application, or (as shown above) an import string to the target ASGI application. The latter is required if using auto-reload or multiple workers.
* `**kwargs`: Keyword arguments that mirror command line options (see [Settings](/settings.md)): `--host <str>` becomes `host=<str>`, `--log-level <str>` becomes `log_level=<str>`, etc.

(I think this kind of info is valuable in docs, but maybe we might want to kick off an API Reference page for this kind of thing. I'm OK if we defer this bit from this PR.)

Copy link
Sponsor Member Author

Choose a reason for hiding this comment

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

We improved the uvicorn.run type annotation, so we can have autocomplete on it:

uvicorn/uvicorn/main.py

Lines 445 to 492 in b1a4582

def run(
app: typing.Union[ASGIApplication, str],
*,
host: str = "127.0.0.1",
port: int = 8000,
uds: typing.Optional[str] = None,
fd: typing.Optional[int] = None,
loop: LoopSetupType = "auto",
http: HTTPProtocolType = "auto",
ws: WSProtocolType = "auto",
ws_max_size: int = 16777216,
ws_ping_interval: float = 20.0,
ws_ping_timeout: float = 20.0,
ws_per_message_deflate: bool = True,
lifespan: LifespanType = "auto",
interface: InterfaceType = "auto",
debug: bool = False,
reload: bool = False,
reload_dirs: typing.Optional[typing.List[str]] = None,
reload_includes: typing.Optional[typing.List[str]] = None,
reload_excludes: typing.Optional[typing.List[str]] = None,
reload_delay: float = 0.25,
workers: typing.Optional[int] = None,
env_file: typing.Optional[str] = None,
log_config: typing.Optional[typing.Union[dict, str]] = None,
log_level: typing.Optional[str] = None,
access_log: bool = True,
proxy_headers: bool = True,
server_header: bool = True,
date_header: bool = True,
forwarded_allow_ips: typing.Optional[str] = None,
root_path: str = "",
limit_concurrency: typing.Optional[int] = None,
backlog: int = 2048,
limit_max_requests: typing.Optional[int] = None,
timeout_keep_alive: int = 5,
ssl_keyfile: typing.Optional[str] = None,
ssl_certfile: typing.Optional[str] = None,
ssl_keyfile_password: typing.Optional[str] = None,
ssl_version: int = int(SSL_PROTOCOL_VERSION),
ssl_cert_reqs: int = int(ssl.CERT_NONE),
ssl_ca_certs: typing.Optional[str] = None,
ssl_ciphers: str = "TLSv1",
headers: typing.Optional[typing.List[typing.Tuple[str, str]]] = None,
use_colors: typing.Optional[bool] = None,
app_dir: typing.Optional[str] = None,
factory: bool = False,
) -> None:

I'll not apply this for now. An API reference would be fine by me.


#### Explicitly setup `Config` and `Server` instances
Kludex marked this conversation as resolved.
Show resolved Hide resolved

You can create the `uvicorn.Config` and `uvicorn.Server` instances manually, and then use `uvicorn.Server.run()`:
Kludex marked this conversation as resolved.
Show resolved Hide resolved

```python
import uvicorn
Expand All @@ -207,7 +223,29 @@ async def app(scope, receive, send):
...

if __name__ == "__main__":
uvicorn.run("example:app", host="127.0.0.1", port=5000, log_level="info")
config = uvicorn.Config("main:app", host="127.0.0.1", post=5000, log_level="info")
server = uvicorn.Server(config)
server.run()
```

#### Run inside the event loop

There are cases that `async` logic needs to be performed, and it's interesting to run `uvicorn` inside the event loop:

```python
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
```python
main.py
```python

Copy link
Member

Choose a reason for hiding this comment

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

just to say this is the name of the file, I'll approve it without this change but we have too many people not understanding how the app string works

Copy link
Sponsor Member Author

Choose a reason for hiding this comment

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

Added on the first file here.

import asyncio
import uvicorn

async def app(scope, receive, send):
...

async def main():
config = uvicorn.Config("main:app", host="127.0.0.1", post=5000, log_level="info")
server = uvicorn.Server(config)
await server.serve()

if __name__ == "__main__":
asyncio.run(main())
Kludex marked this conversation as resolved.
Show resolved Hide resolved
```

### Running with Gunicorn
Expand Down