diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000..106a4748 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,35 @@ +name: Lint + +on: [push, pull_request] + +concurrency: + group: check-${{ github.ref }} + cancel-in-progress: true + +jobs: + test: + name: test with ${{ matrix.py }} on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + py: + - "3.8" + os: + - ubuntu-latest + - macos-latest + - windows-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Setup python for test ${{ matrix.py }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.py }} + - name: Install tox + run: python -m pip install tox-gh>=1.2 + - name: Setup test suite + run: tox -vv --notest + - name: Run test suite + run: tox --skip-pkg-install \ No newline at end of file diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 00000000..7f414711 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,63 @@ +# Based on +# https://packaging.python.org/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/ + +name: Publish Python distribution to PyPI + +on: + release: + types: [published] + push: + branches: [main] + pull_request: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + build: + name: Build distribution + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.x" + - name: Install pypa/build + run: | + python -m pip install --upgrade build + python -m pip list + - name: Build a binary wheel and a source tarball + run: python -m build + - name: Store the distribution packages + uses: actions/upload-artifact@v4 + with: + name: python-package-distributions + path: dist/ + + publish-to-pypi: + name: >- + Publish Python distribution to PyPI + if: github.event_name == 'release' # only publish to PyPI on releases + needs: + - build + runs-on: ubuntu-latest + environment: + name: release + url: https://pypi.org/p/compat-fork-hyper + permissions: + id-token: write # IMPORTANT: mandatory for trusted publishing + + steps: + - name: Download all the dists + uses: actions/download-artifact@v4 + with: + name: python-package-distributions + path: dist/ + - name: Publish distribution to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/hyper/common/headers.py b/hyper/common/headers.py index 655a591a..94f32993 100644 --- a/hyper/common/headers.py +++ b/hyper/common/headers.py @@ -5,12 +5,12 @@ Contains hyper's structures for storing and working with HTTP headers. """ -import collections +import collections.abc from hyper.common.util import to_bytestring, to_bytestring_tuple -class HTTPHeaderMap(collections.MutableMapping): +class HTTPHeaderMap(collections.abc.MutableMapping): """ A structure that contains HTTP headers. diff --git a/hyper/http11/connection.py b/hyper/http11/connection.py index 4311d307..e9523745 100644 --- a/hyper/http11/connection.py +++ b/hyper/http11/connection.py @@ -10,7 +10,7 @@ import socket import base64 -from collections import Iterable, Mapping +from collections.abc import Iterable, Mapping import collections from hyperframe.frame import SettingsFrame diff --git a/hyper/http20/connection.py b/hyper/http20/connection.py index b8be292b..5be9162c 100644 --- a/hyper/http20/connection.py +++ b/hyper/http20/connection.py @@ -32,6 +32,12 @@ import threading import itertools +try: + from h2.settings import ENABLE_PUSH +except ImportError: + from h2.settings import SettingCodes + ENABLE_PUSH = SettingCodes.ENABLE_PUSH + log = logging.getLogger(__name__) DEFAULT_WINDOW_SIZE = 65535 @@ -403,7 +409,7 @@ def _connect_upgrade(self, sock): with self._conn as conn: conn.initiate_upgrade_connection() conn.update_settings( - {h2.settings.ENABLE_PUSH: int(self._enable_push)} + {ENABLE_PUSH: int(self._enable_push)} ) self._send_outstanding_data() @@ -424,7 +430,7 @@ def _send_preamble(self): with self._conn as conn: conn.initiate_connection() conn.update_settings( - {h2.settings.ENABLE_PUSH: int(self._enable_push)} + {ENABLE_PUSH: int(self._enable_push)} ) self._send_outstanding_data() diff --git a/hyper/httplib_compat.py b/hyper/httplib_compat.py index deb07cf6..76a34e9e 100644 --- a/hyper/httplib_compat.py +++ b/hyper/httplib_compat.py @@ -26,7 +26,6 @@ # The HTTPConnection object is currently always the underlying one. HTTPConnection = httplib.HTTPConnection -HTTPSConnection = httplib.HTTPSConnection # If we have NPN support, define our custom one, otherwise just use the # default. @@ -114,3 +113,5 @@ def _delayed_connect(self): self._conn = tempconn return +else: + HTTPSConnection = httplib.HTTPSConnection diff --git a/setup.py b/setup.py index 94cd8d21..4f876b35 100644 --- a/setup.py +++ b/setup.py @@ -69,15 +69,11 @@ def run_tests(self): 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: Implementation :: CPython', ], install_requires=[ - 'h2>=2.4,<3.0,!=2.5.0', 'hyperframe>=3.2,<4.0', 'rfc3986>=1.1.0,<2.0', 'brotlipy>=0.7.0,<1.0' + 'h2>=2.4', 'hyperframe>=3.2', 'rfc3986>=1.1.0', 'brotlipy>=0.7.0' ], tests_require=['pytest', 'requests', 'mock'], cmdclass={'test': PyTest}, @@ -88,15 +84,5 @@ def run_tests(self): }, extras_require={ 'fast': ['pycohttpparser'], - # Fallback to good SSL on bad Python versions. - ':python_full_version < "2.7.9"': [ - 'pyOpenSSL>=0.15', 'service_identity>=14.0.0' - ], - # PyPy with bad SSL modules will likely also need the cryptography - # module at lower than 1.0, because it doesn't support CFFI v1.0 yet. - ':platform_python_implementation == "PyPy" and python_full_version < "2.7.9"': [ - 'cryptography<1.0' - ], - ':python_version == "2.7" or python_version == "3.3"': ['enum34>=1.0.4, <2'] } ) diff --git a/test/test_http11.py b/test/test_http11.py index 9f3fd3d0..af9304b1 100644 --- a/test/test_http11.py +++ b/test/test_http11.py @@ -12,7 +12,7 @@ from collections import namedtuple from io import BytesIO, StringIO -import mock +from unittest import mock import pytest import hyper @@ -986,7 +986,7 @@ def send(self, data): self.queue.append(data) - def recv(self, l): + def recv(self, l): # noqa: E741 data = self._buffer.read(l) self._read_counter += len(data) return memoryview(data) diff --git a/test/test_http20.py b/test/test_http20.py index d187c315..cdc24b9d 100644 --- a/test/test_http20.py +++ b/test/test_http20.py @@ -6,7 +6,7 @@ Unit tests for hyper's HTTP/2.0 implementation. """ import pytest -from mock import patch +from unittest.mock import patch from server import SocketLevelTest diff --git a/test/test_hyper.py b/test/test_hyper.py index b826c63c..dd3b526e 100644 --- a/test/test_hyper.py +++ b/test/test_hyper.py @@ -8,7 +8,7 @@ WindowUpdateFrame, HeadersFrame, ContinuationFrame, GoAwayFrame, PingFrame, FRAME_MAX_ALLOWED_LEN ) -from hpack.hpack_compat import Encoder +from hpack.hpack import Encoder from hyper.common.connection import HTTPConnection from hyper.http20.connection import HTTP20Connection from hyper.http20.response import HTTP20Response, HTTP20Push @@ -29,6 +29,12 @@ import brotli from io import BytesIO +try: + from h2.settings import INITIAL_WINDOW_SIZE +except ImportError: + from h2.settings import SettingCodes + INITIAL_WINDOW_SIZE = SettingCodes.INITIAL_WINDOW_SIZE + TEST_DIR = os.path.abspath(os.path.dirname(__file__)) TEST_CERTS_DIR = os.path.join(TEST_DIR, 'certs') CLIENT_PEM_FILE = os.path.join(TEST_CERTS_DIR, 'nopassword.pem') @@ -766,7 +772,7 @@ def test_incrementing_window_after_close(self): # the default max frame size (16,384 bytes). That will, on the third # frame, trigger the processing to increment the flow control window, # which should then not happen. - f = SettingsFrame(0, settings={h2.settings.INITIAL_WINDOW_SIZE: 100}) + f = SettingsFrame(0, settings={INITIAL_WINDOW_SIZE: 100}) c = HTTP20Connection('www.google.com') c._sock = DummySocket() @@ -1660,7 +1666,7 @@ def send(self, data): sendall = send - def recv(self, l): + def recv(self, l): # noqa: E741 data = self._buffer.read(l) self._read_counter += len(data) return memoryview(data) diff --git a/test/test_integration.py b/test/test_integration.py index 6a9ece42..d98a6978 100644 --- a/test/test_integration.py +++ b/test/test_integration.py @@ -15,7 +15,7 @@ import pytest from socket import timeout as SocketTimeout from contextlib import contextmanager -from mock import patch +from unittest.mock import patch from concurrent.futures import ThreadPoolExecutor, TimeoutError from h2.frame_buffer import FrameBuffer from hyper.compat import ssl diff --git a/test_requirements.txt b/test_requirements.txt index cae2fbc6..49f205dd 100644 --- a/test_requirements.txt +++ b/test_requirements.txt @@ -2,5 +2,3 @@ pytest>=3.0 pytest-xdist pytest-cov requests -mock -futures; python_version < '3.0' diff --git a/tox.ini b/tox.ini index 046619e9..463b44c9 100644 --- a/tox.ini +++ b/tox.ini @@ -1,17 +1,21 @@ [tox] -envlist = py{27,34,35,36}, pypy, lint +envlist = py38, pypy, lint [testenv] deps= -r{toxinidir}/test_requirements.txt commands= - coverage run -m py.test {toxinidir}/test/ + coverage run -m pytest {toxinidir}/test/ coverage report [testenv:pypy] # temporarily disable coverage testing on PyPy due to performance problems -commands= py.test {toxinidir}/test/ +commands= pytest {toxinidir}/test/ [testenv:lint] basepython=python3 -deps = flake8==2.5.4 +deps = flake8==7.1.1 commands = flake8 hyper test + +[gh] +python = + 3.8 = py38,lint