Source code for _pytest.config

""" command line options, ini-file and processing. """
import argparse
import copy
import inspect
import os
import shlex
import sys
import types
import warnings
from functools import lru_cache
from pathlib import Path

import attr
import py
from packaging.version import Version
from pluggy import HookimplMarker
from pluggy import HookspecMarker
from pluggy import PluginManager

import _pytest._code
import _pytest.assertion
import _pytest.deprecated
import _pytest.hookspec  # the extension point definitions
from .exceptions import PrintHelp
from .exceptions import UsageError
from .findpaths import determine_setup
from .findpaths import exists
from _pytest._code import ExceptionInfo
from _pytest._code import filter_traceback
from _pytest.compat import importlib_metadata
from _pytest.outcomes import fail
from _pytest.outcomes import Skipped
from _pytest.warning_types import PytestConfigWarning

hookimpl = HookimplMarker("pytest")
hookspec = HookspecMarker("pytest")

class ConftestImportFailure(Exception):
    def __init__(self, path, excinfo):
        Exception.__init__(self, path, excinfo)
        self.path = path
        self.excinfo = excinfo

[docs]def main(args=None, plugins=None): """ return exit code, after performing an in-process test run. :arg args: list of command line arguments. :arg plugins: list of plugin objects to be auto-registered during initialization. """ from _pytest.main import ExitCode try: try: config = _prepareconfig(args, plugins) except ConftestImportFailure as e: exc_info = ExceptionInfo(e.excinfo) tw = tw.line( "ImportError while loading conftest '{e.path}'.".format(e=e), red=True ) exc_info.traceback = exc_info.traceback.filter(filter_traceback) exc_repr = ( exc_info.getrepr(style="short", chain=False) if exc_info.traceback else exc_info.exconly() ) formatted_tb = str(exc_repr) for line in formatted_tb.splitlines(): tw.line(line.rstrip(), red=True) return 4 else: try: return config.hook.pytest_cmdline_main(config=config) finally: config._ensure_unconfigure() except UsageError as e: tw = for msg in e.args: tw.line("ERROR: {}\n".format(msg), red=True) return ExitCode.USAGE_ERROR
class cmdline: # compatibility namespace main = staticmethod(main) def filename_arg(path, optname): """ Argparse type validator for filename arguments. :path: path of filename :optname: name of the option """ if os.path.isdir(path): raise UsageError("{} must be a filename, given: {}".format(optname, path)) return path def directory_arg(path, optname): """Argparse type validator for directory arguments. :path: path of directory :optname: name of the option """ if not os.path.isdir(path): raise UsageError("{} must be a directory, given: {}".format(optname, path)) return path # Plugins that cannot be disabled via "-p no:X" currently. essential_plugins = ( # fmt: off "mark", "main", "runner", "fixtures", "helpconfig", # Provides -p. ) # fmt: on default_plugins = essential_plugins + ( "python", "terminal", "debugging", "unittest", "capture", "skipping", "tmpdir", "monkeypatch", "recwarn", "pastebin", "nose", "assertion", "junitxml", "resultlog", "doctest", "cacheprovider", "freeze_support", "setuponly", "setupplan", "stepwise", "warnings", "logging", "reports", "faulthandler", ) builtin_plugins = set(default_plugins) builtin_plugins.add("pytester") def get_config(args=None, plugins=None): # subsequent calls to main will create a fresh instance pluginmanager = PytestPluginManager() config = Config( pluginmanager, invocation_params=Config.InvocationParams( args=args, plugins=plugins, dir=Path().resolve() ), ) if args is not None: # Handle any "-p no:plugin" args. pluginmanager.consider_preparse(args) for spec in default_plugins: pluginmanager.import_plugin(spec) return config def get_plugin_manager(): """ Obtain a new instance of the :py:class:`_pytest.config.PytestPluginManager`, with default plugins already loaded. This function can be used by integration with other tools, like hooking into pytest to run tests into an IDE. """ return get_config().pluginmanager def _prepareconfig(args=None, plugins=None): warning = None if args is None: args = sys.argv[1:] elif isinstance(args, py.path.local): args = [str(args)] elif not isinstance(args, (tuple, list)): msg = "`args` parameter expected to be a list or tuple of strings, got: {!r} (type: {})" raise TypeError(msg.format(args, type(args))) config = get_config(args, plugins) pluginmanager = config.pluginmanager try: if plugins: for plugin in plugins: if isinstance(plugin, str): pluginmanager.consider_pluginarg(plugin) else: pluginmanager.register(plugin) if warning: from _pytest.warnings import _issue_warning_captured _issue_warning_captured(warning, hook=config.hook, stacklevel=4) return pluginmanager.hook.pytest_cmdline_parse( pluginmanager=pluginmanager, args=args ) except BaseException: config._ensure_unconfigure() raise def _fail_on_non_top_pytest_plugins(conftestpath, confcutdir): msg = ( "Defining 'pytest_plugins' in a non-top-level conftest is no longer supported:\n" "It affects the entire test suite instead of just below the conftest as expected.\n" " {}\n" "Please move it to a top level conftest file at the rootdir:\n" " {}\n" "For more information, visit:\n" "" ) fail(msg.format(conftestpath, confcutdir), pytrace=False)
[docs]class PytestPluginManager(PluginManager): """ Overwrites :py:class:`pluggy.PluginManager <pluggy.PluginManager>` to add pytest-specific functionality: * loading plugins from the command line, ``PYTEST_PLUGINS`` env variable and ``pytest_plugins`` global variables found in plugins being loaded; * ```` loading during start-up; """ def __init__(self): super().__init__("pytest") self._conftest_plugins = set() # state related to local conftest plugins self._dirpath2confmods = {} self._conftestpath2mod = {} self._confcutdir = None self._noconftest = False self._duplicatepaths = set() self.add_hookspecs(_pytest.hookspec) self.register(self) if os.environ.get("PYTEST_DEBUG"): err = sys.stderr encoding = getattr(err, "encoding", "utf8") try: err =, encoding=encoding) except Exception: pass self.trace.root.setwriter(err.write) self.enable_tracing() # Config._consider_importhook will set a real object if required. self.rewrite_hook = _pytest.assertion.DummyRewriteHook() # Used to know when we are importing conftests after the pytest_configure stage self._configured = False
[docs] def parse_hookimpl_opts(self, plugin, name): # pytest hooks are always prefixed with pytest_ # so we avoid accessing possibly non-readable attributes # (see issue #1073) if not name.startswith("pytest_"): return # ignore names which can not be hooks if name == "pytest_plugins": return method = getattr(plugin, name) opts = super().parse_hookimpl_opts(plugin, name) # consider only actual functions for hooks (#3775) if not inspect.isroutine(method): return # collect unmarked hooks as long as they have the `pytest_' prefix if opts is None and name.startswith("pytest_"): opts = {} if opts is not None: # TODO: DeprecationWarning, people should use hookimpl # known_marks = { for m in getattr(method, "pytestmark", [])} for name in ("tryfirst", "trylast", "optionalhook", "hookwrapper"): opts.setdefault(name, hasattr(method, name) or name in known_marks) return opts
[docs] def parse_hookspec_opts(self, module_or_class, name): opts = super().parse_hookspec_opts(module_or_class, name) if opts is None: method = getattr(module_or_class, name) if name.startswith("pytest_"): # todo: deprecate hookspec hacks # known_marks = { for m in getattr(method, "pytestmark", [])} opts = { "firstresult": hasattr(method, "firstresult") or "firstresult" in known_marks, "historic": hasattr(method, "historic") or "historic" in known_marks, } return opts
[docs] def register(self, plugin, name=None): if name in _pytest.deprecated.DEPRECATED_EXTERNAL_PLUGINS: warnings.warn( PytestConfigWarning( "{} plugin has been merged into the core, " "please remove it from your requirements.".format( name.replace("_", "-") ) ) ) return ret = super().register(plugin, name) if ret: self.hook.pytest_plugin_registered.call_historic( kwargs=dict(plugin=plugin, manager=self) ) if isinstance(plugin, types.ModuleType): self.consider_module(plugin) return ret
[docs] def getplugin(self, name): # support deprecated naming because plugins (xdist e.g.) use it return self.get_plugin(name)
[docs] def hasplugin(self, name): """Return True if the plugin with the given name is registered.""" return bool(self.get_plugin(name))
[docs] def pytest_configure(self, config): # XXX now that the pluginmanager exposes hookimpl(tryfirst...) # we should remove tryfirst/trylast as markers config.addinivalue_line( "markers", "tryfirst: mark a hook implementation function such that the " "plugin machinery will try to call it first/as early as possible.", ) config.addinivalue_line( "markers", "trylast: mark a hook implementation function such that the " "plugin machinery will try to call it last/as late as possible.", ) self._configured = True
# # internal API for local conftest plugin handling # def _set_initial_conftests(self, namespace): """ load initial conftest files given a preparsed "namespace". As conftest files may add their own command line options which have arguments ('--my-opt somepath') we might get some false positives. All builtin and 3rd party plugins will have been loaded, however, so common options will not confuse our logic here. """ current = py.path.local() self._confcutdir = ( current.join(namespace.confcutdir, abs=True) if namespace.confcutdir else None ) self._noconftest = namespace.noconftest self._using_pyargs = namespace.pyargs testpaths = namespace.file_or_dir foundanchor = False for path in testpaths: path = str(path) # remove node-id syntax i = path.find("::") if i != -1: path = path[:i] anchor = current.join(path, abs=1) if exists(anchor): # we found some file object self._try_load_conftest(anchor) foundanchor = True if not foundanchor: self._try_load_conftest(current) def _try_load_conftest(self, anchor): self._getconftestmodules(anchor) # let's also consider test* subdirs if anchor.check(dir=1): for x in anchor.listdir("test*"): if x.check(dir=1): self._getconftestmodules(x) @lru_cache(maxsize=128) def _getconftestmodules(self, path): if self._noconftest: return [] if path.isfile(): directory = path.dirpath() else: directory = path # XXX these days we may rather want to use config.rootdir # and allow users to opt into looking into the rootdir parent # directories instead of requiring to specify confcutdir clist = [] for parent in directory.realpath().parts(): if self._confcutdir and self._confcutdir.relto(parent): continue conftestpath = parent.join("") if conftestpath.isfile(): # Use realpath to avoid loading the same conftest twice # with build systems that create build directories containing # symlinks to actual files. mod = self._importconftest(conftestpath.realpath()) clist.append(mod) self._dirpath2confmods[directory] = clist return clist def _rget_with_confmod(self, name, path): modules = self._getconftestmodules(path) for mod in reversed(modules): try: return mod, getattr(mod, name) except AttributeError: continue raise KeyError(name) def _importconftest(self, conftestpath): try: return self._conftestpath2mod[conftestpath] except KeyError: pkgpath = conftestpath.pypkgpath() if pkgpath is None: _ensure_removed_sysmodule(conftestpath.purebasename) try: mod = conftestpath.pyimport() if ( hasattr(mod, "pytest_plugins") and self._configured and not self._using_pyargs ): _fail_on_non_top_pytest_plugins(conftestpath, self._confcutdir) except Exception: raise ConftestImportFailure(conftestpath, sys.exc_info()) self._conftest_plugins.add(mod) self._conftestpath2mod[conftestpath] = mod dirpath = conftestpath.dirpath() if dirpath in self._dirpath2confmods: for path, mods in self._dirpath2confmods.items(): if path and path.relto(dirpath) or path == dirpath: assert mod not in mods mods.append(mod) self.trace("loaded conftestmodule %r" % (mod)) self.consider_conftest(mod) return mod # # API for bootstrapping plugin loading # #
[docs] def consider_preparse(self, args): i = 0 n = len(args) while i < n: opt = args[i] i += 1 if isinstance(opt, str): if opt == "-p": try: parg = args[i] except IndexError: return i += 1 elif opt.startswith("-p"): parg = opt[2:] else: continue self.consider_pluginarg(parg)
[docs] def consider_pluginarg(self, arg): if arg.startswith("no:"): name = arg[3:] if name in essential_plugins: raise UsageError("plugin %s cannot be disabled" % name) # PR #4304 : remove stepwise if cacheprovider is blocked if name == "cacheprovider": self.set_blocked("stepwise") self.set_blocked("pytest_stepwise") self.set_blocked(name) if not name.startswith("pytest_"): self.set_blocked("pytest_" + name) else: name = arg # Unblock the plugin. None indicates that it has been blocked. # There is no interface with pluggy for this. if self._name2plugin.get(name, -1) is None: del self._name2plugin[name] if not name.startswith("pytest_"): if self._name2plugin.get("pytest_" + name, -1) is None: del self._name2plugin["pytest_" + name] self.import_plugin(arg, consider_entry_points=True)
[docs] def consider_conftest(self, conftestmodule): self.register(conftestmodule, name=conftestmodule.__file__)
[docs] def consider_env(self): self._import_plugin_specs(os.environ.get("PYTEST_PLUGINS"))
[docs] def consider_module(self, mod): self._import_plugin_specs(getattr(mod, "pytest_plugins", []))
def _import_plugin_specs(self, spec): plugins = _get_plugin_specs_as_list(spec) for import_spec in plugins: self.import_plugin(import_spec)
[docs] def import_plugin(self, modname, consider_entry_points=False): """ Imports a plugin with ``modname``. If ``consider_entry_points`` is True, entry point names are also considered to find a plugin. """ # most often modname refers to builtin modules, e.g. "pytester", # "terminal" or "capture". Those plugins are registered under their # basename for historic purposes but must be imported with the # _pytest prefix. assert isinstance(modname, str), ( "module name as text required, got %r" % modname ) modname = str(modname) if self.is_blocked(modname) or self.get_plugin(modname) is not None: return importspec = "_pytest." + modname if modname in builtin_plugins else modname self.rewrite_hook.mark_rewrite(importspec) if consider_entry_points: loaded = self.load_setuptools_entrypoints("pytest11", name=modname) if loaded: return try: __import__(importspec) except ImportError as e: new_exc_message = 'Error importing plugin "{}": {}'.format( modname, str(e.args[0]) ) new_exc = ImportError(new_exc_message) tb = sys.exc_info()[2] raise new_exc.with_traceback(tb) except Skipped as e: from _pytest.warnings import _issue_warning_captured _issue_warning_captured( PytestConfigWarning("skipped plugin {!r}: {}".format(modname, e.msg)), self.hook, stacklevel=1, ) else: mod = sys.modules[importspec] self.register(mod, modname)
def _get_plugin_specs_as_list(specs): """ Parses a list of "plugin specs" and returns a list of plugin names. Plugin specs can be given as a list of strings separated by "," or already as a list/tuple in which case it is returned as a list. Specs can also be `None` in which case an empty list is returned. """ if specs is not None and not isinstance(specs, types.ModuleType): if isinstance(specs, str): specs = specs.split(",") if specs else [] if not isinstance(specs, (list, tuple)): raise UsageError( "Plugin specs must be a ','-separated string or a " "list/tuple of strings for plugin names. Given: %r" % specs ) return list(specs) return [] def _ensure_removed_sysmodule(modname): try: del sys.modules[modname] except KeyError: pass class Notset: def __repr__(self): return "<NOTSET>" notset = Notset() def _iter_rewritable_modules(package_files): for fn in package_files: is_simple_module = "/" not in fn and fn.endswith(".py") is_package = fn.count("/") == 1 and fn.endswith("") if is_simple_module: module_name, _ = os.path.splitext(fn) yield module_name elif is_package: package_name = os.path.dirname(fn) yield package_name
[docs]class Config: """ Access to configuration values, pluginmanager and plugin hooks. :ivar PytestPluginManager pluginmanager: the plugin manager handles plugin registration and hook invocation. :ivar argparse.Namespace option: access to command line option as attributes. :ivar InvocationParams invocation_params: Object containing the parameters regarding the ``pytest.main`` invocation. Contains the following read-only attributes: * ``args``: list of command-line arguments as passed to ``pytest.main()``. * ``plugins``: list of extra plugins, might be None. * ``dir``: directory where ``pytest.main()`` was invoked from. """
[docs] @attr.s(frozen=True) class InvocationParams: """Holds parameters passed during ``pytest.main()`` .. note:: Currently the environment variable PYTEST_ADDOPTS is also handled by pytest implicitly, not being part of the invocation. Plugins accessing ``InvocationParams`` must be aware of that. """ args = attr.ib() plugins = attr.ib() dir = attr.ib()
def __init__(self, pluginmanager, *, invocation_params=None): from .argparsing import Parser, FILE_OR_DIR if invocation_params is None: invocation_params = self.InvocationParams( args=(), plugins=None, dir=Path().resolve() ) self.option = argparse.Namespace() self.invocation_params = invocation_params _a = FILE_OR_DIR self._parser = Parser( usage="%(prog)s [options] [{}] [{}] [...]".format(_a, _a), processopt=self._processopt, ) self.pluginmanager = pluginmanager self.trace = self.pluginmanager.trace.root.get("config") self.hook = self.pluginmanager.hook self._inicache = {} self._override_ini = () self._opt2dest = {} self._cleanup = [] self.pluginmanager.register(self, "pytestconfig") self._configured = False self.hook.pytest_addoption.call_historic(kwargs=dict(parser=self._parser)) @property def invocation_dir(self): """Backward compatibility""" return py.path.local(str(self.invocation_params.dir))
[docs] def add_cleanup(self, func): """ Add a function to be called when the config object gets out of use (usually coninciding with pytest_unconfigure).""" self._cleanup.append(func)
def _do_configure(self): assert not self._configured self._configured = True with warnings.catch_warnings(): warnings.simplefilter("default") self.hook.pytest_configure.call_historic(kwargs=dict(config=self)) def _ensure_unconfigure(self): if self._configured: self._configured = False self.hook.pytest_unconfigure(config=self) self.hook.pytest_configure._call_history = [] while self._cleanup: fin = self._cleanup.pop() fin() def get_terminal_writer(self): return self.pluginmanager.get_plugin("terminalreporter")._tw def pytest_cmdline_parse(self, pluginmanager, args): try: self.parse(args) except UsageError: # Handle --version and --help here in a minimal fashion. # This gets done via helpconfig normally, but its # pytest_cmdline_main is not called in case of errors. if getattr(self.option, "version", False) or "--version" in args: from _pytest.helpconfig import showversion showversion(self) elif ( getattr(self.option, "help", False) or "--help" in args or "-h" in args ): self._parser._getparser().print_help() sys.stdout.write( "\nNOTE: displaying only minimal help due to UsageError.\n\n" ) raise return self def notify_exception(self, excinfo, option=None): if option and getattr(option, "fulltrace", False): style = "long" else: style = "native" excrepr = excinfo.getrepr( funcargs=True, showlocals=getattr(option, "showlocals", False), style=style ) res = self.hook.pytest_internalerror(excrepr=excrepr, excinfo=excinfo) if not any(res): for line in str(excrepr).split("\n"): sys.stderr.write("INTERNALERROR> %s\n" % line) sys.stderr.flush() def cwd_relative_nodeid(self, nodeid): # nodeid's are relative to the rootpath, compute relative to cwd if self.invocation_dir != self.rootdir: fullpath = self.rootdir.join(nodeid) nodeid = self.invocation_dir.bestrelpath(fullpath) return nodeid
[docs] @classmethod def fromdictargs(cls, option_dict, args): """ constructor useable for subprocesses. """ config = get_config(args) config.option.__dict__.update(option_dict) config.parse(args, addopts=False) for x in config.option.plugins: config.pluginmanager.consider_pluginarg(x) return config
def _processopt(self, opt): for name in opt._short_opts + opt._long_opts: self._opt2dest[name] = opt.dest if hasattr(opt, "default") and opt.dest: if not hasattr(self.option, opt.dest): setattr(self.option, opt.dest, opt.default) @hookimpl(trylast=True) def pytest_load_initial_conftests(self, early_config): self.pluginmanager._set_initial_conftests(early_config.known_args_namespace) def _initini(self, args): ns, unknown_args = self._parser.parse_known_and_unknown_args( args, namespace=copy.copy(self.option) ) r = determine_setup( ns.inifilename, ns.file_or_dir + unknown_args, rootdir_cmd_arg=ns.rootdir or None, config=self, ) self.rootdir, self.inifile, self.inicfg = r self._parser.extra_info["rootdir"] = self.rootdir self._parser.extra_info["inifile"] = self.inifile self._parser.addini("addopts", "extra command line options", "args") self._parser.addini("minversion", "minimally required pytest version") self._override_ini = ns.override_ini or () def _consider_importhook(self, args): """Install the PEP 302 import hook if using assertion rewriting. Needs to parse the --assert=<mode> option from the commandline and find all the installed plugins to mark them for rewriting by the importhook. """ ns, unknown_args = self._parser.parse_known_and_unknown_args(args) mode = getattr(ns, "assertmode", "plain") if mode == "rewrite": try: hook = _pytest.assertion.install_importhook(self) except SystemError: mode = "plain" else: self._mark_plugins_for_rewrite(hook) _warn_about_missing_assertion(mode) def _mark_plugins_for_rewrite(self, hook): """ Given an importhook, mark for rewrite any top-level modules or packages in the distribution package for all pytest plugins. """ self.pluginmanager.rewrite_hook = hook if os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD"): # We don't autoload from setuptools entry points, no need to continue. return package_files = ( str(file) for dist in importlib_metadata.distributions() if any( == "pytest11" for ep in dist.entry_points) for file in dist.files or [] ) for name in _iter_rewritable_modules(package_files): hook.mark_rewrite(name) def _validate_args(self, args, via): """Validate known args.""" self._parser._config_source_hint = via try: self._parser.parse_known_and_unknown_args( args, namespace=copy.copy(self.option) ) finally: del self._parser._config_source_hint return args def _preparse(self, args, addopts=True): if addopts: env_addopts = os.environ.get("PYTEST_ADDOPTS", "") if len(env_addopts): args[:] = ( self._validate_args(shlex.split(env_addopts), "via PYTEST_ADDOPTS") + args ) self._initini(args) if addopts: args[:] = ( self._validate_args(self.getini("addopts"), "via addopts config") + args ) self._checkversion() self._consider_importhook(args) self.pluginmanager.consider_preparse(args) if not os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD"): # Don't autoload from setuptools entry point. Only explicitly specified # plugins are going to be loaded. self.pluginmanager.load_setuptools_entrypoints("pytest11") self.pluginmanager.consider_env() self.known_args_namespace = ns = self._parser.parse_known_args( args, namespace=copy.copy(self.option) ) if self.known_args_namespace.confcutdir is None and self.inifile: confcutdir = py.path.local(self.inifile).dirname self.known_args_namespace.confcutdir = confcutdir try: self.hook.pytest_load_initial_conftests( early_config=self, args=args, parser=self._parser ) except ConftestImportFailure: e = sys.exc_info()[1] if or ns.version: # we don't want to prevent --help/--version to work # so just let is pass and print a warning at the end from _pytest.warnings import _issue_warning_captured _issue_warning_captured( PytestConfigWarning( "could not load initial conftests: {}".format(e.path) ), self.hook, stacklevel=2, ) else: raise def _checkversion(self): import pytest minver = self.inicfg.get("minversion", None) if minver: if Version(minver) > Version(pytest.__version__): raise pytest.UsageError( "%s:%d: requires pytest-%s, actual pytest-%s'" % ( self.inicfg.config.path, self.inicfg.lineof("minversion"), minver, pytest.__version__, ) ) def parse(self, args, addopts=True): # parse given cmdline arguments into this config object. assert not hasattr( self, "args" ), "can only parse cmdline args at most once per Config object" assert self.invocation_params.args == args self.hook.pytest_addhooks.call_historic( kwargs=dict(pluginmanager=self.pluginmanager) ) self._preparse(args, addopts=addopts) # XXX deprecated hook: self.hook.pytest_cmdline_preparse(config=self, args=args) self._parser.after_preparse = True try: args = self._parser.parse_setoption( args, self.option, namespace=self.option ) if not args: if self.invocation_dir == self.rootdir: args = self.getini("testpaths") if not args: args = [str(self.invocation_dir)] self.args = args except PrintHelp: pass
[docs] def addinivalue_line(self, name, line): """ add a line to an ini-file option. The option must have been declared but might not yet be set in which case the line becomes the the first line in its value. """ x = self.getini(name) assert isinstance(x, list) x.append(line) # modifies the cached list inline
[docs] def getini(self, name): """ return configuration value from an :ref:`ini file <inifiles>`. If the specified name hasn't been registered through a prior :py:func:`parser.addini <_pytest.config.Parser.addini>` call (usually from a plugin), a ValueError is raised. """ try: return self._inicache[name] except KeyError: self._inicache[name] = val = self._getini(name) return val
def _getini(self, name): try: description, type, default = self._parser._inidict[name] except KeyError: raise ValueError("unknown configuration value: {!r}".format(name)) value = self._get_override_ini_value(name) if value is None: try: value = self.inicfg[name] except KeyError: if default is not None: return default if type is None: return "" return [] if type == "pathlist": dp = py.path.local(self.inicfg.config.path).dirpath() values = [] for relpath in shlex.split(value): values.append(dp.join(relpath, abs=True)) return values elif type == "args": return shlex.split(value) elif type == "linelist": return [t for t in map(lambda x: x.strip(), value.split("\n")) if t] elif type == "bool": return bool(_strtobool(value.strip())) else: assert type is None return value def _getconftest_pathlist(self, name, path): try: mod, relroots = self.pluginmanager._rget_with_confmod(name, path) except KeyError: return None modpath = py.path.local(mod.__file__).dirpath() values = [] for relroot in relroots: if not isinstance(relroot, py.path.local): relroot = relroot.replace("/", py.path.local.sep) relroot = modpath.join(relroot, abs=True) values.append(relroot) return values def _get_override_ini_value(self, name): value = None # override_ini is a list of "ini=value" options # always use the last item if multiple values are set for same ini-name, # e.g. -o foo=bar1 -o foo=bar2 will set foo to bar2 for ini_config in self._override_ini: try: key, user_ini_value = ini_config.split("=", 1) except ValueError: raise UsageError("-o/--override-ini expects option=value style.") else: if key == name: value = user_ini_value return value
[docs] def getoption(self, name, default=notset, skip=False): """ return command line option value. :arg name: name of the option. You may also specify the literal ``--OPT`` option instead of the "dest" option name. :arg default: default value if no option of that name exists. :arg skip: if True raise pytest.skip if option does not exists or has a None value. """ name = self._opt2dest.get(name, name) try: val = getattr(self.option, name) if val is None and skip: raise AttributeError(name) return val except AttributeError: if default is not notset: return default if skip: import pytest pytest.skip("no {!r} option found".format(name)) raise ValueError("no option named {!r}".format(name))
[docs] def getvalue(self, name, path=None): """ (deprecated, use getoption()) """ return self.getoption(name)
[docs] def getvalueorskip(self, name, path=None): """ (deprecated, use getoption(skip=True)) """ return self.getoption(name, skip=True)
def _assertion_supported(): try: assert False except AssertionError: return True else: return False def _warn_about_missing_assertion(mode): if not _assertion_supported(): if mode == "plain": sys.stderr.write( "WARNING: ASSERTIONS ARE NOT EXECUTED" " and FAILING TESTS WILL PASS. Are you" " using python -O?" ) else: sys.stderr.write( "WARNING: assertions not in test modules or" " plugins will be ignored" " because assert statements are not executed " "by the underlying Python interpreter " "(are you using python -O?)\n" ) def setns(obj, dic): import pytest for name, value in dic.items(): if isinstance(value, dict): mod = getattr(obj, name, None) if mod is None: modname = "pytest.%s" % name mod = types.ModuleType(modname) sys.modules[modname] = mod mod.__all__ = [] setattr(obj, name, mod) obj.__all__.append(name) setns(mod, value) else: setattr(obj, name, value) obj.__all__.append(name) # if obj != pytest: # pytest.__all__.append(name) setattr(pytest, name, value) def create_terminal_writer(config, *args, **kwargs): """Create a TerminalWriter instance configured according to the options in the config object. Every code which requires a TerminalWriter object and has access to a config object should use this function. """ tw =*args, **kwargs) if config.option.color == "yes": tw.hasmarkup = True if config.option.color == "no": tw.hasmarkup = False return tw def _strtobool(val): """Convert a string representation of truth to true (1) or false (0). True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if 'val' is anything else. .. note:: copied from distutils.util """ val = val.lower() if val in ("y", "yes", "t", "true", "on", "1"): return 1 elif val in ("n", "no", "f", "false", "off", "0"): return 0 else: raise ValueError("invalid truth value {!r}".format(val))