|
| 1 | +"""`mcp.shared._lazy_submodules`: package attribute access imports the submodule of that name.""" |
| 2 | + |
| 3 | +import subprocess |
| 4 | +import sys |
| 5 | +from types import ModuleType |
| 6 | + |
| 7 | +import pytest |
| 8 | +from inline_snapshot import snapshot |
| 9 | + |
| 10 | +import tests.shared.lazy_submodule_fixture as fixture_pkg |
| 11 | + |
| 12 | + |
| 13 | +def test_package_attribute_imports_the_submodule_of_that_name(): |
| 14 | + """SDK-defined: `pkg.<name>` imports `pkg.<name>` on first touch and binds it on the package.""" |
| 15 | + submodule = getattr(fixture_pkg, "fine") |
| 16 | + assert isinstance(submodule, ModuleType) |
| 17 | + assert submodule.MARKER == "ok" |
| 18 | + assert submodule is sys.modules["tests.shared.lazy_submodule_fixture.fine"] |
| 19 | + assert vars(fixture_pkg)["fine"] is submodule |
| 20 | + |
| 21 | + |
| 22 | +def test_missing_submodule_raises_attribute_error(): |
| 23 | + """SDK-defined: a name with no such submodule is a plain AttributeError.""" |
| 24 | + with pytest.raises(AttributeError): |
| 25 | + getattr(fixture_pkg, "no_such_submodule") |
| 26 | + |
| 27 | + |
| 28 | +def test_dunder_probe_raises_attribute_error_without_a_submodule_search(): |
| 29 | + """SDK-defined: dunder names are protocol probes, never submodules.""" |
| 30 | + with pytest.raises(AttributeError): |
| 31 | + getattr(fixture_pkg, "__no_such_dunder__") |
| 32 | + |
| 33 | + |
| 34 | +def test_submodule_with_a_missing_dependency_surfaces_the_real_import_error(): |
| 35 | + """SDK-defined: an existing submodule that fails to import raises its own error, not AttributeError.""" |
| 36 | + with pytest.raises(ModuleNotFoundError) as exc_info: |
| 37 | + getattr(fixture_pkg, "broken") |
| 38 | + assert exc_info.value.name == "definitely_not_installed_dependency_xyz" |
| 39 | + |
| 40 | + |
| 41 | +def test_bare_import_mcp_still_resolves_deep_submodule_chains(): |
| 42 | + """SDK-defined: after only `import mcp`, `mcp.client.stdio.stdio_client` etc. still resolve. |
| 43 | +
|
| 44 | + A fresh interpreter is required: this process already imported those modules. |
| 45 | + """ |
| 46 | + probe = ( |
| 47 | + "import mcp\n" |
| 48 | + "print([mcp.client.stdio.stdio_client.__name__, mcp.server.stdio.stdio_server.__name__,\n" |
| 49 | + " mcp.shared.memory.__name__, mcp.os.posix.__name__])\n" |
| 50 | + ) |
| 51 | + result = subprocess.run([sys.executable, "-c", probe], capture_output=True, text=True, check=False, timeout=20) |
| 52 | + assert result.returncode == 0, result.stderr |
| 53 | + assert result.stdout == snapshot("['stdio_client', 'stdio_server', 'mcp.shared.memory', 'mcp.os.posix']\n") |
0 commit comments