You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@skywalking.apache.org by wu...@apache.org on 2026/04/12 00:34:07 UTC
(skywalking-python) 01/01: feat(plugin): add urllib3 2.x support for Python 3.12+
This is an automated email from the ASF dual-hosted git repository.
wusheng pushed a commit to branch feat/urllib3-v2-plugin
in repository https://gitbox.apache.org/repos/asf/skywalking-python.git
commit a64c4e951cf8d27b22b46b19097a4f8170040814
Author: Wu Sheng <wu...@foxmail.com>
AuthorDate: Sun Apr 12 08:33:54 2026 +0800
feat(plugin): add urllib3 2.x support for Python 3.12+
urllib3 2.x removed `urllib3.request.RequestMethods` which the existing
plugin hooks. Add a new plugin `sw_urllib3_v2` that hooks
`PoolManager.request` directly (the 2.x entry point).
Auto-detection logic:
- sw_urllib3: tries `from urllib3.request import RequestMethods`.
Succeeds on 1.x, fails on 2.x (skipped).
- sw_urllib3_v2: checks if `RequestMethods` exists.
If yes (1.x), returns early. If no (2.x), hooks PoolManager.
Both plugins share the same test directory. The test merges
version vectors from both plugins' support_matrix.
Verified locally on Python 3.13:
- urllib3==2.3 PASSED (span validation)
- urllib3==2.0 PASSED (span validation)
Co-Authored-By: Claude Opus 4.6 (1M context) <no...@anthropic.com>
---
docs/en/setup/Plugins.md | 3 +++
skywalking/plugins/sw_urllib3.py | 4 ++--
.../plugins/{sw_urllib3.py => sw_urllib3_v2.py} | 26 +++++++++++++++-------
tests/plugin/http/sw_urllib3/test_urllib3.py | 9 ++++++--
4 files changed, 30 insertions(+), 12 deletions(-)
diff --git a/docs/en/setup/Plugins.md b/docs/en/setup/Plugins.md
index 0aa38a3..baea214 100644
--- a/docs/en/setup/Plugins.md
+++ b/docs/en/setup/Plugins.md
@@ -47,6 +47,7 @@ or a limitation of SkyWalking auto-instrumentation (welcome to contribute!)
| [sanic](https://sanic.readthedocs.io/en/latest) | Python >=3.10 - NOT SUPPORTED YET; Python >=3.7 - ['20.12']; | `sw_sanic` |
| [tornado](https://www.tornadoweb.org) | Python >=3.14 - ['6.4']; Python >=3.10 - ['6.0', '6.1']; | `sw_tornado` |
| [urllib3](https://urllib3.readthedocs.io/en/latest/) | Python >=3.12 - NOT SUPPORTED YET; Python >=3.10 - ['1.26', '1.25']; | `sw_urllib3` |
+| [urllib3](https://urllib3.readthedocs.io/en/latest/) | Python >=3.12 - ['2.3', '2.0']; | `sw_urllib3_v2` |
| [urllib_request](https://docs.python.org/3/library/urllib.request.html) | Python >=3.7 - ['*']; | `sw_urllib_request` |
| [websockets](https://websockets.readthedocs.io) | Python >=3.7 - ['10.3', '10.4']; | `sw_websockets` |
### Notes
@@ -58,6 +59,8 @@ Hug is believed to be abandoned project, use this plugin with a bit more caution
Instead of Hug, plugin test should move to test actual Falcon.
- The Neo4j plugin integrates neo4j python driver 5.x.x versions which
support both Neo4j 5 and 4.4 DBMS.
+- urllib3 1.x plugin. For urllib3 2.x, see sw_urllib3_v2.
+- urllib3 2.x plugin. For urllib3 1.x, see sw_urllib3.
- The websocket instrumentation only traces client side connection handshake,
the actual message exchange (send/recv) is not traced since injecting headers to socket message
body is the only way to propagate the trace context, which requires customization of message structure
diff --git a/skywalking/plugins/sw_urllib3.py b/skywalking/plugins/sw_urllib3.py
index ae57b9c..56f7f73 100644
--- a/skywalking/plugins/sw_urllib3.py
+++ b/skywalking/plugins/sw_urllib3.py
@@ -23,11 +23,11 @@ from skywalking.trace.tags import TagHttpMethod, TagHttpURL, TagHttpStatusCode
link_vector = ['https://urllib3.readthedocs.io/en/latest/']
support_matrix = {
'urllib3': {
- '>=3.12': [], # urllib3 2.x removed urllib3.request.RequestMethods, plugin needs adaptation
+ '>=3.12': [], # urllib3 1.x can't install on 3.12+, see sw_urllib3_v2 for 2.x
'>=3.10': ['1.26', '1.25'],
}
}
-note = """"""
+note = """urllib3 1.x plugin. For urllib3 2.x, see sw_urllib3_v2."""
def install():
diff --git a/skywalking/plugins/sw_urllib3.py b/skywalking/plugins/sw_urllib3_v2.py
similarity index 68%
copy from skywalking/plugins/sw_urllib3.py
copy to skywalking/plugins/sw_urllib3_v2.py
index ae57b9c..1e8e3e0 100644
--- a/skywalking/plugins/sw_urllib3.py
+++ b/skywalking/plugins/sw_urllib3_v2.py
@@ -23,19 +23,27 @@ from skywalking.trace.tags import TagHttpMethod, TagHttpURL, TagHttpStatusCode
link_vector = ['https://urllib3.readthedocs.io/en/latest/']
support_matrix = {
'urllib3': {
- '>=3.12': [], # urllib3 2.x removed urllib3.request.RequestMethods, plugin needs adaptation
- '>=3.10': ['1.26', '1.25'],
+ '>=3.12': ['2.3', '2.0'],
}
}
-note = """"""
+note = """urllib3 2.x plugin. For urllib3 1.x, see sw_urllib3."""
def install():
- from urllib3.request import RequestMethods
+ from urllib3 import PoolManager
- _request = RequestMethods.request
+ # urllib3 2.x removed RequestMethods base class;
+ # PoolManager.request is the direct entry point.
+ # Guard: if RequestMethods still exists, this is urllib3 1.x — let sw_urllib3 handle it.
+ try:
+ from urllib3.request import RequestMethods # noqa: F401
+ return # urllib3 1.x detected, skip — sw_urllib3 handles it
+ except ImportError:
+ pass # urllib3 2.x, proceed
- def _sw_request(this: RequestMethods, method, url, fields=None, headers=None, **urlopen_kw):
+ _request = PoolManager.request
+
+ def _sw_request(this: PoolManager, method, url, body=None, fields=None, headers=None, json=None, **urlopen_kw):
from skywalking.utils.filter import sw_urlparse
url_param = sw_urlparse(url)
@@ -50,13 +58,15 @@ def install():
if headers is None:
headers = {}
+ else:
+ headers = dict(headers)
for item in carrier:
headers[item.key] = item.val
span.tag(TagHttpMethod(method.upper()))
span.tag(TagHttpURL(url_param.geturl()))
- res = _request(this, method, url, fields=fields, headers=headers, **urlopen_kw)
+ res = _request(this, method, url, body=body, fields=fields, headers=headers, json=json, **urlopen_kw)
span.tag(TagHttpStatusCode(res.status))
if res.status >= 400:
@@ -64,4 +74,4 @@ def install():
return res
- RequestMethods.request = _sw_request
+ PoolManager.request = _sw_request
diff --git a/tests/plugin/http/sw_urllib3/test_urllib3.py b/tests/plugin/http/sw_urllib3/test_urllib3.py
index 082cd33..2fff31d 100644
--- a/tests/plugin/http/sw_urllib3/test_urllib3.py
+++ b/tests/plugin/http/sw_urllib3/test_urllib3.py
@@ -19,10 +19,15 @@ from typing import Callable
import pytest
import requests
-from skywalking.plugins.sw_urllib3 import support_matrix
+from skywalking.plugins.sw_urllib3 import support_matrix as v1_matrix
+from skywalking.plugins.sw_urllib3_v2 import support_matrix as v2_matrix
from tests.orchestrator import get_test_vector
from tests.plugin.base import TestPluginBase
+# Merge v1 and v2 test vectors — get_test_vector returns versions for the current Python
+_versions = (get_test_vector(lib_name='urllib3', support_matrix=v1_matrix)
+ + get_test_vector(lib_name='urllib3', support_matrix=v2_matrix))
+
@pytest.fixture
def prepare():
@@ -31,6 +36,6 @@ def prepare():
class TestPlugin(TestPluginBase):
- @pytest.mark.parametrize('version', get_test_vector(lib_name='urllib3', support_matrix=support_matrix))
+ @pytest.mark.parametrize('version', _versions)
def test_plugin(self, docker_compose, version):
self.validate()