codekingpro/portable-devtools
114k
1from __future__ import annotations2 3import asyncio4import logging5import os6import platform7import ssl8import sys9import warnings10from collections.abc import Callable11from configparser import RawConfigParser12from typing import IO, Any, get_args13 14import click15 16import uvicorn17from uvicorn._types import ASGIApplication18from uvicorn.config import (19 INTERFACES,20 LIFESPAN,21 LOG_LEVELS,22 LOGGING_CONFIG,23 SSL_PROTOCOL_VERSION,24 Config,25 HTTPProtocolType,26 InterfaceType,27 LifespanType,28 LoopFactoryType,29 WSProtocolType,30)31from uvicorn.server import Server32from uvicorn.supervisors import ChangeReload, Multiprocess33 34LEVEL_CHOICES = click.Choice(list(LOG_LEVELS.keys()))35LIFESPAN_CHOICES = click.Choice(list(LIFESPAN.keys()))36INTERFACE_CHOICES = click.Choice(INTERFACES)37 38 39def _metavar_from_type(_type: Any) -> str:40 return f"[{'|'.join(key for key in get_args(_type) if key != 'none')}]"41 42 43STARTUP_FAILURE = 344 45logger = logging.getLogger("uvicorn.error")46 47 48def print_version(ctx: click.Context, param: click.Parameter, value: bool) -> None:49 if not value or ctx.resilient_parsing:50 return51 click.echo(52 "Running uvicorn {version} with {py_implementation} {py_version} on {system}".format( # noqa: UP03253 version=uvicorn.__version__,54 py_implementation=platform.python_implementation(),55 py_version=platform.python_version(),56 system=platform.system(),57 )58 )59 ctx.exit()60 61 62@click.command(context_settings={"auto_envvar_prefix": "UVICORN"})63@click.argument("app", envvar="UVICORN_APP")64@click.option(65 "--host",66 type=str,67 default="127.0.0.1",68 help="Bind socket to this host.",69 show_default=True,70)71@click.option(72 "--port",73 type=int,74 default=8000,75 help="Bind socket to this port. If 0, an available port will be picked.",76 show_default=True,77)78@click.option("--uds", type=str, default=None, help="Bind to a UNIX domain socket.")79@click.option("--fd", type=int, default=None, help="Bind to socket from this file descriptor.")80@click.option("--reload", is_flag=True, default=False, help="Enable auto-reload.")81@click.option(82 "--reload-dir",83 "reload_dirs",84 multiple=True,85 help="Set reload directories explicitly, instead of using the current working directory.",86 type=click.Path(exists=True),87)88@click.option(89 "--reload-include",90 "reload_includes",91 multiple=True,92 help="Set glob patterns to include while watching for files. Includes '*.py' "93 "by default; these defaults can be overridden with `--reload-exclude`. "94 "This option has no effect unless watchfiles is installed.",95)96@click.option(97 "--reload-exclude",98 "reload_excludes",99 multiple=True,100 help="Set glob patterns to exclude while watching for files. Includes "101 "'.*, .py[cod], .sw.*, ~*' by default; these defaults can be overridden "102 "with `--reload-include`. This option has no effect unless watchfiles is "103 "installed.",104)105@click.option(106 "--reload-delay",107 type=float,108 default=0.25,109 show_default=True,110 help="Delay between previous and next check if application needs to be. Defaults to 0.25s.",111)112@click.option(113 "--workers",114 default=None,115 type=int,116 help="Number of worker processes. Defaults to the $WEB_CONCURRENCY environment"117 " variable if available, or 1. Not valid with --reload.",118)119@click.option(120 "--loop",121 type=str,122 metavar=_metavar_from_type(LoopFactoryType),123 default="auto",124 help="Event loop factory implementation.",125 show_default=True,126)127@click.option(128 "--http",129 type=str,130 metavar=_metavar_from_type(HTTPProtocolType),131 default="auto",132 help="HTTP protocol implementation.",133 show_default=True,134)135@click.option(136 "--ws",137 type=str,138 metavar=_metavar_from_type(WSProtocolType),139 default="auto",140 help="WebSocket protocol implementation.",141 show_default=True,142)143@click.option(144 "--ws-max-size",145 type=int,146 default=16777216,147 help="WebSocket max size message in bytes",148 show_default=True,149)150@click.option(151 "--ws-max-queue",152 type=int,153 default=32,154 help="The maximum length of the WebSocket message queue.",155 show_default=True,156)157@click.option(158 "--ws-ping-interval",159 type=float,160 default=20.0,161 help="WebSocket ping interval in seconds.",162 show_default=True,163)164@click.option(165 "--ws-ping-timeout",166 type=float,167 default=20.0,168 help="WebSocket ping timeout in seconds.",169 show_default=True,170)171@click.option(172 "--ws-per-message-deflate",173 type=bool,174 default=True,175 help="WebSocket per-message-deflate compression",176 show_default=True,177)178@click.option(179 "--lifespan",180 type=LIFESPAN_CHOICES,181 default="auto",182 help="Lifespan implementation.",183 show_default=True,184)185@click.option(186 "--interface",187 type=INTERFACE_CHOICES,188 default="auto",189 help="Select ASGI3, ASGI2, or WSGI as the application interface.",190 show_default=True,191)192@click.option(193 "--env-file",194 type=click.Path(exists=True),195 default=None,196 help="Environment configuration file.",197 show_default=True,198)199@click.option(200 "--log-config",201 type=click.Path(exists=True),202 default=None,203 help="Logging configuration file. Supported formats: .ini, .json, .yaml.",204 show_default=True,205)206@click.option(207 "--log-level",208 type=LEVEL_CHOICES,209 default=None,210 help="Log level. [default: info]",211 show_default=True,212)213@click.option(214 "--access-log/--no-access-log",215 is_flag=True,216 default=True,217 help="Enable/Disable access log.",218)219@click.option(220 "--use-colors/--no-use-colors",221 is_flag=True,222 default=None,223 help="Enable/Disable colorized logging.",224)225@click.option(226 "--proxy-headers/--no-proxy-headers",227 is_flag=True,228 default=True,229 help="Enable/Disable X-Forwarded-Proto, X-Forwarded-For to populate url scheme and remote address info.",230)231@click.option(232 "--server-header/--no-server-header",233 is_flag=True,234 default=True,235 help="Enable/Disable default Server header.",236)237@click.option(238 "--date-header/--no-date-header",239 is_flag=True,240 default=True,241 help="Enable/Disable default Date header.",242)243@click.option(244 "--forwarded-allow-ips",245 type=str,246 default=None,247 help="Comma separated list of IP Addresses, IP Networks, or literals "248 "(e.g. UNIX Socket path) to trust with proxy headers. Defaults to the "249 "$FORWARDED_ALLOW_IPS environment variable if available, or '127.0.0.1'. "250 "The literal '*' means trust everything.",251)252@click.option(253 "--root-path",254 type=str,255 default="",256 help="Set the ASGI 'root_path' for applications submounted below a given URL path.",257)258@click.option(259 "--limit-concurrency",260 type=int,261 default=None,262 help="Maximum number of concurrent connections or tasks to allow, before issuing HTTP 503 responses.",263)264@click.option(265 "--backlog",266 type=int,267 default=2048,268 help="Maximum number of connections to hold in backlog",269)270@click.option(271 "--limit-max-requests",272 type=int,273 default=None,274 help="Maximum number of requests to service before terminating the process.",275)276@click.option(277 "--limit-max-requests-jitter",278 type=int,279 default=0,280 help="Maximum jitter to add to limit_max_requests."281 " Staggers worker restarts to avoid all workers restarting simultaneously.",282 show_default=True,283)284@click.option(285 "--timeout-keep-alive",286 type=int,287 default=5,288 help="Close Keep-Alive connections if no new data is received within this timeout (in seconds).",289 show_default=True,290)291@click.option(292 "--timeout-graceful-shutdown",293 type=int,294 default=None,295 help="Maximum number of seconds to wait for graceful shutdown.",296)297@click.option(298 "--timeout-worker-healthcheck",299 type=int,300 default=5,301 help="Maximum number of seconds to wait for a worker to respond to a healthcheck.",302 show_default=True,303)304@click.option("--ssl-keyfile", type=str, default=None, help="SSL key file", show_default=True)305@click.option(306 "--ssl-certfile",307 type=str,308 default=None,309 help="SSL certificate file",310 show_default=True,311)312@click.option(313 "--ssl-keyfile-password",314 type=str,315 default=None,316 help="SSL keyfile password",317 show_default=True,318)319@click.option(320 "--ssl-version",321 type=int,322 default=int(SSL_PROTOCOL_VERSION),323 help="SSL version to use (see stdlib ssl module's)",324 show_default=True,325)326@click.option(327 "--ssl-cert-reqs",328 type=int,329 default=int(ssl.CERT_NONE),330 help="Whether client certificate is required (see stdlib ssl module's)",331 show_default=True,332)333@click.option(334 "--ssl-ca-certs",335 type=str,336 default=None,337 help="CA certificates file",338 show_default=True,339)340@click.option(341 "--ssl-ciphers",342 type=str,343 default="TLSv1",344 help="Ciphers to use (see stdlib ssl module's)",345 show_default=True,346)347@click.option(348 "--header",349 "headers",350 multiple=True,351 help="Specify custom default HTTP response headers as a Name:Value pair",352)353@click.option(354 "--version",355 is_flag=True,356 callback=print_version,357 expose_value=False,358 is_eager=True,359 help="Display the uvicorn version and exit.",360)361@click.option(362 "--app-dir",363 default="",364 show_default=True,365 help="Look for APP in the specified directory, by adding this to the PYTHONPATH."366 " Defaults to the current working directory.",367)368@click.option(369 "--h11-max-incomplete-event-size",370 "h11_max_incomplete_event_size",371 type=int,372 default=None,373 help="For h11, the maximum number of bytes to buffer of an incomplete event.",374)375@click.option(376 "--reset-contextvars",377 is_flag=True,378 default=False,379 help="Run each ASGI request in a fresh contextvars.Context. Hides context set in the lifespan.",380 show_default=True,381)382@click.option(383 "--factory",384 is_flag=True,385 default=False,386 help="Treat APP as an application factory, i.e. a () -> <ASGI app> callable.",387 show_default=True,388)389def main(390 app: str,391 host: str,392 port: int,393 uds: str,394 fd: int,395 loop: LoopFactoryType | str,396 http: HTTPProtocolType | str,397 ws: WSProtocolType | str,398 ws_max_size: int,399 ws_max_queue: int,400 ws_ping_interval: float,401 ws_ping_timeout: float,402 ws_per_message_deflate: bool,403 lifespan: LifespanType,404 interface: InterfaceType,405 reload: bool,406 reload_dirs: list[str],407 reload_includes: list[str],408 reload_excludes: list[str],409 reload_delay: float,410 workers: int,411 env_file: str,412 log_config: str,413 log_level: str,414 access_log: bool,415 proxy_headers: bool,416 server_header: bool,417 date_header: bool,418 forwarded_allow_ips: str,419 root_path: str,420 limit_concurrency: int,421 backlog: int,422 limit_max_requests: int,423 limit_max_requests_jitter: int,424 timeout_keep_alive: int,425 timeout_graceful_shutdown: int | None,426 timeout_worker_healthcheck: int,427 ssl_keyfile: str,428 ssl_certfile: str,429 ssl_keyfile_password: str,430 ssl_version: int,431 ssl_cert_reqs: int,432 ssl_ca_certs: str,433 ssl_ciphers: str,434 headers: list[str],435 use_colors: bool,436 app_dir: str,437 h11_max_incomplete_event_size: int | None,438 reset_contextvars: bool,439 factory: bool,440) -> None:441 run(442 app,443 host=host,444 port=port,445 uds=uds,446 fd=fd,447 loop=loop,448 http=http,449 ws=ws,450 ws_max_size=ws_max_size,451 ws_max_queue=ws_max_queue,452 ws_ping_interval=ws_ping_interval,453 ws_ping_timeout=ws_ping_timeout,454 ws_per_message_deflate=ws_per_message_deflate,455 lifespan=lifespan,456 env_file=env_file,457 log_config=LOGGING_CONFIG if log_config is None else log_config,458 log_level=log_level,459 access_log=access_log,460 interface=interface,461 reload=reload,462 reload_dirs=reload_dirs or None,463 reload_includes=reload_includes or None,464 reload_excludes=reload_excludes or None,465 reload_delay=reload_delay,466 workers=workers,467 proxy_headers=proxy_headers,468 server_header=server_header,469 date_header=date_header,470 forwarded_allow_ips=forwarded_allow_ips,471 root_path=root_path,472 limit_concurrency=limit_concurrency,473 backlog=backlog,474 limit_max_requests=limit_max_requests,475 limit_max_requests_jitter=limit_max_requests_jitter,476 timeout_keep_alive=timeout_keep_alive,477 timeout_graceful_shutdown=timeout_graceful_shutdown,478 timeout_worker_healthcheck=timeout_worker_healthcheck,479 ssl_keyfile=ssl_keyfile,480 ssl_certfile=ssl_certfile,481 ssl_keyfile_password=ssl_keyfile_password,482 ssl_version=ssl_version,483 ssl_cert_reqs=ssl_cert_reqs,484 ssl_ca_certs=ssl_ca_certs,485 ssl_ciphers=ssl_ciphers,486 headers=[header.split(":", 1) for header in headers], # type: ignore[misc]487 use_colors=use_colors,488 factory=factory,489 app_dir=app_dir,490 h11_max_incomplete_event_size=h11_max_incomplete_event_size,491 reset_contextvars=reset_contextvars,492 )493 494 495def run(496 app: ASGIApplication | Callable[..., Any] | str,497 *,498 host: str = "127.0.0.1",499 port: int = 8000,500 uds: str | None = None,501 fd: int | None = None,502 loop: LoopFactoryType | str = "auto",503 http: type[asyncio.Protocol] | HTTPProtocolType | str = "auto",504 ws: type[asyncio.Protocol] | WSProtocolType | str = "auto",505 ws_max_size: int = 16777216,506 ws_max_queue: int = 32,507 ws_ping_interval: float | None = 20.0,508 ws_ping_timeout: float | None = 20.0,509 ws_per_message_deflate: bool = True,510 lifespan: LifespanType = "auto",511 interface: InterfaceType = "auto",512 reload: bool = False,513 reload_dirs: list[str] | str | None = None,514 reload_includes: list[str] | str | None = None,515 reload_excludes: list[str] | str | None = None,516 reload_delay: float = 0.25,517 workers: int | None = None,518 env_file: str | os.PathLike[str] | None = None,519 log_config: dict[str, Any] | str | os.PathLike[str] | RawConfigParser | IO[Any] | None = LOGGING_CONFIG,520 log_level: str | int | None = None,521 access_log: bool = True,522 proxy_headers: bool = True,523 server_header: bool = True,524 date_header: bool = True,525 forwarded_allow_ips: list[str] | str | None = None,526 root_path: str = "",527 limit_concurrency: int | None = None,528 backlog: int = 2048,529 limit_max_requests: int | None = None,530 limit_max_requests_jitter: int = 0,531 timeout_keep_alive: int = 5,532 timeout_graceful_shutdown: int | None = None,533 timeout_worker_healthcheck: int = 5,534 ssl_keyfile: str | os.PathLike[str] | None = None,535 ssl_certfile: str | os.PathLike[str] | None = None,536 ssl_keyfile_password: str | None = None,537 ssl_version: int = SSL_PROTOCOL_VERSION,538 ssl_cert_reqs: int = ssl.CERT_NONE,539 ssl_ca_certs: str | os.PathLike[str] | None = None,540 ssl_ciphers: str = "TLSv1",541 headers: list[tuple[str, str]] | None = None,542 use_colors: bool | None = None,543 app_dir: str | None = None,544 factory: bool = False,545 h11_max_incomplete_event_size: int | None = None,546 reset_contextvars: bool = False,547) -> None:548 if app_dir is not None:549 sys.path.insert(0, app_dir)550 551 config = Config(552 app,553 host=host,554 port=port,555 uds=uds,556 fd=fd,557 loop=loop,558 http=http,559 ws=ws,560 ws_max_size=ws_max_size,561 ws_max_queue=ws_max_queue,562 ws_ping_interval=ws_ping_interval,563 ws_ping_timeout=ws_ping_timeout,564 ws_per_message_deflate=ws_per_message_deflate,565 lifespan=lifespan,566 interface=interface,567 reload=reload,568 reload_dirs=reload_dirs,569 reload_includes=reload_includes,570 reload_excludes=reload_excludes,571 reload_delay=reload_delay,572 workers=workers,573 env_file=env_file,574 log_config=log_config,575 log_level=log_level,576 access_log=access_log,577 proxy_headers=proxy_headers,578 server_header=server_header,579 date_header=date_header,580 forwarded_allow_ips=forwarded_allow_ips,581 root_path=root_path,582 limit_concurrency=limit_concurrency,583 backlog=backlog,584 limit_max_requests=limit_max_requests,585 limit_max_requests_jitter=limit_max_requests_jitter,586 timeout_keep_alive=timeout_keep_alive,587 timeout_graceful_shutdown=timeout_graceful_shutdown,588 timeout_worker_healthcheck=timeout_worker_healthcheck,589 ssl_keyfile=ssl_keyfile,590 ssl_certfile=ssl_certfile,591 ssl_keyfile_password=ssl_keyfile_password,592 ssl_version=ssl_version,593 ssl_cert_reqs=ssl_cert_reqs,594 ssl_ca_certs=ssl_ca_certs,595 ssl_ciphers=ssl_ciphers,596 headers=headers,597 use_colors=use_colors,598 factory=factory,599 h11_max_incomplete_event_size=h11_max_incomplete_event_size,600 reset_contextvars=reset_contextvars,601 )602 server = Server(config=config)603 604 if (config.reload or config.workers > 1) and not isinstance(app, str):605 logger = logging.getLogger("uvicorn.error")606 logger.warning("You must pass the application as an import string to enable 'reload' or 'workers'.")607 sys.exit(1)608 609 try:610 if config.should_reload:611 sock = config.bind_socket()612 ChangeReload(config, target=server.run, sockets=[sock]).run()613 elif config.workers > 1:614 sock = config.bind_socket()615 Multiprocess(config, target=server.run, sockets=[sock]).run()616 else:617 server.run()618 except KeyboardInterrupt:619 pass # pragma: full coverage620 finally:621 if config.uds and os.path.exists(config.uds):622 os.remove(config.uds) # pragma: py-win32623 624 if not server.started and not config.should_reload and config.workers == 1:625 sys.exit(STARTUP_FAILURE)626 627 628def __getattr__(name: str) -> Any:629 if name == "ServerState":630 warnings.warn(631 "uvicorn.main.ServerState is deprecated, use uvicorn.server.ServerState instead.",632 DeprecationWarning,633 )634 from uvicorn.server import ServerState635 636 return ServerState637 raise AttributeError(f"module {__name__} has no attribute {name}")638 639 640if __name__ == "__main__":641 main() # pragma: no cover642 