From 3c9729d70205b8261bcc2bceafb68c97ad6b8cba Mon Sep 17 00:00:00 2001 From: Christian Clauss Date: Thu, 17 Sep 2026 19:38:53 +0200 Subject: [PATCH] ci: Test httpx and httpx2 side by side --- .github/workflows/ci.yml | 49 ++++++++++++++++++++++---------------- httpx_gssapi/__init__.py | 2 +- httpx_gssapi/exceptions.py | 6 ++++- httpx_gssapi/gssapi_.py | 9 +++++-- setup.cfg | 3 ++- tests/conftest.py | 10 +++++++- tests/test_end_to_end.py | 7 +++++- tests/test_mocked.py | 6 ++++- 8 files changed, 64 insertions(+), 28 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f67e86f..b5130f6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,14 +14,14 @@ jobs: name: Flake8 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5.0.0 + - uses: actions/checkout@v7 - name: Set Up Python - uses: actions/setup-python@v6.0.0 + uses: actions/setup-python@v7 with: python-version: '3.10' - name: Install dependencies run: | - python -m pip install -U pip + python -m pip install --upgrade pip python -m pip install flake8 - name: Check run: python -m flake8 @@ -30,16 +30,18 @@ jobs: name: MyPy runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5.0.0 + - uses: actions/checkout@v7 - name: Setup Build Tools - run: sudo apt-get install -y libkrb5-dev krb5-user krb5-kdc krb5-admin-server krb5-multidev + run: | + sudo apt-get update -qq + sudo apt-get install --yes libkrb5-dev krb5-user krb5-kdc krb5-admin-server krb5-multidev - name: Set Up Python - uses: actions/setup-python@v6.0.0 + uses: actions/setup-python@v7 with: - python-version: '3.13' + python-version: '3.x' - name: Install dependencies run: | - python -m pip install -U pip + python -m pip install --upgrade pip python -m pip install mypy .[all] - name: Check run: python -m mypy --install-types --non-interactive @@ -51,22 +53,29 @@ jobs: - mypy runs-on: ubuntu-latest strategy: + fail-fast: false matrix: - python: [ '3.9', '3.10', '3.11', '3.12', '3.13'] + python: [ '3.9', '3.10', '3.11', '3.12', '3.13', '3.14' , '3.15' ] + httpx: [ 'httpx', 'httpx2' ] steps: - - uses: actions/checkout@v5.0.0 + - uses: actions/checkout@v7 - name: Setup Build Tools - run: sudo apt-get install -y libkrb5-dev krb5-user krb5-kdc krb5-admin-server krb5-multidev + run: | + sudo apt-get update -qq + sudo apt-get install --yes libkrb5-dev krb5-user krb5-kdc krb5-admin-server krb5-multidev - name: Set Up Python - uses: actions/setup-python@v6.0.0 + uses: actions/setup-python@v7 with: + allow-prereleases: true python-version: ${{ matrix.python }} - name: Install dependencies run: | - python -m pip install -U pip - python -m pip install -U setuptools wheel - python -m pip install -U .[all] pytest-cov codecov + python -m pip install --upgrade pip + python -m pip install --upgrade setuptools wheel + python -m pip install --upgrade .[all] pytest-cov codecov - name: Run Tests + env: + HTTPX: ${{ matrix.httpx }} run: python -m pytest --cov=httpx_gssapi --cov-report=xml - name: Upload Coverage run: python -m codecov --token=${{ secrets.CODECOV_TOKEN }} @@ -78,17 +87,17 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Code - uses: actions/checkout@v5.0.0 + uses: actions/checkout@v7 with: fetch-depth: 0 - name: Set Up Python - uses: actions/setup-python@v6.0.0 + uses: actions/setup-python@v7 with: - python-version: '3.10' + python-version: '3.x' - name: Install dependencies run: | - python -m pip install -U pip - python -m pip install -U setuptools wheel + python -m pip install --upgrade pip + python -m pip install --upgrade setuptools wheel - name: Build run: python setup.py sdist bdist_wheel - name: Change Log diff --git a/httpx_gssapi/__init__.py b/httpx_gssapi/__init__.py index 3c53224..36a3da5 100644 --- a/httpx_gssapi/__init__.py +++ b/httpx_gssapi/__init__.py @@ -7,7 +7,7 @@ of requests_gssapi to HTTPX and adds optional GSSAPI authentication support and supports mutual authentication. Basic GET usage: - >>> import httpx + >>> import httpx # or `import httpx2` >>> from httpx_gssapi import HTTPSPNEGOAuth >>> r = httpx.get("http://example.org", auth=HTTPSPNEGOAuth()) diff --git a/httpx_gssapi/exceptions.py b/httpx_gssapi/exceptions.py index 86a6868..daaf36d 100644 --- a/httpx_gssapi/exceptions.py +++ b/httpx_gssapi/exceptions.py @@ -6,9 +6,13 @@ """ +from os import getenv from typing import Optional -from httpx import RequestError, Request, Response +if getenv("HTTPX") == "httpx2": + from httpx2 import RequestError, Request, Response +else: + from httpx import RequestError, Request, Response # type: ignore[assignment] class MutualAuthenticationError(RequestError): diff --git a/httpx_gssapi/gssapi_.py b/httpx_gssapi/gssapi_.py index 0f5a99b..a9e70a3 100644 --- a/httpx_gssapi/gssapi_.py +++ b/httpx_gssapi/gssapi_.py @@ -2,6 +2,7 @@ import logging from itertools import chain from functools import wraps +from os import getenv from typing import Generator, Optional, List, Any, Union from base64 import b64encode, b64decode @@ -10,8 +11,12 @@ from gssapi import SecurityContext from gssapi.exceptions import GSSError -import httpx -from httpx import Auth, Request, Response +if getenv("HTTPX") == "httpx2": + import httpx2 as httpx + from httpx2 import Auth, Request, Response +else: + import httpx # type: ignore[no-redef] + from httpx import Auth, Request, Response # type: ignore[assignment] from .exceptions import MutualAuthenticationError, SPNEGOExchangeError diff --git a/setup.cfg b/setup.cfg index 8f63dea..dc80528 100644 --- a/setup.cfg +++ b/setup.cfg @@ -26,6 +26,7 @@ python_requires = >=3.9 packages = httpx_gssapi install_requires = httpx>=0.16,<0.29 + httpx2 gssapi [options.extras_require] @@ -81,7 +82,7 @@ packages = tests [tox:tox] -envlist = py36, py37, py38, py39, py310 +envlist = py39, py310, py311, py312, py313, py314, py315 [testenv] deps = diff --git a/tests/conftest.py b/tests/conftest.py index 79fc925..945c43d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -109,7 +109,15 @@ def http_server_port() -> int: @pytest.fixture(scope='session') def http_server(request, krb_realm: k5test.K5Realm, http_server_port: int): - ps = mp.Process( + # Python 3.14 changed the default multiprocessing start method on POSIX + # from "fork" to "forkserver". "forkserver" (and "spawn") pickle the + # target's arguments to hand them to the worker process, but the + # ``k5test.K5Realm`` object holds an unpicklable ``threading.Lock`` + # (``TypeError: cannot pickle '_thread.lock' object``). Force the classic + # "fork" method so the realm is inherited via memory instead of pickled. + # These tests are POSIX/Kerberos-only, so "fork" is always available here. + ctx = mp.get_context('fork') + ps = ctx.Process( target=start_http_server, args=(krb_realm,), kwargs={'port': http_server_port}, diff --git a/tests/test_end_to_end.py b/tests/test_end_to_end.py index 96e5cc8..4dc70dd 100644 --- a/tests/test_end_to_end.py +++ b/tests/test_end_to_end.py @@ -1,7 +1,12 @@ #!/usr/bin/env python """Tests for httpx_gssapi.""" -import httpx +from os import getenv + +if getenv("HTTPX") == "httpx2": + import httpx2 as httpx +else: + import httpx # type: ignore[no-redef] import pytest import httpx_gssapi diff --git a/tests/test_mocked.py b/tests/test_mocked.py index 770bfc3..7bc0508 100644 --- a/tests/test_mocked.py +++ b/tests/test_mocked.py @@ -3,11 +3,15 @@ import logging from base64 import b64encode +from os import getenv from unittest.mock import Mock, patch import pytest -import httpx +if getenv("HTTPX") == "httpx2": + import httpx2 as httpx +else: + import httpx # type: ignore[no-redef] import gssapi import gssapi.exceptions