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 <[email protected]> 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) <[email protected]> --- 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()
