aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/pytest/py3/_pytest/pathlib.py
diff options
context:
space:
mode:
authorarcadia-devtools <arcadia-devtools@yandex-team.ru>2022-02-14 00:49:36 +0300
committerarcadia-devtools <arcadia-devtools@yandex-team.ru>2022-02-14 00:49:36 +0300
commit82cfd1b7cab2d843cdf5467d9737f72597a493bd (patch)
tree1dfdcfe81a1a6b193ceacc2a828c521b657a339b /contrib/python/pytest/py3/_pytest/pathlib.py
parent3df7211d3e3691f8e33b0a1fb1764fe810d59302 (diff)
downloadydb-82cfd1b7cab2d843cdf5467d9737f72597a493bd.tar.gz
intermediate changes
ref:68b1302de4b5da30b6bdf02193f7a2604d8b5cf8
Diffstat (limited to 'contrib/python/pytest/py3/_pytest/pathlib.py')
-rw-r--r--contrib/python/pytest/py3/_pytest/pathlib.py92
1 files changed, 81 insertions, 11 deletions
diff --git a/contrib/python/pytest/py3/_pytest/pathlib.py b/contrib/python/pytest/py3/_pytest/pathlib.py
index 7d9269a1855..b44753e1a41 100644
--- a/contrib/python/pytest/py3/_pytest/pathlib.py
+++ b/contrib/python/pytest/py3/_pytest/pathlib.py
@@ -23,6 +23,7 @@ from pathlib import PurePath
from posixpath import sep as posix_sep
from types import ModuleType
from typing import Callable
+from typing import Dict
from typing import Iterable
from typing import Iterator
from typing import Optional
@@ -30,8 +31,6 @@ from typing import Set
from typing import TypeVar
from typing import Union
-import py
-
from _pytest.compat import assert_never
from _pytest.outcomes import skip
from _pytest.warning_types import PytestWarning
@@ -347,7 +346,11 @@ def cleanup_numbered_dir(
def make_numbered_dir_with_cleanup(
- root: Path, prefix: str, keep: int, lock_timeout: float, mode: int,
+ root: Path,
+ prefix: str,
+ keep: int,
+ lock_timeout: float,
+ mode: int,
) -> Path:
"""Create a numbered dir with a cleanup lock and remove old ones."""
e = None
@@ -382,7 +385,7 @@ def resolve_from_str(input: str, rootpath: Path) -> Path:
return rootpath.joinpath(input)
-def fnmatch_ex(pattern: str, path) -> bool:
+def fnmatch_ex(pattern: str, path: Union[str, "os.PathLike[str]"]) -> bool:
"""A port of FNMatcher from py.path.common which works with PurePath() instances.
The difference between this algorithm and PurePath.match() is that the
@@ -449,9 +452,10 @@ class ImportPathMismatchError(ImportError):
def import_path(
- p: Union[str, py.path.local, Path],
+ p: Union[str, "os.PathLike[str]"],
*,
mode: Union[str, ImportMode] = ImportMode.prepend,
+ root: Path,
) -> ModuleType:
"""Import and return a module from the given path, which can be a file (a module) or
a directory (a package).
@@ -469,19 +473,24 @@ def import_path(
to import the module, which avoids having to use `__import__` and muck with `sys.path`
at all. It effectively allows having same-named test modules in different places.
+ :param root:
+ Used as an anchor when mode == ImportMode.importlib to obtain
+ a unique name for the module being imported so it can safely be stored
+ into ``sys.modules``.
+
:raises ImportPathMismatchError:
If after importing the given `path` and the module `__file__`
are different. Only raised in `prepend` and `append` modes.
"""
mode = ImportMode(mode)
- path = Path(str(p))
+ path = Path(p)
if not path.exists():
raise ImportError(path)
if mode is ImportMode.importlib:
- module_name = path.stem
+ module_name = module_name_from_path(path, root)
for meta_importer in sys.meta_path:
spec = meta_importer.find_spec(module_name, [str(path.parent)])
@@ -491,11 +500,11 @@ def import_path(
spec = importlib.util.spec_from_file_location(module_name, str(path))
if spec is None:
- raise ImportError(
- "Can't find module {} at location {}".format(module_name, str(path))
- )
+ raise ImportError(f"Can't find module {module_name} at location {path}")
mod = importlib.util.module_from_spec(spec)
+ sys.modules[module_name] = mod
spec.loader.exec_module(mod) # type: ignore[union-attr]
+ insert_missing_modules(sys.modules, module_name)
return mod
pkg_path = resolve_package_path(path)
@@ -560,6 +569,47 @@ else:
return os.path.samefile(f1, f2)
+def module_name_from_path(path: Path, root: Path) -> str:
+ """
+ Return a dotted module name based on the given path, anchored on root.
+
+ For example: path="projects/src/tests/test_foo.py" and root="/projects", the
+ resulting module name will be "src.tests.test_foo".
+ """
+ path = path.with_suffix("")
+ try:
+ relative_path = path.relative_to(root)
+ except ValueError:
+ # If we can't get a relative path to root, use the full path, except
+ # for the first part ("d:\\" or "/" depending on the platform, for example).
+ path_parts = path.parts[1:]
+ else:
+ # Use the parts for the relative path to the root path.
+ path_parts = relative_path.parts
+
+ return ".".join(path_parts)
+
+
+def insert_missing_modules(modules: Dict[str, ModuleType], module_name: str) -> None:
+ """
+ Used by ``import_path`` to create intermediate modules when using mode=importlib.
+
+ When we want to import a module as "src.tests.test_foo" for example, we need
+ to create empty modules "src" and "src.tests" after inserting "src.tests.test_foo",
+ otherwise "src.tests.test_foo" is not importable by ``__import__``.
+ """
+ module_parts = module_name.split(".")
+ while module_name:
+ if module_name not in modules:
+ module = ModuleType(
+ module_name,
+ doc="Empty module created by pytest's importmode=importlib.",
+ )
+ modules[module_name] = module
+ module_parts.pop(-1)
+ module_name = ".".join(module_parts)
+
+
def resolve_package_path(path: Path) -> Optional[Path]:
"""Return the Python package path by looking for the last
directory upwards which still contains an __init__.py.
@@ -578,7 +628,7 @@ def resolve_package_path(path: Path) -> Optional[Path]:
def visit(
- path: str, recurse: Callable[["os.DirEntry[str]"], bool]
+ path: Union[str, "os.PathLike[str]"], recurse: Callable[["os.DirEntry[str]"], bool]
) -> Iterator["os.DirEntry[str]"]:
"""Walk a directory recursively, in breadth-first order.
@@ -635,6 +685,8 @@ def bestrelpath(directory: Path, dest: Path) -> str:
If no such path can be determined, returns dest.
"""
+ assert isinstance(directory, Path)
+ assert isinstance(dest, Path)
if dest == directory:
return os.curdir
# Find the longest common directory.
@@ -652,3 +704,21 @@ def bestrelpath(directory: Path, dest: Path) -> str:
# Forward from base to dest.
*reldest.parts,
)
+
+
+# Originates from py. path.local.copy(), with siginficant trims and adjustments.
+# TODO(py38): Replace with shutil.copytree(..., symlinks=True, dirs_exist_ok=True)
+def copytree(source: Path, target: Path) -> None:
+ """Recursively copy a source directory to target."""
+ assert source.is_dir()
+ for entry in visit(source, recurse=lambda entry: not entry.is_symlink()):
+ x = Path(entry)
+ relpath = x.relative_to(source)
+ newx = target / relpath
+ newx.parent.mkdir(exist_ok=True)
+ if x.is_symlink():
+ newx.symlink_to(os.readlink(x))
+ elif x.is_file():
+ shutil.copyfile(x, newx)
+ elif x.is_dir():
+ newx.mkdir(exist_ok=True)