Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 37 additions & 4 deletions tests/test_common/magic_import_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@

import os
import sys
from pathlib import PurePath

import pytest

Expand Down Expand Up @@ -61,6 +62,12 @@
"jenkins/scripts/cbts",
)

# Prefixes of the current installation.
_INTERPRETER_ROOTS = frozenset(
os.path.realpath(prefix)
for prefix in (sys.prefix, sys.base_prefix, sys.exec_prefix, sys.base_exec_prefix)
)

# Debug variable for local runs to skip this check.
_SKIP_CHECK_ENV = "TRTLLM_SKIP_SYS_PATH_CHECK"

Expand Down Expand Up @@ -150,6 +157,16 @@ def pytest_runtest_protocol(item, nextitem):
MagicFinder.purge_magic_sys_modules()


def _within(path: str, directory: str) -> bool:
"""Whether ``path`` is ``directory`` itself or sits below it.

Compared as whole path components, so a sibling whose name merely starts
with the directory's -- ``.venv-3.12-notes`` beside ``.venv-3.12`` -- is not
treated as being inside it.
"""
return PurePath(path).is_relative_to(directory)


def _project_relative(entry: str) -> str | None:
"""Path of ``entry`` relative to the project root, or None if outside it.

Expand All @@ -158,7 +175,7 @@ def _project_relative(entry: str) -> str | None:
"""
root = os.path.realpath(MagicFinder.project_root)
resolved = os.path.realpath(entry or os.getcwd())
if resolved != root and not resolved.startswith(root + os.sep):
if not _within(resolved, root):
return None
return os.path.relpath(resolved, root)

Expand Down Expand Up @@ -192,12 +209,25 @@ def _under_exempt_tree(relative: str) -> bool:
nested path such as a build directory matches its whole tree.
"""
for tree in _NON_TEST_TREES:
prefix = tree.replace("/", os.sep)
if relative == prefix or relative.startswith(prefix + os.sep):
if _within(relative, tree.replace("/", os.sep)):
return True
return False


def _under_interpreter_root(resolved: str, project_root: str) -> bool:
"""Whether an already-resolved path is part of the interpreter's install.

Other packages may live inside them and add their paths to sys.path,
which is not our fault so don't report those paths.

However, we should still report paths under our project root
even if our project root lives inside any of them.
"""
return any(
_within(resolved, root) for root in _INTERPRETER_ROOTS if not _within(project_root, root)
)


def _expected_entries(config) -> set[str]:
"""Project-relative sys.path entries that a run is allowed to contain."""
expected = {"."} # the project root itself
Expand Down Expand Up @@ -244,7 +274,10 @@ def _check_sys_path(config) -> list[str]:
continue
if _under_exempt_tree(relative):
continue
if _is_pytest_basedir(os.path.join(root, relative)):
resolved = os.path.join(root, relative)
if _under_interpreter_root(resolved, root):
continue
if _is_pytest_basedir(resolved):
continue
if relative not in unexpected:
unexpected.append(relative)
Expand Down
52 changes: 52 additions & 0 deletions tests/unittest/others/test_magic_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from types import ModuleType, SimpleNamespace

import pytest
from test_common import magic_import_hooks
from test_common.magic_import import MagicFinder
from test_common.magic_import_hooks import (
_NON_TEST_TREES,
Expand Down Expand Up @@ -808,6 +809,57 @@ def test_sys_path_check_matches_exempt_trees_by_path_component(sys_path_report,
assert sys_path_report(sibling) == [(exempt + "_other").replace("/", os.sep)]


@pytest.fixture
def in_tree_venv(tree, monkeypatch):
"""Points the interpreter-root exemption at a venv inside the project.

Patches the resolved roots rather than ``sys.prefix``, which the module
reads once at import time.
"""
venv = tree.project_root / ".venv-3.12"
site_packages = venv / "lib" / "python3.12" / "site-packages"
site_packages.mkdir(parents=True)
monkeypatch.setattr(magic_import_hooks, "_INTERPRETER_ROOTS", (str(venv),))
Comment thread
tongyuantongyu marked this conversation as resolved.
Comment thread
tongyuantongyu marked this conversation as resolved.
return SimpleNamespace(prefix=venv, site_packages=site_packages)


def test_sys_path_check_accepts_an_in_tree_virtual_environment(sys_path_report, in_tree_venv):
"""A venv under the project root is supported layout, not a leak.

build_wheel.py defaults to ``<project>/.venv-<pyver>``, so a developer who
builds that way and then runs pytest from it would otherwise have the
interpreter's own library directories reported as if a test file had added
them.
"""
vendored = in_tree_venv.site_packages / "some_pkg" / "vendored"

assert sys_path_report(in_tree_venv.prefix / "bin", vendored) == []


def test_sys_path_check_still_reports_a_sibling_of_the_venv(sys_path_report, tree, in_tree_venv):
"""The venv exemption matches whole components, so a sibling still counts."""
sibling = tree.project_root / (in_tree_venv.prefix.name + "-notes")
sibling.mkdir(parents=True)

assert sys_path_report(sibling) == [sibling.name]


def test_sys_path_check_ignores_an_interpreter_root_above_the_project(
sys_path_report, tree, monkeypatch
):
"""A prefix containing the project root must not exempt the whole tree.

Honouring it would place every entry under the root inside the exemption,
so the check would report nothing at all -- sys.base_prefix is ``/usr`` on a
system interpreter, which contains a checkout under ``/usr/local``.
"""
helpers = tree.project_root / "tests" / "helpers"
helpers.mkdir(parents=True)
monkeypatch.setattr(magic_import_hooks, "_INTERPRETER_ROOTS", (str(tree.project_root.parent),))

assert sys_path_report(helpers) == [os.path.join("tests", "helpers")]


def test_sys_path_check_accepts_pythonpath_ini_entries(sys_path_report, tree):
"""pytest.ini's pythonpath is a declared, reviewed entry, not a leak."""
declared = tree.project_root / "tests" / "declared"
Expand Down
Loading