Skip to content

Commit 0ccc72d

Browse files
dsfacciniclaude
andcommitted
Mirror Starlette docstring; rewrite shim tests in logfire's reload+patch.dict style
Test diff: -33 / +12. Replaces the bespoke `builtins.__import__` monkeypatch with `mock.patch.dict('sys.modules', {'httpx2': None | <fake>})` + `importlib.reload`, matching `logfire/tests/otel_integrations/test_httpx.py::test_missing_opentelemetry_dependency`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 240b701 commit 0ccc72d

2 files changed

Lines changed: 25 additions & 44 deletions

File tree

src/mcp/shared/exceptions.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,12 @@ def __str__(self) -> str:
4242

4343

4444
class MCPDeprecationWarning(UserWarning):
45-
"""Deprecation warning emitted by the `mcp` package.
45+
"""A custom deprecation warning for MCP.
4646
47-
Subclasses `UserWarning` (not `DeprecationWarning`) so it is visible by default —
48-
`DeprecationWarning` is silenced at the Python level for non-`__main__` callers.
47+
Unlike the built-in DeprecationWarning, this inherits from UserWarning to ensure it is visible by default, helping
48+
users discover deprecated features without needing to enable warnings explicitly.
49+
50+
Reference: https://sethmlarson.dev/deprecations-via-warnings-dont-work-for-python-libraries
4951
"""
5052

5153

tests/shared/test_httpx_shim.py

Lines changed: 20 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,40 @@
11
"""Tests for the `httpx` → `httpx2` migration shim in `mcp.shared._httpx`.
22
33
`mcp` prefers `httpx2` and falls back to `httpx` with an `MCPDeprecationWarning` emitted at
4-
the shim's import time. Today the lockfile pins `httpx` (not `httpx2`), so importing the shim
5-
exercises the fallback.
4+
the shim's import time. The lockfile pins `httpx` (not `httpx2`), so the canonical state of
5+
the shim is the fallback path.
66
"""
77

88
from __future__ import annotations
99

1010
import importlib
11-
import sys
1211
import warnings
12+
from unittest import mock
1313

1414
import pytest
1515

16+
import mcp.shared._httpx
1617
from mcp.shared.exceptions import MCPDeprecationWarning
1718

1819

19-
def _force_reimport_shim() -> None:
20-
"""Drop the cached shim module so the next import re-runs its top-level code."""
21-
sys.modules.pop("mcp.shared._httpx", None)
20+
@pytest.fixture(autouse=True)
21+
def _restore_shim_state():
22+
"""Reload the shim after each test so a simulated `httpx2` doesn't leak into later tests."""
23+
yield
24+
importlib.reload(mcp.shared._httpx)
2225

2326

24-
def test_fallback_emits_warning_at_import(monkeypatch: pytest.MonkeyPatch) -> None:
25-
"""With only `httpx` installed, importing the shim emits `MCPDeprecationWarning`."""
26-
monkeypatch.delitem(sys.modules, "httpx2", raising=False)
27-
_force_reimport_shim()
27+
def test_fallback_emits_warning() -> None:
28+
with mock.patch.dict("sys.modules", {"httpx2": None}):
29+
with pytest.warns(MCPDeprecationWarning, match=r"install `httpx2` instead"):
30+
importlib.reload(mcp.shared._httpx)
2831

29-
from collections.abc import Mapping, Sequence
3032

31-
real_import = __import__
33+
def test_httpx2_present_is_silent() -> None:
34+
import httpx
3235

33-
def fake_import(
34-
name: str,
35-
globals: Mapping[str, object] | None = None,
36-
locals: Mapping[str, object] | None = None,
37-
fromlist: Sequence[str] = (),
38-
level: int = 0,
39-
) -> object:
40-
if name == "httpx2":
41-
raise ImportError("simulated: httpx2 not installed")
42-
return real_import(name, globals, locals, fromlist, level)
43-
44-
monkeypatch.setattr("builtins.__import__", fake_import)
45-
with pytest.warns(MCPDeprecationWarning, match=r"install `httpx2` instead"):
46-
importlib.import_module("mcp.shared._httpx")
47-
48-
49-
def test_httpx2_present_is_silent(monkeypatch: pytest.MonkeyPatch) -> None:
50-
"""When `httpx2` is importable, the shim selects it and emits no warning."""
51-
import httpx as real_httpx
52-
53-
monkeypatch.setitem(sys.modules, "httpx2", real_httpx)
54-
_force_reimport_shim()
55-
56-
with warnings.catch_warnings(record=True) as caught:
57-
warnings.simplefilter("always", MCPDeprecationWarning)
58-
reloaded = importlib.import_module("mcp.shared._httpx")
59-
60-
assert reloaded.httpx is real_httpx
61-
assert [w for w in caught if issubclass(w.category, MCPDeprecationWarning)] == []
36+
with mock.patch.dict("sys.modules", {"httpx2": httpx}):
37+
with warnings.catch_warnings():
38+
warnings.simplefilter("error", MCPDeprecationWarning)
39+
importlib.reload(mcp.shared._httpx)
40+
assert mcp.shared._httpx.httpx is httpx

0 commit comments

Comments
 (0)