Skip to content

Commit edd0eb7

Browse files
committed
test: measure packages introduced by isolated imports
1 parent 128faab commit edd0eb7

2 files changed

Lines changed: 45 additions & 2 deletions

File tree

‎tests/unittests/isolated_import_utils.py‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,11 @@ def run_isolated(source: str) -> subprocess.CompletedProcess[str]:
5656

5757

5858
def loaded_top_level_packages(source: str) -> frozenset[str]:
59-
"""Returns the third-party top-level packages source leaves imported.
59+
"""Returns third-party top-level packages introduced by source.
60+
61+
Interpreter startup hooks can import packages before source runs. Those
62+
modules are not costs introduced by source. Snapshot full module names so a
63+
newly imported submodule still counts even when its parent was preloaded.
6064
6165
Standard-library modules, private modules and the pseudo-modules the
6266
interpreter injects carry no install or startup cost of their own, so they
@@ -70,12 +74,14 @@ def loaded_top_level_packages(source: str) -> frozenset[str]:
7074
result = run_isolated(f"""
7175
import json
7276
import sys
77+
modules_before = set(sys.modules)
7378
{source}
7479
7580
names = {{
7681
name.partition('.')[0]
7782
for name, module in sys.modules.items()
78-
if getattr(module, '__spec__', None) is not None
83+
if name not in modules_before
84+
and getattr(module, '__spec__', None) is not None
7985
and module.__spec__.origin is not None
8086
}}
8187
print(json.dumps(sorted(

‎tests/unittests/test_import_loading.py‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,43 @@ def test_entry_point_loads_only_allowlisted_packages(statement: str) -> None:
247247
)
248248

249249

250+
@pytest.fixture
251+
def preloaded_startup_package(tmp_path, monkeypatch):
252+
package = tmp_path / 'startup_package'
253+
package.mkdir()
254+
(package / '__init__.py').write_text('value = 1\n')
255+
(package / 'extra.py').write_text('value = 2\n')
256+
(tmp_path / 'sitecustomize.py').write_text('import startup_package\n')
257+
monkeypatch.setenv('PYTHONPATH', str(tmp_path))
258+
return tmp_path
259+
260+
261+
def test_import_measurement_excludes_interpreter_startup(
262+
preloaded_startup_package,
263+
):
264+
assert (
265+
loaded_top_level_packages("assert 'startup_package' in sys.modules")
266+
== frozenset()
267+
)
268+
269+
270+
def test_import_measurement_detects_new_package_after_startup(
271+
preloaded_startup_package,
272+
):
273+
(preloaded_startup_package / 'new_package.py').write_text('value = 3\n')
274+
assert loaded_top_level_packages(
275+
"assert 'startup_package' in sys.modules\nimport new_package"
276+
) == frozenset({'new_package'})
277+
278+
279+
def test_import_measurement_detects_new_submodule_of_preloaded_package(
280+
preloaded_startup_package,
281+
):
282+
assert loaded_top_level_packages(
283+
"assert 'startup_package' in sys.modules\nimport startup_package.extra"
284+
) == frozenset({'startup_package'})
285+
286+
250287
def test_constructing_agent_defers_optional_mcp_server_stack():
251288
"""A normal Agent does not import MCP just because its extra is installed."""
252289
if importlib.util.find_spec('mcp') is None:

0 commit comments

Comments
 (0)