def pytest_ignore_collect(path: py.path.local, config: Config) -> Optional[bool]: path_ = Path(path) ignore_paths = config._getconftest_pathlist("collect_ignore", path=path_.parent) ignore_paths = ignore_paths or [] excludeopt = config.getoption("ignore") if excludeopt: ignore_paths.extend(absolutepath(x) for x in excludeopt) if path_ in ignore_paths: return True ignore_globs = config._getconftest_pathlist( "collect_ignore_glob", path=path_.parent ) ignore_globs = ignore_globs or [] excludeglobopt = config.getoption("ignore_glob") if excludeglobopt: ignore_globs.extend(absolutepath(x) for x in excludeglobopt) if any(fnmatch.fnmatch(str(path), str(glob)) for glob in ignore_globs): return True allow_in_venv = config.getoption("collect_in_virtualenv") if not allow_in_venv and _in_venv(path): return True return None
def getfslineno(obj: object) -> Tuple[Union[str, Path], int]: """Return source location (path, lineno) for the given object. If the source cannot be determined return ("", -1). The line number is 0-based. """ # xxx let decorators etc specify a sane ordering # NOTE: this used to be done in _pytest.compat.getfslineno, initially added # in 6ec13a2b9. It ("place_as") appears to be something very custom. obj = get_real_func(obj) if hasattr(obj, "place_as"): obj = obj.place_as # type: ignore[attr-defined] try: code = Code.from_function(obj) except TypeError: try: fn = inspect.getsourcefile(obj) or inspect.getfile( obj) # type: ignore[arg-type] except TypeError: return "", -1 fspath = fn and absolutepath(fn) or "" lineno = -1 if fspath: try: _, lineno = findsource(obj) except OSError: pass return fspath, lineno return code.path, code.firstlineno
def get_dirs_from_args(args: Iterable[str]) -> List[Path]: def is_option(x: str) -> bool: return x.startswith("-") def get_file_part_from_node_id(x: str) -> str: return x.split("::")[0] def get_dir_from_path(path: Path) -> Path: if path.is_dir(): return path return path.parent def safe_exists(path: Path) -> bool: # This can throw on paths that contain characters unrepresentable at the OS level, # or with invalid syntax on Windows (https://bugs.python.org/issue35306) try: return path.exists() except OSError: return False # These look like paths but may not exist possible_paths = (absolutepath(get_file_part_from_node_id(arg)) for arg in args if not is_option(arg)) return [ get_dir_from_path(path) for path in possible_paths if safe_exists(path) ]
def get_location(self, config: Config) -> Optional[str]: """Return the more user-friendly information about the location of a warning, or None.""" if self.nodeid: return self.nodeid if self.fslocation: filename, linenum = self.fslocation relpath = bestrelpath(config.invocation_params.dir, absolutepath(filename)) return f"{relpath}:{linenum}" return None
def determine_setup( inifile: Optional[str], args: Sequence[str], rootdir_cmd_arg: Optional[str] = None, config: Optional["Config"] = None, ) -> Tuple[Path, Optional[Path], Dict[str, Union[str, List[str]]]]: rootdir = None dirs = get_dirs_from_args(args) if inifile: inipath_ = absolutepath(inifile) inipath = inipath_ # type: Optional[Path] inicfg = load_config_dict_from_file(inipath_) or {} if rootdir_cmd_arg is None: rootdir = get_common_ancestor(dirs) else: ancestor = get_common_ancestor(dirs) rootdir, inipath, inicfg = locate_config([ancestor]) if rootdir is None and rootdir_cmd_arg is None: for possible_rootdir in itertools.chain( (ancestor, ), reversed(ancestor.parents)): if (possible_rootdir / "setup.py").is_file(): rootdir = possible_rootdir break else: if dirs != [ancestor]: rootdir, inipath, inicfg = locate_config(dirs) if rootdir is None: if config is not None: cwd = config.invocation_params.dir else: cwd = Path.cwd() rootdir = get_common_ancestor([cwd, ancestor]) is_fs_root = os.path.splitdrive(str(rootdir))[1] == "/" if is_fs_root: rootdir = ancestor if rootdir_cmd_arg: rootdir = absolutepath(os.path.expandvars(rootdir_cmd_arg)) if not rootdir.is_dir(): raise UsageError( "Directory '{}' not found. Check your '--rootdir' option.". format(rootdir)) assert rootdir is not None return rootdir, inipath, inicfg or {}
def get_location(self, config: Config) -> Optional[str]: """Return the more user-friendly information about the location of a warning, or None.""" if self.nodeid: return self.nodeid if self.fslocation: if isinstance(self.fslocation, tuple) and len(self.fslocation) >= 2: filename, linenum = self.fslocation[:2] relpath = bestrelpath( config.invocation_params.dir, absolutepath(filename) ) return "{}:{}".format(relpath, linenum) else: return str(self.fslocation) return None
def path(self) -> Union[Path, str]: """Return a path object pointing to source code, or an ``str`` in case of ``OSError`` / non-existing file.""" if not self.raw.co_filename: return "" try: p = absolutepath(self.raw.co_filename) # maybe don't try this checking if not p.exists(): raise OSError("path check failed.") return p except OSError: # XXX maybe try harder like the weird logic # in the standard lib [linecache.updatecache] does? return self.raw.co_filename
def resolve_collection_argument( invocation_path: Path, arg: str, *, as_pypath: bool = False ) -> Tuple[Path, List[str]]: """Parse path arguments optionally containing selection parts and return (fspath, names). Command-line arguments can point to files and/or directories, and optionally contain parts for specific tests selection, for example: "pkg/tests/test_foo.py::TestClass::test_foo" This function ensures the path exists, and returns a tuple: (Path("/full/path/to/pkg/tests/test_foo.py"), ["TestClass", "test_foo"]) When as_pypath is True, expects that the command-line argument actually contains module paths instead of file-system paths: "pkg.tests.test_foo::TestClass::test_foo" In which case we search sys.path for a matching module, and then return the *path* to the found module. If the path doesn't exist, raise UsageError. If the path is a directory and selection parts are present, raise UsageError. """ base, squacket, rest = str(arg).partition("[") strpath, *parts = base.split("::") if parts: parts[-1] = f"{parts[-1]}{squacket}{rest}" if as_pypath: strpath = search_pypath(strpath) fspath = invocation_path / strpath fspath = absolutepath(fspath) if not fspath.exists(): msg = ( "module or package not found: {arg} (missing __init__.py?)" if as_pypath else "file or directory not found: {arg}" ) raise UsageError(msg.format(arg=arg)) if parts and fspath.is_dir(): msg = ( "package argument cannot contain :: selection parts: {arg}" if as_pypath else "directory argument cannot contain :: selection parts: {arg}" ) raise UsageError(msg.format(arg=arg)) return fspath, parts
def _should_rewrite(self, name: str, fn: str, state: "AssertionState") -> bool: # always rewrite conftest files if os.path.basename(fn) == "conftest.py": state.trace(f"rewriting conftest file: {fn!r}") return True if self.session is not None: if self.session.isinitpath(absolutepath(fn)): state.trace(f"matched test file (was specified on cmdline): {fn!r}") return True # modules not passed explicitly on the command line are only # rewritten if they match the naming convention for test files fn_path = PurePath(fn) for pat in self.fnpats: if fnmatch_ex(pat, fn_path): state.trace(f"matched test file {fn!r}") return True return self._is_marked_for_rewrite(name, state)
def resolve_collection_argument( invocation_dir: py.path.local, arg: str, *, as_pypath: bool = False ) -> Tuple[py.path.local, List[str]]: """Parse path arguments optionally containing selection parts and return (fspath, names). Command-line arguments can point to files and/or directories, and optionally contain parts for specific tests selection, for example: "pkg/tests/test_foo.py::TestClass::test_foo" This function ensures the path exists, and returns a tuple: (py.path.path("/full/path/to/pkg/tests/test_foo.py"), ["TestClass", "test_foo"]) When as_pypath is True, expects that the command-line argument actually contains module paths instead of file-system paths: "pkg.tests.test_foo::TestClass::test_foo" In which case we search sys.path for a matching module, and then return the *path* to the found module. If the path doesn't exist, raise UsageError. """ strpath, *parts = str(arg).split("::") if as_pypath: strpath = search_pypath(strpath) fspath = Path(str(invocation_dir), strpath) fspath = absolutepath(fspath) if not fspath.exists(): msg = ( "module or package not found: {arg} (missing __init__.py?)" if as_pypath else "file or directory not found: {arg}" ) raise UsageError(msg.format(arg=arg)) return py.path.local(str(fspath)), parts
def locate_config( args: Iterable[Path], ) -> Tuple[Optional[Path], Optional[Path], Dict[str, Union[str, List[str]]], ]: """Search in the list of arguments for a valid ini-file for pytest, and return a tuple of (rootdir, inifile, cfg-dict).""" config_names = [ "pytest.ini", "pyproject.toml", "tox.ini", "setup.cfg", ] args = [x for x in args if not str(x).startswith("-")] if not args: args = [Path.cwd()] for arg in args: argpath = absolutepath(arg) for base in (argpath, *argpath.parents): for config_name in config_names: p = base / config_name if p.is_file(): ini_config = load_config_dict_from_file(p) if ini_config is not None: return base, p, ini_config return None, None, {}
def location(self) -> Tuple[str, Optional[int], str]: location = self.reportinfo() fspath = absolutepath(str(location[0])) relfspath = self.session._node_location_to_relpath(fspath) assert type(location[2]) is str return (relfspath, location[1], location[2])