Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Python 3.11 support #1384

Merged
merged 21 commits into from
Apr 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions .github/workflows/test-library.yml
Original file line number Diff line number Diff line change
Expand Up @@ -446,8 +446,9 @@ jobs:
# NOTE: The latest and the lowest supported Pythons are prioritized
# NOTE: to improve the responsiveness. It's nice to see the most
# NOTE: important results first.
- '3.10'
- '3.11'
- 3.6
- '3.10'
- 3.9
- 3.8
- 3.7
Expand All @@ -463,7 +464,7 @@ jobs:
env:
PY_COLORS: 1
TOX_PARALLEL_NO_SPINNER: 1
TOXENV: python
TOXENV: py

steps:
- name: Switch to using Python v${{ matrix.python }}
Expand Down Expand Up @@ -500,7 +501,15 @@ jobs:
steps.calc-cache-key-py.outputs.py-hash-key
}}-
${{ runner.os }}-pip-
- name: Install tox
- name: Install tox for >= 3.11
if: matrix.python == '3.11'
run: >-
python -m
pip install
--user
tox==4.14.2
- name: Install tox for < 3.11
if: matrix.python != '3.11'
run: >-
python -m
pip install
Expand Down
12 changes: 8 additions & 4 deletions .pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ disable=raw-checker-failed,
useless-return,
useless-super-delegation,
wrong-import-order,
# Required because to support 3.11
# we added unnecessary-dunder-call which is not supported for <=3.11
# see https://github.com/abhinavsingh/proxy.py/actions/runs/8671404475/job/23780537848?pr=1384
bad-option-value

# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option
Expand Down Expand Up @@ -419,7 +423,7 @@ contextmanager-decorators=contextlib.contextmanager
# List of members which are set dynamically and missed by pylint inference
# system, and so shouldn't trigger E1101 when accessed. Python regular
# expressions are accepted.
generated-members=
generated-members=os,io

# Tells whether missing members accessed in mixin class should be ignored. A
# mixin class is detected if its name ends with "mixin" (case insensitive).
Expand All @@ -446,7 +450,7 @@ ignored-classes=optparse.Values,thread._local,_thread._local
# (useful for modules/projects where namespaces are manipulated during runtime
# and thus existing member attributes cannot be deduced by static analysis). It
# supports qualified module names, as well as Unix pattern matching.
ignored-modules=
ignored-modules=abc

# Show a hint with possible names when a member name was not found. The aspect
# of finding the hint is based on edit distance.
Expand Down Expand Up @@ -605,5 +609,5 @@ preferred-modules=

# Exceptions that will emit a warning when being caught. Defaults to
# "BaseException, Exception".
overgeneral-exceptions=BaseException,
Exception
overgeneral-exceptions=builtins.BaseException,
builtins.Exception
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.10-alpine as base
FROM python:3.11-alpine as base

LABEL com.abhinavsingh.name="abhinavsingh/proxy.py" \
com.abhinavsingh.description="⚡ Fast • 🪶 Lightweight • 0️⃣ Dependency • 🔌 Pluggable • \
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ lib-mypy:
tox -e lint -- mypy --all-files

lib-pytest:
$(PYTHON) -m tox -e python -- -v
$(PYTHON) -m tox -e py -- -v

lib-test: lib-clean lib-check lib-lint lib-pytest

Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
[![iOS, iOS Simulator](https://img.shields.io/static/v1?label=tested%20with&message=iOS%20%F0%9F%93%B1%20%7C%20iOS%20Simulator%20%F0%9F%93%B1&color=darkgreen&style=for-the-badge)](https://abhinavsingh.com/proxy-py-a-lightweight-single-file-http-proxy-server-in-python/)

[![pypi version](https://img.shields.io/pypi/v/proxy.py?style=flat-square)](https://pypi.org/project/proxy.py/)
[![Python 3.x](https://img.shields.io/static/v1?label=Python&message=3.6%20%7C%203.7%20%7C%203.8%20%7C%203.9%20%7C%203.10&color=blue&style=flat-square)](https://www.python.org/)
[![Python 3.x](https://img.shields.io/static/v1?label=Python&message=3.6%20%7C%203.7%20%7C%203.8%20%7C%203.9%20%7C%203.10%20%7C%203.11&color=blue&style=flat-square)](https://www.python.org/)
[![Checked with mypy](https://img.shields.io/static/v1?label=MyPy&message=checked&color=blue&style=flat-square)](http://mypy-lang.org/)

[![doc](https://img.shields.io/readthedocs/proxypy/latest?style=flat-square&color=darkgreen)](https://proxypy.readthedocs.io/)
Expand Down Expand Up @@ -2366,7 +2366,7 @@ usage: -m [-h] [--tunnel-hostname TUNNEL_HOSTNAME] [--tunnel-port TUNNEL_PORT]
[--filtered-client-ips FILTERED_CLIENT_IPS]
[--filtered-url-regex-config FILTERED_URL_REGEX_CONFIG]

proxy.py v2.4.4rc5.dev36+g6c9d0315.d20240411
proxy.py v2.4.4rc6.dev11+gac1f05d7.d20240413

options:
-h, --help show this help message and exit
Expand Down Expand Up @@ -2489,8 +2489,8 @@ options:
Default: None. Signing certificate to use for signing
dynamically generated HTTPS certificates. If used,
must also pass --ca-key-file and --ca-signing-key-file
--ca-file CA_FILE Default: /Users/abhinavsingh/Dev/proxy.py/.venv/lib/py
thon3.10/site-packages/certifi/cacert.pem. Provide
--ca-file CA_FILE Default: /Users/abhinavsingh/Dev/proxy.py/.venv3118/li
b/python3.11/site-packages/certifi/cacert.pem. Provide
path to custom CA bundle for peer certificate
verification
--ca-signing-key-file CA_SIGNING_KEY_FILE
Expand Down
2 changes: 1 addition & 1 deletion benchmark/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
aiohttp==3.8.1
aiohttp==3.8.2
# Blacksheep depends upon essentials_openapi which is pinned to pyyaml==5.4.1
# and pyyaml>5.3.1 is broken for cython 3
# See https://github.com/yaml/pyyaml/issues/724#issuecomment-1638587228
Expand Down
2 changes: 2 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,8 @@
(_py_class_role, 'HostPort'),
(_py_class_role, 'TcpOrTlsSocket'),
(_py_class_role, 're.Pattern'),
(_py_class_role, 'proxy.core.base.tcp_server.T'),
(_py_class_role, 'proxy.common.types.RePattern'),
(_py_obj_role, 'proxy.core.work.threadless.T'),
(_py_obj_role, 'proxy.core.work.work.T'),
(_py_obj_role, 'proxy.core.base.tcp_server.T'),
Expand Down
7 changes: 7 additions & 0 deletions examples/web_scraper.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
:license: BSD, see LICENSE for more details.
"""
import time
from abc import abstractmethod
from typing import Any

from proxy import Proxy
from proxy.core.work import Work
Expand Down Expand Up @@ -52,6 +54,11 @@ async def handle_events(
Return True to shutdown work."""
return False

@staticmethod
@abstractmethod
def create(*args: Any) -> TcpClientConnection:
raise NotImplementedError()


if __name__ == '__main__':
with Proxy(
Expand Down
8 changes: 4 additions & 4 deletions proxy/common/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import queue
import socket
import ipaddress
from typing import TYPE_CHECKING, Any, Dict, List, Tuple, Union
from typing import TYPE_CHECKING, Any, Dict, List, Tuple, Union, TypeVar


if TYPE_CHECKING: # pragma: no cover
Expand All @@ -34,8 +34,8 @@
HostPort = Tuple[str, int]

if sys.version_info.minor == 6:
RePattern = Any
RePattern = TypeVar('RePattern', bound=Any)
elif sys.version_info.minor in (7, 8):
RePattern = re.Pattern # type: ignore
RePattern = TypeVar('RePattern', bound=re.Pattern) # type: ignore
else:
RePattern = re.Pattern[Any] # type: ignore
RePattern = TypeVar('RePattern', bound=re.Pattern[Any]) # type: ignore
5 changes: 5 additions & 0 deletions proxy/core/base/tcp_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,3 +240,8 @@ def _optionally_wrap_socket(self, conn: socket.socket) -> TcpOrTlsSocket:
conn = wrap_socket(conn, self.flags.keyfile, self.flags.certfile)
self.work._conn = conn
return conn

@staticmethod
@abstractmethod
def create(*args: Any) -> T:
raise NotImplementedError()
15 changes: 15 additions & 0 deletions proxy/core/work/fd/fd.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
:license: BSD, see LICENSE for more details.
"""
import socket
import asyncio
import logging
from abc import abstractmethod
from typing import Any, TypeVar, Optional

from ...event import eventNames
Expand Down Expand Up @@ -47,3 +49,16 @@ def work(self, *args: Any) -> None:
exc_info=e,
)
self._cleanup(fileno)

@property
@abstractmethod
def loop(self) -> Optional[asyncio.AbstractEventLoop]:
raise NotImplementedError()

@abstractmethod
def receive_from_work_queue(self) -> bool:
raise NotImplementedError()

@abstractmethod
def work_queue_fileno(self) -> Optional[int]:
raise NotImplementedError()
5 changes: 5 additions & 0 deletions proxy/core/work/local.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import queue
import asyncio
import contextlib
from abc import abstractmethod
from typing import Any, Optional

from .threadless import Threadless
Expand Down Expand Up @@ -40,3 +41,7 @@ def receive_from_work_queue(self) -> bool:
return True
self.work(work)
return False

@abstractmethod
def work(self, *args: Any) -> None:
raise NotImplementedError()
5 changes: 5 additions & 0 deletions proxy/core/work/remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
:license: BSD, see LICENSE for more details.
"""
import asyncio
from abc import abstractmethod
from typing import Any, Optional
from multiprocessing import connection

Expand Down Expand Up @@ -37,3 +38,7 @@ def close_work_queue(self) -> None:
def receive_from_work_queue(self) -> bool:
self.work(self.work_queue.recv())
return False

@abstractmethod
def work(self, *args: Any) -> None:
raise NotImplementedError()
2 changes: 1 addition & 1 deletion proxy/http/server/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ def before_routing(self, request: HttpParser) -> Optional[HttpParser]:

def handle_route(self, request: HttpParser, pattern: RePattern) -> Url:
"""Implement this method if you have configured dynamic routes."""
pass
raise NotImplementedError()

def regexes(self) -> List[str]:
"""Helper method to return list of route regular expressions."""
Expand Down
1 change: 1 addition & 0 deletions proxy/plugin/proxy_pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ def handle_upstream_chunk(self, chunk: memoryview) -> Optional[memoryview]:
"""Will never be called since we didn't establish an upstream connection."""
if not self.upstream:
return chunk
# pylint: disable=broad-exception-raised
raise Exception("This should have never been called")

def on_upstream_connection_close(self) -> None:
Expand Down
1 change: 1 addition & 0 deletions proxy/testing/test_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def setUpClass(cls) -> None:
cls.PROXY.flags.plugins[b'HttpProxyBasePlugin'].append(
CacheResponsesPlugin,
)
# pylint: disable=unnecessary-dunder-call
cls.PROXY.__enter__()
assert cls.PROXY.acceptors
cls.wait_for_server(cls.PROXY.flags.port)
Expand Down
29 changes: 20 additions & 9 deletions requirements-testing.txt
Original file line number Diff line number Diff line change
@@ -1,21 +1,32 @@
wheel==0.37.1
python-coveralls==2.9.3
coverage==6.2
coverage==6.2; python_version < '3.11'
coverage==7.4.4; python_version >= '3.11'
flake8==4.0.1
pytest==7.0.1
pytest-cov==3.0.0
pytest-xdist == 2.5.0
pytest-mock==3.6.1
pytest-asyncio==0.16.0
# pytest for Python<3.11
pytest==7.0.1; python_version < '3.11'
pytest-cov==3.0.0; python_version < '3.11'
pytest-xdist==2.5.0; python_version < '3.11'
pytest-mock==3.6.1; python_version < '3.11'
pytest-asyncio==0.16.0; python_version < '3.11'
# pytest for Python>=3.11
pytest==8.1.1; python_version >= '3.11'
pytest-cov==5.0.0; python_version >= '3.11'
pytest-xdist==3.5.0; python_version >= '3.11'
pytest-mock==3.14.0; python_version >= '3.11'
pytest-asyncio==0.21.1; python_version >= '3.11'
autopep8==1.6.0
mypy==0.971
py-spy==0.3.12
tox==3.28.0
tox==3.28.0; python_version < '3.11'
tox==4.14.2; python_version >= '3.11'
mccabe==0.6.1
pylint==2.13.7
pylint==2.13.7; python_version < '3.11'
pylint==3.1.0; python_version >= '3.11'
rope==1.1.1
# Required by test_http2.py
httpx==0.22.0
httpx==0.22.0; python_version < '3.11'
httpx==0.27.0; python_version >= '3.11'
h2==4.1.0
hpack==4.0.0
hyperframe==6.0.1
Expand Down
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ classifiers =
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Programming Language :: Python :: 3.11

Topic :: Internet
Topic :: Internet :: Proxy Servers
Expand Down
18 changes: 15 additions & 3 deletions tests/http/proxy/test_http2.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
:copyright: (c) 2013-present by Abhinav Singh and contributors.
:license: BSD, see LICENSE for more details.
"""
import sys
from typing import Any, Dict

import httpx

from proxy import TestCase
Expand All @@ -17,14 +20,23 @@ class TestHttp2WithProxy(TestCase):

def test_http2_via_proxy(self) -> None:
assert self.PROXY
proxy_url = 'http://localhost:%d' % self.PROXY.flags.port
proxies: Dict[str, Any] = (
{
'proxies': {
'all://': proxy_url,
},
}
# For Python>=3.11, proxies keyword is deprecated by httpx
if sys.version_info < (3, 11, 0)
else {'proxy': proxy_url}
)
response = httpx.get(
'https://www.google.com',
headers={'accept': 'application/json'},
verify=httpx.create_ssl_context(http2=True),
timeout=httpx.Timeout(timeout=5.0),
proxies={
'all://': 'http://localhost:%d' % self.PROXY.flags.port,
},
**proxies,
)
self.assertEqual(response.status_code, 200)

Expand Down
1 change: 0 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,6 @@ deps =
pre-commit
pylint >= 2.5.3
pylint-pytest < 1.1.0
pytest-mock == 3.6.1
-r docs/requirements.in
-r requirements-tunnel.txt
-r requirements-testing.txt
Expand Down
Loading