1# -*- coding: utf-8 -*- 2""" command line options, ini-file and conftest.py processing. """ 3from __future__ import absolute_import 4from __future__ import division 5from __future__ import print_function 6 7import argparse 8import copy 9import inspect 10import os 11import shlex 12import sys 13import types 14import warnings 15 16import attr 17import py 18import six 19from packaging.version import Version 20from pluggy import HookimplMarker 21from pluggy import HookspecMarker 22from pluggy import PluginManager 23 24import _pytest._code 25import _pytest.assertion 26import _pytest.hookspec # the extension point definitions 27from .exceptions import PrintHelp 28from .exceptions import UsageError 29from .findpaths import determine_setup 30from .findpaths import exists 31from _pytest import deprecated 32from _pytest._code import ExceptionInfo 33from _pytest._code import filter_traceback 34from _pytest.compat import importlib_metadata 35from _pytest.compat import lru_cache 36from _pytest.compat import safe_str 37from _pytest.outcomes import fail 38from _pytest.outcomes import Skipped 39from _pytest.pathlib import Path 40from _pytest.warning_types import PytestConfigWarning 41 42hookimpl = HookimplMarker("pytest") 43hookspec = HookspecMarker("pytest") 44 45 46class ConftestImportFailure(Exception): 47 def __init__(self, path, excinfo): 48 Exception.__init__(self, path, excinfo) 49 self.path = path 50 self.excinfo = excinfo 51 52 53def main(args=None, plugins=None): 54 """ return exit code, after performing an in-process test run. 55 56 :arg args: list of command line arguments. 57 58 :arg plugins: list of plugin objects to be auto-registered during 59 initialization. 60 """ 61 from _pytest.main import EXIT_USAGEERROR 62 63 try: 64 try: 65 config = _prepareconfig(args, plugins) 66 except ConftestImportFailure as e: 67 exc_info = ExceptionInfo(e.excinfo) 68 tw = py.io.TerminalWriter(sys.stderr) 69 tw.line( 70 "ImportError while loading conftest '{e.path}'.".format(e=e), red=True 71 ) 72 exc_info.traceback = exc_info.traceback.filter(filter_traceback) 73 exc_repr = ( 74 exc_info.getrepr(style="short", chain=False) 75 if exc_info.traceback 76 else exc_info.exconly() 77 ) 78 formatted_tb = safe_str(exc_repr) 79 for line in formatted_tb.splitlines(): 80 tw.line(line.rstrip(), red=True) 81 return 4 82 else: 83 try: 84 return config.hook.pytest_cmdline_main(config=config) 85 finally: 86 config._ensure_unconfigure() 87 except UsageError as e: 88 tw = py.io.TerminalWriter(sys.stderr) 89 for msg in e.args: 90 tw.line("ERROR: {}\n".format(msg), red=True) 91 return EXIT_USAGEERROR 92 93 94class cmdline(object): # compatibility namespace 95 main = staticmethod(main) 96 97 98def filename_arg(path, optname): 99 """ Argparse type validator for filename arguments. 100 101 :path: path of filename 102 :optname: name of the option 103 """ 104 if os.path.isdir(path): 105 raise UsageError("{} must be a filename, given: {}".format(optname, path)) 106 return path 107 108 109def directory_arg(path, optname): 110 """Argparse type validator for directory arguments. 111 112 :path: path of directory 113 :optname: name of the option 114 """ 115 if not os.path.isdir(path): 116 raise UsageError("{} must be a directory, given: {}".format(optname, path)) 117 return path 118 119 120# Plugins that cannot be disabled via "-p no:X" currently. 121essential_plugins = ( 122 "mark", 123 "main", 124 "runner", 125 "fixtures", 126 "helpconfig", # Provides -p. 127) 128 129default_plugins = essential_plugins + ( 130 "python", 131 "terminal", 132 "debugging", 133 "unittest", 134 "capture", 135 "skipping", 136 "tmpdir", 137 "monkeypatch", 138 "recwarn", 139 "pastebin", 140 "nose", 141 "assertion", 142 "junitxml", 143 "resultlog", 144 "doctest", 145 "cacheprovider", 146 "freeze_support", 147 "setuponly", 148 "setupplan", 149 "stepwise", 150 "warnings", 151 "logging", 152 "reports", 153) 154 155builtin_plugins = set(default_plugins) 156builtin_plugins.add("pytester") 157 158 159def get_config(args=None, plugins=None): 160 # subsequent calls to main will create a fresh instance 161 pluginmanager = PytestPluginManager() 162 config = Config( 163 pluginmanager, 164 invocation_params=Config.InvocationParams( 165 args=args, plugins=plugins, dir=Path().resolve() 166 ), 167 ) 168 169 if args is not None: 170 # Handle any "-p no:plugin" args. 171 pluginmanager.consider_preparse(args) 172 173 for spec in default_plugins: 174 pluginmanager.import_plugin(spec) 175 return config 176 177 178def get_plugin_manager(): 179 """ 180 Obtain a new instance of the 181 :py:class:`_pytest.config.PytestPluginManager`, with default plugins 182 already loaded. 183 184 This function can be used by integration with other tools, like hooking 185 into pytest to run tests into an IDE. 186 """ 187 return get_config().pluginmanager 188 189 190def _prepareconfig(args=None, plugins=None): 191 warning = None 192 if args is None: 193 args = sys.argv[1:] 194 elif isinstance(args, py.path.local): 195 args = [str(args)] 196 elif not isinstance(args, (tuple, list)): 197 msg = "`args` parameter expected to be a list or tuple of strings, got: {!r} (type: {})" 198 raise TypeError(msg.format(args, type(args))) 199 200 config = get_config(args, plugins) 201 pluginmanager = config.pluginmanager 202 try: 203 if plugins: 204 for plugin in plugins: 205 if isinstance(plugin, six.string_types): 206 pluginmanager.consider_pluginarg(plugin) 207 else: 208 pluginmanager.register(plugin) 209 if warning: 210 from _pytest.warnings import _issue_warning_captured 211 212 _issue_warning_captured(warning, hook=config.hook, stacklevel=4) 213 return pluginmanager.hook.pytest_cmdline_parse( 214 pluginmanager=pluginmanager, args=args 215 ) 216 except BaseException: 217 config._ensure_unconfigure() 218 raise 219 220 221class PytestPluginManager(PluginManager): 222 """ 223 Overwrites :py:class:`pluggy.PluginManager <pluggy.PluginManager>` to add pytest-specific 224 functionality: 225 226 * loading plugins from the command line, ``PYTEST_PLUGINS`` env variable and 227 ``pytest_plugins`` global variables found in plugins being loaded; 228 * ``conftest.py`` loading during start-up; 229 """ 230 231 def __init__(self): 232 super(PytestPluginManager, self).__init__("pytest") 233 self._conftest_plugins = set() 234 235 # state related to local conftest plugins 236 self._dirpath2confmods = {} 237 self._conftestpath2mod = {} 238 self._confcutdir = None 239 self._noconftest = False 240 self._duplicatepaths = set() 241 242 self.add_hookspecs(_pytest.hookspec) 243 self.register(self) 244 if os.environ.get("PYTEST_DEBUG"): 245 err = sys.stderr 246 encoding = getattr(err, "encoding", "utf8") 247 try: 248 err = py.io.dupfile(err, encoding=encoding) 249 except Exception: 250 pass 251 self.trace.root.setwriter(err.write) 252 self.enable_tracing() 253 254 # Config._consider_importhook will set a real object if required. 255 self.rewrite_hook = _pytest.assertion.DummyRewriteHook() 256 # Used to know when we are importing conftests after the pytest_configure stage 257 self._configured = False 258 259 def addhooks(self, module_or_class): 260 """ 261 .. deprecated:: 2.8 262 263 Use :py:meth:`pluggy.PluginManager.add_hookspecs <PluginManager.add_hookspecs>` 264 instead. 265 """ 266 warnings.warn(deprecated.PLUGIN_MANAGER_ADDHOOKS, stacklevel=2) 267 return self.add_hookspecs(module_or_class) 268 269 def parse_hookimpl_opts(self, plugin, name): 270 # pytest hooks are always prefixed with pytest_ 271 # so we avoid accessing possibly non-readable attributes 272 # (see issue #1073) 273 if not name.startswith("pytest_"): 274 return 275 # ignore names which can not be hooks 276 if name == "pytest_plugins": 277 return 278 279 method = getattr(plugin, name) 280 opts = super(PytestPluginManager, self).parse_hookimpl_opts(plugin, name) 281 282 # consider only actual functions for hooks (#3775) 283 if not inspect.isroutine(method): 284 return 285 286 # collect unmarked hooks as long as they have the `pytest_' prefix 287 if opts is None and name.startswith("pytest_"): 288 opts = {} 289 if opts is not None: 290 # TODO: DeprecationWarning, people should use hookimpl 291 # https://github.com/pytest-dev/pytest/issues/4562 292 known_marks = {m.name for m in getattr(method, "pytestmark", [])} 293 294 for name in ("tryfirst", "trylast", "optionalhook", "hookwrapper"): 295 opts.setdefault(name, hasattr(method, name) or name in known_marks) 296 return opts 297 298 def parse_hookspec_opts(self, module_or_class, name): 299 opts = super(PytestPluginManager, self).parse_hookspec_opts( 300 module_or_class, name 301 ) 302 if opts is None: 303 method = getattr(module_or_class, name) 304 305 if name.startswith("pytest_"): 306 # todo: deprecate hookspec hacks 307 # https://github.com/pytest-dev/pytest/issues/4562 308 known_marks = {m.name for m in getattr(method, "pytestmark", [])} 309 opts = { 310 "firstresult": hasattr(method, "firstresult") 311 or "firstresult" in known_marks, 312 "historic": hasattr(method, "historic") 313 or "historic" in known_marks, 314 } 315 return opts 316 317 def register(self, plugin, name=None): 318 if name in ["pytest_catchlog", "pytest_capturelog"]: 319 warnings.warn( 320 PytestConfigWarning( 321 "{} plugin has been merged into the core, " 322 "please remove it from your requirements.".format( 323 name.replace("_", "-") 324 ) 325 ) 326 ) 327 return 328 ret = super(PytestPluginManager, self).register(plugin, name) 329 if ret: 330 self.hook.pytest_plugin_registered.call_historic( 331 kwargs=dict(plugin=plugin, manager=self) 332 ) 333 334 if isinstance(plugin, types.ModuleType): 335 self.consider_module(plugin) 336 return ret 337 338 def getplugin(self, name): 339 # support deprecated naming because plugins (xdist e.g.) use it 340 return self.get_plugin(name) 341 342 def hasplugin(self, name): 343 """Return True if the plugin with the given name is registered.""" 344 return bool(self.get_plugin(name)) 345 346 def pytest_configure(self, config): 347 # XXX now that the pluginmanager exposes hookimpl(tryfirst...) 348 # we should remove tryfirst/trylast as markers 349 config.addinivalue_line( 350 "markers", 351 "tryfirst: mark a hook implementation function such that the " 352 "plugin machinery will try to call it first/as early as possible.", 353 ) 354 config.addinivalue_line( 355 "markers", 356 "trylast: mark a hook implementation function such that the " 357 "plugin machinery will try to call it last/as late as possible.", 358 ) 359 self._configured = True 360 361 # 362 # internal API for local conftest plugin handling 363 # 364 def _set_initial_conftests(self, namespace): 365 """ load initial conftest files given a preparsed "namespace". 366 As conftest files may add their own command line options 367 which have arguments ('--my-opt somepath') we might get some 368 false positives. All builtin and 3rd party plugins will have 369 been loaded, however, so common options will not confuse our logic 370 here. 371 """ 372 current = py.path.local() 373 self._confcutdir = ( 374 current.join(namespace.confcutdir, abs=True) 375 if namespace.confcutdir 376 else None 377 ) 378 self._noconftest = namespace.noconftest 379 self._using_pyargs = namespace.pyargs 380 testpaths = namespace.file_or_dir 381 foundanchor = False 382 for path in testpaths: 383 path = str(path) 384 # remove node-id syntax 385 i = path.find("::") 386 if i != -1: 387 path = path[:i] 388 anchor = current.join(path, abs=1) 389 if exists(anchor): # we found some file object 390 self._try_load_conftest(anchor) 391 foundanchor = True 392 if not foundanchor: 393 self._try_load_conftest(current) 394 395 def _try_load_conftest(self, anchor): 396 self._getconftestmodules(anchor) 397 # let's also consider test* subdirs 398 if anchor.check(dir=1): 399 for x in anchor.listdir("test*"): 400 if x.check(dir=1): 401 self._getconftestmodules(x) 402 403 @lru_cache(maxsize=128) 404 def _getconftestmodules(self, path): 405 if self._noconftest: 406 return [] 407 408 if path.isfile(): 409 directory = path.dirpath() 410 else: 411 directory = path 412 413 if six.PY2: # py2 is not using lru_cache. 414 try: 415 return self._dirpath2confmods[directory] 416 except KeyError: 417 pass 418 419 # XXX these days we may rather want to use config.rootdir 420 # and allow users to opt into looking into the rootdir parent 421 # directories instead of requiring to specify confcutdir 422 clist = [] 423 for parent in directory.realpath().parts(): 424 if self._confcutdir and self._confcutdir.relto(parent): 425 continue 426 conftestpath = parent.join("conftest.py") 427 if conftestpath.isfile(): 428 # Use realpath to avoid loading the same conftest twice 429 # with build systems that create build directories containing 430 # symlinks to actual files. 431 mod = self._importconftest(conftestpath.realpath()) 432 clist.append(mod) 433 self._dirpath2confmods[directory] = clist 434 return clist 435 436 def _rget_with_confmod(self, name, path): 437 modules = self._getconftestmodules(path) 438 for mod in reversed(modules): 439 try: 440 return mod, getattr(mod, name) 441 except AttributeError: 442 continue 443 raise KeyError(name) 444 445 def _importconftest(self, conftestpath): 446 try: 447 return self._conftestpath2mod[conftestpath] 448 except KeyError: 449 pkgpath = conftestpath.pypkgpath() 450 if pkgpath is None: 451 _ensure_removed_sysmodule(conftestpath.purebasename) 452 try: 453 mod = conftestpath.pyimport() 454 if ( 455 hasattr(mod, "pytest_plugins") 456 and self._configured 457 and not self._using_pyargs 458 ): 459 from _pytest.deprecated import ( 460 PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST, 461 ) 462 463 fail( 464 PYTEST_PLUGINS_FROM_NON_TOP_LEVEL_CONFTEST.format( 465 conftestpath, self._confcutdir 466 ), 467 pytrace=False, 468 ) 469 except Exception: 470 raise ConftestImportFailure(conftestpath, sys.exc_info()) 471 472 self._conftest_plugins.add(mod) 473 self._conftestpath2mod[conftestpath] = mod 474 dirpath = conftestpath.dirpath() 475 if dirpath in self._dirpath2confmods: 476 for path, mods in self._dirpath2confmods.items(): 477 if path and path.relto(dirpath) or path == dirpath: 478 assert mod not in mods 479 mods.append(mod) 480 self.trace("loaded conftestmodule %r" % (mod)) 481 self.consider_conftest(mod) 482 return mod 483 484 # 485 # API for bootstrapping plugin loading 486 # 487 # 488 489 def consider_preparse(self, args): 490 i = 0 491 n = len(args) 492 while i < n: 493 opt = args[i] 494 i += 1 495 if isinstance(opt, six.string_types): 496 if opt == "-p": 497 try: 498 parg = args[i] 499 except IndexError: 500 return 501 i += 1 502 elif opt.startswith("-p"): 503 parg = opt[2:] 504 else: 505 continue 506 self.consider_pluginarg(parg) 507 508 def consider_pluginarg(self, arg): 509 if arg.startswith("no:"): 510 name = arg[3:] 511 if name in essential_plugins: 512 raise UsageError("plugin %s cannot be disabled" % name) 513 514 # PR #4304 : remove stepwise if cacheprovider is blocked 515 if name == "cacheprovider": 516 self.set_blocked("stepwise") 517 self.set_blocked("pytest_stepwise") 518 519 self.set_blocked(name) 520 if not name.startswith("pytest_"): 521 self.set_blocked("pytest_" + name) 522 else: 523 name = arg 524 # Unblock the plugin. None indicates that it has been blocked. 525 # There is no interface with pluggy for this. 526 if self._name2plugin.get(name, -1) is None: 527 del self._name2plugin[name] 528 if not name.startswith("pytest_"): 529 if self._name2plugin.get("pytest_" + name, -1) is None: 530 del self._name2plugin["pytest_" + name] 531 self.import_plugin(arg, consider_entry_points=True) 532 533 def consider_conftest(self, conftestmodule): 534 self.register(conftestmodule, name=conftestmodule.__file__) 535 536 def consider_env(self): 537 self._import_plugin_specs(os.environ.get("PYTEST_PLUGINS")) 538 539 def consider_module(self, mod): 540 self._import_plugin_specs(getattr(mod, "pytest_plugins", [])) 541 542 def _import_plugin_specs(self, spec): 543 plugins = _get_plugin_specs_as_list(spec) 544 for import_spec in plugins: 545 self.import_plugin(import_spec) 546 547 def import_plugin(self, modname, consider_entry_points=False): 548 """ 549 Imports a plugin with ``modname``. If ``consider_entry_points`` is True, entry point 550 names are also considered to find a plugin. 551 """ 552 # most often modname refers to builtin modules, e.g. "pytester", 553 # "terminal" or "capture". Those plugins are registered under their 554 # basename for historic purposes but must be imported with the 555 # _pytest prefix. 556 assert isinstance(modname, six.string_types), ( 557 "module name as text required, got %r" % modname 558 ) 559 modname = str(modname) 560 if self.is_blocked(modname) or self.get_plugin(modname) is not None: 561 return 562 563 importspec = "_pytest." + modname if modname in builtin_plugins else modname 564 self.rewrite_hook.mark_rewrite(importspec) 565 566 if consider_entry_points: 567 loaded = self.load_setuptools_entrypoints("pytest11", name=modname) 568 if loaded: 569 return 570 571 try: 572 __import__(importspec) 573 except ImportError as e: 574 new_exc_message = 'Error importing plugin "%s": %s' % ( 575 modname, 576 safe_str(e.args[0]), 577 ) 578 new_exc = ImportError(new_exc_message) 579 tb = sys.exc_info()[2] 580 581 six.reraise(ImportError, new_exc, tb) 582 583 except Skipped as e: 584 from _pytest.warnings import _issue_warning_captured 585 586 _issue_warning_captured( 587 PytestConfigWarning("skipped plugin %r: %s" % (modname, e.msg)), 588 self.hook, 589 stacklevel=1, 590 ) 591 else: 592 mod = sys.modules[importspec] 593 self.register(mod, modname) 594 595 596def _get_plugin_specs_as_list(specs): 597 """ 598 Parses a list of "plugin specs" and returns a list of plugin names. 599 600 Plugin specs can be given as a list of strings separated by "," or already as a list/tuple in 601 which case it is returned as a list. Specs can also be `None` in which case an 602 empty list is returned. 603 """ 604 if specs is not None and not isinstance(specs, types.ModuleType): 605 if isinstance(specs, six.string_types): 606 specs = specs.split(",") if specs else [] 607 if not isinstance(specs, (list, tuple)): 608 raise UsageError( 609 "Plugin specs must be a ','-separated string or a " 610 "list/tuple of strings for plugin names. Given: %r" % specs 611 ) 612 return list(specs) 613 return [] 614 615 616def _ensure_removed_sysmodule(modname): 617 try: 618 del sys.modules[modname] 619 except KeyError: 620 pass 621 622 623class Notset(object): 624 def __repr__(self): 625 return "<NOTSET>" 626 627 628notset = Notset() 629 630 631def _iter_rewritable_modules(package_files): 632 """ 633 Given an iterable of file names in a source distribution, return the "names" that should 634 be marked for assertion rewrite (for example the package "pytest_mock/__init__.py" should 635 be added as "pytest_mock" in the assertion rewrite mechanism. 636 637 This function has to deal with dist-info based distributions and egg based distributions 638 (which are still very much in use for "editable" installs). 639 640 Here are the file names as seen in a dist-info based distribution: 641 642 pytest_mock/__init__.py 643 pytest_mock/_version.py 644 pytest_mock/plugin.py 645 pytest_mock.egg-info/PKG-INFO 646 647 Here are the file names as seen in an egg based distribution: 648 649 src/pytest_mock/__init__.py 650 src/pytest_mock/_version.py 651 src/pytest_mock/plugin.py 652 src/pytest_mock.egg-info/PKG-INFO 653 LICENSE 654 setup.py 655 656 We have to take in account those two distribution flavors in order to determine which 657 names should be considered for assertion rewriting. 658 659 More information: 660 https://github.com/pytest-dev/pytest-mock/issues/167 661 """ 662 package_files = list(package_files) 663 seen_some = False 664 for fn in package_files: 665 is_simple_module = "/" not in fn and fn.endswith(".py") 666 is_package = fn.count("/") == 1 and fn.endswith("__init__.py") 667 if is_simple_module: 668 module_name, _ = os.path.splitext(fn) 669 # we ignore "setup.py" at the root of the distribution 670 if module_name != "setup": 671 seen_some = True 672 yield module_name 673 elif is_package: 674 package_name = os.path.dirname(fn) 675 seen_some = True 676 yield package_name 677 678 if not seen_some: 679 # at this point we did not find any packages or modules suitable for assertion 680 # rewriting, so we try again by stripping the first path component (to account for 681 # "src" based source trees for example) 682 # this approach lets us have the common case continue to be fast, as egg-distributions 683 # are rarer 684 new_package_files = [] 685 for fn in package_files: 686 parts = fn.split("/") 687 new_fn = "/".join(parts[1:]) 688 if new_fn: 689 new_package_files.append(new_fn) 690 if new_package_files: 691 for _module in _iter_rewritable_modules(new_package_files): 692 yield _module 693 694 695class Config(object): 696 """ 697 Access to configuration values, pluginmanager and plugin hooks. 698 699 :ivar PytestPluginManager pluginmanager: the plugin manager handles plugin registration and hook invocation. 700 701 :ivar argparse.Namespace option: access to command line option as attributes. 702 703 :ivar InvocationParams invocation_params: 704 705 Object containing the parameters regarding the ``pytest.main`` 706 invocation. 707 Contains the followinig read-only attributes: 708 * ``args``: list of command-line arguments as passed to ``pytest.main()``. 709 * ``plugins``: list of extra plugins, might be None 710 * ``dir``: directory where ``pytest.main()`` was invoked from. 711 """ 712 713 @attr.s(frozen=True) 714 class InvocationParams(object): 715 """Holds parameters passed during ``pytest.main()`` 716 717 .. note:: 718 719 Currently the environment variable PYTEST_ADDOPTS is also handled by 720 pytest implicitly, not being part of the invocation. 721 722 Plugins accessing ``InvocationParams`` must be aware of that. 723 """ 724 725 args = attr.ib() 726 plugins = attr.ib() 727 dir = attr.ib() 728 729 def __init__(self, pluginmanager, invocation_params=None, *args): 730 from .argparsing import Parser, FILE_OR_DIR 731 732 if invocation_params is None: 733 invocation_params = self.InvocationParams( 734 args=(), plugins=None, dir=Path().resolve() 735 ) 736 737 #: access to command line option as attributes. 738 #: (deprecated), use :py:func:`getoption() <_pytest.config.Config.getoption>` instead 739 self.option = argparse.Namespace() 740 741 self.invocation_params = invocation_params 742 743 _a = FILE_OR_DIR 744 self._parser = Parser( 745 usage="%%(prog)s [options] [%s] [%s] [...]" % (_a, _a), 746 processopt=self._processopt, 747 ) 748 #: a pluginmanager instance 749 self.pluginmanager = pluginmanager 750 self.trace = self.pluginmanager.trace.root.get("config") 751 self.hook = self.pluginmanager.hook 752 self._inicache = {} 753 self._override_ini = () 754 self._opt2dest = {} 755 self._cleanup = [] 756 self.pluginmanager.register(self, "pytestconfig") 757 self._configured = False 758 self.hook.pytest_addoption.call_historic(kwargs=dict(parser=self._parser)) 759 760 @property 761 def invocation_dir(self): 762 """Backward compatibility""" 763 return py.path.local(str(self.invocation_params.dir)) 764 765 def add_cleanup(self, func): 766 """ Add a function to be called when the config object gets out of 767 use (usually coninciding with pytest_unconfigure).""" 768 self._cleanup.append(func) 769 770 def _do_configure(self): 771 assert not self._configured 772 self._configured = True 773 self.hook.pytest_configure.call_historic(kwargs=dict(config=self)) 774 775 def _ensure_unconfigure(self): 776 if self._configured: 777 self._configured = False 778 self.hook.pytest_unconfigure(config=self) 779 self.hook.pytest_configure._call_history = [] 780 while self._cleanup: 781 fin = self._cleanup.pop() 782 fin() 783 784 def get_terminal_writer(self): 785 return self.pluginmanager.get_plugin("terminalreporter")._tw 786 787 def pytest_cmdline_parse(self, pluginmanager, args): 788 try: 789 self.parse(args) 790 except UsageError: 791 792 # Handle --version and --help here in a minimal fashion. 793 # This gets done via helpconfig normally, but its 794 # pytest_cmdline_main is not called in case of errors. 795 if getattr(self.option, "version", False) or "--version" in args: 796 from _pytest.helpconfig import showversion 797 798 showversion(self) 799 elif ( 800 getattr(self.option, "help", False) or "--help" in args or "-h" in args 801 ): 802 self._parser._getparser().print_help() 803 sys.stdout.write( 804 "\nNOTE: displaying only minimal help due to UsageError.\n\n" 805 ) 806 807 raise 808 809 return self 810 811 def notify_exception(self, excinfo, option=None): 812 if option and getattr(option, "fulltrace", False): 813 style = "long" 814 else: 815 style = "native" 816 excrepr = excinfo.getrepr( 817 funcargs=True, showlocals=getattr(option, "showlocals", False), style=style 818 ) 819 res = self.hook.pytest_internalerror(excrepr=excrepr, excinfo=excinfo) 820 if not any(res): 821 for line in str(excrepr).split("\n"): 822 sys.stderr.write("INTERNALERROR> %s\n" % line) 823 sys.stderr.flush() 824 825 def cwd_relative_nodeid(self, nodeid): 826 # nodeid's are relative to the rootpath, compute relative to cwd 827 if self.invocation_dir != self.rootdir: 828 fullpath = self.rootdir.join(nodeid) 829 nodeid = self.invocation_dir.bestrelpath(fullpath) 830 return nodeid 831 832 @classmethod 833 def fromdictargs(cls, option_dict, args): 834 """ constructor useable for subprocesses. """ 835 config = get_config(args) 836 config.option.__dict__.update(option_dict) 837 config.parse(args, addopts=False) 838 for x in config.option.plugins: 839 config.pluginmanager.consider_pluginarg(x) 840 return config 841 842 def _processopt(self, opt): 843 for name in opt._short_opts + opt._long_opts: 844 self._opt2dest[name] = opt.dest 845 846 if hasattr(opt, "default") and opt.dest: 847 if not hasattr(self.option, opt.dest): 848 setattr(self.option, opt.dest, opt.default) 849 850 @hookimpl(trylast=True) 851 def pytest_load_initial_conftests(self, early_config): 852 self.pluginmanager._set_initial_conftests(early_config.known_args_namespace) 853 854 def _initini(self, args): 855 ns, unknown_args = self._parser.parse_known_and_unknown_args( 856 args, namespace=copy.copy(self.option) 857 ) 858 r = determine_setup( 859 ns.inifilename, 860 ns.file_or_dir + unknown_args, 861 rootdir_cmd_arg=ns.rootdir or None, 862 config=self, 863 ) 864 self.rootdir, self.inifile, self.inicfg = r 865 self._parser.extra_info["rootdir"] = self.rootdir 866 self._parser.extra_info["inifile"] = self.inifile 867 self._parser.addini("addopts", "extra command line options", "args") 868 self._parser.addini("minversion", "minimally required pytest version") 869 self._override_ini = ns.override_ini or () 870 871 def _consider_importhook(self, args): 872 """Install the PEP 302 import hook if using assertion rewriting. 873 874 Needs to parse the --assert=<mode> option from the commandline 875 and find all the installed plugins to mark them for rewriting 876 by the importhook. 877 """ 878 ns, unknown_args = self._parser.parse_known_and_unknown_args(args) 879 mode = getattr(ns, "assertmode", "plain") 880 if mode == "rewrite": 881 try: 882 hook = _pytest.assertion.install_importhook(self) 883 except SystemError: 884 mode = "plain" 885 else: 886 self._mark_plugins_for_rewrite(hook) 887 _warn_about_missing_assertion(mode) 888 889 def _mark_plugins_for_rewrite(self, hook): 890 """ 891 Given an importhook, mark for rewrite any top-level 892 modules or packages in the distribution package for 893 all pytest plugins. 894 """ 895 self.pluginmanager.rewrite_hook = hook 896 897 if os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD"): 898 # We don't autoload from setuptools entry points, no need to continue. 899 return 900 901 package_files = ( 902 str(file) 903 for dist in importlib_metadata.distributions() 904 if any(ep.group == "pytest11" for ep in dist.entry_points) 905 for file in dist.files or [] 906 ) 907 908 for name in _iter_rewritable_modules(package_files): 909 hook.mark_rewrite(name) 910 911 def _validate_args(self, args, via): 912 """Validate known args.""" 913 self._parser._config_source_hint = via 914 try: 915 self._parser.parse_known_and_unknown_args( 916 args, namespace=copy.copy(self.option) 917 ) 918 finally: 919 del self._parser._config_source_hint 920 921 return args 922 923 def _preparse(self, args, addopts=True): 924 if addopts: 925 env_addopts = os.environ.get("PYTEST_ADDOPTS", "") 926 if len(env_addopts): 927 args[:] = ( 928 self._validate_args(shlex.split(env_addopts), "via PYTEST_ADDOPTS") 929 + args 930 ) 931 self._initini(args) 932 if addopts: 933 args[:] = ( 934 self._validate_args(self.getini("addopts"), "via addopts config") + args 935 ) 936 937 self._checkversion() 938 self._consider_importhook(args) 939 self.pluginmanager.consider_preparse(args) 940 if not os.environ.get("PYTEST_DISABLE_PLUGIN_AUTOLOAD"): 941 # Don't autoload from setuptools entry point. Only explicitly specified 942 # plugins are going to be loaded. 943 self.pluginmanager.load_setuptools_entrypoints("pytest11") 944 self.pluginmanager.consider_env() 945 self.known_args_namespace = ns = self._parser.parse_known_args( 946 args, namespace=copy.copy(self.option) 947 ) 948 if self.known_args_namespace.confcutdir is None and self.inifile: 949 confcutdir = py.path.local(self.inifile).dirname 950 self.known_args_namespace.confcutdir = confcutdir 951 try: 952 self.hook.pytest_load_initial_conftests( 953 early_config=self, args=args, parser=self._parser 954 ) 955 except ConftestImportFailure: 956 e = sys.exc_info()[1] 957 if ns.help or ns.version: 958 # we don't want to prevent --help/--version to work 959 # so just let is pass and print a warning at the end 960 from _pytest.warnings import _issue_warning_captured 961 962 _issue_warning_captured( 963 PytestConfigWarning( 964 "could not load initial conftests: {}".format(e.path) 965 ), 966 self.hook, 967 stacklevel=2, 968 ) 969 else: 970 raise 971 972 def _checkversion(self): 973 import pytest 974 975 minver = self.inicfg.get("minversion", None) 976 if minver: 977 if Version(minver) > Version(pytest.__version__): 978 raise pytest.UsageError( 979 "%s:%d: requires pytest-%s, actual pytest-%s'" 980 % ( 981 self.inicfg.config.path, 982 self.inicfg.lineof("minversion"), 983 minver, 984 pytest.__version__, 985 ) 986 ) 987 988 def parse(self, args, addopts=True): 989 # parse given cmdline arguments into this config object. 990 assert not hasattr( 991 self, "args" 992 ), "can only parse cmdline args at most once per Config object" 993 self._origargs = args 994 self.hook.pytest_addhooks.call_historic( 995 kwargs=dict(pluginmanager=self.pluginmanager) 996 ) 997 self._preparse(args, addopts=addopts) 998 # XXX deprecated hook: 999 self.hook.pytest_cmdline_preparse(config=self, args=args) 1000 self._parser.after_preparse = True 1001 try: 1002 args = self._parser.parse_setoption( 1003 args, self.option, namespace=self.option 1004 ) 1005 if not args: 1006 if self.invocation_dir == self.rootdir: 1007 args = self.getini("testpaths") 1008 if not args: 1009 args = [str(self.invocation_dir)] 1010 self.args = args 1011 except PrintHelp: 1012 pass 1013 1014 def addinivalue_line(self, name, line): 1015 """ add a line to an ini-file option. The option must have been 1016 declared but might not yet be set in which case the line becomes the 1017 the first line in its value. """ 1018 x = self.getini(name) 1019 assert isinstance(x, list) 1020 x.append(line) # modifies the cached list inline 1021 1022 def getini(self, name): 1023 """ return configuration value from an :ref:`ini file <inifiles>`. If the 1024 specified name hasn't been registered through a prior 1025 :py:func:`parser.addini <_pytest.config.Parser.addini>` 1026 call (usually from a plugin), a ValueError is raised. """ 1027 try: 1028 return self._inicache[name] 1029 except KeyError: 1030 self._inicache[name] = val = self._getini(name) 1031 return val 1032 1033 def _getini(self, name): 1034 try: 1035 description, type, default = self._parser._inidict[name] 1036 except KeyError: 1037 raise ValueError("unknown configuration value: %r" % (name,)) 1038 value = self._get_override_ini_value(name) 1039 if value is None: 1040 try: 1041 value = self.inicfg[name] 1042 except KeyError: 1043 if default is not None: 1044 return default 1045 if type is None: 1046 return "" 1047 return [] 1048 if type == "pathlist": 1049 dp = py.path.local(self.inicfg.config.path).dirpath() 1050 values = [] 1051 for relpath in shlex.split(value): 1052 values.append(dp.join(relpath, abs=True)) 1053 return values 1054 elif type == "args": 1055 return shlex.split(value) 1056 elif type == "linelist": 1057 return [t for t in map(lambda x: x.strip(), value.split("\n")) if t] 1058 elif type == "bool": 1059 return bool(_strtobool(value.strip())) 1060 else: 1061 assert type is None 1062 return value 1063 1064 def _getconftest_pathlist(self, name, path): 1065 try: 1066 mod, relroots = self.pluginmanager._rget_with_confmod(name, path) 1067 except KeyError: 1068 return None 1069 modpath = py.path.local(mod.__file__).dirpath() 1070 values = [] 1071 for relroot in relroots: 1072 if not isinstance(relroot, py.path.local): 1073 relroot = relroot.replace("/", py.path.local.sep) 1074 relroot = modpath.join(relroot, abs=True) 1075 values.append(relroot) 1076 return values 1077 1078 def _get_override_ini_value(self, name): 1079 value = None 1080 # override_ini is a list of "ini=value" options 1081 # always use the last item if multiple values are set for same ini-name, 1082 # e.g. -o foo=bar1 -o foo=bar2 will set foo to bar2 1083 for ini_config in self._override_ini: 1084 try: 1085 key, user_ini_value = ini_config.split("=", 1) 1086 except ValueError: 1087 raise UsageError("-o/--override-ini expects option=value style.") 1088 else: 1089 if key == name: 1090 value = user_ini_value 1091 return value 1092 1093 def getoption(self, name, default=notset, skip=False): 1094 """ return command line option value. 1095 1096 :arg name: name of the option. You may also specify 1097 the literal ``--OPT`` option instead of the "dest" option name. 1098 :arg default: default value if no option of that name exists. 1099 :arg skip: if True raise pytest.skip if option does not exists 1100 or has a None value. 1101 """ 1102 name = self._opt2dest.get(name, name) 1103 try: 1104 val = getattr(self.option, name) 1105 if val is None and skip: 1106 raise AttributeError(name) 1107 return val 1108 except AttributeError: 1109 if default is not notset: 1110 return default 1111 if skip: 1112 import pytest 1113 1114 pytest.skip("no %r option found" % (name,)) 1115 raise ValueError("no option named %r" % (name,)) 1116 1117 def getvalue(self, name, path=None): 1118 """ (deprecated, use getoption()) """ 1119 return self.getoption(name) 1120 1121 def getvalueorskip(self, name, path=None): 1122 """ (deprecated, use getoption(skip=True)) """ 1123 return self.getoption(name, skip=True) 1124 1125 1126def _assertion_supported(): 1127 try: 1128 assert False 1129 except AssertionError: 1130 return True 1131 else: 1132 return False 1133 1134 1135def _warn_about_missing_assertion(mode): 1136 if not _assertion_supported(): 1137 if mode == "plain": 1138 sys.stderr.write( 1139 "WARNING: ASSERTIONS ARE NOT EXECUTED" 1140 " and FAILING TESTS WILL PASS. Are you" 1141 " using python -O?" 1142 ) 1143 else: 1144 sys.stderr.write( 1145 "WARNING: assertions not in test modules or" 1146 " plugins will be ignored" 1147 " because assert statements are not executed " 1148 "by the underlying Python interpreter " 1149 "(are you using python -O?)\n" 1150 ) 1151 1152 1153def setns(obj, dic): 1154 import pytest 1155 1156 for name, value in dic.items(): 1157 if isinstance(value, dict): 1158 mod = getattr(obj, name, None) 1159 if mod is None: 1160 modname = "pytest.%s" % name 1161 mod = types.ModuleType(modname) 1162 sys.modules[modname] = mod 1163 mod.__all__ = [] 1164 setattr(obj, name, mod) 1165 obj.__all__.append(name) 1166 setns(mod, value) 1167 else: 1168 setattr(obj, name, value) 1169 obj.__all__.append(name) 1170 # if obj != pytest: 1171 # pytest.__all__.append(name) 1172 setattr(pytest, name, value) 1173 1174 1175def create_terminal_writer(config, *args, **kwargs): 1176 """Create a TerminalWriter instance configured according to the options 1177 in the config object. Every code which requires a TerminalWriter object 1178 and has access to a config object should use this function. 1179 """ 1180 tw = py.io.TerminalWriter(*args, **kwargs) 1181 if config.option.color == "yes": 1182 tw.hasmarkup = True 1183 if config.option.color == "no": 1184 tw.hasmarkup = False 1185 return tw 1186 1187 1188def _strtobool(val): 1189 """Convert a string representation of truth to true (1) or false (0). 1190 1191 True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values 1192 are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if 1193 'val' is anything else. 1194 1195 .. note:: copied from distutils.util 1196 """ 1197 val = val.lower() 1198 if val in ("y", "yes", "t", "true", "on", "1"): 1199 return 1 1200 elif val in ("n", "no", "f", "false", "off", "0"): 1201 return 0 1202 else: 1203 raise ValueError("invalid truth value %r" % (val,)) 1204