Declare Support for Python 3.12 (#6434)

This commit is contained in:
Maximilian Hils 2023-10-31 18:57:21 +01:00 committed by GitHub
parent f55ee1d44f
commit c637032af8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 56 additions and 45 deletions

View File

@ -1 +1 @@
3.11 3.12

View File

@ -42,10 +42,12 @@ jobs:
matrix: matrix:
include: include:
- os: ubuntu-latest - os: ubuntu-latest
py: "3.11" py: "3.12"
- os: windows-latest - os: windows-latest
py: "3.11" py: "3.12"
- os: macos-latest - os: macos-latest
py: "3.12"
- os: ubuntu-latest
py: "3.11" py: "3.11"
- os: ubuntu-latest - os: ubuntu-latest
py: "3.10" py: "3.10"

View File

@ -10,6 +10,8 @@
([#6410](https://github.com/mitmproxy/mitmproxy/pull/6410), @mmaxim) ([#6410](https://github.com/mitmproxy/mitmproxy/pull/6410), @mmaxim)
* Fix path() documentation that the return value might include the query string * Fix path() documentation that the return value might include the query string
([#6412](https://github.com/mitmproxy/mitmproxy/pull/6412), @tddschn) ([#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. * Fix root-relative URLs so that mitmweb can run in subdirectories.
([#6411](https://github.com/mitmproxy/mitmproxy/pull/6411), @davet2001) ([#6411](https://github.com/mitmproxy/mitmproxy/pull/6411), @davet2001)
* Add an optional parameter(ldap search filter key) to ProxyAuth-LDAP. * Add an optional parameter(ldap search filter key) to ProxyAuth-LDAP.

View File

@ -71,9 +71,12 @@ class MitmLogHandler(logging.Handler):
def filter(self, record: logging.LogRecord) -> bool: def filter(self, record: logging.LogRecord) -> bool:
# We can't remove stale handlers here because that would modify .handlers during iteration! # We can't remove stale handlers here because that would modify .handlers during iteration!
return super().filter(record) and ( return bool(
not self._initiated_in_test super().filter(record)
or self._initiated_in_test == os.environ.get("PYTEST_CURRENT_TEST") and (
not self._initiated_in_test
or self._initiated_in_test == os.environ.get("PYTEST_CURRENT_TEST")
)
) )
def install(self) -> None: def install(self) -> None:

View File

@ -286,7 +286,8 @@ class AsyncioServerInstance(ServerInstance[M], metaclass=ABCMeta):
try: try:
for s in self._servers: for s in self._servers:
s.close() 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: finally:
# we always reset _server and ignore failures # we always reset _server and ignore failures
self._servers = [] self._servers = []

View File

@ -18,6 +18,7 @@ classifiers = [
"Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: CPython",
"Topic :: Security", "Topic :: Security",
"Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP",
@ -30,7 +31,7 @@ classifiers = [
# https://packaging.python.org/en/latest/discussions/install-requires-vs-requirements/#install-requires # 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. # It is not considered best practice to use install_requires to pin dependencies to specific versions.
dependencies = [ dependencies = [
"aioquic_mitmproxy>=0.9.20,<0.10", "aioquic_mitmproxy>=0.9.21,<0.10",
"asgiref>=3.2.10,<3.8", "asgiref>=3.2.10,<3.8",
"Brotli>=1.0,<1.2", "Brotli>=1.0,<1.2",
"certifi>=2019.9.11", # no semver here - this should always be on the last release! "certifi>=2019.9.11", # no semver here - this should always be on the last release!
@ -204,13 +205,13 @@ commands =
[testenv:mypy] [testenv:mypy]
deps = deps =
mypy==1.4.1 mypy==1.6.1
types-certifi==2021.10.8.3 types-certifi==2021.10.8.3
types-Flask==1.1.6 types-Flask==1.1.6
types-Werkzeug==1.0.9 types-Werkzeug==1.0.9
types-requests==2.31.0.2 types-requests==2.31.0.10
types-cryptography==3.3.23.2 types-cryptography==3.3.23.2
types-pyOpenSSL==23.2.0.2 types-pyOpenSSL==23.3.0.0
-e .[dev] -e .[dev]
commands = commands =

View File

@ -281,7 +281,7 @@ class TestNextLayer:
@dataclass @dataclass
class TestConf: class TConf:
before: list[type[Layer]] before: list[type[Layer]]
after: list[type[Layer]] after: list[type[Layer]]
proxy_mode: str = "regular" proxy_mode: str = "regular"
@ -296,14 +296,14 @@ class TestConf:
explicit_proxy_configs = [ explicit_proxy_configs = [
pytest.param( pytest.param(
TestConf( TConf(
before=[modes.HttpProxy], before=[modes.HttpProxy],
after=[modes.HttpProxy, HttpLayer], after=[modes.HttpProxy, HttpLayer],
), ),
id=f"explicit proxy: regular http", id=f"explicit proxy: regular http",
), ),
pytest.param( pytest.param(
TestConf( TConf(
before=[modes.HttpProxy], before=[modes.HttpProxy],
after=[modes.HttpProxy, ClientTLSLayer, HttpLayer], after=[modes.HttpProxy, ClientTLSLayer, HttpLayer],
data_client=client_hello_no_extensions, data_client=client_hello_no_extensions,
@ -311,14 +311,14 @@ explicit_proxy_configs = [
id=f"explicit proxy: secure web proxy", id=f"explicit proxy: secure web proxy",
), ),
pytest.param( pytest.param(
TestConf( TConf(
before=[modes.HttpUpstreamProxy], before=[modes.HttpUpstreamProxy],
after=[modes.HttpUpstreamProxy, HttpLayer], after=[modes.HttpUpstreamProxy, HttpLayer],
), ),
id=f"explicit proxy: upstream proxy", id=f"explicit proxy: upstream proxy",
), ),
pytest.param( pytest.param(
TestConf( TConf(
before=[modes.HttpUpstreamProxy], before=[modes.HttpUpstreamProxy],
after=[modes.HttpUpstreamProxy, ClientQuicLayer, HttpLayer], after=[modes.HttpUpstreamProxy, ClientQuicLayer, HttpLayer],
transport_protocol="udp", transport_protocol="udp",
@ -326,7 +326,7 @@ explicit_proxy_configs = [
id=f"explicit proxy: experimental http3", id=f"explicit proxy: experimental http3",
), ),
pytest.param( pytest.param(
TestConf( TConf(
before=[ before=[
modes.HttpProxy, modes.HttpProxy,
partial(HttpLayer, mode=HTTPMode.regular), partial(HttpLayer, mode=HTTPMode.regular),
@ -338,7 +338,7 @@ explicit_proxy_configs = [
id=f"explicit proxy: HTTP over regular proxy", id=f"explicit proxy: HTTP over regular proxy",
), ),
pytest.param( pytest.param(
TestConf( TConf(
before=[ before=[
modes.HttpProxy, modes.HttpProxy,
partial(HttpLayer, mode=HTTPMode.regular), partial(HttpLayer, mode=HTTPMode.regular),
@ -356,7 +356,7 @@ explicit_proxy_configs = [
id=f"explicit proxy: TLS over regular proxy", id=f"explicit proxy: TLS over regular proxy",
), ),
pytest.param( pytest.param(
TestConf( TConf(
before=[ before=[
modes.HttpProxy, modes.HttpProxy,
partial(HttpLayer, mode=HTTPMode.regular), partial(HttpLayer, mode=HTTPMode.regular),
@ -377,7 +377,7 @@ explicit_proxy_configs = [
id=f"explicit proxy: HTTPS over regular proxy", id=f"explicit proxy: HTTPS over regular proxy",
), ),
pytest.param( pytest.param(
TestConf( TConf(
before=[ before=[
modes.HttpProxy, modes.HttpProxy,
partial(HttpLayer, mode=HTTPMode.regular), partial(HttpLayer, mode=HTTPMode.regular),
@ -404,7 +404,7 @@ for proto_plain, proto_enc, app_layer in [
reverse_proxy_configs.extend( reverse_proxy_configs.extend(
[ [
pytest.param( pytest.param(
TestConf( TConf(
before=[modes.ReverseProxy], before=[modes.ReverseProxy],
after=[modes.ReverseProxy, app_layer], after=[modes.ReverseProxy, app_layer],
proxy_mode=f"reverse:{proto_plain}://example.com:42", 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}", id=f"reverse proxy: {proto_plain} -> {proto_plain}",
), ),
pytest.param( pytest.param(
TestConf( TConf(
before=[modes.ReverseProxy], before=[modes.ReverseProxy],
after=[ after=[
modes.ReverseProxy, modes.ReverseProxy,
@ -426,7 +426,7 @@ for proto_plain, proto_enc, app_layer in [
id=f"reverse proxy: {proto_enc} -> {proto_enc}", id=f"reverse proxy: {proto_enc} -> {proto_enc}",
), ),
pytest.param( pytest.param(
TestConf( TConf(
before=[modes.ReverseProxy], before=[modes.ReverseProxy],
after=[modes.ReverseProxy, ClientTLSLayer, app_layer], after=[modes.ReverseProxy, ClientTLSLayer, app_layer],
proxy_mode=f"reverse:{proto_plain}://example.com:42", 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}", id=f"reverse proxy: {proto_enc} -> {proto_plain}",
), ),
pytest.param( pytest.param(
TestConf( TConf(
before=[modes.ReverseProxy], before=[modes.ReverseProxy],
after=[modes.ReverseProxy, ServerTLSLayer, app_layer], after=[modes.ReverseProxy, ServerTLSLayer, app_layer],
proxy_mode=f"reverse:{proto_enc}://example.com:42", 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( reverse_proxy_configs.extend(
[ [
pytest.param( pytest.param(
TestConf( TConf(
before=[modes.ReverseProxy], before=[modes.ReverseProxy],
after=[modes.ReverseProxy, DNSLayer], after=[modes.ReverseProxy, DNSLayer],
proxy_mode="reverse:dns://example.com:53", proxy_mode="reverse:dns://example.com:53",
@ -456,7 +456,7 @@ reverse_proxy_configs.extend(
id="reverse proxy: dns", id="reverse proxy: dns",
), ),
pytest.param( pytest.param(
TestConf( TConf(
before=[modes.ReverseProxy], before=[modes.ReverseProxy],
after=[modes.ReverseProxy, ServerQuicLayer, ClientQuicLayer, HttpLayer], after=[modes.ReverseProxy, ServerQuicLayer, ClientQuicLayer, HttpLayer],
proxy_mode="reverse:http3://example.com", proxy_mode="reverse:http3://example.com",
@ -464,7 +464,7 @@ reverse_proxy_configs.extend(
id="reverse proxy: http3", id="reverse proxy: http3",
), ),
pytest.param( pytest.param(
TestConf( TConf(
before=[modes.ReverseProxy], before=[modes.ReverseProxy],
after=[ after=[
modes.ReverseProxy, modes.ReverseProxy,
@ -481,7 +481,7 @@ reverse_proxy_configs.extend(
transparent_proxy_configs = [ transparent_proxy_configs = [
pytest.param( pytest.param(
TestConf( TConf(
before=[modes.TransparentProxy], before=[modes.TransparentProxy],
after=[modes.TransparentProxy, ServerTLSLayer, ClientTLSLayer], after=[modes.TransparentProxy, ServerTLSLayer, ClientTLSLayer],
data_client=client_hello_no_extensions, data_client=client_hello_no_extensions,
@ -489,7 +489,7 @@ transparent_proxy_configs = [
id=f"transparent proxy: tls", id=f"transparent proxy: tls",
), ),
pytest.param( pytest.param(
TestConf( TConf(
before=[modes.TransparentProxy], before=[modes.TransparentProxy],
after=[modes.TransparentProxy, ServerTLSLayer, ClientTLSLayer], after=[modes.TransparentProxy, ServerTLSLayer, ClientTLSLayer],
data_client=dtls_client_hello_with_extensions, data_client=dtls_client_hello_with_extensions,
@ -498,7 +498,7 @@ transparent_proxy_configs = [
id=f"transparent proxy: dtls", id=f"transparent proxy: dtls",
), ),
pytest.param( pytest.param(
TestConf( TConf(
before=[modes.TransparentProxy], before=[modes.TransparentProxy],
after=[modes.TransparentProxy, ServerQuicLayer, ClientQuicLayer], after=[modes.TransparentProxy, ServerQuicLayer, ClientQuicLayer],
data_client=quic_client_hello, data_client=quic_client_hello,
@ -507,7 +507,7 @@ transparent_proxy_configs = [
id="transparent proxy: quic", id="transparent proxy: quic",
), ),
pytest.param( pytest.param(
TestConf( TConf(
before=[modes.TransparentProxy], before=[modes.TransparentProxy],
after=[modes.TransparentProxy, TCPLayer], after=[modes.TransparentProxy, TCPLayer],
data_server=b"220 service ready", data_server=b"220 service ready",
@ -515,7 +515,7 @@ transparent_proxy_configs = [
id="transparent proxy: raw tcp", id="transparent proxy: raw tcp",
), ),
pytest.param( pytest.param(
http := TestConf( http := TConf(
before=[modes.TransparentProxy], before=[modes.TransparentProxy],
after=[modes.TransparentProxy, HttpLayer], after=[modes.TransparentProxy, HttpLayer],
data_client=b"GET / HTTP/1.1\r\n", data_client=b"GET / HTTP/1.1\r\n",
@ -540,7 +540,7 @@ transparent_proxy_configs = [
id="transparent proxy: ignore_hosts", id="transparent proxy: ignore_hosts",
), ),
pytest.param( pytest.param(
dns := TestConf( dns := TConf(
before=[modes.TransparentProxy], before=[modes.TransparentProxy],
after=[modes.TransparentProxy, DNSLayer], after=[modes.TransparentProxy, DNSLayer],
transport_protocol="udp", transport_protocol="udp",
@ -549,7 +549,7 @@ transparent_proxy_configs = [
id="transparent proxy: dns", id="transparent proxy: dns",
), ),
pytest.param( pytest.param(
TestConf( TConf(
before=[modes.TransparentProxy], before=[modes.TransparentProxy],
after=[modes.TransparentProxy, UDPLayer], after=[modes.TransparentProxy, UDPLayer],
transport_protocol="udp", transport_protocol="udp",
@ -577,7 +577,7 @@ transparent_proxy_configs = [
], ],
) )
def test_next_layer( def test_next_layer(
test_conf: TestConf, test_conf: TConf,
): ):
nl = NextLayer() nl = NextLayer()
with taddons.context(nl) as tctx: with taddons.context(nl) as tctx:

View File

@ -62,9 +62,7 @@ def test_pack():
name = f"www.{label}.com" name = f"www.{label}.com"
with pytest.raises( with pytest.raises(
ValueError, ValueError,
match=re.escape( match="label too long",
"encoding with 'idna' codec failed (UnicodeError: label too long)"
),
): ):
domain_names.pack(name) domain_names.pack(name)
assert domain_names.pack("www.example.org") == b"\x03www\x07example\x03org\x00" assert domain_names.pack("www.example.org") == b"\x03www\x07example\x03org\x00"

View File

@ -840,7 +840,7 @@ class TestServerTLS:
playbook playbook
>> events.Wakeup(playbook.actual[9]) >> events.Wakeup(playbook.actual[9])
<< commands.Log( << 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, WARNING,
) )
<< tls.TlsFailedServerHook(tls_hook_data) << tls.TlsFailedServerHook(tls_hook_data)
@ -848,12 +848,12 @@ class TestServerTLS:
<< commands.CloseConnection(tctx.server) << commands.CloseConnection(tctx.server)
<< commands.SendData( << commands.SendData(
tctx.client, 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 ( assert (
tls_hook_data().conn.error 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 assert not tctx.server.tls_established
@ -1133,7 +1133,7 @@ class TestClientTLS:
playbook playbook
>> events.Wakeup(playbook.actual[7]) >> events.Wakeup(playbook.actual[7])
<< commands.Log( << 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, WARNING,
) )
<< tls.TlsFailedClientHook(tls_hook_data) << tls.TlsFailedClientHook(tls_hook_data)

View File

@ -48,7 +48,11 @@ async def test_last_exception_and_running(monkeypatch):
manager = MagicMock() manager = MagicMock()
err = ValueError("something else") err = ValueError("something else")
async def _raise(*_): def _raise(*_):
nonlocal err
raise err
async def _raise_async(*_):
nonlocal err nonlocal err
raise err raise err
@ -57,12 +61,12 @@ async def test_last_exception_and_running(monkeypatch):
await inst1.start() await inst1.start()
assert inst1.last_exception is None assert inst1.last_exception is None
assert inst1.is_running 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)): with pytest.raises(type(err), match=str(err)):
await inst1.stop() await inst1.stop()
assert inst1.last_exception is err 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) inst2 = ServerInstance.make("regular@127.0.0.1:0", manager)
assert inst2.last_exception is None assert inst2.last_exception is None
with pytest.raises(type(err), match=str(err)): with pytest.raises(type(err), match=str(err)):