From c637032af8454ddcadaa9f72dfd7eaa777d8bc8e Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Tue, 31 Oct 2023 18:57:21 +0100 Subject: [PATCH] Declare Support for Python 3.12 (#6434) --- .github/python-version.txt | 2 +- .github/workflows/main.yml | 6 ++- CHANGELOG.md | 2 + mitmproxy/log.py | 9 ++-- mitmproxy/proxy/mode_servers.py | 3 +- pyproject.toml | 9 ++-- test/mitmproxy/addons/test_next_layer.py | 48 ++++++++++----------- test/mitmproxy/net/dns/test_domain_names.py | 4 +- test/mitmproxy/proxy/layers/test_quic.py | 8 ++-- test/mitmproxy/proxy/test_mode_servers.py | 10 +++-- 10 files changed, 56 insertions(+), 45 deletions(-) diff --git a/.github/python-version.txt b/.github/python-version.txt index 2c0733315..e4fba2183 100644 --- a/.github/python-version.txt +++ b/.github/python-version.txt @@ -1 +1 @@ -3.11 +3.12 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 37bd4a531..157279f6c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -42,10 +42,12 @@ jobs: matrix: include: - os: ubuntu-latest - py: "3.11" + py: "3.12" - os: windows-latest - py: "3.11" + py: "3.12" - os: macos-latest + py: "3.12" + - os: ubuntu-latest py: "3.11" - os: ubuntu-latest py: "3.10" diff --git a/CHANGELOG.md b/CHANGELOG.md index 93e3feaf5..d7fb70035 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ ([#6410](https://github.com/mitmproxy/mitmproxy/pull/6410), @mmaxim) * Fix path() documentation that the return value might include the query string ([#6412](https://github.com/mitmproxy/mitmproxy/pull/6412), @tddschn) +* mitmproxy now officially supports Python 3.12. + ([#6434](https://github.com/mitmproxy/mitmproxy/pull/6434), @mhils) * Fix root-relative URLs so that mitmweb can run in subdirectories. ([#6411](https://github.com/mitmproxy/mitmproxy/pull/6411), @davet2001) * Add an optional parameter(ldap search filter key) to ProxyAuth-LDAP. diff --git a/mitmproxy/log.py b/mitmproxy/log.py index dc009f201..221b12a93 100644 --- a/mitmproxy/log.py +++ b/mitmproxy/log.py @@ -71,9 +71,12 @@ class MitmLogHandler(logging.Handler): def filter(self, record: logging.LogRecord) -> bool: # We can't remove stale handlers here because that would modify .handlers during iteration! - return super().filter(record) and ( - not self._initiated_in_test - or self._initiated_in_test == os.environ.get("PYTEST_CURRENT_TEST") + return bool( + super().filter(record) + and ( + not self._initiated_in_test + or self._initiated_in_test == os.environ.get("PYTEST_CURRENT_TEST") + ) ) def install(self) -> None: diff --git a/mitmproxy/proxy/mode_servers.py b/mitmproxy/proxy/mode_servers.py index 1d505ae77..1b47ebe84 100644 --- a/mitmproxy/proxy/mode_servers.py +++ b/mitmproxy/proxy/mode_servers.py @@ -286,7 +286,8 @@ class AsyncioServerInstance(ServerInstance[M], metaclass=ABCMeta): try: for s in self._servers: s.close() - await asyncio.gather(*[s.wait_closed() for s in self._servers]) + # https://github.com/python/cpython/issues/104344 + # await asyncio.gather(*[s.wait_closed() for s in self._servers]) finally: # we always reset _server and ignore failures self._servers = [] diff --git a/pyproject.toml b/pyproject.toml index dbee08818..fd363778f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,6 +18,7 @@ classifiers = [ "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Programming Language :: Python :: Implementation :: CPython", "Topic :: Security", "Topic :: Internet :: WWW/HTTP", @@ -30,7 +31,7 @@ classifiers = [ # https://packaging.python.org/en/latest/discussions/install-requires-vs-requirements/#install-requires # It is not considered best practice to use install_requires to pin dependencies to specific versions. dependencies = [ - "aioquic_mitmproxy>=0.9.20,<0.10", + "aioquic_mitmproxy>=0.9.21,<0.10", "asgiref>=3.2.10,<3.8", "Brotli>=1.0,<1.2", "certifi>=2019.9.11", # no semver here - this should always be on the last release! @@ -204,13 +205,13 @@ commands = [testenv:mypy] deps = - mypy==1.4.1 + mypy==1.6.1 types-certifi==2021.10.8.3 types-Flask==1.1.6 types-Werkzeug==1.0.9 - types-requests==2.31.0.2 + types-requests==2.31.0.10 types-cryptography==3.3.23.2 - types-pyOpenSSL==23.2.0.2 + types-pyOpenSSL==23.3.0.0 -e .[dev] commands = diff --git a/test/mitmproxy/addons/test_next_layer.py b/test/mitmproxy/addons/test_next_layer.py index 6bd9c8511..c2a084071 100644 --- a/test/mitmproxy/addons/test_next_layer.py +++ b/test/mitmproxy/addons/test_next_layer.py @@ -281,7 +281,7 @@ class TestNextLayer: @dataclass -class TestConf: +class TConf: before: list[type[Layer]] after: list[type[Layer]] proxy_mode: str = "regular" @@ -296,14 +296,14 @@ class TestConf: explicit_proxy_configs = [ pytest.param( - TestConf( + TConf( before=[modes.HttpProxy], after=[modes.HttpProxy, HttpLayer], ), id=f"explicit proxy: regular http", ), pytest.param( - TestConf( + TConf( before=[modes.HttpProxy], after=[modes.HttpProxy, ClientTLSLayer, HttpLayer], data_client=client_hello_no_extensions, @@ -311,14 +311,14 @@ explicit_proxy_configs = [ id=f"explicit proxy: secure web proxy", ), pytest.param( - TestConf( + TConf( before=[modes.HttpUpstreamProxy], after=[modes.HttpUpstreamProxy, HttpLayer], ), id=f"explicit proxy: upstream proxy", ), pytest.param( - TestConf( + TConf( before=[modes.HttpUpstreamProxy], after=[modes.HttpUpstreamProxy, ClientQuicLayer, HttpLayer], transport_protocol="udp", @@ -326,7 +326,7 @@ explicit_proxy_configs = [ id=f"explicit proxy: experimental http3", ), pytest.param( - TestConf( + TConf( before=[ modes.HttpProxy, partial(HttpLayer, mode=HTTPMode.regular), @@ -338,7 +338,7 @@ explicit_proxy_configs = [ id=f"explicit proxy: HTTP over regular proxy", ), pytest.param( - TestConf( + TConf( before=[ modes.HttpProxy, partial(HttpLayer, mode=HTTPMode.regular), @@ -356,7 +356,7 @@ explicit_proxy_configs = [ id=f"explicit proxy: TLS over regular proxy", ), pytest.param( - TestConf( + TConf( before=[ modes.HttpProxy, partial(HttpLayer, mode=HTTPMode.regular), @@ -377,7 +377,7 @@ explicit_proxy_configs = [ id=f"explicit proxy: HTTPS over regular proxy", ), pytest.param( - TestConf( + TConf( before=[ modes.HttpProxy, partial(HttpLayer, mode=HTTPMode.regular), @@ -404,7 +404,7 @@ for proto_plain, proto_enc, app_layer in [ reverse_proxy_configs.extend( [ pytest.param( - TestConf( + TConf( before=[modes.ReverseProxy], after=[modes.ReverseProxy, app_layer], proxy_mode=f"reverse:{proto_plain}://example.com:42", @@ -412,7 +412,7 @@ for proto_plain, proto_enc, app_layer in [ id=f"reverse proxy: {proto_plain} -> {proto_plain}", ), pytest.param( - TestConf( + TConf( before=[modes.ReverseProxy], after=[ modes.ReverseProxy, @@ -426,7 +426,7 @@ for proto_plain, proto_enc, app_layer in [ id=f"reverse proxy: {proto_enc} -> {proto_enc}", ), pytest.param( - TestConf( + TConf( before=[modes.ReverseProxy], after=[modes.ReverseProxy, ClientTLSLayer, app_layer], proxy_mode=f"reverse:{proto_plain}://example.com:42", @@ -435,7 +435,7 @@ for proto_plain, proto_enc, app_layer in [ id=f"reverse proxy: {proto_enc} -> {proto_plain}", ), pytest.param( - TestConf( + TConf( before=[modes.ReverseProxy], after=[modes.ReverseProxy, ServerTLSLayer, app_layer], proxy_mode=f"reverse:{proto_enc}://example.com:42", @@ -448,7 +448,7 @@ for proto_plain, proto_enc, app_layer in [ reverse_proxy_configs.extend( [ pytest.param( - TestConf( + TConf( before=[modes.ReverseProxy], after=[modes.ReverseProxy, DNSLayer], proxy_mode="reverse:dns://example.com:53", @@ -456,7 +456,7 @@ reverse_proxy_configs.extend( id="reverse proxy: dns", ), pytest.param( - TestConf( + TConf( before=[modes.ReverseProxy], after=[modes.ReverseProxy, ServerQuicLayer, ClientQuicLayer, HttpLayer], proxy_mode="reverse:http3://example.com", @@ -464,7 +464,7 @@ reverse_proxy_configs.extend( id="reverse proxy: http3", ), pytest.param( - TestConf( + TConf( before=[modes.ReverseProxy], after=[ modes.ReverseProxy, @@ -481,7 +481,7 @@ reverse_proxy_configs.extend( transparent_proxy_configs = [ pytest.param( - TestConf( + TConf( before=[modes.TransparentProxy], after=[modes.TransparentProxy, ServerTLSLayer, ClientTLSLayer], data_client=client_hello_no_extensions, @@ -489,7 +489,7 @@ transparent_proxy_configs = [ id=f"transparent proxy: tls", ), pytest.param( - TestConf( + TConf( before=[modes.TransparentProxy], after=[modes.TransparentProxy, ServerTLSLayer, ClientTLSLayer], data_client=dtls_client_hello_with_extensions, @@ -498,7 +498,7 @@ transparent_proxy_configs = [ id=f"transparent proxy: dtls", ), pytest.param( - TestConf( + TConf( before=[modes.TransparentProxy], after=[modes.TransparentProxy, ServerQuicLayer, ClientQuicLayer], data_client=quic_client_hello, @@ -507,7 +507,7 @@ transparent_proxy_configs = [ id="transparent proxy: quic", ), pytest.param( - TestConf( + TConf( before=[modes.TransparentProxy], after=[modes.TransparentProxy, TCPLayer], data_server=b"220 service ready", @@ -515,7 +515,7 @@ transparent_proxy_configs = [ id="transparent proxy: raw tcp", ), pytest.param( - http := TestConf( + http := TConf( before=[modes.TransparentProxy], after=[modes.TransparentProxy, HttpLayer], data_client=b"GET / HTTP/1.1\r\n", @@ -540,7 +540,7 @@ transparent_proxy_configs = [ id="transparent proxy: ignore_hosts", ), pytest.param( - dns := TestConf( + dns := TConf( before=[modes.TransparentProxy], after=[modes.TransparentProxy, DNSLayer], transport_protocol="udp", @@ -549,7 +549,7 @@ transparent_proxy_configs = [ id="transparent proxy: dns", ), pytest.param( - TestConf( + TConf( before=[modes.TransparentProxy], after=[modes.TransparentProxy, UDPLayer], transport_protocol="udp", @@ -577,7 +577,7 @@ transparent_proxy_configs = [ ], ) def test_next_layer( - test_conf: TestConf, + test_conf: TConf, ): nl = NextLayer() with taddons.context(nl) as tctx: diff --git a/test/mitmproxy/net/dns/test_domain_names.py b/test/mitmproxy/net/dns/test_domain_names.py index 811d203df..1d0f54d6c 100644 --- a/test/mitmproxy/net/dns/test_domain_names.py +++ b/test/mitmproxy/net/dns/test_domain_names.py @@ -62,9 +62,7 @@ def test_pack(): name = f"www.{label}.com" with pytest.raises( ValueError, - match=re.escape( - "encoding with 'idna' codec failed (UnicodeError: label too long)" - ), + match="label too long", ): domain_names.pack(name) assert domain_names.pack("www.example.org") == b"\x03www\x07example\x03org\x00" diff --git a/test/mitmproxy/proxy/layers/test_quic.py b/test/mitmproxy/proxy/layers/test_quic.py index 2a66d5818..54ae91f34 100644 --- a/test/mitmproxy/proxy/layers/test_quic.py +++ b/test/mitmproxy/proxy/layers/test_quic.py @@ -840,7 +840,7 @@ class TestServerTLS: playbook >> events.Wakeup(playbook.actual[9]) << commands.Log( - "Server QUIC handshake failed. hostname 'wrong.host.mitmproxy.org' doesn't match 'example.mitmproxy.org'", + "Server QUIC handshake failed. Certificate does not match hostname 'wrong.host.mitmproxy.org'", WARNING, ) << tls.TlsFailedServerHook(tls_hook_data) @@ -848,12 +848,12 @@ class TestServerTLS: << commands.CloseConnection(tctx.server) << commands.SendData( tctx.client, - b"open-connection failed: hostname 'wrong.host.mitmproxy.org' doesn't match 'example.mitmproxy.org'", + b"open-connection failed: Certificate does not match hostname 'wrong.host.mitmproxy.org'", ) ) assert ( tls_hook_data().conn.error - == "hostname 'wrong.host.mitmproxy.org' doesn't match 'example.mitmproxy.org'" + == "Certificate does not match hostname 'wrong.host.mitmproxy.org'" ) assert not tctx.server.tls_established @@ -1133,7 +1133,7 @@ class TestClientTLS: playbook >> events.Wakeup(playbook.actual[7]) << commands.Log( - "Client QUIC handshake failed. hostname 'wrong.host.mitmproxy.org' doesn't match 'example.mitmproxy.org'", + "Client QUIC handshake failed. Certificate does not match hostname 'wrong.host.mitmproxy.org'", WARNING, ) << tls.TlsFailedClientHook(tls_hook_data) diff --git a/test/mitmproxy/proxy/test_mode_servers.py b/test/mitmproxy/proxy/test_mode_servers.py index a07dbba93..08c1d9adb 100644 --- a/test/mitmproxy/proxy/test_mode_servers.py +++ b/test/mitmproxy/proxy/test_mode_servers.py @@ -48,7 +48,11 @@ async def test_last_exception_and_running(monkeypatch): manager = MagicMock() err = ValueError("something else") - async def _raise(*_): + def _raise(*_): + nonlocal err + raise err + + async def _raise_async(*_): nonlocal err raise err @@ -57,12 +61,12 @@ async def test_last_exception_and_running(monkeypatch): await inst1.start() assert inst1.last_exception is None assert inst1.is_running - monkeypatch.setattr(inst1._servers[0], "wait_closed", _raise) + monkeypatch.setattr(inst1._servers[0], "close", _raise) with pytest.raises(type(err), match=str(err)): await inst1.stop() assert inst1.last_exception is err - monkeypatch.setattr(asyncio, "start_server", _raise) + monkeypatch.setattr(asyncio, "start_server", _raise_async) inst2 = ServerInstance.make("regular@127.0.0.1:0", manager) assert inst2.last_exception is None with pytest.raises(type(err), match=str(err)):