GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — test: guard private client hooks and pin client extras · proxymesh/python-proxy-headers@f5ed79d

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
  • .github/workflows
    • proxy_integration_tests.yml
  • CHANGELOG.md
  • README.md
  • docs
    • index.rst
  • pyproject.toml
  • test_client_internals.py
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎.github/workflows/proxy_integration_tests.yml‎

Copy file name to clipboardExpand all lines: .github/workflows/proxy_integration_tests.yml
+21-12Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1-
# Integration tests against a real proxy (PROXY_URL). Add repository secret PROXY_URL
2-
# under Settings → Secrets and variables → Actions, then mark this workflow as a required
3-
# status check under branch protection (pull_request events only receive secrets for PRs
4-
# from the same repository, not from forks).
1+
# Pull-request checks.
2+
# - unit: Python 3.10 and 3.15, client extras, header tests, the httpx guard,
3+
# and private-hook checks (no PROXY_URL).
4+
# - integration: real proxy via the PROXY_URL secret. pull_request events only
5+
# receive secrets for PRs from this repository, not from forks. Add PROXY_URL
6+
# under Settings → Secrets and variables → Actions, and mark this workflow as
7+
# a required status check under branch protection.
8+
# Runners are ubuntu-24.04. Do not switch these jobs to ubuntu-latest.
59

610
name: Proxy integration tests
711

@@ -14,6 +18,10 @@ permissions:
1418
jobs:
1519
unit:
1620
runs-on: ubuntu-24.04
21+
strategy:
22+
fail-fast: false
23+
matrix:
24+
python-version: ["3.10", "3.15"]
1725
steps:
1826
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
1927
with:
@@ -22,20 +30,22 @@ jobs:
2230
- name: Set up Python
2331
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
2432
with:
25-
python-version: "3.x"
33+
python-version: ${{ matrix.python-version }}
2634

2735
- name: Install system dependencies (pycurl)
2836
run: sudo apt-get update && sudo apt-get install -y libcurl4-openssl-dev
2937

30-
- name: Install package and test dependencies
38+
- name: Install package and compatible client extras
3139
run: |
3240
python -m pip install --upgrade pip
33-
pip install -e .
34-
pip install urllib3 requests aiohttp httpx pycurl
41+
pip install -e ".[urllib3,requests,httpx,aiohttp,pycurl,cloudscraper,autoscraper]"
3542
3643
- name: Run header security and httpx guard tests
3744
run: python test_header_security.py && python test_httpx_guard.py
3845

46+
- name: Run private-hook tests
47+
run: python test_client_internals.py
48+
3949
integration:
4050
runs-on: ubuntu-24.04
4151

@@ -47,16 +57,15 @@ jobs:
4757
- name: Set up Python
4858
uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
4959
with:
50-
python-version: "3.x"
60+
python-version: "3.15"
5161

5262
- name: Install system dependencies (pycurl)
5363
run: sudo apt-get update && sudo apt-get install -y libcurl4-openssl-dev
5464

55-
- name: Install package and test dependencies
65+
- name: Install package and compatible client extras
5666
run: |
5767
python -m pip install --upgrade pip
58-
pip install -e .
59-
pip install urllib3 requests aiohttp httpx cloudscraper autoscraper pycurl
68+
pip install -e ".[urllib3,requests,httpx,aiohttp,pycurl,cloudscraper,autoscraper]"
6069
6170
- name: Require PROXY_URL Actions secret
6271
env:
Collapse file

‎CHANGELOG.md‎

Copy file name to clipboardExpand all lines: CHANGELOG.md
+3Lines changed: 3 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
## Unreleased
44

55
- Importing `python_proxy_headers.httpx_proxy` on httpx >= 1 raises `ImportError`. httpx 1.0 removed proxy support. The `[httpx]` extra is `httpx>=0.28,<1`.
6+
- Optional extras pin the client ranges CI installs: `[urllib3]`, `[requests]`, `[httpx]`, `[aiohttp]`, `[pycurl]`, `[cloudscraper]`, and `[autoscraper]`.
7+
- `requires-python` is `>=3.10`. Python 3.8 and 3.9 are no longer supported. Those clients require 3.10+, and the httpx adapter already uses `str | None`.
8+
- Keywords, trove classifiers, and the changelog URL now point at this file.
69

710
## 0.2.4 - 2026-11-09
811

Collapse file

‎README.md‎

Copy file name to clipboardExpand all lines: README.md
+22-3Lines changed: 22 additions & 3 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,21 @@ This library solves both problems for popular Python HTTP libraries.
3333
pip install python-proxy-headers
3434
```
3535

36-
Then install the HTTP library you want to use (e.g., `pip install requests`).
36+
Or install the client extra you need. Each extra pins a range that CI tests:
3737

38-
> **Note:** This package has no dependencies by default - install only what you need.
38+
```bash
39+
pip install 'python-proxy-headers[requests]'
40+
pip install 'python-proxy-headers[urllib3]'
41+
pip install 'python-proxy-headers[httpx]'
42+
pip install 'python-proxy-headers[aiohttp]'
43+
pip install 'python-proxy-headers[pycurl]'
44+
pip install 'python-proxy-headers[cloudscraper]'
45+
pip install 'python-proxy-headers[autoscraper]'
46+
```
47+
48+
The `httpx` extra is `httpx>=0.28,<1`. Importing `python_proxy_headers.httpx_proxy` on httpx 1.x raises `ImportError`. See the [httpx docs](https://python-proxy-headers.readthedocs.io/en/latest/httpx.html). Proxy header names are specific to your provider; ProxyMesh documents theirs at [docs.proxymesh.com](https://docs.proxymesh.com/).
49+
50+
> **Note:** This package has no required dependencies. Install only the extra you need.
3951
4052
## Quick Start
4153

@@ -122,6 +134,11 @@ response = scraper.get('https://example.com')
122134
A test harness is included to verify proxy header functionality:
123135

124136
```bash
137+
# Header checks and private-hook checks (no proxy credentials)
138+
python test_header_security.py
139+
python test_httpx_guard.py
140+
python test_client_internals.py
141+
125142
# Set your proxy
126143
export PROXY_URL='http://user:[email protected]:8080'
127144

@@ -142,8 +159,10 @@ For detailed documentation, API reference, and more examples:
142159
- **Full Documentation:** [python-proxy-headers.readthedocs.io](https://python-proxy-headers.readthedocs.io/en/latest/)
143160
- **Example Code:** [proxy-examples for Python](https://github.com/proxymesh/proxy-examples/tree/main/python)
144161

145-
## Related Projects
162+
## Other languages
146163

164+
- **[javascript-proxy-headers](https://github.com/proxymesh/javascript-proxy-headers)** - Send and read custom proxy headers on HTTPS CONNECT for Node.js HTTP clients
165+
- **[ruby-proxy-headers](https://github.com/proxymesh/ruby-proxy-headers)** - Send and read custom proxy headers on HTTPS CONNECT for Ruby HTTP clients
147166
- **[scrapy-proxy-headers](https://github.com/proxymesh/scrapy-proxy-headers)** - Proxy header support for Scrapy
148167

149168
## About
Collapse file

‎docs/index.rst‎

Copy file name to clipboardExpand all lines: docs/index.rst
+10-3Lines changed: 10 additions & 3 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,27 @@ None of these modules provide good support for parsing custom response headers f
2525

2626
If you are looking for `Scrapy <https://scrapy.org/>`_ support, please see our `scrapy-proxy-headers <https://github.com/proxymesh/scrapy-proxy-headers>`_ project.
2727

28+
The same CONNECT header support in other languages:
29+
30+
* `javascript-proxy-headers <https://github.com/proxymesh/javascript-proxy-headers>`_
31+
* `ruby-proxy-headers <https://github.com/proxymesh/ruby-proxy-headers>`_
32+
2833
Installation
2934
------------
3035

3136
To use these extension modules, you must first do the following:
3237

33-
1. Install the package:
38+
1. Install the package, optionally with the client you use:
3439

3540
.. code-block:: bash
3641
3742
pip install python-proxy-headers
43+
pip install 'python-proxy-headers[requests]'
44+
pip install 'python-proxy-headers[httpx]'
3845
39-
2. Install the appropriate package based on the Python module you want to use.
46+
2. Or install the HTTP library yourself. Extras pin the ranges we test. The ``httpx`` extra is ``httpx>=0.28,<1``. Importing it on httpx 1.x raises ``ImportError``; see :doc:`httpx`. Proxy-specific headers are documented by your provider. ProxyMesh's are at `docs.proxymesh.com <https://docs.proxymesh.com/>`_.
4047

41-
This package does not have any dependencies because we don't know which module you want to use.
48+
This package does not have any required dependencies because we don't know which module you want to use.
4249

4350
You can also find more example code in our `proxy-examples for python <https://github.com/proxymesh/proxy-examples/tree/main/python>`_.
4451

Collapse file

‎pyproject.toml‎

Copy file name to clipboardExpand all lines: pyproject.toml
+37-5Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,54 @@ authors = [
1010
]
1111
description = "Handle custom proxy headers for http & https requests in various python libraries"
1212
readme = "README.md"
13-
requires-python = ">=3.8"
13+
requires-python = ">=3.10"
14+
keywords = [
15+
"proxy",
16+
"proxy-headers",
17+
"https-proxy",
18+
"connect",
19+
"tunnel",
20+
"requests",
21+
"urllib3",
22+
"httpx",
23+
"aiohttp",
24+
"web-scraping",
25+
"proxymesh",
26+
]
1427
classifiers = [
28+
"Development Status :: 4 - Beta",
1529
"Programming Language :: Python :: 3",
30+
"Programming Language :: Python :: 3 :: Only",
31+
"Programming Language :: Python :: 3.10",
32+
"Programming Language :: Python :: 3.11",
33+
"Programming Language :: Python :: 3.12",
34+
"Programming Language :: Python :: 3.13",
35+
"Programming Language :: Python :: 3.14",
36+
"Programming Language :: Python :: 3.15",
1637
"Operating System :: OS Independent",
17-
"License :: OSI Approved :: MIT License",
18-
"Intended Audience :: Developers",
19-
"Topic :: Internet :: WWW/HTTP",
38+
"License :: OSI Approved :: MIT License",
39+
"Intended Audience :: Developers",
40+
"Topic :: Internet :: WWW/HTTP",
41+
"Topic :: Internet :: Proxy Servers",
2042
"Topic :: Software Development :: Libraries :: Python Modules",
43+
"Framework :: AsyncIO",
2144
]
2245

46+
# Lower bounds are the oldest minor whose private hooks still match the
47+
# adapters. CI installs the newest release inside each range. httpx 1.x is
48+
# excluded: 1.0.dev6 drops httpcore and proxy support (encode/httpx#947).
2349
[project.optional-dependencies]
50+
urllib3 = ["urllib3>=2.2,<3"]
51+
requests = ["requests>=2.31,<3"]
2452
httpx = ["httpx>=0.28,<1"]
53+
aiohttp = ["aiohttp>=3.10,<4"]
54+
pycurl = ["pycurl>=7.45,<8"]
55+
cloudscraper = ["cloudscraper>=1.2.71,<2"]
56+
autoscraper = ["autoscraper>=1.1.14,<2"]
2557

2658
[project.urls]
2759
Homepage = "https://github.com/proxymesh/python-proxy-headers"
28-
Changelog = "https://github.com/proxymesh/python-proxy-headers/commits/main/"
60+
Changelog = "https://github.com/proxymesh/python-proxy-headers/blob/main/CHANGELOG.md"
2961
Issues = "https://github.com/proxymesh/python-proxy-headers/issues"
3062
Documentation = "https://python-proxy-headers.readthedocs.io/en/latest/"
3163
ProxyMesh = "https://proxymesh.com"
Collapse file

‎test_client_internals.py‎

Copy file name to clipboard
+86Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
"""The private hooks this package overrides are still present upstream."""
2+
3+
import http.client
4+
import inspect
5+
import sys
6+
import unittest
7+
8+
from aiohttp.connector import TCPConnector
9+
from httpcore._async.http_proxy import AsyncHTTPProxy, AsyncTunnelHTTPConnection
10+
from httpcore._sync.http_proxy import HTTPProxy, TunnelHTTPConnection
11+
from urllib3.connectionpool import HTTPSConnectionPool
12+
13+
from python_proxy_headers.aiohttp_proxy import ProxyTCPConnector
14+
from python_proxy_headers.httpx_proxy import (
15+
AsyncHTTPProxyHeaders,
16+
AsyncProxyTunnelHTTPConnection,
17+
HTTPProxyHeaders,
18+
ProxyTunnelHTTPConnection,
19+
)
20+
from python_proxy_headers.urllib3_proxy_manager import (
21+
HTTPSProxyConnection,
22+
HTTPSProxyConnectionPool,
23+
)
24+
25+
26+
class HookTests(unittest.TestCase):
27+
def test_urllib3_prepare_proxy(self) -> None:
28+
self.assertIsNot(
29+
HTTPSProxyConnectionPool._prepare_proxy,
30+
HTTPSConnectionPool._prepare_proxy,
31+
)
32+
self.assertEqual(
33+
list(inspect.signature(HTTPSProxyConnectionPool._prepare_proxy).parameters),
34+
list(inspect.signature(HTTPSConnectionPool._prepare_proxy).parameters),
35+
)
36+
self.assertTrue(callable(http.client._read_headers))
37+
if sys.version_info < (3, 12):
38+
self.assertTrue(callable(HTTPSProxyConnection._tunnel))
39+
else:
40+
self.assertTrue(callable(http.client.HTTPConnection._tunnel))
41+
self.assertTrue(
42+
callable(http.client.HTTPConnection.get_proxy_response_headers)
43+
)
44+
45+
def test_httpx_handle_request(self) -> None:
46+
self.assertIsNot(
47+
ProxyTunnelHTTPConnection.handle_request,
48+
TunnelHTTPConnection.handle_request,
49+
)
50+
self.assertIsNot(
51+
AsyncProxyTunnelHTTPConnection.handle_async_request,
52+
AsyncTunnelHTTPConnection.handle_async_request,
53+
)
54+
self.assertIsNot(
55+
HTTPProxyHeaders.create_connection,
56+
HTTPProxy.create_connection,
57+
)
58+
self.assertIsNot(
59+
AsyncHTTPProxyHeaders.create_connection,
60+
AsyncHTTPProxy.create_connection,
61+
)
62+
params = inspect.signature(TunnelHTTPConnection.__init__).parameters
63+
for name in (
64+
"proxy_origin",
65+
"remote_origin",
66+
"ssl_context",
67+
"proxy_ssl_context",
68+
"proxy_headers",
69+
"keepalive_expiry",
70+
"http1",
71+
"http2",
72+
"network_backend",
73+
):
74+
self.assertIn(name, params)
75+
76+
def test_aiohttp_create_proxy_connection(self) -> None:
77+
self.assertEqual(
78+
list(
79+
inspect.signature(ProxyTCPConnector._create_proxy_connection).parameters
80+
),
81+
list(inspect.signature(TCPConnector._create_proxy_connection).parameters),
82+
)
83+
84+
85+
if __name__ == "__main__":
86+
unittest.main()

0 commit comments

Comments
0 (0)
You are visitor 01402520 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.