From 0219897f4b5133aebf6ea512c47bdfa72624d7a5 Mon Sep 17 00:00:00 2001 From: euri10 Date: Wed, 20 Apr 2022 15:02:02 +0200 Subject: [PATCH 1/8] Fix OSError: [WinError 87] The parameter is incorrect --- uvicorn/config.py | 2 +- uvicorn/loops/asyncio.py | 16 +++++++++++++--- uvicorn/loops/auto.py | 9 ++++++--- uvicorn/loops/uvloop.py | 3 ++- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/uvicorn/config.py b/uvicorn/config.py index c96f16691..6336efdcf 100644 --- a/uvicorn/config.py +++ b/uvicorn/config.py @@ -503,7 +503,7 @@ def load(self) -> None: def setup_event_loop(self) -> None: loop_setup: Optional[Callable] = import_from_string(LOOP_SETUPS[self.loop]) if loop_setup is not None: - loop_setup(reload=self.reload) + loop_setup(reload=self.reload, workers=self.workers) def bind_socket(self) -> socket.socket: logger_args: List[Union[str, int]] diff --git a/uvicorn/loops/asyncio.py b/uvicorn/loops/asyncio.py index 9a9ed6495..6c04acbfa 100644 --- a/uvicorn/loops/asyncio.py +++ b/uvicorn/loops/asyncio.py @@ -1,11 +1,21 @@ import asyncio import logging import sys +from typing import Optional logger = logging.getLogger("uvicorn.error") -def asyncio_setup(reload: bool = False) -> None: # pragma: no cover - if sys.version_info >= (3, 8) and sys.platform == "win32" and reload: - logger.warning("The --reload flag should not be used in production on Windows.") +def asyncio_setup( + reload: bool = False, workers: Optional[int] = None +) -> None: # pragma: no cover + if ( + sys.version_info >= (3, 8) + and sys.platform == "win32" + and any([reload, workers]) + ): + if reload: + logger.warning( + "The --reload flag should not be used in production on Windows." + ) asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) diff --git a/uvicorn/loops/auto.py b/uvicorn/loops/auto.py index ff272c112..f4a94bd49 100644 --- a/uvicorn/loops/auto.py +++ b/uvicorn/loops/auto.py @@ -1,11 +1,14 @@ -def auto_loop_setup(reload: bool = False) -> None: +from typing import Optional + + +def auto_loop_setup(reload: bool = False, workers: Optional[int] = None) -> None: try: import uvloop # noqa except ImportError: # pragma: no cover from uvicorn.loops.asyncio import asyncio_setup as loop_setup - loop_setup(reload=reload) + loop_setup(reload=reload, workers=workers) else: # pragma: no cover from uvicorn.loops.uvloop import uvloop_setup - uvloop_setup(reload=reload) + uvloop_setup(reload=reload, workers=workers) diff --git a/uvicorn/loops/uvloop.py b/uvicorn/loops/uvloop.py index 119c9f28a..9f081d5c0 100644 --- a/uvicorn/loops/uvloop.py +++ b/uvicorn/loops/uvloop.py @@ -1,7 +1,8 @@ import asyncio +from typing import Optional import uvloop -def uvloop_setup(reload: bool = False) -> None: +def uvloop_setup(reload: bool = False, workers: Optional[int] = None) -> None: asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) From 5329864d6d160dc0a006d792f9c27cc97489a0e6 Mon Sep 17 00:00:00 2001 From: euri10 Date: Mon, 13 Jun 2022 08:39:51 +0200 Subject: [PATCH 2/8] Added use_subprocess to be more generic --- tests/test_config.py | 15 +++++++++++++++ uvicorn/config.py | 6 +++++- uvicorn/loops/asyncio.py | 16 +++------------- uvicorn/loops/auto.py | 9 +++------ uvicorn/loops/uvloop.py | 3 +-- 5 files changed, 27 insertions(+), 22 deletions(-) diff --git a/tests/test_config.py b/tests/test_config.py index 39fd74742..fb3a5df40 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -544,3 +544,18 @@ def test_bind_fd_works_with_reload_or_workers(reload, workers): # pragma: py-wi assert sock.getsockname() == "" sock.close() fdsock.close() + + +@pytest.mark.parametrize( + "reload, workers, expected", + [ + (True, 1, True), + (False, 2, True), + (False, 1, False), + ], + ids=["--reload=True --workers=1", "--reload=False --workers=2", "--reload=False --workers=1"], +) +def test_config_use_subprocess(reload, workers, expected): + config = Config(app=asgi_app, reload=reload, workers=workers) + config.load() + assert config.use_subprocess == expected diff --git a/uvicorn/config.py b/uvicorn/config.py index 6336efdcf..9fad8f9a1 100644 --- a/uvicorn/config.py +++ b/uvicorn/config.py @@ -373,6 +373,10 @@ def asgi_version(self) -> Literal["2.0", "3.0"]: def is_ssl(self) -> bool: return bool(self.ssl_keyfile or self.ssl_certfile) + @property + def use_subprocess(self) -> bool: + return bool(self.reload or self.workers > 1) + def configure_logging(self) -> None: logging.addLevelName(TRACE_LOG_LEVEL, "TRACE") @@ -503,7 +507,7 @@ def load(self) -> None: def setup_event_loop(self) -> None: loop_setup: Optional[Callable] = import_from_string(LOOP_SETUPS[self.loop]) if loop_setup is not None: - loop_setup(reload=self.reload, workers=self.workers) + loop_setup(use_subprocess=self.use_subprocess) def bind_socket(self) -> socket.socket: logger_args: List[Union[str, int]] diff --git a/uvicorn/loops/asyncio.py b/uvicorn/loops/asyncio.py index 6c04acbfa..d027686ef 100644 --- a/uvicorn/loops/asyncio.py +++ b/uvicorn/loops/asyncio.py @@ -1,21 +1,11 @@ import asyncio import logging import sys -from typing import Optional logger = logging.getLogger("uvicorn.error") -def asyncio_setup( - reload: bool = False, workers: Optional[int] = None -) -> None: # pragma: no cover - if ( - sys.version_info >= (3, 8) - and sys.platform == "win32" - and any([reload, workers]) - ): - if reload: - logger.warning( - "The --reload flag should not be used in production on Windows." - ) +def asyncio_setup(use_subprocess: bool = False) -> None: # pragma: no cover + if sys.version_info >= (3, 8) and sys.platform == "win32" and use_subprocess: + logger.warning("The --reload flag should not be used in production on Windows.") asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) diff --git a/uvicorn/loops/auto.py b/uvicorn/loops/auto.py index f4a94bd49..2285457bf 100644 --- a/uvicorn/loops/auto.py +++ b/uvicorn/loops/auto.py @@ -1,14 +1,11 @@ -from typing import Optional - - -def auto_loop_setup(reload: bool = False, workers: Optional[int] = None) -> None: +def auto_loop_setup(use_subprocess: bool = False) -> None: try: import uvloop # noqa except ImportError: # pragma: no cover from uvicorn.loops.asyncio import asyncio_setup as loop_setup - loop_setup(reload=reload, workers=workers) + loop_setup(use_subprocess=use_subprocess) else: # pragma: no cover from uvicorn.loops.uvloop import uvloop_setup - uvloop_setup(reload=reload, workers=workers) + uvloop_setup(use_subprocess=use_subprocess) diff --git a/uvicorn/loops/uvloop.py b/uvicorn/loops/uvloop.py index 9f081d5c0..0e2fd1eb0 100644 --- a/uvicorn/loops/uvloop.py +++ b/uvicorn/loops/uvloop.py @@ -1,8 +1,7 @@ import asyncio -from typing import Optional import uvloop -def uvloop_setup(reload: bool = False, workers: Optional[int] = None) -> None: +def uvloop_setup(use_subprocess: bool = False) -> None: asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) From 0d36ebe2dad641e415ca5cb4d86818c08cd4d8b4 Mon Sep 17 00:00:00 2001 From: euri10 Date: Mon, 13 Jun 2022 08:43:20 +0200 Subject: [PATCH 3/8] Black --- tests/test_config.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test_config.py b/tests/test_config.py index fb3a5df40..a2215aa5a 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -553,7 +553,11 @@ def test_bind_fd_works_with_reload_or_workers(reload, workers): # pragma: py-wi (False, 2, True), (False, 1, False), ], - ids=["--reload=True --workers=1", "--reload=False --workers=2", "--reload=False --workers=1"], + ids=[ + "--reload=True --workers=1", + "--reload=False --workers=2", + "--reload=False --workers=1", + ], ) def test_config_use_subprocess(reload, workers, expected): config = Config(app=asgi_app, reload=reload, workers=workers) From b0ac2edd3ea7cb5cbf8afe90a2de033adc7abab0 Mon Sep 17 00:00:00 2001 From: euri10 Date: Tue, 21 Jun 2022 18:15:11 +0200 Subject: [PATCH 4/8] Revert "Added use_subprocess to be more generic" This reverts commit d1b5f838 --- uvicorn/config.py | 14 +++++--------- uvicorn/loops/asyncio.py | 16 +++++++++++++--- uvicorn/loops/auto.py | 9 ++++++--- uvicorn/loops/uvloop.py | 3 ++- 4 files changed, 26 insertions(+), 16 deletions(-) diff --git a/uvicorn/config.py b/uvicorn/config.py index 9fad8f9a1..d5c00e77f 100644 --- a/uvicorn/config.py +++ b/uvicorn/config.py @@ -125,7 +125,7 @@ def create_ssl_context( ctx = ssl.SSLContext(ssl_version) get_password = (lambda: password) if password else None ctx.load_cert_chain(certfile, keyfile, get_password) - ctx.verify_mode = ssl.VerifyMode(cert_reqs) + ctx.verify_mode = cert_reqs if ca_certs: ctx.load_verify_locations(ca_certs) if ciphers: @@ -174,7 +174,7 @@ def resolve_reload_patterns( for j in range(len(directories)): for k in range(j + 1, len(directories)): if directories[j] in directories[k].parents: - children.append(directories[k]) # pragma: py-darwin + children.append(directories[k]) elif directories[k] in directories[j].parents: children.append(directories[j]) @@ -238,7 +238,7 @@ def __init__( ssl_cert_reqs: int = ssl.CERT_NONE, ssl_ca_certs: Optional[str] = None, ssl_ciphers: str = "TLSv1", - headers: Optional[List[Tuple[str, str]]] = None, + headers: Optional[List[List[str]]] = None, factory: bool = False, ): self.app = app @@ -280,7 +280,7 @@ def __init__( self.ssl_cert_reqs = ssl_cert_reqs self.ssl_ca_certs = ssl_ca_certs self.ssl_ciphers = ssl_ciphers - self.headers: List[Tuple[str, str]] = headers or [] + self.headers: List[List[str]] = headers or [] self.encoded_headers: List[Tuple[bytes, bytes]] = [] self.factory = factory @@ -373,10 +373,6 @@ def asgi_version(self) -> Literal["2.0", "3.0"]: def is_ssl(self) -> bool: return bool(self.ssl_keyfile or self.ssl_certfile) - @property - def use_subprocess(self) -> bool: - return bool(self.reload or self.workers > 1) - def configure_logging(self) -> None: logging.addLevelName(TRACE_LOG_LEVEL, "TRACE") @@ -507,7 +503,7 @@ def load(self) -> None: def setup_event_loop(self) -> None: loop_setup: Optional[Callable] = import_from_string(LOOP_SETUPS[self.loop]) if loop_setup is not None: - loop_setup(use_subprocess=self.use_subprocess) + loop_setup(reload=self.reload, workers=self.workers) def bind_socket(self) -> socket.socket: logger_args: List[Union[str, int]] diff --git a/uvicorn/loops/asyncio.py b/uvicorn/loops/asyncio.py index d027686ef..6c04acbfa 100644 --- a/uvicorn/loops/asyncio.py +++ b/uvicorn/loops/asyncio.py @@ -1,11 +1,21 @@ import asyncio import logging import sys +from typing import Optional logger = logging.getLogger("uvicorn.error") -def asyncio_setup(use_subprocess: bool = False) -> None: # pragma: no cover - if sys.version_info >= (3, 8) and sys.platform == "win32" and use_subprocess: - logger.warning("The --reload flag should not be used in production on Windows.") +def asyncio_setup( + reload: bool = False, workers: Optional[int] = None +) -> None: # pragma: no cover + if ( + sys.version_info >= (3, 8) + and sys.platform == "win32" + and any([reload, workers]) + ): + if reload: + logger.warning( + "The --reload flag should not be used in production on Windows." + ) asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) diff --git a/uvicorn/loops/auto.py b/uvicorn/loops/auto.py index 2285457bf..f4a94bd49 100644 --- a/uvicorn/loops/auto.py +++ b/uvicorn/loops/auto.py @@ -1,11 +1,14 @@ -def auto_loop_setup(use_subprocess: bool = False) -> None: +from typing import Optional + + +def auto_loop_setup(reload: bool = False, workers: Optional[int] = None) -> None: try: import uvloop # noqa except ImportError: # pragma: no cover from uvicorn.loops.asyncio import asyncio_setup as loop_setup - loop_setup(use_subprocess=use_subprocess) + loop_setup(reload=reload, workers=workers) else: # pragma: no cover from uvicorn.loops.uvloop import uvloop_setup - uvloop_setup(use_subprocess=use_subprocess) + uvloop_setup(reload=reload, workers=workers) diff --git a/uvicorn/loops/uvloop.py b/uvicorn/loops/uvloop.py index 0e2fd1eb0..9f081d5c0 100644 --- a/uvicorn/loops/uvloop.py +++ b/uvicorn/loops/uvloop.py @@ -1,7 +1,8 @@ import asyncio +from typing import Optional import uvloop -def uvloop_setup(use_subprocess: bool = False) -> None: +def uvloop_setup(reload: bool = False, workers: Optional[int] = None) -> None: asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) From 15877c29f8798a5be7594a51d4b43e9b3ef23c71 Mon Sep 17 00:00:00 2001 From: euri10 Date: Tue, 21 Jun 2022 18:15:46 +0200 Subject: [PATCH 5/8] Removed reload warning --- uvicorn/loops/asyncio.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/uvicorn/loops/asyncio.py b/uvicorn/loops/asyncio.py index 6c04acbfa..85ba449c4 100644 --- a/uvicorn/loops/asyncio.py +++ b/uvicorn/loops/asyncio.py @@ -14,8 +14,4 @@ def asyncio_setup( and sys.platform == "win32" and any([reload, workers]) ): - if reload: - logger.warning( - "The --reload flag should not be used in production on Windows." - ) asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) From 7c03ef525775f0c221fd43fcdcddee94682f7a5d Mon Sep 17 00:00:00 2001 From: euri10 Date: Tue, 21 Jun 2022 18:22:12 +0200 Subject: [PATCH 6/8] Min diff, not sure where this came from --- uvicorn/config.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uvicorn/config.py b/uvicorn/config.py index d5c00e77f..7760208ce 100644 --- a/uvicorn/config.py +++ b/uvicorn/config.py @@ -125,7 +125,7 @@ def create_ssl_context( ctx = ssl.SSLContext(ssl_version) get_password = (lambda: password) if password else None ctx.load_cert_chain(certfile, keyfile, get_password) - ctx.verify_mode = cert_reqs + ctx.verify_mode = ssl.VerifyMode(cert_reqs) if ca_certs: ctx.load_verify_locations(ca_certs) if ciphers: @@ -174,7 +174,7 @@ def resolve_reload_patterns( for j in range(len(directories)): for k in range(j + 1, len(directories)): if directories[j] in directories[k].parents: - children.append(directories[k]) + children.append(directories[k]) # pragma: py-darwin elif directories[k] in directories[j].parents: children.append(directories[j]) @@ -238,7 +238,7 @@ def __init__( ssl_cert_reqs: int = ssl.CERT_NONE, ssl_ca_certs: Optional[str] = None, ssl_ciphers: str = "TLSv1", - headers: Optional[List[List[str]]] = None, + headers: Optional[List[Tuple[str, str]]] = None, factory: bool = False, ): self.app = app From a13821bfd9b3b414aadff6dd2dba213868d063fb Mon Sep 17 00:00:00 2001 From: euri10 Date: Tue, 21 Jun 2022 18:23:00 +0200 Subject: [PATCH 7/8] Min diff 2 --- uvicorn/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uvicorn/config.py b/uvicorn/config.py index 7760208ce..6336efdcf 100644 --- a/uvicorn/config.py +++ b/uvicorn/config.py @@ -280,7 +280,7 @@ def __init__( self.ssl_cert_reqs = ssl_cert_reqs self.ssl_ca_certs = ssl_ca_certs self.ssl_ciphers = ssl_ciphers - self.headers: List[List[str]] = headers or [] + self.headers: List[Tuple[str, str]] = headers or [] self.encoded_headers: List[Tuple[bytes, bytes]] = [] self.factory = factory From baec732d7e31e78a7101a248d21aa919fad15e41 Mon Sep 17 00:00:00 2001 From: euri10 Date: Wed, 22 Jun 2022 09:36:05 +0200 Subject: [PATCH 8/8] Redo use_subprocess --- uvicorn/config.py | 6 +++++- uvicorn/loops/asyncio.py | 11 ++--------- uvicorn/loops/auto.py | 9 +++------ uvicorn/loops/uvloop.py | 3 +-- 4 files changed, 11 insertions(+), 18 deletions(-) diff --git a/uvicorn/config.py b/uvicorn/config.py index 6336efdcf..9fad8f9a1 100644 --- a/uvicorn/config.py +++ b/uvicorn/config.py @@ -373,6 +373,10 @@ def asgi_version(self) -> Literal["2.0", "3.0"]: def is_ssl(self) -> bool: return bool(self.ssl_keyfile or self.ssl_certfile) + @property + def use_subprocess(self) -> bool: + return bool(self.reload or self.workers > 1) + def configure_logging(self) -> None: logging.addLevelName(TRACE_LOG_LEVEL, "TRACE") @@ -503,7 +507,7 @@ def load(self) -> None: def setup_event_loop(self) -> None: loop_setup: Optional[Callable] = import_from_string(LOOP_SETUPS[self.loop]) if loop_setup is not None: - loop_setup(reload=self.reload, workers=self.workers) + loop_setup(use_subprocess=self.use_subprocess) def bind_socket(self) -> socket.socket: logger_args: List[Union[str, int]] diff --git a/uvicorn/loops/asyncio.py b/uvicorn/loops/asyncio.py index 85ba449c4..867545f2a 100644 --- a/uvicorn/loops/asyncio.py +++ b/uvicorn/loops/asyncio.py @@ -1,17 +1,10 @@ import asyncio import logging import sys -from typing import Optional logger = logging.getLogger("uvicorn.error") -def asyncio_setup( - reload: bool = False, workers: Optional[int] = None -) -> None: # pragma: no cover - if ( - sys.version_info >= (3, 8) - and sys.platform == "win32" - and any([reload, workers]) - ): +def asyncio_setup(use_subprocess: bool = False) -> None: # pragma: no cover + if sys.version_info >= (3, 8) and sys.platform == "win32" and use_subprocess: asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) diff --git a/uvicorn/loops/auto.py b/uvicorn/loops/auto.py index f4a94bd49..2285457bf 100644 --- a/uvicorn/loops/auto.py +++ b/uvicorn/loops/auto.py @@ -1,14 +1,11 @@ -from typing import Optional - - -def auto_loop_setup(reload: bool = False, workers: Optional[int] = None) -> None: +def auto_loop_setup(use_subprocess: bool = False) -> None: try: import uvloop # noqa except ImportError: # pragma: no cover from uvicorn.loops.asyncio import asyncio_setup as loop_setup - loop_setup(reload=reload, workers=workers) + loop_setup(use_subprocess=use_subprocess) else: # pragma: no cover from uvicorn.loops.uvloop import uvloop_setup - uvloop_setup(reload=reload, workers=workers) + uvloop_setup(use_subprocess=use_subprocess) diff --git a/uvicorn/loops/uvloop.py b/uvicorn/loops/uvloop.py index 9f081d5c0..0e2fd1eb0 100644 --- a/uvicorn/loops/uvloop.py +++ b/uvicorn/loops/uvloop.py @@ -1,8 +1,7 @@ import asyncio -from typing import Optional import uvloop -def uvloop_setup(reload: bool = False, workers: Optional[int] = None) -> None: +def uvloop_setup(use_subprocess: bool = False) -> None: asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())