parent
199d4e2b73
commit
111c0d910e
|
@ -547,6 +547,8 @@ class PytestPluginManager(PluginManager):
|
|||
confcutdir: Optional[Path],
|
||||
invocation_dir: Path,
|
||||
importmode: Union[ImportMode, str],
|
||||
*,
|
||||
consider_namespace_packages: bool,
|
||||
) -> None:
|
||||
"""Load initial conftest files given a preparsed "namespace".
|
||||
|
||||
|
@ -572,10 +574,20 @@ class PytestPluginManager(PluginManager):
|
|||
# Ensure we do not break if what appears to be an anchor
|
||||
# is in fact a very long option (#10169, #11394).
|
||||
if safe_exists(anchor):
|
||||
self._try_load_conftest(anchor, importmode, rootpath)
|
||||
self._try_load_conftest(
|
||||
anchor,
|
||||
importmode,
|
||||
rootpath,
|
||||
consider_namespace_packages=consider_namespace_packages,
|
||||
)
|
||||
foundanchor = True
|
||||
if not foundanchor:
|
||||
self._try_load_conftest(invocation_dir, importmode, rootpath)
|
||||
self._try_load_conftest(
|
||||
invocation_dir,
|
||||
importmode,
|
||||
rootpath,
|
||||
consider_namespace_packages=consider_namespace_packages,
|
||||
)
|
||||
|
||||
def _is_in_confcutdir(self, path: Path) -> bool:
|
||||
"""Whether to consider the given path to load conftests from."""
|
||||
|
@ -593,17 +605,37 @@ class PytestPluginManager(PluginManager):
|
|||
return path not in self._confcutdir.parents
|
||||
|
||||
def _try_load_conftest(
|
||||
self, anchor: Path, importmode: Union[str, ImportMode], rootpath: Path
|
||||
self,
|
||||
anchor: Path,
|
||||
importmode: Union[str, ImportMode],
|
||||
rootpath: Path,
|
||||
*,
|
||||
consider_namespace_packages: bool,
|
||||
) -> None:
|
||||
self._loadconftestmodules(anchor, importmode, rootpath)
|
||||
self._loadconftestmodules(
|
||||
anchor,
|
||||
importmode,
|
||||
rootpath,
|
||||
consider_namespace_packages=consider_namespace_packages,
|
||||
)
|
||||
# let's also consider test* subdirs
|
||||
if anchor.is_dir():
|
||||
for x in anchor.glob("test*"):
|
||||
if x.is_dir():
|
||||
self._loadconftestmodules(x, importmode, rootpath)
|
||||
self._loadconftestmodules(
|
||||
x,
|
||||
importmode,
|
||||
rootpath,
|
||||
consider_namespace_packages=consider_namespace_packages,
|
||||
)
|
||||
|
||||
def _loadconftestmodules(
|
||||
self, path: Path, importmode: Union[str, ImportMode], rootpath: Path
|
||||
self,
|
||||
path: Path,
|
||||
importmode: Union[str, ImportMode],
|
||||
rootpath: Path,
|
||||
*,
|
||||
consider_namespace_packages: bool,
|
||||
) -> None:
|
||||
if self._noconftest:
|
||||
return
|
||||
|
@ -620,7 +652,12 @@ class PytestPluginManager(PluginManager):
|
|||
if self._is_in_confcutdir(parent):
|
||||
conftestpath = parent / "conftest.py"
|
||||
if conftestpath.is_file():
|
||||
mod = self._importconftest(conftestpath, importmode, rootpath)
|
||||
mod = self._importconftest(
|
||||
conftestpath,
|
||||
importmode,
|
||||
rootpath,
|
||||
consider_namespace_packages=consider_namespace_packages,
|
||||
)
|
||||
clist.append(mod)
|
||||
self._dirpath2confmods[directory] = clist
|
||||
|
||||
|
@ -642,7 +679,12 @@ class PytestPluginManager(PluginManager):
|
|||
raise KeyError(name)
|
||||
|
||||
def _importconftest(
|
||||
self, conftestpath: Path, importmode: Union[str, ImportMode], rootpath: Path
|
||||
self,
|
||||
conftestpath: Path,
|
||||
importmode: Union[str, ImportMode],
|
||||
rootpath: Path,
|
||||
*,
|
||||
consider_namespace_packages: bool,
|
||||
) -> types.ModuleType:
|
||||
conftestpath_plugin_name = str(conftestpath)
|
||||
existing = self.get_plugin(conftestpath_plugin_name)
|
||||
|
@ -661,7 +703,12 @@ class PytestPluginManager(PluginManager):
|
|||
pass
|
||||
|
||||
try:
|
||||
mod = import_path(conftestpath, mode=importmode, root=rootpath)
|
||||
mod = import_path(
|
||||
conftestpath,
|
||||
mode=importmode,
|
||||
root=rootpath,
|
||||
consider_namespace_packages=consider_namespace_packages,
|
||||
)
|
||||
except Exception as e:
|
||||
assert e.__traceback__ is not None
|
||||
raise ConftestImportFailure(conftestpath, cause=e) from e
|
||||
|
@ -1177,6 +1224,9 @@ class Config:
|
|||
confcutdir=early_config.known_args_namespace.confcutdir,
|
||||
invocation_dir=early_config.invocation_params.dir,
|
||||
importmode=early_config.known_args_namespace.importmode,
|
||||
consider_namespace_packages=early_config.getini(
|
||||
"consider_namespace_packages"
|
||||
),
|
||||
)
|
||||
|
||||
def _initini(self, args: Sequence[str]) -> None:
|
||||
|
|
|
@ -222,6 +222,12 @@ def pytest_addoption(parser: Parser) -> None:
|
|||
help="Prepend/append to sys.path when importing test modules and conftest "
|
||||
"files. Default: prepend.",
|
||||
)
|
||||
parser.addini(
|
||||
"consider_namespace_packages",
|
||||
type="bool",
|
||||
default=False,
|
||||
help="Consider namespace packages when resolving module names during import",
|
||||
)
|
||||
|
||||
group = parser.getgroup("debugconfig", "test session debugging and configuration")
|
||||
group.addoption(
|
||||
|
|
|
@ -488,6 +488,7 @@ def import_path(
|
|||
*,
|
||||
mode: Union[str, ImportMode] = ImportMode.prepend,
|
||||
root: Path,
|
||||
consider_namespace_packages: bool,
|
||||
) -> ModuleType:
|
||||
"""
|
||||
Import and return a module from the given path, which can be a file (a module) or
|
||||
|
@ -515,6 +516,9 @@ def import_path(
|
|||
a unique name for the module being imported so it can safely be stored
|
||||
into ``sys.modules``.
|
||||
|
||||
:param consider_namespace_packages:
|
||||
If True, consider namespace packages when resolving module names.
|
||||
|
||||
:raises ImportPathMismatchError:
|
||||
If after importing the given `path` and the module `__file__`
|
||||
are different. Only raised in `prepend` and `append` modes.
|
||||
|
@ -530,7 +534,7 @@ def import_path(
|
|||
# without touching sys.path.
|
||||
try:
|
||||
pkg_root, module_name = resolve_pkg_root_and_module_name(
|
||||
path, consider_ns_packages=True
|
||||
path, consider_namespace_packages=consider_namespace_packages
|
||||
)
|
||||
except CouldNotResolvePathError:
|
||||
pass
|
||||
|
@ -556,7 +560,7 @@ def import_path(
|
|||
|
||||
try:
|
||||
pkg_root, module_name = resolve_pkg_root_and_module_name(
|
||||
path, consider_ns_packages=True
|
||||
path, consider_namespace_packages=consider_namespace_packages
|
||||
)
|
||||
except CouldNotResolvePathError:
|
||||
pkg_root, module_name = path.parent, path.stem
|
||||
|
@ -674,7 +678,7 @@ def module_name_from_path(path: Path, root: Path) -> str:
|
|||
# Module names cannot contain ".", normalize them to "_". This prevents
|
||||
# a directory having a "." in the name (".env.310" for example) causing extra intermediate modules.
|
||||
# Also, important to replace "." at the start of paths, as those are considered relative imports.
|
||||
path_parts = [x.replace(".", "_") for x in path_parts]
|
||||
path_parts = tuple(x.replace(".", "_") for x in path_parts)
|
||||
|
||||
return ".".join(path_parts)
|
||||
|
||||
|
@ -738,7 +742,7 @@ def resolve_package_path(path: Path) -> Optional[Path]:
|
|||
|
||||
|
||||
def resolve_pkg_root_and_module_name(
|
||||
path: Path, *, consider_ns_packages: bool = False
|
||||
path: Path, *, consider_namespace_packages: bool = False
|
||||
) -> Tuple[Path, str]:
|
||||
"""
|
||||
Return the path to the directory of the root package that contains the
|
||||
|
@ -753,7 +757,7 @@ def resolve_pkg_root_and_module_name(
|
|||
|
||||
Passing the full path to `models.py` will yield Path("src") and "app.core.models".
|
||||
|
||||
If consider_ns_packages is True, then we additionally check upwards in the hierarchy
|
||||
If consider_namespace_packages is True, then we additionally check upwards in the hierarchy
|
||||
until we find a directory that is reachable from sys.path, which marks it as a namespace package:
|
||||
|
||||
https://packaging.python.org/en/latest/guides/packaging-namespace-packages
|
||||
|
@ -764,7 +768,7 @@ def resolve_pkg_root_and_module_name(
|
|||
if pkg_path is not None:
|
||||
pkg_root = pkg_path.parent
|
||||
# https://packaging.python.org/en/latest/guides/packaging-namespace-packages/
|
||||
if consider_ns_packages:
|
||||
if consider_namespace_packages:
|
||||
# Go upwards in the hierarchy, if we find a parent path included
|
||||
# in sys.path, it means the package found by resolve_package_path()
|
||||
# actually belongs to a namespace package.
|
||||
|
|
|
@ -516,7 +516,12 @@ def importtestmodule(
|
|||
# We assume we are only called once per module.
|
||||
importmode = config.getoption("--import-mode")
|
||||
try:
|
||||
mod = import_path(path, mode=importmode, root=config.rootpath)
|
||||
mod = import_path(
|
||||
path,
|
||||
mode=importmode,
|
||||
root=config.rootpath,
|
||||
consider_namespace_packages=config.getini("consider_namespace_packages"),
|
||||
)
|
||||
except SyntaxError as e:
|
||||
raise nodes.Collector.CollectError(
|
||||
ExceptionInfo.from_current().getrepr(style="short")
|
||||
|
|
|
@ -380,6 +380,9 @@ def pytest_make_collect_report(collector: Collector) -> CollectReport:
|
|||
collector.path,
|
||||
collector.config.getoption("importmode"),
|
||||
rootpath=collector.config.rootpath,
|
||||
consider_namespace_packages=collector.config.getini(
|
||||
"consider_namespace_packages"
|
||||
),
|
||||
)
|
||||
|
||||
return list(collector.collect())
|
||||
|
|
|
@ -180,7 +180,7 @@ class TestTraceback_f_g_h:
|
|||
def test_traceback_cut_excludepath(self, pytester: Pytester) -> None:
|
||||
p = pytester.makepyfile("def f(): raise ValueError")
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
import_path(p, root=pytester.path).f() # type: ignore[attr-defined]
|
||||
import_path(p, root=pytester.path, consider_namespace_packages=False).f() # type: ignore[attr-defined]
|
||||
basedir = Path(pytest.__file__).parent
|
||||
newtraceback = excinfo.traceback.cut(excludepath=basedir)
|
||||
for x in newtraceback:
|
||||
|
@ -543,7 +543,9 @@ class TestFormattedExcinfo:
|
|||
tmp_path.joinpath("__init__.py").touch()
|
||||
modpath.write_text(source, encoding="utf-8")
|
||||
importlib.invalidate_caches()
|
||||
return import_path(modpath, root=tmp_path)
|
||||
return import_path(
|
||||
modpath, root=tmp_path, consider_namespace_packages=False
|
||||
)
|
||||
|
||||
return importasmod
|
||||
|
||||
|
|
|
@ -296,7 +296,7 @@ def test_source_of_class_at_eof_without_newline(_sys_snapshot, tmp_path: Path) -
|
|||
)
|
||||
path = tmp_path.joinpath("a.py")
|
||||
path.write_text(str(source), encoding="utf-8")
|
||||
mod: Any = import_path(path, root=tmp_path)
|
||||
mod: Any = import_path(path, root=tmp_path, consider_namespace_packages=False)
|
||||
s2 = Source(mod.A)
|
||||
assert str(source).strip() == str(s2).strip()
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ def conftest_setinitial(
|
|||
confcutdir=confcutdir,
|
||||
invocation_dir=Path.cwd(),
|
||||
importmode="prepend",
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
|
||||
|
||||
|
@ -64,7 +65,9 @@ class TestConftestValueAccessGlobal:
|
|||
def test_basic_init(self, basedir: Path) -> None:
|
||||
conftest = PytestPluginManager()
|
||||
p = basedir / "adir"
|
||||
conftest._loadconftestmodules(p, importmode="prepend", rootpath=basedir)
|
||||
conftest._loadconftestmodules(
|
||||
p, importmode="prepend", rootpath=basedir, consider_namespace_packages=False
|
||||
)
|
||||
assert conftest._rget_with_confmod("a", p)[1] == 1
|
||||
|
||||
def test_immediate_initialiation_and_incremental_are_the_same(
|
||||
|
@ -72,15 +75,26 @@ class TestConftestValueAccessGlobal:
|
|||
) -> None:
|
||||
conftest = PytestPluginManager()
|
||||
assert not len(conftest._dirpath2confmods)
|
||||
conftest._loadconftestmodules(basedir, importmode="prepend", rootpath=basedir)
|
||||
conftest._loadconftestmodules(
|
||||
basedir,
|
||||
importmode="prepend",
|
||||
rootpath=basedir,
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
snap1 = len(conftest._dirpath2confmods)
|
||||
assert snap1 == 1
|
||||
conftest._loadconftestmodules(
|
||||
basedir / "adir", importmode="prepend", rootpath=basedir
|
||||
basedir / "adir",
|
||||
importmode="prepend",
|
||||
rootpath=basedir,
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
assert len(conftest._dirpath2confmods) == snap1 + 1
|
||||
conftest._loadconftestmodules(
|
||||
basedir / "b", importmode="prepend", rootpath=basedir
|
||||
basedir / "b",
|
||||
importmode="prepend",
|
||||
rootpath=basedir,
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
assert len(conftest._dirpath2confmods) == snap1 + 2
|
||||
|
||||
|
@ -92,10 +106,18 @@ class TestConftestValueAccessGlobal:
|
|||
def test_value_access_by_path(self, basedir: Path) -> None:
|
||||
conftest = ConftestWithSetinitial(basedir)
|
||||
adir = basedir / "adir"
|
||||
conftest._loadconftestmodules(adir, importmode="prepend", rootpath=basedir)
|
||||
conftest._loadconftestmodules(
|
||||
adir,
|
||||
importmode="prepend",
|
||||
rootpath=basedir,
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
assert conftest._rget_with_confmod("a", adir)[1] == 1
|
||||
conftest._loadconftestmodules(
|
||||
adir / "b", importmode="prepend", rootpath=basedir
|
||||
adir / "b",
|
||||
importmode="prepend",
|
||||
rootpath=basedir,
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
assert conftest._rget_with_confmod("a", adir / "b")[1] == 1.5
|
||||
|
||||
|
@ -152,7 +174,12 @@ def test_conftest_global_import(pytester: Pytester) -> None:
|
|||
import pytest
|
||||
from _pytest.config import PytestPluginManager
|
||||
conf = PytestPluginManager()
|
||||
mod = conf._importconftest(Path("conftest.py"), importmode="prepend", rootpath=Path.cwd())
|
||||
mod = conf._importconftest(
|
||||
Path("conftest.py"),
|
||||
importmode="prepend",
|
||||
rootpath=Path.cwd(),
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
assert mod.x == 3
|
||||
import conftest
|
||||
assert conftest is mod, (conftest, mod)
|
||||
|
@ -160,7 +187,12 @@ def test_conftest_global_import(pytester: Pytester) -> None:
|
|||
sub.mkdir()
|
||||
subconf = sub / "conftest.py"
|
||||
subconf.write_text("y=4", encoding="utf-8")
|
||||
mod2 = conf._importconftest(subconf, importmode="prepend", rootpath=Path.cwd())
|
||||
mod2 = conf._importconftest(
|
||||
subconf,
|
||||
importmode="prepend",
|
||||
rootpath=Path.cwd(),
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
assert mod != mod2
|
||||
assert mod2.y == 4
|
||||
import conftest
|
||||
|
@ -176,17 +208,30 @@ def test_conftestcutdir(pytester: Pytester) -> None:
|
|||
p = pytester.mkdir("x")
|
||||
conftest = PytestPluginManager()
|
||||
conftest_setinitial(conftest, [pytester.path], confcutdir=p)
|
||||
conftest._loadconftestmodules(p, importmode="prepend", rootpath=pytester.path)
|
||||
conftest._loadconftestmodules(
|
||||
p,
|
||||
importmode="prepend",
|
||||
rootpath=pytester.path,
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
values = conftest._getconftestmodules(p)
|
||||
assert len(values) == 0
|
||||
conftest._loadconftestmodules(
|
||||
conf.parent, importmode="prepend", rootpath=pytester.path
|
||||
conf.parent,
|
||||
importmode="prepend",
|
||||
rootpath=pytester.path,
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
values = conftest._getconftestmodules(conf.parent)
|
||||
assert len(values) == 0
|
||||
assert not conftest.has_plugin(str(conf))
|
||||
# but we can still import a conftest directly
|
||||
conftest._importconftest(conf, importmode="prepend", rootpath=pytester.path)
|
||||
conftest._importconftest(
|
||||
conf,
|
||||
importmode="prepend",
|
||||
rootpath=pytester.path,
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
values = conftest._getconftestmodules(conf.parent)
|
||||
assert values[0].__file__ is not None
|
||||
assert values[0].__file__.startswith(str(conf))
|
||||
|
@ -405,13 +450,18 @@ def test_conftest_import_order(pytester: Pytester, monkeypatch: MonkeyPatch) ->
|
|||
ct2 = sub / "conftest.py"
|
||||
ct2.write_text("", encoding="utf-8")
|
||||
|
||||
def impct(p, importmode, root):
|
||||
def impct(p, importmode, root, consider_namespace_packages):
|
||||
return p
|
||||
|
||||
conftest = PytestPluginManager()
|
||||
conftest._confcutdir = pytester.path
|
||||
monkeypatch.setattr(conftest, "_importconftest", impct)
|
||||
conftest._loadconftestmodules(sub, importmode="prepend", rootpath=pytester.path)
|
||||
conftest._loadconftestmodules(
|
||||
sub,
|
||||
importmode="prepend",
|
||||
rootpath=pytester.path,
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
mods = cast(List[Path], conftest._getconftestmodules(sub))
|
||||
expected = [ct1, ct2]
|
||||
assert mods == expected
|
||||
|
|
|
@ -171,13 +171,17 @@ class TestImportPath:
|
|||
)
|
||||
|
||||
def test_smoke_test(self, path1: Path) -> None:
|
||||
obj = import_path(path1 / "execfile.py", root=path1)
|
||||
obj = import_path(
|
||||
path1 / "execfile.py", root=path1, consider_namespace_packages=False
|
||||
)
|
||||
assert obj.x == 42 # type: ignore[attr-defined]
|
||||
assert obj.__name__ == "execfile"
|
||||
|
||||
def test_import_path_missing_file(self, path1: Path) -> None:
|
||||
with pytest.raises(ImportPathMismatchError):
|
||||
import_path(path1 / "sampledir", root=path1)
|
||||
import_path(
|
||||
path1 / "sampledir", root=path1, consider_namespace_packages=False
|
||||
)
|
||||
|
||||
def test_renamed_dir_creates_mismatch(
|
||||
self, tmp_path: Path, monkeypatch: MonkeyPatch
|
||||
|
@ -185,25 +189,37 @@ class TestImportPath:
|
|||
tmp_path.joinpath("a").mkdir()
|
||||
p = tmp_path.joinpath("a", "test_x123.py")
|
||||
p.touch()
|
||||
import_path(p, root=tmp_path)
|
||||
import_path(p, root=tmp_path, consider_namespace_packages=False)
|
||||
tmp_path.joinpath("a").rename(tmp_path.joinpath("b"))
|
||||
with pytest.raises(ImportPathMismatchError):
|
||||
import_path(tmp_path.joinpath("b", "test_x123.py"), root=tmp_path)
|
||||
import_path(
|
||||
tmp_path.joinpath("b", "test_x123.py"),
|
||||
root=tmp_path,
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
|
||||
# Errors can be ignored.
|
||||
monkeypatch.setenv("PY_IGNORE_IMPORTMISMATCH", "1")
|
||||
import_path(tmp_path.joinpath("b", "test_x123.py"), root=tmp_path)
|
||||
import_path(
|
||||
tmp_path.joinpath("b", "test_x123.py"),
|
||||
root=tmp_path,
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
|
||||
# PY_IGNORE_IMPORTMISMATCH=0 does not ignore error.
|
||||
monkeypatch.setenv("PY_IGNORE_IMPORTMISMATCH", "0")
|
||||
with pytest.raises(ImportPathMismatchError):
|
||||
import_path(tmp_path.joinpath("b", "test_x123.py"), root=tmp_path)
|
||||
import_path(
|
||||
tmp_path.joinpath("b", "test_x123.py"),
|
||||
root=tmp_path,
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
|
||||
def test_messy_name(self, tmp_path: Path) -> None:
|
||||
# https://bitbucket.org/hpk42/py-trunk/issue/129
|
||||
path = tmp_path / "foo__init__.py"
|
||||
path.touch()
|
||||
module = import_path(path, root=tmp_path)
|
||||
module = import_path(path, root=tmp_path, consider_namespace_packages=False)
|
||||
assert module.__name__ == "foo__init__"
|
||||
|
||||
def test_dir(self, tmp_path: Path) -> None:
|
||||
|
@ -211,31 +227,39 @@ class TestImportPath:
|
|||
p.mkdir()
|
||||
p_init = p / "__init__.py"
|
||||
p_init.touch()
|
||||
m = import_path(p, root=tmp_path)
|
||||
m = import_path(p, root=tmp_path, consider_namespace_packages=False)
|
||||
assert m.__name__ == "hello_123"
|
||||
m = import_path(p_init, root=tmp_path)
|
||||
m = import_path(p_init, root=tmp_path, consider_namespace_packages=False)
|
||||
assert m.__name__ == "hello_123"
|
||||
|
||||
def test_a(self, path1: Path) -> None:
|
||||
otherdir = path1 / "otherdir"
|
||||
mod = import_path(otherdir / "a.py", root=path1)
|
||||
mod = import_path(
|
||||
otherdir / "a.py", root=path1, consider_namespace_packages=False
|
||||
)
|
||||
assert mod.result == "got it" # type: ignore[attr-defined]
|
||||
assert mod.__name__ == "otherdir.a"
|
||||
|
||||
def test_b(self, path1: Path) -> None:
|
||||
otherdir = path1 / "otherdir"
|
||||
mod = import_path(otherdir / "b.py", root=path1)
|
||||
mod = import_path(
|
||||
otherdir / "b.py", root=path1, consider_namespace_packages=False
|
||||
)
|
||||
assert mod.stuff == "got it" # type: ignore[attr-defined]
|
||||
assert mod.__name__ == "otherdir.b"
|
||||
|
||||
def test_c(self, path1: Path) -> None:
|
||||
otherdir = path1 / "otherdir"
|
||||
mod = import_path(otherdir / "c.py", root=path1)
|
||||
mod = import_path(
|
||||
otherdir / "c.py", root=path1, consider_namespace_packages=False
|
||||
)
|
||||
assert mod.value == "got it" # type: ignore[attr-defined]
|
||||
|
||||
def test_d(self, path1: Path) -> None:
|
||||
otherdir = path1 / "otherdir"
|
||||
mod = import_path(otherdir / "d.py", root=path1)
|
||||
mod = import_path(
|
||||
otherdir / "d.py", root=path1, consider_namespace_packages=False
|
||||
)
|
||||
assert mod.value2 == "got it" # type: ignore[attr-defined]
|
||||
|
||||
def test_import_after(self, tmp_path: Path) -> None:
|
||||
|
@ -243,7 +267,7 @@ class TestImportPath:
|
|||
tmp_path.joinpath("xxxpackage", "__init__.py").touch()
|
||||
mod1path = tmp_path.joinpath("xxxpackage", "module1.py")
|
||||
mod1path.touch()
|
||||
mod1 = import_path(mod1path, root=tmp_path)
|
||||
mod1 = import_path(mod1path, root=tmp_path, consider_namespace_packages=False)
|
||||
assert mod1.__name__ == "xxxpackage.module1"
|
||||
from xxxpackage import module1
|
||||
|
||||
|
@ -262,7 +286,9 @@ class TestImportPath:
|
|||
pseudopath.touch()
|
||||
mod.__file__ = str(pseudopath)
|
||||
mp.setitem(sys.modules, name, mod)
|
||||
newmod = import_path(p, root=tmp_path)
|
||||
newmod = import_path(
|
||||
p, root=tmp_path, consider_namespace_packages=False
|
||||
)
|
||||
assert mod == newmod
|
||||
mod = ModuleType(name)
|
||||
pseudopath = tmp_path.joinpath(name + "123.py")
|
||||
|
@ -270,7 +296,7 @@ class TestImportPath:
|
|||
mod.__file__ = str(pseudopath)
|
||||
monkeypatch.setitem(sys.modules, name, mod)
|
||||
with pytest.raises(ImportPathMismatchError) as excinfo:
|
||||
import_path(p, root=tmp_path)
|
||||
import_path(p, root=tmp_path, consider_namespace_packages=False)
|
||||
modname, modfile, orig = excinfo.value.args
|
||||
assert modname == name
|
||||
assert modfile == str(pseudopath)
|
||||
|
@ -283,13 +309,19 @@ class TestImportPath:
|
|||
file1 = root1 / "x123.py"
|
||||
file1.touch()
|
||||
assert str(root1) not in sys.path
|
||||
import_path(file1, mode="append", root=tmp_path)
|
||||
import_path(
|
||||
file1, mode="append", root=tmp_path, consider_namespace_packages=False
|
||||
)
|
||||
assert str(root1) == sys.path[-1]
|
||||
assert str(root1) not in sys.path[:-1]
|
||||
|
||||
def test_invalid_path(self, tmp_path: Path) -> None:
|
||||
with pytest.raises(ImportError):
|
||||
import_path(tmp_path / "invalid.py", root=tmp_path)
|
||||
import_path(
|
||||
tmp_path / "invalid.py",
|
||||
root=tmp_path,
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
|
||||
@pytest.fixture
|
||||
def simple_module(
|
||||
|
@ -307,7 +339,12 @@ class TestImportPath:
|
|||
self, simple_module: Path, tmp_path: Path, request: pytest.FixtureRequest
|
||||
) -> None:
|
||||
"""`importlib` mode does not change sys.path."""
|
||||
module = import_path(simple_module, mode="importlib", root=tmp_path)
|
||||
module = import_path(
|
||||
simple_module,
|
||||
mode="importlib",
|
||||
root=tmp_path,
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
assert module.foo(2) == 42 # type: ignore[attr-defined]
|
||||
assert str(simple_module.parent) not in sys.path
|
||||
assert module.__name__ in sys.modules
|
||||
|
@ -319,8 +356,18 @@ class TestImportPath:
|
|||
self, simple_module: Path, tmp_path: Path
|
||||
) -> None:
|
||||
"""`importlib` mode called remembers previous module (#10341, #10811)."""
|
||||
module1 = import_path(simple_module, mode="importlib", root=tmp_path)
|
||||
module2 = import_path(simple_module, mode="importlib", root=tmp_path)
|
||||
module1 = import_path(
|
||||
simple_module,
|
||||
mode="importlib",
|
||||
root=tmp_path,
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
module2 = import_path(
|
||||
simple_module,
|
||||
mode="importlib",
|
||||
root=tmp_path,
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
assert module1 is module2
|
||||
|
||||
def test_no_meta_path_found(
|
||||
|
@ -328,7 +375,12 @@ class TestImportPath:
|
|||
) -> None:
|
||||
"""Even without any meta_path should still import module."""
|
||||
monkeypatch.setattr(sys, "meta_path", [])
|
||||
module = import_path(simple_module, mode="importlib", root=tmp_path)
|
||||
module = import_path(
|
||||
simple_module,
|
||||
mode="importlib",
|
||||
root=tmp_path,
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
assert module.foo(2) == 42 # type: ignore[attr-defined]
|
||||
|
||||
# mode='importlib' fails if no spec is found to load the module
|
||||
|
@ -341,7 +393,12 @@ class TestImportPath:
|
|||
importlib.util, "spec_from_file_location", lambda *args: None
|
||||
)
|
||||
with pytest.raises(ImportError):
|
||||
import_path(simple_module, mode="importlib", root=tmp_path)
|
||||
import_path(
|
||||
simple_module,
|
||||
mode="importlib",
|
||||
root=tmp_path,
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
|
||||
|
||||
def test_resolve_package_path(tmp_path: Path) -> None:
|
||||
|
@ -477,7 +534,9 @@ def test_samefile_false_negatives(tmp_path: Path, monkeypatch: MonkeyPatch) -> N
|
|||
# the paths too. Using a context to narrow the patch as much as possible given
|
||||
# this is an important system function.
|
||||
mp.setattr(os.path, "samefile", lambda x, y: False)
|
||||
module = import_path(module_path, root=tmp_path)
|
||||
module = import_path(
|
||||
module_path, root=tmp_path, consider_namespace_packages=False
|
||||
)
|
||||
assert getattr(module, "foo")() == 42
|
||||
|
||||
|
||||
|
@ -499,7 +558,9 @@ class TestImportLibMode:
|
|||
encoding="utf-8",
|
||||
)
|
||||
|
||||
module = import_path(fn, mode="importlib", root=tmp_path)
|
||||
module = import_path(
|
||||
fn, mode="importlib", root=tmp_path, consider_namespace_packages=False
|
||||
)
|
||||
Data: Any = getattr(module, "Data")
|
||||
data = Data(value="foo")
|
||||
assert data.value == "foo"
|
||||
|
@ -525,7 +586,9 @@ class TestImportLibMode:
|
|||
encoding="utf-8",
|
||||
)
|
||||
|
||||
module = import_path(fn, mode="importlib", root=tmp_path)
|
||||
module = import_path(
|
||||
fn, mode="importlib", root=tmp_path, consider_namespace_packages=False
|
||||
)
|
||||
round_trip = getattr(module, "round_trip")
|
||||
action = round_trip()
|
||||
assert action() == 42
|
||||
|
@ -575,10 +638,14 @@ class TestImportLibMode:
|
|||
s = pickle.dumps(obj)
|
||||
return pickle.loads(s)
|
||||
|
||||
module = import_path(fn1, mode="importlib", root=tmp_path)
|
||||
module = import_path(
|
||||
fn1, mode="importlib", root=tmp_path, consider_namespace_packages=False
|
||||
)
|
||||
Data1 = getattr(module, "Data")
|
||||
|
||||
module = import_path(fn2, mode="importlib", root=tmp_path)
|
||||
module = import_path(
|
||||
fn2, mode="importlib", root=tmp_path, consider_namespace_packages=False
|
||||
)
|
||||
Data2 = getattr(module, "Data")
|
||||
|
||||
assert round_trip(Data1(20)) == Data1(20)
|
||||
|
@ -635,7 +702,7 @@ class TestImportLibMode:
|
|||
# If we add tmp_path to sys.path, src becomes a namespace package.
|
||||
monkeypatch.syspath_prepend(tmp_path)
|
||||
assert resolve_pkg_root_and_module_name(
|
||||
models_py, consider_ns_packages=True
|
||||
models_py, consider_namespace_packages=True
|
||||
) == (
|
||||
tmp_path,
|
||||
"src.app.core.models",
|
||||
|
@ -709,7 +776,12 @@ class TestImportLibMode:
|
|||
encoding="ascii",
|
||||
)
|
||||
|
||||
mod = import_path(init, root=tmp_path, mode=ImportMode.importlib)
|
||||
mod = import_path(
|
||||
init,
|
||||
root=tmp_path,
|
||||
mode=ImportMode.importlib,
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
assert len(mod.instance.INSTANCES) == 1
|
||||
|
||||
def test_importlib_root_is_package(self, pytester: Pytester) -> None:
|
||||
|
@ -828,16 +900,31 @@ class TestImportLibMode:
|
|||
)
|
||||
|
||||
# core_py is reached from sys.path, so should be imported normally.
|
||||
mod = import_path(core_py, mode="importlib", root=pytester.path)
|
||||
mod = import_path(
|
||||
core_py,
|
||||
mode="importlib",
|
||||
root=pytester.path,
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
assert mod.__name__ == "app.core"
|
||||
assert mod.__file__ and Path(mod.__file__) == core_py
|
||||
|
||||
# tests are not reachable from sys.path, so they are imported as a standalone modules.
|
||||
# Instead of '.tests.a.test_core', we import as "_tests.a.test_core" because
|
||||
# importlib considers module names starting with '.' to be local imports.
|
||||
mod = import_path(test_path1, mode="importlib", root=pytester.path)
|
||||
mod = import_path(
|
||||
test_path1,
|
||||
mode="importlib",
|
||||
root=pytester.path,
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
assert mod.__name__ == "_tests.a.test_core"
|
||||
mod = import_path(test_path2, mode="importlib", root=pytester.path)
|
||||
mod = import_path(
|
||||
test_path2,
|
||||
mode="importlib",
|
||||
root=pytester.path,
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
assert mod.__name__ == "_tests.b.test_core"
|
||||
|
||||
def test_import_using_normal_mechanism_first_integration(
|
||||
|
@ -889,14 +976,22 @@ class TestImportLibMode:
|
|||
# The 'x.py' module from sys.path was not imported for sure because
|
||||
# otherwise we would get an AssertionError.
|
||||
mod = import_path(
|
||||
x_in_sub_folder, mode=ImportMode.importlib, root=pytester.path
|
||||
x_in_sub_folder,
|
||||
mode=ImportMode.importlib,
|
||||
root=pytester.path,
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
assert mod.__file__ and Path(mod.__file__) == x_in_sub_folder
|
||||
assert mod.X == "a/b/x"
|
||||
|
||||
# Attempt to import root 'x.py'.
|
||||
with pytest.raises(AssertionError, match="x at root"):
|
||||
_ = import_path(x_at_root, mode=ImportMode.importlib, root=pytester.path)
|
||||
_ = import_path(
|
||||
x_at_root,
|
||||
mode=ImportMode.importlib,
|
||||
root=pytester.path,
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
|
||||
|
||||
def test_safe_exists(tmp_path: Path) -> None:
|
||||
|
@ -979,26 +1074,33 @@ class TestNamespacePackages:
|
|||
)
|
||||
|
||||
pkg_root, module_name = resolve_pkg_root_and_module_name(
|
||||
models_py, consider_ns_packages=True
|
||||
models_py, consider_namespace_packages=True
|
||||
)
|
||||
assert (pkg_root, module_name) == (
|
||||
tmp_path / "src/dist1",
|
||||
"com.company.app.core.models",
|
||||
)
|
||||
|
||||
mod = import_path(models_py, mode=import_mode, root=tmp_path)
|
||||
mod = import_path(
|
||||
models_py, mode=import_mode, root=tmp_path, consider_namespace_packages=True
|
||||
)
|
||||
assert mod.__name__ == "com.company.app.core.models"
|
||||
assert mod.__file__ == str(models_py)
|
||||
|
||||
pkg_root, module_name = resolve_pkg_root_and_module_name(
|
||||
algorithms_py, consider_ns_packages=True
|
||||
algorithms_py, consider_namespace_packages=True
|
||||
)
|
||||
assert (pkg_root, module_name) == (
|
||||
tmp_path / "src/dist2",
|
||||
"com.company.calc.algo.algorithms",
|
||||
)
|
||||
|
||||
mod = import_path(algorithms_py, mode=import_mode, root=tmp_path)
|
||||
mod = import_path(
|
||||
algorithms_py,
|
||||
mode=import_mode,
|
||||
root=tmp_path,
|
||||
consider_namespace_packages=True,
|
||||
)
|
||||
assert mod.__name__ == "com.company.calc.algo.algorithms"
|
||||
assert mod.__file__ == str(algorithms_py)
|
||||
|
||||
|
@ -1019,7 +1121,7 @@ class TestNamespacePackages:
|
|||
(tmp_path / "src/dist1/com/__init__.py").touch()
|
||||
|
||||
pkg_root, module_name = resolve_pkg_root_and_module_name(
|
||||
models_py, consider_ns_packages=True
|
||||
models_py, consider_namespace_packages=True
|
||||
)
|
||||
assert (pkg_root, module_name) == (
|
||||
tmp_path / "src/dist1/com/company",
|
||||
|
|
|
@ -46,7 +46,10 @@ class TestPytestPluginInteractions:
|
|||
kwargs=dict(pluginmanager=config.pluginmanager)
|
||||
)
|
||||
config.pluginmanager._importconftest(
|
||||
conf, importmode="prepend", rootpath=pytester.path
|
||||
conf,
|
||||
importmode="prepend",
|
||||
rootpath=pytester.path,
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
# print(config.pluginmanager.get_plugins())
|
||||
res = config.hook.pytest_myhook(xyz=10)
|
||||
|
@ -75,7 +78,10 @@ class TestPytestPluginInteractions:
|
|||
"""
|
||||
)
|
||||
config.pluginmanager._importconftest(
|
||||
p, importmode="prepend", rootpath=pytester.path
|
||||
p,
|
||||
importmode="prepend",
|
||||
rootpath=pytester.path,
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
assert config.option.test123
|
||||
|
||||
|
@ -115,6 +121,7 @@ class TestPytestPluginInteractions:
|
|||
conftest,
|
||||
importmode="prepend",
|
||||
rootpath=pytester.path,
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
plugin = config.pluginmanager.get_plugin(str(conftest))
|
||||
assert plugin is mod
|
||||
|
@ -123,6 +130,7 @@ class TestPytestPluginInteractions:
|
|||
conftest_upper_case,
|
||||
importmode="prepend",
|
||||
rootpath=pytester.path,
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
plugin_uppercase = config.pluginmanager.get_plugin(str(conftest_upper_case))
|
||||
assert plugin_uppercase is mod_uppercase
|
||||
|
@ -174,12 +182,18 @@ class TestPytestPluginInteractions:
|
|||
conftest2 = pytester.path.joinpath("tests/subdir/conftest.py")
|
||||
|
||||
config.pluginmanager._importconftest(
|
||||
conftest1, importmode="prepend", rootpath=pytester.path
|
||||
conftest1,
|
||||
importmode="prepend",
|
||||
rootpath=pytester.path,
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
ihook_a = session.gethookproxy(pytester.path / "tests")
|
||||
assert ihook_a is not None
|
||||
config.pluginmanager._importconftest(
|
||||
conftest2, importmode="prepend", rootpath=pytester.path
|
||||
conftest2,
|
||||
importmode="prepend",
|
||||
rootpath=pytester.path,
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
ihook_b = session.gethookproxy(pytester.path / "tests")
|
||||
assert ihook_a is not ihook_b
|
||||
|
@ -398,7 +412,9 @@ class TestPytestPluginManager:
|
|||
pytestpm: PytestPluginManager,
|
||||
) -> None:
|
||||
mod = import_path(
|
||||
pytester.makepyfile("pytest_plugins='xyz'"), root=pytester.path
|
||||
pytester.makepyfile("pytest_plugins='xyz'"),
|
||||
root=pytester.path,
|
||||
consider_namespace_packages=False,
|
||||
)
|
||||
with pytest.raises(ImportError):
|
||||
pytestpm.consider_conftest(mod, registration_name="unused")
|
||||
|
|
Loading…
Reference in New Issue