From eaf3d91b0e42b6e791db0d2d84399ea3ae55b381 Mon Sep 17 00:00:00 2001 From: Marcelo Trylesinski Date: Sun, 27 Mar 2022 08:49:09 +0200 Subject: [PATCH 1/6] Annotate `run` function --- uvicorn/main.py | 115 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 1 deletion(-) diff --git a/uvicorn/main.py b/uvicorn/main.py index 0db1216c5..2cb9b8975 100644 --- a/uvicorn/main.py +++ b/uvicorn/main.py @@ -19,6 +19,11 @@ SSL_PROTOCOL_VERSION, WS_PROTOCOLS, Config, + HTTPProtocolType, + InterfaceType, + LifespanType, + LoopSetupType, + WSProtocolType, ) from uvicorn.server import Server, ServerState # noqa: F401 # Used to be defined here. from uvicorn.supervisors import ChangeReload, Multiprocess @@ -233,7 +238,7 @@ def print_version(ctx: click.Context, param: click.Parameter, value: bool) -> No "--forwarded-allow-ips", type=str, default=None, - help="Comma seperated list of IPs to trust with proxy headers. Defaults to" + help="Comma separated list of IPs to trust with proxy headers. Defaults to" " the $FORWARDED_ALLOW_IPS environment variable if available, or '127.0.0.1'.", ) @click.option( @@ -437,6 +442,114 @@ def main( run(app, **kwargs) +@typing.overload +def run( + app: typing.Union[ASGIApplication, str], + host: str, + port: int, + uds: str, + fd: int, + loop: LoopSetupType, + http: HTTPProtocolType, + ws: WSProtocolType, + ws_max_size: int, + ws_ping_interval: float, + ws_ping_timeout: float, + ws_per_message_deflate: bool, + lifespan: LifespanType, + interface: InterfaceType, + debug: bool, + reload: bool, + reload_dirs: typing.List[str], + reload_includes: typing.List[str], + reload_excludes: typing.List[str], + reload_delay: float, + workers: int, + env_file: str, + log_config: str, + log_level: str, + access_log: bool, + proxy_headers: bool, + server_header: bool, + date_header: bool, + forwarded_allow_ips: str, + root_path: str, + limit_concurrency: int, + backlog: int, + limit_max_requests: int, + timeout_keep_alive: int, + ssl_keyfile: str, + ssl_certfile: str, + ssl_keyfile_password: str, + ssl_version: int, + ssl_cert_reqs: int, + ssl_ca_certs: str, + ssl_ciphers: str, + headers: typing.Sequence[typing.Sequence[str]], + use_colors: bool, + app_dir: str, + factory: bool, +) -> None: + """Run uvicorn via Python code. + + Args: + app (typing.Union[ASGIApplication, str]): ASGI application itself or path to it. + Use the latter with `--reload`. + host (str): Hostname to listen on. + port (int): Port to listen on. + uds (str): Path to a Unix domain socket to listen on. + fd (int): File descriptor number to listen on. + loop (Literal["auto", "uvloop", "asyncio"]): Event loop implementation. + http (Literal["auto", "h11", "httptools"]): HTTP implementation. + ws (Literal["auto", "none", "websockets", "wsproto"]): WebSocket + implementation. + ws_max_size (int): Maximum message size. + ws_ping_interval (float): Websocket ping interval. + ws_ping_timeout (float): Websocket ping timeout. + ws_per_message_deflate (bool): Enable per-message deflate. + lifespan (Literal["auto", "on", "off"]): Lifespan of the application. + interface (Literal["auto", "asgi3", "asgi2", "wsgi"]): Application interface. + debug (bool): Turn on debug mode. + reload (bool): Turn on auto reload. + reload_dirs (typing.List[str]): Directories to watch for changes. + reload_includes (typing.List[str]): Filename patterns to include. + reload_excludes (typing.List[str]): Filename patterns to exclude. + reload_delay (float): Delay to watch for changes. + workers (int): Number of workers. + env_file (str): Path to a file to load environment variables from. + log_config (str): Path to a file to load logging configuration from. + log_level (str): Logging level. + access_log (bool): Turn on access log. + proxy_headers (bool): Turn on proxy headers support. + server_header (bool): Turn on server header support. + date_header (bool): Turn on date header support. + forwarded_allow_ips (str): Comma separated list of IPs to trust with proxy + headers. + root_path (str): Set the ASGI 'root_path' for applications submounted below a + given URL path. + limit_concurrency (int): Maximum number of concurrent connections or tasks to + allow, before issuing HTTP 503 responses. + backlog (int): Maximum number of connections to hold in backlog. + limit_max_requests (int): Maximum number of requests to service before + terminating the process. + timeout_keep_alive (int): Timeout for keep-alive connections. + ssl_keyfile (str): Path to a file containing the SSL key. + ssl_certfile (str): Path to a file containing the SSL certificate. + ssl_keyfile_password (str): Password to unlock the SSL key. + ssl_version (int): SSL version to use. + ssl_cert_reqs (int): SSL certificate requirements. + ssl_ca_certs (str): Path to a file containing the SSL certificate authority. + ssl_ciphers (str): SSL ciphers to use. + headers (typing.Sequence[typing.Sequence[str]]): Custom default HTTP response + headers. + use_colors (bool): Turn on colorized logging. + app_dir (str): Look for APP in the specified directory, by adding this to the + PYTHONPATH. + factory (bool): Use the ASGI factory mode. + """ + ... + + def run(app: typing.Union[ASGIApplication, str], **kwargs: typing.Any) -> None: app_dir = kwargs.pop("app_dir", None) if app_dir is not None: From a0ce1e853004c5cc6be2aa3d8ad6791945a8517a Mon Sep 17 00:00:00 2001 From: Marcelo Trylesinski Date: Sun, 27 Mar 2022 09:02:34 +0200 Subject: [PATCH 2/6] Fix mypy issue --- docs/deployment.md | 2 +- docs/index.md | 2 +- uvicorn/main.py | 113 +++++++++++++++++++++++---------------------- 3 files changed, 59 insertions(+), 58 deletions(-) diff --git a/docs/deployment.md b/docs/deployment.md index 2224f98f4..f1895bcfa 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -86,7 +86,7 @@ Options: Enable/Disable default Server header. --date-header / --no-date-header Enable/Disable default Date header. - --forwarded-allow-ips TEXT Comma seperated list of IPs to trust with + --forwarded-allow-ips TEXT Comma separated list of IPs to trust with proxy headers. Defaults to the $FORWARDED_ALLOW_IPS environment variable if available, or '127.0.0.1'. diff --git a/docs/index.md b/docs/index.md index 97726a5ae..2befd121a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -155,7 +155,7 @@ Options: Enable/Disable default Server header. --date-header / --no-date-header Enable/Disable default Date header. - --forwarded-allow-ips TEXT Comma seperated list of IPs to trust with + --forwarded-allow-ips TEXT Comma separated list of IPs to trust with proxy headers. Defaults to the $FORWARDED_ALLOW_IPS environment variable if available, or '127.0.0.1'. diff --git a/uvicorn/main.py b/uvicorn/main.py index 2cb9b8975..aeeae7d93 100644 --- a/uvicorn/main.py +++ b/uvicorn/main.py @@ -352,15 +352,15 @@ def main( port: int, uds: str, fd: int, - loop: str, - http: str, - ws: str, + loop: LoopSetupType, + http: HTTPProtocolType, + ws: WSProtocolType, ws_max_size: int, ws_ping_interval: float, ws_ping_timeout: float, ws_per_message_deflate: bool, - lifespan: str, - interface: str, + lifespan: LifespanType, + interface: InterfaceType, debug: bool, reload: bool, reload_dirs: typing.List[str], @@ -393,58 +393,59 @@ def main( app_dir: str, factory: bool, ) -> None: - kwargs = { - "host": host, - "port": port, - "uds": uds, - "fd": fd, - "loop": loop, - "http": http, - "ws": ws, - "ws_max_size": ws_max_size, - "ws_ping_interval": ws_ping_interval, - "ws_ping_timeout": ws_ping_timeout, - "ws_per_message_deflate": ws_per_message_deflate, - "lifespan": lifespan, - "env_file": env_file, - "log_config": LOGGING_CONFIG if log_config is None else log_config, - "log_level": log_level, - "access_log": access_log, - "interface": interface, - "debug": debug, - "reload": reload, - "reload_dirs": reload_dirs if reload_dirs else None, - "reload_includes": reload_includes if reload_includes else None, - "reload_excludes": reload_excludes if reload_excludes else None, - "reload_delay": reload_delay, - "workers": workers, - "proxy_headers": proxy_headers, - "server_header": server_header, - "date_header": date_header, - "forwarded_allow_ips": forwarded_allow_ips, - "root_path": root_path, - "limit_concurrency": limit_concurrency, - "backlog": backlog, - "limit_max_requests": limit_max_requests, - "timeout_keep_alive": timeout_keep_alive, - "ssl_keyfile": ssl_keyfile, - "ssl_certfile": ssl_certfile, - "ssl_keyfile_password": ssl_keyfile_password, - "ssl_version": ssl_version, - "ssl_cert_reqs": ssl_cert_reqs, - "ssl_ca_certs": ssl_ca_certs, - "ssl_ciphers": ssl_ciphers, - "headers": [header.split(":", 1) for header in headers], - "use_colors": use_colors, - "factory": factory, - "app_dir": app_dir, - } - run(app, **kwargs) + run( + app, + host=host, + port=port, + uds=uds, + fd=fd, + loop=loop, + http=http, + ws=ws, + ws_max_size=ws_max_size, + ws_ping_interval=ws_ping_interval, + ws_ping_timeout=ws_ping_timeout, + ws_per_message_deflate=ws_per_message_deflate, + lifespan=lifespan, + env_file=env_file, + log_config=LOGGING_CONFIG if log_config is None else log_config, + log_level=log_level, + access_log=access_log, + interface=interface, + debug=debug, + reload=reload, + reload_dirs=reload_dirs if reload_dirs else None, + reload_includes=reload_includes if reload_includes else None, + reload_excludes=reload_excludes if reload_excludes else None, + reload_delay=reload_delay, + workers=workers, + proxy_headers=proxy_headers, + server_header=server_header, + date_header=date_header, + forwarded_allow_ips=forwarded_allow_ips, + root_path=root_path, + limit_concurrency=limit_concurrency, + backlog=backlog, + limit_max_requests=limit_max_requests, + timeout_keep_alive=timeout_keep_alive, + ssl_keyfile=ssl_keyfile, + ssl_certfile=ssl_certfile, + ssl_keyfile_password=ssl_keyfile_password, + ssl_version=ssl_version, + ssl_cert_reqs=ssl_cert_reqs, + ssl_ca_certs=ssl_ca_certs, + ssl_ciphers=ssl_ciphers, + headers=[header.split(":", 1) for header in headers], + use_colors=use_colors, + factory=factory, + app_dir=app_dir, + ) -@typing.overload +@typing.overload # type: ignore[misc] def run( app: typing.Union[ASGIApplication, str], + *, host: str, port: int, uds: str, @@ -460,9 +461,9 @@ def run( interface: InterfaceType, debug: bool, reload: bool, - reload_dirs: typing.List[str], - reload_includes: typing.List[str], - reload_excludes: typing.List[str], + reload_dirs: typing.Optional[typing.List[str]], + reload_includes: typing.Optional[typing.List[str]], + reload_excludes: typing.Optional[typing.List[str]], reload_delay: float, workers: int, env_file: str, From cb7f7c3e97a369a24d0e32bccded0a4998af9ced Mon Sep 17 00:00:00 2001 From: Marcelo Trylesinski Date: Mon, 28 Mar 2022 07:32:20 +0200 Subject: [PATCH 3/6] Annotate `run` function (2) --- uvicorn/main.py | 141 ++++++++++++++++++++++++++++++------------------ 1 file changed, 90 insertions(+), 51 deletions(-) diff --git a/uvicorn/main.py b/uvicorn/main.py index aeeae7d93..e8e7701b1 100644 --- a/uvicorn/main.py +++ b/uvicorn/main.py @@ -442,54 +442,53 @@ def main( ) -@typing.overload # type: ignore[misc] def run( app: typing.Union[ASGIApplication, str], *, - host: str, - port: int, - uds: str, - fd: int, - loop: LoopSetupType, - http: HTTPProtocolType, - ws: WSProtocolType, - ws_max_size: int, - ws_ping_interval: float, - ws_ping_timeout: float, - ws_per_message_deflate: bool, - lifespan: LifespanType, - interface: InterfaceType, - debug: bool, - reload: bool, - reload_dirs: typing.Optional[typing.List[str]], - reload_includes: typing.Optional[typing.List[str]], - reload_excludes: typing.Optional[typing.List[str]], - reload_delay: float, - workers: int, - env_file: str, - log_config: str, - log_level: str, - access_log: bool, - proxy_headers: bool, - server_header: bool, - date_header: bool, - forwarded_allow_ips: str, - root_path: str, - limit_concurrency: int, - backlog: int, - limit_max_requests: int, - timeout_keep_alive: int, - ssl_keyfile: str, - ssl_certfile: str, - ssl_keyfile_password: str, - ssl_version: int, - ssl_cert_reqs: int, - ssl_ca_certs: str, - ssl_ciphers: str, - headers: typing.Sequence[typing.Sequence[str]], - use_colors: bool, - app_dir: str, - factory: bool, + 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[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.Sequence[typing.Sequence[str]] = (), + use_colors: typing.Optional[bool] = None, + app_dir: typing.Optional[str] = None, + factory: bool = False, ) -> None: """Run uvicorn via Python code. @@ -548,15 +547,55 @@ def run( PYTHONPATH. factory (bool): Use the ASGI factory mode. """ - ... - - -def run(app: typing.Union[ASGIApplication, str], **kwargs: typing.Any) -> None: - app_dir = kwargs.pop("app_dir", None) if app_dir is not None: sys.path.insert(0, app_dir) - config = Config(app, **kwargs) + config = Config( + app, + host=host, + port=port, + uds=uds, + fd=fd, + loop=loop, + http=http, + ws=ws, + ws_max_size=ws_max_size, + ws_ping_interval=ws_ping_interval, + ws_ping_timeout=ws_ping_timeout, + ws_per_message_deflate=ws_per_message_deflate, + lifespan=lifespan, + interface=interface, + debug=debug, + reload=reload, + reload_dirs=reload_dirs, + reload_includes=reload_includes, + reload_excludes=reload_excludes, + reload_delay=reload_delay, + workers=workers, + env_file=env_file, + log_config=log_config, + log_level=log_level, + access_log=access_log, + proxy_headers=proxy_headers, + server_header=server_header, + date_header=date_header, + forwarded_allow_ips=forwarded_allow_ips, + root_path=root_path, + limit_concurrency=limit_concurrency, + backlog=backlog, + limit_max_requests=limit_max_requests, + timeout_keep_alive=timeout_keep_alive, + ssl_keyfile=ssl_keyfile, + ssl_certfile=ssl_certfile, + ssl_keyfile_password=ssl_keyfile_password, + ssl_version=ssl_version, + ssl_cert_reqs=ssl_cert_reqs, + ssl_ca_certs=ssl_ca_certs, + ssl_ciphers=ssl_ciphers, + headers=headers, + use_colors=use_colors, + factory=factory, + ) server = Server(config=config) if (config.reload or config.workers > 1) and not isinstance(app, str): From a8ebc8f4d9764d7e936738cb3fbe3420683d7893 Mon Sep 17 00:00:00 2001 From: Marcelo Trylesinski Date: Mon, 28 Mar 2022 10:23:06 +0200 Subject: [PATCH 4/6] Remove docstring --- uvicorn/main.py | 59 +------------------------------------------------ 1 file changed, 1 insertion(+), 58 deletions(-) diff --git a/uvicorn/main.py b/uvicorn/main.py index e8e7701b1..94afe627f 100644 --- a/uvicorn/main.py +++ b/uvicorn/main.py @@ -485,68 +485,11 @@ def run( ssl_cert_reqs: int = int(ssl.CERT_NONE), ssl_ca_certs: typing.Optional[str] = None, ssl_ciphers: str = "TLSv1", - headers: typing.Sequence[typing.Sequence[str]] = (), + headers: typing.Optional[typing.List[typing.List[str]]] = None, use_colors: typing.Optional[bool] = None, app_dir: typing.Optional[str] = None, factory: bool = False, ) -> None: - """Run uvicorn via Python code. - - Args: - app (typing.Union[ASGIApplication, str]): ASGI application itself or path to it. - Use the latter with `--reload`. - host (str): Hostname to listen on. - port (int): Port to listen on. - uds (str): Path to a Unix domain socket to listen on. - fd (int): File descriptor number to listen on. - loop (Literal["auto", "uvloop", "asyncio"]): Event loop implementation. - http (Literal["auto", "h11", "httptools"]): HTTP implementation. - ws (Literal["auto", "none", "websockets", "wsproto"]): WebSocket - implementation. - ws_max_size (int): Maximum message size. - ws_ping_interval (float): Websocket ping interval. - ws_ping_timeout (float): Websocket ping timeout. - ws_per_message_deflate (bool): Enable per-message deflate. - lifespan (Literal["auto", "on", "off"]): Lifespan of the application. - interface (Literal["auto", "asgi3", "asgi2", "wsgi"]): Application interface. - debug (bool): Turn on debug mode. - reload (bool): Turn on auto reload. - reload_dirs (typing.List[str]): Directories to watch for changes. - reload_includes (typing.List[str]): Filename patterns to include. - reload_excludes (typing.List[str]): Filename patterns to exclude. - reload_delay (float): Delay to watch for changes. - workers (int): Number of workers. - env_file (str): Path to a file to load environment variables from. - log_config (str): Path to a file to load logging configuration from. - log_level (str): Logging level. - access_log (bool): Turn on access log. - proxy_headers (bool): Turn on proxy headers support. - server_header (bool): Turn on server header support. - date_header (bool): Turn on date header support. - forwarded_allow_ips (str): Comma separated list of IPs to trust with proxy - headers. - root_path (str): Set the ASGI 'root_path' for applications submounted below a - given URL path. - limit_concurrency (int): Maximum number of concurrent connections or tasks to - allow, before issuing HTTP 503 responses. - backlog (int): Maximum number of connections to hold in backlog. - limit_max_requests (int): Maximum number of requests to service before - terminating the process. - timeout_keep_alive (int): Timeout for keep-alive connections. - ssl_keyfile (str): Path to a file containing the SSL key. - ssl_certfile (str): Path to a file containing the SSL certificate. - ssl_keyfile_password (str): Password to unlock the SSL key. - ssl_version (int): SSL version to use. - ssl_cert_reqs (int): SSL certificate requirements. - ssl_ca_certs (str): Path to a file containing the SSL certificate authority. - ssl_ciphers (str): SSL ciphers to use. - headers (typing.Sequence[typing.Sequence[str]]): Custom default HTTP response - headers. - use_colors (bool): Turn on colorized logging. - app_dir (str): Look for APP in the specified directory, by adding this to the - PYTHONPATH. - factory (bool): Use the ASGI factory mode. - """ if app_dir is not None: sys.path.insert(0, app_dir) From f0ae4deebbb480eb8e719fd4207c39fe2fea53b8 Mon Sep 17 00:00:00 2001 From: Marcelo Trylesinski Date: Fri, 6 May 2022 08:28:08 +0200 Subject: [PATCH 5/6] Fix log_config type --- uvicorn/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uvicorn/main.py b/uvicorn/main.py index 94afe627f..f7b1e0de0 100644 --- a/uvicorn/main.py +++ b/uvicorn/main.py @@ -466,7 +466,7 @@ def run( reload_delay: float = 0.25, workers: typing.Optional[int] = None, env_file: typing.Optional[str] = None, - log_config: 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, From 8b9873e71a07e644487329926a79242d4a514afa Mon Sep 17 00:00:00 2001 From: Marcelo Trylesinski Date: Fri, 6 May 2022 17:23:01 +0200 Subject: [PATCH 6/6] Ignore headers type --- uvicorn/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/uvicorn/main.py b/uvicorn/main.py index f7b1e0de0..bfeeeb584 100644 --- a/uvicorn/main.py +++ b/uvicorn/main.py @@ -435,7 +435,7 @@ def main( ssl_cert_reqs=ssl_cert_reqs, ssl_ca_certs=ssl_ca_certs, ssl_ciphers=ssl_ciphers, - headers=[header.split(":", 1) for header in headers], + headers=[header.split(":", 1) for header in headers], # type: ignore[misc] use_colors=use_colors, factory=factory, app_dir=app_dir, @@ -485,7 +485,7 @@ def run( 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.List[str]]] = None, + 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,