From 889bc7a5edfa9f6ce17ae563f146c72d50d7bbba Mon Sep 17 00:00:00 2001 From: Gabriel Gore Date: Fri, 26 Apr 2024 09:18:14 -0400 Subject: [PATCH 01/19] mypy config --- pyproject.toml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index e8f58b6..fbfba9d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -55,6 +55,7 @@ test = [ "twine", "fastapi", "wait-for-it", + "mypy", ] speedups = [ "xxhash;platform_python_implementation=='CPython'", @@ -81,3 +82,20 @@ include = [ exclude = [ ".*", ] + +[tool.mypy] +python_version = "3.8" +files = ["mocket/", "tests/"] +strict = true +warn_unused_configs = true +ignore_missing_imports = true +warn_redundant_casts = true +warn_unused_ignores = true +show_error_codes = true +implicit_reexport = false +disallow_any_generics = true +enable_error_code = ['ignore-without-code', 'explicit-override'] + +[[tool.mypy.overrides]] +module = "tests.*" +disable_error_code = ['type-arg', 'no-untyped-def'] From 57b529eb77f1ba030ece79485f9f3c677ebe78c3 Mon Sep 17 00:00:00 2001 From: Gabriel Gore Date: Fri, 26 Apr 2024 09:29:09 -0400 Subject: [PATCH 02/19] narrow initial type-checking --- pyproject.toml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index fbfba9d..15b1436 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -85,7 +85,10 @@ exclude = [ [tool.mypy] python_version = "3.8" -files = ["mocket/", "tests/"] +files = [ + "mocket/utils.py", + # "tests/" + ] strict = true warn_unused_configs = true ignore_missing_imports = true @@ -94,6 +97,7 @@ warn_unused_ignores = true show_error_codes = true implicit_reexport = false disallow_any_generics = true +follow_imports = "silent" # enable this once majority is typed enable_error_code = ['ignore-without-code', 'explicit-override'] [[tool.mypy.overrides]] From 208b61db8f0bf0a1d010d488e5fb4d8e3551ddb2 Mon Sep 17 00:00:00 2001 From: Gabriel Gore Date: Fri, 26 Apr 2024 09:48:05 -0400 Subject: [PATCH 03/19] add type stubs --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 15b1436..3f60d4b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,6 +56,7 @@ test = [ "fastapi", "wait-for-it", "mypy", + "types-decorator", ] speedups = [ "xxhash;platform_python_implementation=='CPython'", From 8339e8cb746a1a7b47842513a7df51a93177d53d Mon Sep 17 00:00:00 2001 From: Gabriel Gore Date: Fri, 26 Apr 2024 09:48:24 -0400 Subject: [PATCH 04/19] typing issues part 1 --- mocket/utils.py | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/mocket/utils.py b/mocket/utils.py index 2f17838..352cdf1 100644 --- a/mocket/utils.py +++ b/mocket/utils.py @@ -1,17 +1,26 @@ +from __future__ import annotations + import binascii import io import os import ssl -from typing import Tuple, Union +from typing import Tuple, Union, Callable, TYPE_CHECKING, TypeVar, Any, ClassVar +from typing_extensions import override from .compat import decode_from_bytes, encode_to_bytes from .exceptions import StrictMocketException +if TYPE_CHECKING: + from _typeshed import ReadableBuffer + from typing_extensions import NoReturn + SSL_PROTOCOL = ssl.PROTOCOL_TLSv1_2 +T = TypeVar("T") class MocketSocketCore(io.BytesIO): - def write(self, content): + @override + def write(self, content: ReadableBuffer) -> None: # type: ignore[override] # BytesIO returns int super(MocketSocketCore, self).write(content) from mocket import Mocket @@ -20,7 +29,7 @@ def write(self, content): os.write(Mocket.w_fd, content) -def hexdump(binary_string): +def hexdump(binary_string: bytes) -> str: r""" >>> hexdump(b"bar foobar foo") == decode_from_bytes(encode_to_bytes("62 61 72 20 66 6F 6F 62 61 72 20 66 6F 6F")) True @@ -29,7 +38,7 @@ def hexdump(binary_string): return " ".join(a + b for a, b in zip(bs[::2], bs[1::2])) -def hexload(string): +def hexload(string: str) -> bytes: r""" >>> hexload("62 61 72 20 66 6F 6F 62 61 72 20 66 6F 6F") == encode_to_bytes("bar foobar foo") True @@ -38,20 +47,20 @@ def hexload(string): return encode_to_bytes(binascii.unhexlify(string_no_spaces)) -def get_mocketize(wrapper_): +def get_mocketize(wrapper_: T) -> T: import decorator - if decorator.__version__ < "5": # pragma: no cover + if decorator.__version__ < "5": # type: ignore[attr-defined] # pragma: no cover return decorator.decorator(wrapper_) return decorator.decorator(wrapper_, kwsyntax=True) class MocketMode: - __shared_state = {} - STRICT = None - STRICT_ALLOWED = None + __shared_state: ClassVar[dict[str, Any]] = {} + STRICT: ClassVar = None + STRICT_ALLOWED: ClassVar = None - def __init__(self): + def __init__(self) -> None: self.__dict__ = self.__shared_state def is_allowed(self, location: Union[str, Tuple[str, int]]) -> bool: @@ -65,7 +74,7 @@ def is_allowed(self, location: Union[str, Tuple[str, int]]) -> bool: return location in self.STRICT_ALLOWED or host in self.STRICT_ALLOWED @staticmethod - def raise_not_allowed(): + def raise_not_allowed() -> NoReturn: from .mocket import Mocket current_entries = [ From cd6402da9c8e1180c475a9e605de9993a8d5a6cf Mon Sep 17 00:00:00 2001 From: Gabriel Gore Date: Fri, 26 Apr 2024 09:54:18 -0400 Subject: [PATCH 05/19] allow any generics --- mocket/utils.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mocket/utils.py b/mocket/utils.py index 352cdf1..900dc3e 100644 --- a/mocket/utils.py +++ b/mocket/utils.py @@ -47,7 +47,7 @@ def hexload(string: str) -> bytes: return encode_to_bytes(binascii.unhexlify(string_no_spaces)) -def get_mocketize(wrapper_: T) -> T: +def get_mocketize(wrapper_: Callable) -> Callable: import decorator if decorator.__version__ < "5": # type: ignore[attr-defined] # pragma: no cover diff --git a/pyproject.toml b/pyproject.toml index 3f60d4b..42800da 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -97,7 +97,7 @@ warn_redundant_casts = true warn_unused_ignores = true show_error_codes = true implicit_reexport = false -disallow_any_generics = true +disallow_any_generics = false follow_imports = "silent" # enable this once majority is typed enable_error_code = ['ignore-without-code', 'explicit-override'] From 1271f0345528e9726fc9ae548376a652532407ff Mon Sep 17 00:00:00 2001 From: Gabriel Gore Date: Fri, 26 Apr 2024 10:05:25 -0400 Subject: [PATCH 06/19] compat typing --- mocket/compat.py | 11 +++++++---- pyproject.toml | 2 ++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/mocket/compat.py b/mocket/compat.py index be72767..eb30282 100644 --- a/mocket/compat.py +++ b/mocket/compat.py @@ -1,27 +1,30 @@ +from __future__ import annotations + import codecs import os import shlex +from typing import Final, Any -ENCODING = os.getenv("MOCKET_ENCODING", "utf-8") +ENCODING: Final[str] = os.getenv("MOCKET_ENCODING", "utf-8") text_type = str byte_type = bytes basestring = (str,) -def encode_to_bytes(s, encoding=ENCODING): +def encode_to_bytes(s: str | bytes, encoding: str = ENCODING) -> bytes: if isinstance(s, text_type): s = s.encode(encoding) return byte_type(s) -def decode_from_bytes(s, encoding=ENCODING): +def decode_from_bytes(s: str | bytes, encoding: str = ENCODING) -> str: if isinstance(s, byte_type): s = codecs.decode(s, encoding, "ignore") return text_type(s) -def shsplit(s): +def shsplit(s: str | bytes) -> list[str]: s = decode_from_bytes(s) return shlex.split(s) diff --git a/pyproject.toml b/pyproject.toml index 42800da..5cf3a58 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -87,6 +87,7 @@ exclude = [ [tool.mypy] python_version = "3.8" files = [ + "mocket/compat.py", "mocket/utils.py", # "tests/" ] @@ -100,6 +101,7 @@ implicit_reexport = false disallow_any_generics = false follow_imports = "silent" # enable this once majority is typed enable_error_code = ['ignore-without-code', 'explicit-override'] +disable_error_code = ["no-untyped-def"] # enable this once full type-coverage is reached [[tool.mypy.overrides]] module = "tests.*" From 135beac022bc9afa224bd6410be0fd8382e0d35b Mon Sep 17 00:00:00 2001 From: Gabriel Gore Date: Fri, 26 Apr 2024 10:09:44 -0400 Subject: [PATCH 07/19] make types --- Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Makefile b/Makefile index 4b727ae..2fc7831 100644 --- a/Makefile +++ b/Makefile @@ -22,6 +22,11 @@ lint-python: flake8 --ignore=E501,E731,W503 --exclude=.git,compat.py --per-file-ignores='mocket/async_mocket.py:E999' mocket @echo "" +types: + @echo "Type checking Python files" + mypy --pretty + @echo "" + setup: develop pre-commit install From b616d7f6fe086bbf555e93b189c4a40cf1535324 Mon Sep 17 00:00:00 2001 From: Gabriel Gore Date: Fri, 26 Apr 2024 10:10:41 -0400 Subject: [PATCH 08/19] add type check step to `make test` --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2fc7831..5b05c79 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,7 @@ setup: develop develop: install-dev-requirements install-test-requirements -test: lint-python test-python +test: lint-python types test-python safetest: export SKIP_TRUE_REDIS=1; export SKIP_TRUE_HTTP=1; make test From 4194d3b1a9011215d74bc8a8c9731237b5177a46 Mon Sep 17 00:00:00 2001 From: Gabriel Gore Date: Fri, 26 Apr 2024 10:21:19 -0400 Subject: [PATCH 09/19] pre-commit fixes --- mocket/compat.py | 2 +- mocket/utils.py | 8 +++++--- pyproject.toml | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/mocket/compat.py b/mocket/compat.py index eb30282..4651b8e 100644 --- a/mocket/compat.py +++ b/mocket/compat.py @@ -3,7 +3,7 @@ import codecs import os import shlex -from typing import Final, Any +from typing import Any, Final ENCODING: Final[str] = os.getenv("MOCKET_ENCODING", "utf-8") diff --git a/mocket/utils.py b/mocket/utils.py index 900dc3e..09eccba 100644 --- a/mocket/utils.py +++ b/mocket/utils.py @@ -4,7 +4,8 @@ import io import os import ssl -from typing import Tuple, Union, Callable, TYPE_CHECKING, TypeVar, Any, ClassVar +from typing import TYPE_CHECKING, Any, Callable, ClassVar, Tuple, TypeVar, Union + from typing_extensions import override from .compat import decode_from_bytes, encode_to_bytes @@ -18,6 +19,7 @@ T = TypeVar("T") + class MocketSocketCore(io.BytesIO): @override def write(self, content: ReadableBuffer) -> None: # type: ignore[override] # BytesIO returns int @@ -29,7 +31,7 @@ def write(self, content: ReadableBuffer) -> None: # type: ignore[override] # By os.write(Mocket.w_fd, content) -def hexdump(binary_string: bytes) -> str: +def hexdump(binary_string: bytes) -> str: r""" >>> hexdump(b"bar foobar foo") == decode_from_bytes(encode_to_bytes("62 61 72 20 66 6F 6F 62 61 72 20 66 6F 6F")) True @@ -50,7 +52,7 @@ def hexload(string: str) -> bytes: def get_mocketize(wrapper_: Callable) -> Callable: import decorator - if decorator.__version__ < "5": # type: ignore[attr-defined] # pragma: no cover + if decorator.__version__ < "5": # type: ignore[attr-defined] # pragma: no cover return decorator.decorator(wrapper_) return decorator.decorator(wrapper_, kwsyntax=True) diff --git a/pyproject.toml b/pyproject.toml index 5cf3a58..0b61524 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -88,7 +88,7 @@ exclude = [ python_version = "3.8" files = [ "mocket/compat.py", - "mocket/utils.py", + "mocket/utils.py", # "tests/" ] strict = true From 6a1b46ff2b6e3e019e21e6572fb53a09126f4be2 Mon Sep 17 00:00:00 2001 From: Gabriel Gore Date: Fri, 26 Apr 2024 10:38:12 -0400 Subject: [PATCH 10/19] remove explicit override this would require depending on typing_extensions --- mocket/utils.py | 8 ++++---- pyproject.toml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mocket/utils.py b/mocket/utils.py index 09eccba..0e0da08 100644 --- a/mocket/utils.py +++ b/mocket/utils.py @@ -6,8 +6,6 @@ import ssl from typing import TYPE_CHECKING, Any, Callable, ClassVar, Tuple, TypeVar, Union -from typing_extensions import override - from .compat import decode_from_bytes, encode_to_bytes from .exceptions import StrictMocketException @@ -21,8 +19,10 @@ class MocketSocketCore(io.BytesIO): - @override - def write(self, content: ReadableBuffer) -> None: # type: ignore[override] # BytesIO returns int + def write( # type: ignore[override] # BytesIO returns int + self, + content: ReadableBuffer, + ) -> None: super(MocketSocketCore, self).write(content) from mocket import Mocket diff --git a/pyproject.toml b/pyproject.toml index 0b61524..e02d052 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -100,7 +100,7 @@ show_error_codes = true implicit_reexport = false disallow_any_generics = false follow_imports = "silent" # enable this once majority is typed -enable_error_code = ['ignore-without-code', 'explicit-override'] +enable_error_code = ['ignore-without-code'] disable_error_code = ["no-untyped-def"] # enable this once full type-coverage is reached [[tool.mypy.overrides]] From 708b7e1a5fe2aa51f63be1de049ee03048be3883 Mon Sep 17 00:00:00 2001 From: Gabriel Gore Date: Fri, 26 Apr 2024 10:43:35 -0400 Subject: [PATCH 11/19] exceptions --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index e02d052..6264b1c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -87,6 +87,7 @@ exclude = [ [tool.mypy] python_version = "3.8" files = [ + "mocket/exceptions.py", "mocket/compat.py", "mocket/utils.py", # "tests/" From dbd515291726c5508f46a4789526adcb6d5e8a05 Mon Sep 17 00:00:00 2001 From: Gabriel Gore Date: Fri, 26 Apr 2024 10:46:28 -0400 Subject: [PATCH 12/19] implicity rexport --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 6264b1c..5551738 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -98,7 +98,7 @@ ignore_missing_imports = true warn_redundant_casts = true warn_unused_ignores = true show_error_codes = true -implicit_reexport = false +implicit_reexport = true disallow_any_generics = false follow_imports = "silent" # enable this once majority is typed enable_error_code = ['ignore-without-code'] From 381e7012256a8bdaa365cb87ac025fbfde8f070e Mon Sep 17 00:00:00 2001 From: Gabriel Gore Date: Fri, 26 Apr 2024 10:53:57 -0400 Subject: [PATCH 13/19] type ignores --- mocket/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mocket/utils.py b/mocket/utils.py index 0e0da08..4985de1 100644 --- a/mocket/utils.py +++ b/mocket/utils.py @@ -54,7 +54,7 @@ def get_mocketize(wrapper_: Callable) -> Callable: if decorator.__version__ < "5": # type: ignore[attr-defined] # pragma: no cover return decorator.decorator(wrapper_) - return decorator.decorator(wrapper_, kwsyntax=True) + return decorator.decorator(wrapper_, kwsyntax=True) # type: ignore[call-arg] # kwsyntax class MocketMode: @@ -65,14 +65,14 @@ class MocketMode: def __init__(self) -> None: self.__dict__ = self.__shared_state - def is_allowed(self, location: Union[str, Tuple[str, int]]) -> bool: + def is_allowed(self, location: str | Tuple[str, int]) -> bool: """ Checks if (`host`, `port`) or at least `host` are allowed locationsto perform real `socket` calls """ if not self.STRICT: return True - host, _ = location + host, _ = location # type: ignore[misc] # can't unpack a string return location in self.STRICT_ALLOWED or host in self.STRICT_ALLOWED @staticmethod From 8e245c3be64456150dd8dea8a39a2d7383810e00 Mon Sep 17 00:00:00 2001 From: Gabriel Gore Date: Fri, 26 Apr 2024 10:56:35 -0400 Subject: [PATCH 14/19] remove unused --- mocket/utils.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/mocket/utils.py b/mocket/utils.py index 4985de1..e8604f6 100644 --- a/mocket/utils.py +++ b/mocket/utils.py @@ -4,7 +4,7 @@ import io import os import ssl -from typing import TYPE_CHECKING, Any, Callable, ClassVar, Tuple, TypeVar, Union +from typing import TYPE_CHECKING, Any, Callable, ClassVar from .compat import decode_from_bytes, encode_to_bytes from .exceptions import StrictMocketException @@ -15,9 +15,6 @@ SSL_PROTOCOL = ssl.PROTOCOL_TLSv1_2 -T = TypeVar("T") - - class MocketSocketCore(io.BytesIO): def write( # type: ignore[override] # BytesIO returns int self, @@ -65,7 +62,7 @@ class MocketMode: def __init__(self) -> None: self.__dict__ = self.__shared_state - def is_allowed(self, location: str | Tuple[str, int]) -> bool: + def is_allowed(self, location: str | tuple[str, int]) -> bool: """ Checks if (`host`, `port`) or at least `host` are allowed locationsto perform real `socket` calls From 435ea61928491d48d62f222ba6aa997361cd5062 Mon Sep 17 00:00:00 2001 From: Gabriel Gore Date: Fri, 26 Apr 2024 12:09:36 -0400 Subject: [PATCH 15/19] formatting --- mocket/utils.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mocket/utils.py b/mocket/utils.py index e8604f6..7b49958 100644 --- a/mocket/utils.py +++ b/mocket/utils.py @@ -15,6 +15,7 @@ SSL_PROTOCOL = ssl.PROTOCOL_TLSv1_2 + class MocketSocketCore(io.BytesIO): def write( # type: ignore[override] # BytesIO returns int self, @@ -51,7 +52,10 @@ def get_mocketize(wrapper_: Callable) -> Callable: if decorator.__version__ < "5": # type: ignore[attr-defined] # pragma: no cover return decorator.decorator(wrapper_) - return decorator.decorator(wrapper_, kwsyntax=True) # type: ignore[call-arg] # kwsyntax + return decorator.decorator( # type: ignore[call-arg] # kwsyntax + wrapper_, + kwsyntax=True, + ) class MocketMode: From bb675523abcb906adebe1b81a6690091b2e30216 Mon Sep 17 00:00:00 2001 From: Giorgio Salluzzo Date: Sun, 5 May 2024 23:45:00 +0100 Subject: [PATCH 16/19] Update pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 2e48ec1..bcc1fdf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -85,7 +85,7 @@ exclude = [ ] [tool.mypy] -python_version = "3.8" +python_version = "3.10" files = [ "mocket/exceptions.py", "mocket/compat.py", From 9d575425e7e14b031379953c50ec141022b80bff Mon Sep 17 00:00:00 2001 From: Giorgio Salluzzo Date: Tue, 7 May 2024 10:12:22 +0100 Subject: [PATCH 17/19] Update pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index bcc1fdf..2e48ec1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -85,7 +85,7 @@ exclude = [ ] [tool.mypy] -python_version = "3.10" +python_version = "3.8" files = [ "mocket/exceptions.py", "mocket/compat.py", From 3abeb56c1a9f736069f5bba6af5b3a20f6db32df Mon Sep 17 00:00:00 2001 From: Giorgio Salluzzo Date: Wed, 8 May 2024 11:04:45 +0100 Subject: [PATCH 18/19] Update Makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 002dafc..edcdec8 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ develop: install-dev-requirements install-test-requirements types: @echo "Type checking Python files" - mypy --pretty + .venv/bin/mypy --pretty @echo "" test: types From f0e5442fb4512f9f476f1f8b2a4fb7663c39b4ab Mon Sep 17 00:00:00 2001 From: Giorgio Salluzzo Date: Wed, 8 May 2024 11:35:42 +0100 Subject: [PATCH 19/19] Update utils.py --- mocket/utils.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/mocket/utils.py b/mocket/utils.py index f8eaf07..7d4bf58 100644 --- a/mocket/utils.py +++ b/mocket/utils.py @@ -73,13 +73,11 @@ def is_allowed(self, location: str | tuple[str, int]) -> bool: """ if not self.STRICT: return True - try: - host, _ = location - except ValueError: - host = None - return location in self.STRICT_ALLOWED or ( - host is not None and host in self.STRICT_ALLOWED - ) + + host_allowed = False + if isinstance(location, tuple): + host_allowed = location[0] in self.STRICT_ALLOWED + return host_allowed or location in self.STRICT_ALLOWED @staticmethod def raise_not_allowed() -> NoReturn: