Ejemplo n.º 1
0
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
Ejemplo n.º 2
0
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
Ejemplo n.º 3
0
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)
    ]
Ejemplo n.º 4
0
 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
Ejemplo n.º 5
0
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 {}
Ejemplo n.º 6
0
 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
Ejemplo n.º 7
0
 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
Ejemplo n.º 8
0
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
Ejemplo n.º 9
0
    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)
Ejemplo n.º 10
0
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
Ejemplo n.º 11
0
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, {}
Ejemplo n.º 12
0
 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])