Source code for _pytest.config.findpaths

import os
from typing import Any
from typing import Iterable
from typing import List
from typing import Optional
from typing import Tuple

import py

from .exceptions import UsageError
from _pytest.compat import TYPE_CHECKING
from _pytest.outcomes import fail

if TYPE_CHECKING:
    from . import Config  # noqa: F401


[docs]def exists(path, ignore=EnvironmentError): try: return path.check() except ignore: return False
[docs]def getcfg(args, config=None): """ Search the list of arguments for a valid ini-file for pytest, and return a tuple of (rootdir, inifile, cfg-dict). note: config is optional and used only to issue warnings explicitly (#2891). """ inibasenames = ["pytest.ini", "tox.ini", "setup.cfg"] args = [x for x in args if not str(x).startswith("-")] if not args: args = [py.path.local()] for arg in args: arg = py.path.local(arg) for base in arg.parts(reverse=True): for inibasename in inibasenames: p = base.join(inibasename) if exists(p): try: iniconfig = py.iniconfig.IniConfig(p) except py.iniconfig.ParseError as exc: raise UsageError(str(exc)) if ( inibasename == "setup.cfg" and "tool:pytest" in iniconfig.sections ): return base, p, iniconfig["tool:pytest"] elif "pytest" in iniconfig.sections: if inibasename == "setup.cfg" and config is not None: fail( CFG_PYTEST_SECTION.format(filename=inibasename), pytrace=False, ) return base, p, iniconfig["pytest"] elif inibasename == "pytest.ini": # allowed to be empty return base, p, {} return None, None, None
[docs]def get_common_ancestor(paths: Iterable[py.path.local]) -> py.path.local: common_ancestor = None for path in paths: if not path.exists(): continue if common_ancestor is None: common_ancestor = path else: if path.relto(common_ancestor) or path == common_ancestor: continue elif common_ancestor.relto(path): common_ancestor = path else: shared = path.common(common_ancestor) if shared is not None: common_ancestor = shared if common_ancestor is None: common_ancestor = py.path.local() elif common_ancestor.isfile(): common_ancestor = common_ancestor.dirpath() return common_ancestor
[docs]def get_dirs_from_args(args: List[str]) -> List[py.path.local]: 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: py.path.local) -> py.path.local: if path.isdir(): return path return py.path.local(path.dirname) # These look like paths but may not exist possible_paths = ( py.path.local(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 path.exists()]
CFG_PYTEST_SECTION = "[pytest] section in {filename} files is no longer supported, change to [tool:pytest] instead."
[docs]def determine_setup( inifile: Optional[str], args: List[str], rootdir_cmd_arg: Optional[str] = None, config: Optional["Config"] = None, ) -> Tuple[py.path.local, Optional[str], Any]: dirs = get_dirs_from_args(args) if inifile: iniconfig = py.iniconfig.IniConfig(inifile) is_cfg_file = str(inifile).endswith(".cfg") sections = ["tool:pytest", "pytest"] if is_cfg_file else ["pytest"] for section in sections: try: inicfg = iniconfig[ section ] # type: Optional[py.iniconfig._SectionWrapper] if is_cfg_file and section == "pytest" and config is not None: fail( CFG_PYTEST_SECTION.format(filename=str(inifile)), pytrace=False ) break except KeyError: inicfg = None if rootdir_cmd_arg is None: rootdir = get_common_ancestor(dirs) else: ancestor = get_common_ancestor(dirs) rootdir, inifile, inicfg = getcfg([ancestor], config=config) if rootdir is None and rootdir_cmd_arg is None: for possible_rootdir in ancestor.parts(reverse=True): if possible_rootdir.join("setup.py").exists(): rootdir = possible_rootdir break else: if dirs != [ancestor]: rootdir, inifile, inicfg = getcfg(dirs, config=config) if rootdir is None: if config is not None: cwd = config.invocation_dir else: cwd = py.path.local() 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 = py.path.local(os.path.expandvars(rootdir_cmd_arg)) if not rootdir.isdir(): raise UsageError( "Directory '{}' not found. Check your '--rootdir' option.".format( rootdir ) ) return rootdir, inifile, inicfg or {}