1""" command line options, ini-file and conftest.py processing. """ 2from __future__ import absolute_import, division, print_function 3import argparse 4import shlex 5import traceback 6import types 7import warnings 8 9import six 10import py 11# DON't import pytest here because it causes import cycle troubles 12import sys 13import os 14import _pytest._code 15import _pytest.hookspec # the extension point definitions 16import _pytest.assertion 17from pluggy import PluginManager, HookimplMarker, HookspecMarker 18from _pytest.compat import safe_str 19 20hookimpl = HookimplMarker("pytest") 21hookspec = HookspecMarker("pytest") 22 23# pytest startup 24# 25 26 27class ConftestImportFailure(Exception): 28 def __init__(self, path, excinfo): 29 Exception.__init__(self, path, excinfo) 30 self.path = path 31 self.excinfo = excinfo 32 33 def __str__(self): 34 etype, evalue, etb = self.excinfo 35 formatted = traceback.format_tb(etb) 36 # The level of the tracebacks we want to print is hand crafted :( 37 return repr(evalue) + '\n' + ''.join(formatted[2:]) 38 39 40def main(args=None, plugins=None): 41 """ return exit code, after performing an in-process test run. 42 43 :arg args: list of command line arguments. 44 45 :arg plugins: list of plugin objects to be auto-registered during 46 initialization. 47 """ 48 try: 49 try: 50 config = _prepareconfig(args, plugins) 51 except ConftestImportFailure as e: 52 tw = py.io.TerminalWriter(sys.stderr) 53 for line in traceback.format_exception(*e.excinfo): 54 tw.line(line.rstrip(), red=True) 55 tw.line("ERROR: could not load %s\n" % (e.path), red=True) 56 return 4 57 else: 58 try: 59 return config.hook.pytest_cmdline_main(config=config) 60 finally: 61 config._ensure_unconfigure() 62 except UsageError as e: 63 for msg in e.args: 64 sys.stderr.write("ERROR: %s\n" % (msg,)) 65 return 4 66 67 68class cmdline: # compatibility namespace 69 main = staticmethod(main) 70 71 72class UsageError(Exception): 73 """ error in pytest usage or invocation""" 74 75 76class PrintHelp(Exception): 77 """Raised when pytest should print it's help to skip the rest of the 78 argument parsing and validation.""" 79 pass 80 81 82def filename_arg(path, optname): 83 """ Argparse type validator for filename arguments. 84 85 :path: path of filename 86 :optname: name of the option 87 """ 88 if os.path.isdir(path): 89 raise UsageError("{0} must be a filename, given: {1}".format(optname, path)) 90 return path 91 92 93def directory_arg(path, optname): 94 """Argparse type validator for directory arguments. 95 96 :path: path of directory 97 :optname: name of the option 98 """ 99 if not os.path.isdir(path): 100 raise UsageError("{0} must be a directory, given: {1}".format(optname, path)) 101 return path 102 103 104default_plugins = ( 105 "mark main terminal runner python fixtures debugging unittest capture skipping " 106 "tmpdir monkeypatch recwarn pastebin helpconfig nose assertion " 107 "junitxml resultlog doctest cacheprovider freeze_support " 108 "setuponly setupplan warnings logging").split() 109 110 111builtin_plugins = set(default_plugins) 112builtin_plugins.add("pytester") 113 114 115def get_config(): 116 # subsequent calls to main will create a fresh instance 117 pluginmanager = PytestPluginManager() 118 config = Config(pluginmanager) 119 for spec in default_plugins: 120 pluginmanager.import_plugin(spec) 121 return config 122 123 124def get_plugin_manager(): 125 """ 126 Obtain a new instance of the 127 :py:class:`_pytest.config.PytestPluginManager`, with default plugins 128 already loaded. 129 130 This function can be used by integration with other tools, like hooking 131 into pytest to run tests into an IDE. 132 """ 133 return get_config().pluginmanager 134 135 136def _prepareconfig(args=None, plugins=None): 137 warning = None 138 if args is None: 139 args = sys.argv[1:] 140 elif isinstance(args, py.path.local): 141 args = [str(args)] 142 elif not isinstance(args, (tuple, list)): 143 if not isinstance(args, str): 144 raise ValueError("not a string or argument list: %r" % (args,)) 145 args = shlex.split(args, posix=sys.platform != "win32") 146 from _pytest import deprecated 147 warning = deprecated.MAIN_STR_ARGS 148 config = get_config() 149 pluginmanager = config.pluginmanager 150 try: 151 if plugins: 152 for plugin in plugins: 153 if isinstance(plugin, six.string_types): 154 pluginmanager.consider_pluginarg(plugin) 155 else: 156 pluginmanager.register(plugin) 157 if warning: 158 config.warn('C1', warning) 159 return pluginmanager.hook.pytest_cmdline_parse( 160 pluginmanager=pluginmanager, args=args) 161 except BaseException: 162 config._ensure_unconfigure() 163 raise 164 165 166class PytestPluginManager(PluginManager): 167 """ 168 Overwrites :py:class:`pluggy.PluginManager <pluggy.PluginManager>` to add pytest-specific 169 functionality: 170 171 * loading plugins from the command line, ``PYTEST_PLUGIN`` env variable and 172 ``pytest_plugins`` global variables found in plugins being loaded; 173 * ``conftest.py`` loading during start-up; 174 """ 175 176 def __init__(self): 177 super(PytestPluginManager, self).__init__("pytest", implprefix="pytest_") 178 self._conftest_plugins = set() 179 180 # state related to local conftest plugins 181 self._path2confmods = {} 182 self._conftestpath2mod = {} 183 self._confcutdir = None 184 self._noconftest = False 185 self._duplicatepaths = set() 186 187 self.add_hookspecs(_pytest.hookspec) 188 self.register(self) 189 if os.environ.get('PYTEST_DEBUG'): 190 err = sys.stderr 191 encoding = getattr(err, 'encoding', 'utf8') 192 try: 193 err = py.io.dupfile(err, encoding=encoding) 194 except Exception: 195 pass 196 self.trace.root.setwriter(err.write) 197 self.enable_tracing() 198 199 # Config._consider_importhook will set a real object if required. 200 self.rewrite_hook = _pytest.assertion.DummyRewriteHook() 201 202 def addhooks(self, module_or_class): 203 """ 204 .. deprecated:: 2.8 205 206 Use :py:meth:`pluggy.PluginManager.add_hookspecs <PluginManager.add_hookspecs>` 207 instead. 208 """ 209 warning = dict(code="I2", 210 fslocation=_pytest._code.getfslineno(sys._getframe(1)), 211 nodeid=None, 212 message="use pluginmanager.add_hookspecs instead of " 213 "deprecated addhooks() method.") 214 self._warn(warning) 215 return self.add_hookspecs(module_or_class) 216 217 def parse_hookimpl_opts(self, plugin, name): 218 # pytest hooks are always prefixed with pytest_ 219 # so we avoid accessing possibly non-readable attributes 220 # (see issue #1073) 221 if not name.startswith("pytest_"): 222 return 223 # ignore some historic special names which can not be hooks anyway 224 if name == "pytest_plugins" or name.startswith("pytest_funcarg__"): 225 return 226 227 method = getattr(plugin, name) 228 opts = super(PytestPluginManager, self).parse_hookimpl_opts(plugin, name) 229 if opts is not None: 230 for name in ("tryfirst", "trylast", "optionalhook", "hookwrapper"): 231 opts.setdefault(name, hasattr(method, name)) 232 return opts 233 234 def parse_hookspec_opts(self, module_or_class, name): 235 opts = super(PytestPluginManager, self).parse_hookspec_opts( 236 module_or_class, name) 237 if opts is None: 238 method = getattr(module_or_class, name) 239 if name.startswith("pytest_"): 240 opts = {"firstresult": hasattr(method, "firstresult"), 241 "historic": hasattr(method, "historic")} 242 return opts 243 244 def register(self, plugin, name=None): 245 if name == 'pytest_catchlog': 246 self._warn('pytest-catchlog plugin has been merged into the core, ' 247 'please remove it from your requirements.') 248 return 249 ret = super(PytestPluginManager, self).register(plugin, name) 250 if ret: 251 self.hook.pytest_plugin_registered.call_historic( 252 kwargs=dict(plugin=plugin, manager=self)) 253 254 if isinstance(plugin, types.ModuleType): 255 self.consider_module(plugin) 256 return ret 257 258 def getplugin(self, name): 259 # support deprecated naming because plugins (xdist e.g.) use it 260 return self.get_plugin(name) 261 262 def hasplugin(self, name): 263 """Return True if the plugin with the given name is registered.""" 264 return bool(self.get_plugin(name)) 265 266 def pytest_configure(self, config): 267 # XXX now that the pluginmanager exposes hookimpl(tryfirst...) 268 # we should remove tryfirst/trylast as markers 269 config.addinivalue_line("markers", 270 "tryfirst: mark a hook implementation function such that the " 271 "plugin machinery will try to call it first/as early as possible.") 272 config.addinivalue_line("markers", 273 "trylast: mark a hook implementation function such that the " 274 "plugin machinery will try to call it last/as late as possible.") 275 276 def _warn(self, message): 277 kwargs = message if isinstance(message, dict) else { 278 'code': 'I1', 279 'message': message, 280 'fslocation': None, 281 'nodeid': None, 282 } 283 self.hook.pytest_logwarning.call_historic(kwargs=kwargs) 284 285 # 286 # internal API for local conftest plugin handling 287 # 288 def _set_initial_conftests(self, namespace): 289 """ load initial conftest files given a preparsed "namespace". 290 As conftest files may add their own command line options 291 which have arguments ('--my-opt somepath') we might get some 292 false positives. All builtin and 3rd party plugins will have 293 been loaded, however, so common options will not confuse our logic 294 here. 295 """ 296 current = py.path.local() 297 self._confcutdir = current.join(namespace.confcutdir, abs=True) \ 298 if namespace.confcutdir else None 299 self._noconftest = namespace.noconftest 300 testpaths = namespace.file_or_dir 301 foundanchor = False 302 for path in testpaths: 303 path = str(path) 304 # remove node-id syntax 305 i = path.find("::") 306 if i != -1: 307 path = path[:i] 308 anchor = current.join(path, abs=1) 309 if exists(anchor): # we found some file object 310 self._try_load_conftest(anchor) 311 foundanchor = True 312 if not foundanchor: 313 self._try_load_conftest(current) 314 315 def _try_load_conftest(self, anchor): 316 self._getconftestmodules(anchor) 317 # let's also consider test* subdirs 318 if anchor.check(dir=1): 319 for x in anchor.listdir("test*"): 320 if x.check(dir=1): 321 self._getconftestmodules(x) 322 323 def _getconftestmodules(self, path): 324 if self._noconftest: 325 return [] 326 try: 327 return self._path2confmods[path] 328 except KeyError: 329 if path.isfile(): 330 clist = self._getconftestmodules(path.dirpath()) 331 else: 332 # XXX these days we may rather want to use config.rootdir 333 # and allow users to opt into looking into the rootdir parent 334 # directories instead of requiring to specify confcutdir 335 clist = [] 336 for parent in path.parts(): 337 if self._confcutdir and self._confcutdir.relto(parent): 338 continue 339 conftestpath = parent.join("conftest.py") 340 if conftestpath.isfile(): 341 mod = self._importconftest(conftestpath) 342 clist.append(mod) 343 344 self._path2confmods[path] = clist 345 return clist 346 347 def _rget_with_confmod(self, name, path): 348 modules = self._getconftestmodules(path) 349 for mod in reversed(modules): 350 try: 351 return mod, getattr(mod, name) 352 except AttributeError: 353 continue 354 raise KeyError(name) 355 356 def _importconftest(self, conftestpath): 357 try: 358 return self._conftestpath2mod[conftestpath] 359 except KeyError: 360 pkgpath = conftestpath.pypkgpath() 361 if pkgpath is None: 362 _ensure_removed_sysmodule(conftestpath.purebasename) 363 try: 364 mod = conftestpath.pyimport() 365 except Exception: 366 raise ConftestImportFailure(conftestpath, sys.exc_info()) 367 368 self._conftest_plugins.add(mod) 369 self._conftestpath2mod[conftestpath] = mod 370 dirpath = conftestpath.dirpath() 371 if dirpath in self._path2confmods: 372 for path, mods in self._path2confmods.items(): 373 if path and path.relto(dirpath) or path == dirpath: 374 assert mod not in mods 375 mods.append(mod) 376 self.trace("loaded conftestmodule %r" % (mod)) 377 self.consider_conftest(mod) 378 return mod 379 380 # 381 # API for bootstrapping plugin loading 382 # 383 # 384 385 def consider_preparse(self, args): 386 for opt1, opt2 in zip(args, args[1:]): 387 if opt1 == "-p": 388 self.consider_pluginarg(opt2) 389 390 def consider_pluginarg(self, arg): 391 if arg.startswith("no:"): 392 name = arg[3:] 393 self.set_blocked(name) 394 if not name.startswith("pytest_"): 395 self.set_blocked("pytest_" + name) 396 else: 397 self.import_plugin(arg) 398 399 def consider_conftest(self, conftestmodule): 400 self.register(conftestmodule, name=conftestmodule.__file__) 401 402 def consider_env(self): 403 self._import_plugin_specs(os.environ.get("PYTEST_PLUGINS")) 404 405 def consider_module(self, mod): 406 self._import_plugin_specs(getattr(mod, 'pytest_plugins', [])) 407 408 def _import_plugin_specs(self, spec): 409 plugins = _get_plugin_specs_as_list(spec) 410 for import_spec in plugins: 411 self.import_plugin(import_spec) 412 413 def import_plugin(self, modname): 414 # most often modname refers to builtin modules, e.g. "pytester", 415 # "terminal" or "capture". Those plugins are registered under their 416 # basename for historic purposes but must be imported with the 417 # _pytest prefix. 418 assert isinstance(modname, (six.text_type, str)), "module name as text required, got %r" % modname 419 modname = str(modname) 420 if self.is_blocked(modname) or self.get_plugin(modname) is not None: 421 return 422 if modname in builtin_plugins: 423 importspec = "_pytest." + modname 424 else: 425 importspec = modname 426 self.rewrite_hook.mark_rewrite(importspec) 427 try: 428 __import__(importspec) 429 except ImportError as e: 430 new_exc_type = ImportError 431 new_exc_message = 'Error importing plugin "%s": %s' % (modname, safe_str(e.args[0])) 432 new_exc = new_exc_type(new_exc_message) 433 434 six.reraise(new_exc_type, new_exc, sys.exc_info()[2]) 435 436 except Exception as e: 437 import pytest 438 if not hasattr(pytest, 'skip') or not isinstance(e, pytest.skip.Exception): 439 raise 440 self._warn("skipped plugin %r: %s" % ((modname, e.msg))) 441 else: 442 mod = sys.modules[importspec] 443 self.register(mod, modname) 444 445 446def _get_plugin_specs_as_list(specs): 447 """ 448 Parses a list of "plugin specs" and returns a list of plugin names. 449 450 Plugin specs can be given as a list of strings separated by "," or already as a list/tuple in 451 which case it is returned as a list. Specs can also be `None` in which case an 452 empty list is returned. 453 """ 454 if specs is not None: 455 if isinstance(specs, str): 456 specs = specs.split(',') if specs else [] 457 if not isinstance(specs, (list, tuple)): 458 raise UsageError("Plugin specs must be a ','-separated string or a " 459 "list/tuple of strings for plugin names. Given: %r" % specs) 460 return list(specs) 461 return [] 462 463 464class Parser: 465 """ Parser for command line arguments and ini-file values. 466 467 :ivar extra_info: dict of generic param -> value to display in case 468 there's an error processing the command line arguments. 469 """ 470 471 def __init__(self, usage=None, processopt=None): 472 self._anonymous = OptionGroup("custom options", parser=self) 473 self._groups = [] 474 self._processopt = processopt 475 self._usage = usage 476 self._inidict = {} 477 self._ininames = [] 478 self.extra_info = {} 479 480 def processoption(self, option): 481 if self._processopt: 482 if option.dest: 483 self._processopt(option) 484 485 def getgroup(self, name, description="", after=None): 486 """ get (or create) a named option Group. 487 488 :name: name of the option group. 489 :description: long description for --help output. 490 :after: name of other group, used for ordering --help output. 491 492 The returned group object has an ``addoption`` method with the same 493 signature as :py:func:`parser.addoption 494 <_pytest.config.Parser.addoption>` but will be shown in the 495 respective group in the output of ``pytest. --help``. 496 """ 497 for group in self._groups: 498 if group.name == name: 499 return group 500 group = OptionGroup(name, description, parser=self) 501 i = 0 502 for i, grp in enumerate(self._groups): 503 if grp.name == after: 504 break 505 self._groups.insert(i + 1, group) 506 return group 507 508 def addoption(self, *opts, **attrs): 509 """ register a command line option. 510 511 :opts: option names, can be short or long options. 512 :attrs: same attributes which the ``add_option()`` function of the 513 `argparse library 514 <http://docs.python.org/2/library/argparse.html>`_ 515 accepts. 516 517 After command line parsing options are available on the pytest config 518 object via ``config.option.NAME`` where ``NAME`` is usually set 519 by passing a ``dest`` attribute, for example 520 ``addoption("--long", dest="NAME", ...)``. 521 """ 522 self._anonymous.addoption(*opts, **attrs) 523 524 def parse(self, args, namespace=None): 525 from _pytest._argcomplete import try_argcomplete 526 self.optparser = self._getparser() 527 try_argcomplete(self.optparser) 528 return self.optparser.parse_args([str(x) for x in args], namespace=namespace) 529 530 def _getparser(self): 531 from _pytest._argcomplete import filescompleter 532 optparser = MyOptionParser(self, self.extra_info) 533 groups = self._groups + [self._anonymous] 534 for group in groups: 535 if group.options: 536 desc = group.description or group.name 537 arggroup = optparser.add_argument_group(desc) 538 for option in group.options: 539 n = option.names() 540 a = option.attrs() 541 arggroup.add_argument(*n, **a) 542 # bash like autocompletion for dirs (appending '/') 543 optparser.add_argument(FILE_OR_DIR, nargs='*').completer = filescompleter 544 return optparser 545 546 def parse_setoption(self, args, option, namespace=None): 547 parsedoption = self.parse(args, namespace=namespace) 548 for name, value in parsedoption.__dict__.items(): 549 setattr(option, name, value) 550 return getattr(parsedoption, FILE_OR_DIR) 551 552 def parse_known_args(self, args, namespace=None): 553 """parses and returns a namespace object with known arguments at this 554 point. 555 """ 556 return self.parse_known_and_unknown_args(args, namespace=namespace)[0] 557 558 def parse_known_and_unknown_args(self, args, namespace=None): 559 """parses and returns a namespace object with known arguments, and 560 the remaining arguments unknown at this point. 561 """ 562 optparser = self._getparser() 563 args = [str(x) for x in args] 564 return optparser.parse_known_args(args, namespace=namespace) 565 566 def addini(self, name, help, type=None, default=None): 567 """ register an ini-file option. 568 569 :name: name of the ini-variable 570 :type: type of the variable, can be ``pathlist``, ``args``, ``linelist`` 571 or ``bool``. 572 :default: default value if no ini-file option exists but is queried. 573 574 The value of ini-variables can be retrieved via a call to 575 :py:func:`config.getini(name) <_pytest.config.Config.getini>`. 576 """ 577 assert type in (None, "pathlist", "args", "linelist", "bool") 578 self._inidict[name] = (help, type, default) 579 self._ininames.append(name) 580 581 582class ArgumentError(Exception): 583 """ 584 Raised if an Argument instance is created with invalid or 585 inconsistent arguments. 586 """ 587 588 def __init__(self, msg, option): 589 self.msg = msg 590 self.option_id = str(option) 591 592 def __str__(self): 593 if self.option_id: 594 return "option %s: %s" % (self.option_id, self.msg) 595 else: 596 return self.msg 597 598 599class Argument: 600 """class that mimics the necessary behaviour of optparse.Option 601 602 its currently a least effort implementation 603 and ignoring choices and integer prefixes 604 https://docs.python.org/3/library/optparse.html#optparse-standard-option-types 605 """ 606 _typ_map = { 607 'int': int, 608 'string': str, 609 'float': float, 610 'complex': complex, 611 } 612 613 def __init__(self, *names, **attrs): 614 """store parms in private vars for use in add_argument""" 615 self._attrs = attrs 616 self._short_opts = [] 617 self._long_opts = [] 618 self.dest = attrs.get('dest') 619 if '%default' in (attrs.get('help') or ''): 620 warnings.warn( 621 'pytest now uses argparse. "%default" should be' 622 ' changed to "%(default)s" ', 623 DeprecationWarning, 624 stacklevel=3) 625 try: 626 typ = attrs['type'] 627 except KeyError: 628 pass 629 else: 630 # this might raise a keyerror as well, don't want to catch that 631 if isinstance(typ, six.string_types): 632 if typ == 'choice': 633 warnings.warn( 634 'type argument to addoption() is a string %r.' 635 ' For parsearg this is optional and when supplied' 636 ' should be a type.' 637 ' (options: %s)' % (typ, names), 638 DeprecationWarning, 639 stacklevel=3) 640 # argparse expects a type here take it from 641 # the type of the first element 642 attrs['type'] = type(attrs['choices'][0]) 643 else: 644 warnings.warn( 645 'type argument to addoption() is a string %r.' 646 ' For parsearg this should be a type.' 647 ' (options: %s)' % (typ, names), 648 DeprecationWarning, 649 stacklevel=3) 650 attrs['type'] = Argument._typ_map[typ] 651 # used in test_parseopt -> test_parse_defaultgetter 652 self.type = attrs['type'] 653 else: 654 self.type = typ 655 try: 656 # attribute existence is tested in Config._processopt 657 self.default = attrs['default'] 658 except KeyError: 659 pass 660 self._set_opt_strings(names) 661 if not self.dest: 662 if self._long_opts: 663 self.dest = self._long_opts[0][2:].replace('-', '_') 664 else: 665 try: 666 self.dest = self._short_opts[0][1:] 667 except IndexError: 668 raise ArgumentError( 669 'need a long or short option', self) 670 671 def names(self): 672 return self._short_opts + self._long_opts 673 674 def attrs(self): 675 # update any attributes set by processopt 676 attrs = 'default dest help'.split() 677 if self.dest: 678 attrs.append(self.dest) 679 for attr in attrs: 680 try: 681 self._attrs[attr] = getattr(self, attr) 682 except AttributeError: 683 pass 684 if self._attrs.get('help'): 685 a = self._attrs['help'] 686 a = a.replace('%default', '%(default)s') 687 # a = a.replace('%prog', '%(prog)s') 688 self._attrs['help'] = a 689 return self._attrs 690 691 def _set_opt_strings(self, opts): 692 """directly from optparse 693 694 might not be necessary as this is passed to argparse later on""" 695 for opt in opts: 696 if len(opt) < 2: 697 raise ArgumentError( 698 "invalid option string %r: " 699 "must be at least two characters long" % opt, self) 700 elif len(opt) == 2: 701 if not (opt[0] == "-" and opt[1] != "-"): 702 raise ArgumentError( 703 "invalid short option string %r: " 704 "must be of the form -x, (x any non-dash char)" % opt, 705 self) 706 self._short_opts.append(opt) 707 else: 708 if not (opt[0:2] == "--" and opt[2] != "-"): 709 raise ArgumentError( 710 "invalid long option string %r: " 711 "must start with --, followed by non-dash" % opt, 712 self) 713 self._long_opts.append(opt) 714 715 def __repr__(self): 716 args = [] 717 if self._short_opts: 718 args += ['_short_opts: ' + repr(self._short_opts)] 719 if self._long_opts: 720 args += ['_long_opts: ' + repr(self._long_opts)] 721 args += ['dest: ' + repr(self.dest)] 722 if hasattr(self, 'type'): 723 args += ['type: ' + repr(self.type)] 724 if hasattr(self, 'default'): 725 args += ['default: ' + repr(self.default)] 726 return 'Argument({0})'.format(', '.join(args)) 727 728 729class OptionGroup: 730 def __init__(self, name, description="", parser=None): 731 self.name = name 732 self.description = description 733 self.options = [] 734 self.parser = parser 735 736 def addoption(self, *optnames, **attrs): 737 """ add an option to this group. 738 739 if a shortened version of a long option is specified it will 740 be suppressed in the help. addoption('--twowords', '--two-words') 741 results in help showing '--two-words' only, but --twowords gets 742 accepted **and** the automatic destination is in args.twowords 743 """ 744 conflict = set(optnames).intersection( 745 name for opt in self.options for name in opt.names()) 746 if conflict: 747 raise ValueError("option names %s already added" % conflict) 748 option = Argument(*optnames, **attrs) 749 self._addoption_instance(option, shortupper=False) 750 751 def _addoption(self, *optnames, **attrs): 752 option = Argument(*optnames, **attrs) 753 self._addoption_instance(option, shortupper=True) 754 755 def _addoption_instance(self, option, shortupper=False): 756 if not shortupper: 757 for opt in option._short_opts: 758 if opt[0] == '-' and opt[1].islower(): 759 raise ValueError("lowercase shortoptions reserved") 760 if self.parser: 761 self.parser.processoption(option) 762 self.options.append(option) 763 764 765class MyOptionParser(argparse.ArgumentParser): 766 def __init__(self, parser, extra_info=None): 767 if not extra_info: 768 extra_info = {} 769 self._parser = parser 770 argparse.ArgumentParser.__init__(self, usage=parser._usage, 771 add_help=False, formatter_class=DropShorterLongHelpFormatter) 772 # extra_info is a dict of (param -> value) to display if there's 773 # an usage error to provide more contextual information to the user 774 self.extra_info = extra_info 775 776 def parse_args(self, args=None, namespace=None): 777 """allow splitting of positional arguments""" 778 args, argv = self.parse_known_args(args, namespace) 779 if argv: 780 for arg in argv: 781 if arg and arg[0] == '-': 782 lines = ['unrecognized arguments: %s' % (' '.join(argv))] 783 for k, v in sorted(self.extra_info.items()): 784 lines.append(' %s: %s' % (k, v)) 785 self.error('\n'.join(lines)) 786 getattr(args, FILE_OR_DIR).extend(argv) 787 return args 788 789 790class DropShorterLongHelpFormatter(argparse.HelpFormatter): 791 """shorten help for long options that differ only in extra hyphens 792 793 - collapse **long** options that are the same except for extra hyphens 794 - special action attribute map_long_option allows surpressing additional 795 long options 796 - shortcut if there are only two options and one of them is a short one 797 - cache result on action object as this is called at least 2 times 798 """ 799 800 def _format_action_invocation(self, action): 801 orgstr = argparse.HelpFormatter._format_action_invocation(self, action) 802 if orgstr and orgstr[0] != '-': # only optional arguments 803 return orgstr 804 res = getattr(action, '_formatted_action_invocation', None) 805 if res: 806 return res 807 options = orgstr.split(', ') 808 if len(options) == 2 and (len(options[0]) == 2 or len(options[1]) == 2): 809 # a shortcut for '-h, --help' or '--abc', '-a' 810 action._formatted_action_invocation = orgstr 811 return orgstr 812 return_list = [] 813 option_map = getattr(action, 'map_long_option', {}) 814 if option_map is None: 815 option_map = {} 816 short_long = {} 817 for option in options: 818 if len(option) == 2 or option[2] == ' ': 819 continue 820 if not option.startswith('--'): 821 raise ArgumentError('long optional argument without "--": [%s]' 822 % (option), self) 823 xxoption = option[2:] 824 if xxoption.split()[0] not in option_map: 825 shortened = xxoption.replace('-', '') 826 if shortened not in short_long or \ 827 len(short_long[shortened]) < len(xxoption): 828 short_long[shortened] = xxoption 829 # now short_long has been filled out to the longest with dashes 830 # **and** we keep the right option ordering from add_argument 831 for option in options: 832 if len(option) == 2 or option[2] == ' ': 833 return_list.append(option) 834 if option[2:] == short_long.get(option.replace('-', '')): 835 return_list.append(option.replace(' ', '=', 1)) 836 action._formatted_action_invocation = ', '.join(return_list) 837 return action._formatted_action_invocation 838 839 840def _ensure_removed_sysmodule(modname): 841 try: 842 del sys.modules[modname] 843 except KeyError: 844 pass 845 846 847class CmdOptions(object): 848 """ holds cmdline options as attributes.""" 849 850 def __init__(self, values=()): 851 self.__dict__.update(values) 852 853 def __repr__(self): 854 return "<CmdOptions %r>" % (self.__dict__,) 855 856 def copy(self): 857 return CmdOptions(self.__dict__) 858 859 860class Notset: 861 def __repr__(self): 862 return "<NOTSET>" 863 864 865notset = Notset() 866FILE_OR_DIR = 'file_or_dir' 867 868 869def _iter_rewritable_modules(package_files): 870 for fn in package_files: 871 is_simple_module = '/' not in fn and fn.endswith('.py') 872 is_package = fn.count('/') == 1 and fn.endswith('__init__.py') 873 if is_simple_module: 874 module_name, _ = os.path.splitext(fn) 875 yield module_name 876 elif is_package: 877 package_name = os.path.dirname(fn) 878 yield package_name 879 880 881class Config(object): 882 """ access to configuration values, pluginmanager and plugin hooks. """ 883 884 def __init__(self, pluginmanager): 885 #: access to command line option as attributes. 886 #: (deprecated), use :py:func:`getoption() <_pytest.config.Config.getoption>` instead 887 self.option = CmdOptions() 888 _a = FILE_OR_DIR 889 self._parser = Parser( 890 usage="%%(prog)s [options] [%s] [%s] [...]" % (_a, _a), 891 processopt=self._processopt, 892 ) 893 #: a pluginmanager instance 894 self.pluginmanager = pluginmanager 895 self.trace = self.pluginmanager.trace.root.get("config") 896 self.hook = self.pluginmanager.hook 897 self._inicache = {} 898 self._override_ini = () 899 self._opt2dest = {} 900 self._cleanup = [] 901 self._warn = self.pluginmanager._warn 902 self.pluginmanager.register(self, "pytestconfig") 903 self._configured = False 904 905 def do_setns(dic): 906 import pytest 907 setns(pytest, dic) 908 909 self.hook.pytest_namespace.call_historic(do_setns, {}) 910 self.hook.pytest_addoption.call_historic(kwargs=dict(parser=self._parser)) 911 912 def add_cleanup(self, func): 913 """ Add a function to be called when the config object gets out of 914 use (usually coninciding with pytest_unconfigure).""" 915 self._cleanup.append(func) 916 917 def _do_configure(self): 918 assert not self._configured 919 self._configured = True 920 self.hook.pytest_configure.call_historic(kwargs=dict(config=self)) 921 922 def _ensure_unconfigure(self): 923 if self._configured: 924 self._configured = False 925 self.hook.pytest_unconfigure(config=self) 926 self.hook.pytest_configure._call_history = [] 927 while self._cleanup: 928 fin = self._cleanup.pop() 929 fin() 930 931 def warn(self, code, message, fslocation=None, nodeid=None): 932 """ generate a warning for this test session. """ 933 self.hook.pytest_logwarning.call_historic(kwargs=dict( 934 code=code, message=message, 935 fslocation=fslocation, nodeid=nodeid)) 936 937 def get_terminal_writer(self): 938 return self.pluginmanager.get_plugin("terminalreporter")._tw 939 940 def pytest_cmdline_parse(self, pluginmanager, args): 941 # REF1 assert self == pluginmanager.config, (self, pluginmanager.config) 942 self.parse(args) 943 return self 944 945 def notify_exception(self, excinfo, option=None): 946 if option and option.fulltrace: 947 style = "long" 948 else: 949 style = "native" 950 excrepr = excinfo.getrepr(funcargs=True, 951 showlocals=getattr(option, 'showlocals', False), 952 style=style, 953 ) 954 res = self.hook.pytest_internalerror(excrepr=excrepr, 955 excinfo=excinfo) 956 if not any(res): 957 for line in str(excrepr).split("\n"): 958 sys.stderr.write("INTERNALERROR> %s\n" % line) 959 sys.stderr.flush() 960 961 def cwd_relative_nodeid(self, nodeid): 962 # nodeid's are relative to the rootpath, compute relative to cwd 963 if self.invocation_dir != self.rootdir: 964 fullpath = self.rootdir.join(nodeid) 965 nodeid = self.invocation_dir.bestrelpath(fullpath) 966 return nodeid 967 968 @classmethod 969 def fromdictargs(cls, option_dict, args): 970 """ constructor useable for subprocesses. """ 971 config = get_config() 972 config.option.__dict__.update(option_dict) 973 config.parse(args, addopts=False) 974 for x in config.option.plugins: 975 config.pluginmanager.consider_pluginarg(x) 976 return config 977 978 def _processopt(self, opt): 979 for name in opt._short_opts + opt._long_opts: 980 self._opt2dest[name] = opt.dest 981 982 if hasattr(opt, 'default') and opt.dest: 983 if not hasattr(self.option, opt.dest): 984 setattr(self.option, opt.dest, opt.default) 985 986 @hookimpl(trylast=True) 987 def pytest_load_initial_conftests(self, early_config): 988 self.pluginmanager._set_initial_conftests(early_config.known_args_namespace) 989 990 def _initini(self, args): 991 ns, unknown_args = self._parser.parse_known_and_unknown_args(args, namespace=self.option.copy()) 992 r = determine_setup(ns.inifilename, ns.file_or_dir + unknown_args, warnfunc=self.warn) 993 self.rootdir, self.inifile, self.inicfg = r 994 self._parser.extra_info['rootdir'] = self.rootdir 995 self._parser.extra_info['inifile'] = self.inifile 996 self.invocation_dir = py.path.local() 997 self._parser.addini('addopts', 'extra command line options', 'args') 998 self._parser.addini('minversion', 'minimally required pytest version') 999 self._override_ini = ns.override_ini or () 1000 1001 def _consider_importhook(self, args): 1002 """Install the PEP 302 import hook if using assertion rewriting. 1003 1004 Needs to parse the --assert=<mode> option from the commandline 1005 and find all the installed plugins to mark them for rewriting 1006 by the importhook. 1007 """ 1008 ns, unknown_args = self._parser.parse_known_and_unknown_args(args) 1009 mode = ns.assertmode 1010 if mode == 'rewrite': 1011 try: 1012 hook = _pytest.assertion.install_importhook(self) 1013 except SystemError: 1014 mode = 'plain' 1015 else: 1016 self._mark_plugins_for_rewrite(hook) 1017 self._warn_about_missing_assertion(mode) 1018 1019 def _mark_plugins_for_rewrite(self, hook): 1020 """ 1021 Given an importhook, mark for rewrite any top-level 1022 modules or packages in the distribution package for 1023 all pytest plugins. 1024 """ 1025 import pkg_resources 1026 self.pluginmanager.rewrite_hook = hook 1027 1028 # 'RECORD' available for plugins installed normally (pip install) 1029 # 'SOURCES.txt' available for plugins installed in dev mode (pip install -e) 1030 # for installed plugins 'SOURCES.txt' returns an empty list, and vice-versa 1031 # so it shouldn't be an issue 1032 metadata_files = 'RECORD', 'SOURCES.txt' 1033 1034 package_files = ( 1035 entry.split(',')[0] 1036 for entrypoint in pkg_resources.iter_entry_points('pytest11') 1037 for metadata in metadata_files 1038 for entry in entrypoint.dist._get_metadata(metadata) 1039 ) 1040 1041 for name in _iter_rewritable_modules(package_files): 1042 hook.mark_rewrite(name) 1043 1044 def _warn_about_missing_assertion(self, mode): 1045 try: 1046 assert False 1047 except AssertionError: 1048 pass 1049 else: 1050 if mode == 'plain': 1051 sys.stderr.write("WARNING: ASSERTIONS ARE NOT EXECUTED" 1052 " and FAILING TESTS WILL PASS. Are you" 1053 " using python -O?") 1054 else: 1055 sys.stderr.write("WARNING: assertions not in test modules or" 1056 " plugins will be ignored" 1057 " because assert statements are not executed " 1058 "by the underlying Python interpreter " 1059 "(are you using python -O?)\n") 1060 1061 def _preparse(self, args, addopts=True): 1062 if addopts: 1063 args[:] = shlex.split(os.environ.get('PYTEST_ADDOPTS', '')) + args 1064 self._initini(args) 1065 if addopts: 1066 args[:] = self.getini("addopts") + args 1067 self._checkversion() 1068 self._consider_importhook(args) 1069 self.pluginmanager.consider_preparse(args) 1070 self.pluginmanager.load_setuptools_entrypoints('pytest11') 1071 self.pluginmanager.consider_env() 1072 self.known_args_namespace = ns = self._parser.parse_known_args(args, namespace=self.option.copy()) 1073 if self.known_args_namespace.confcutdir is None and self.inifile: 1074 confcutdir = py.path.local(self.inifile).dirname 1075 self.known_args_namespace.confcutdir = confcutdir 1076 try: 1077 self.hook.pytest_load_initial_conftests(early_config=self, 1078 args=args, parser=self._parser) 1079 except ConftestImportFailure: 1080 e = sys.exc_info()[1] 1081 if ns.help or ns.version: 1082 # we don't want to prevent --help/--version to work 1083 # so just let is pass and print a warning at the end 1084 self._warn("could not load initial conftests (%s)\n" % e.path) 1085 else: 1086 raise 1087 1088 def _checkversion(self): 1089 import pytest 1090 minver = self.inicfg.get('minversion', None) 1091 if minver: 1092 ver = minver.split(".") 1093 myver = pytest.__version__.split(".") 1094 if myver < ver: 1095 raise pytest.UsageError( 1096 "%s:%d: requires pytest-%s, actual pytest-%s'" % ( 1097 self.inicfg.config.path, self.inicfg.lineof('minversion'), 1098 minver, pytest.__version__)) 1099 1100 def parse(self, args, addopts=True): 1101 # parse given cmdline arguments into this config object. 1102 assert not hasattr(self, 'args'), ( 1103 "can only parse cmdline args at most once per Config object") 1104 self._origargs = args 1105 self.hook.pytest_addhooks.call_historic( 1106 kwargs=dict(pluginmanager=self.pluginmanager)) 1107 self._preparse(args, addopts=addopts) 1108 # XXX deprecated hook: 1109 self.hook.pytest_cmdline_preparse(config=self, args=args) 1110 self._parser.after_preparse = True 1111 try: 1112 args = self._parser.parse_setoption(args, self.option, namespace=self.option) 1113 if not args: 1114 cwd = os.getcwd() 1115 if cwd == self.rootdir: 1116 args = self.getini('testpaths') 1117 if not args: 1118 args = [cwd] 1119 self.args = args 1120 except PrintHelp: 1121 pass 1122 1123 def addinivalue_line(self, name, line): 1124 """ add a line to an ini-file option. The option must have been 1125 declared but might not yet be set in which case the line becomes the 1126 the first line in its value. """ 1127 x = self.getini(name) 1128 assert isinstance(x, list) 1129 x.append(line) # modifies the cached list inline 1130 1131 def getini(self, name): 1132 """ return configuration value from an :ref:`ini file <inifiles>`. If the 1133 specified name hasn't been registered through a prior 1134 :py:func:`parser.addini <_pytest.config.Parser.addini>` 1135 call (usually from a plugin), a ValueError is raised. """ 1136 try: 1137 return self._inicache[name] 1138 except KeyError: 1139 self._inicache[name] = val = self._getini(name) 1140 return val 1141 1142 def _getini(self, name): 1143 try: 1144 description, type, default = self._parser._inidict[name] 1145 except KeyError: 1146 raise ValueError("unknown configuration value: %r" % (name,)) 1147 value = self._get_override_ini_value(name) 1148 if value is None: 1149 try: 1150 value = self.inicfg[name] 1151 except KeyError: 1152 if default is not None: 1153 return default 1154 if type is None: 1155 return '' 1156 return [] 1157 if type == "pathlist": 1158 dp = py.path.local(self.inicfg.config.path).dirpath() 1159 values = [] 1160 for relpath in shlex.split(value): 1161 values.append(dp.join(relpath, abs=True)) 1162 return values 1163 elif type == "args": 1164 return shlex.split(value) 1165 elif type == "linelist": 1166 return [t for t in map(lambda x: x.strip(), value.split("\n")) if t] 1167 elif type == "bool": 1168 return bool(_strtobool(value.strip())) 1169 else: 1170 assert type is None 1171 return value 1172 1173 def _getconftest_pathlist(self, name, path): 1174 try: 1175 mod, relroots = self.pluginmanager._rget_with_confmod(name, path) 1176 except KeyError: 1177 return None 1178 modpath = py.path.local(mod.__file__).dirpath() 1179 values = [] 1180 for relroot in relroots: 1181 if not isinstance(relroot, py.path.local): 1182 relroot = relroot.replace("/", py.path.local.sep) 1183 relroot = modpath.join(relroot, abs=True) 1184 values.append(relroot) 1185 return values 1186 1187 def _get_override_ini_value(self, name): 1188 value = None 1189 # override_ini is a list of list, to support both -o foo1=bar1 foo2=bar2 and 1190 # and -o foo1=bar1 -o foo2=bar2 options 1191 # always use the last item if multiple value set for same ini-name, 1192 # e.g. -o foo=bar1 -o foo=bar2 will set foo to bar2 1193 for ini_config_list in self._override_ini: 1194 for ini_config in ini_config_list: 1195 try: 1196 (key, user_ini_value) = ini_config.split("=", 1) 1197 except ValueError: 1198 raise UsageError("-o/--override-ini expects option=value style.") 1199 if key == name: 1200 value = user_ini_value 1201 return value 1202 1203 def getoption(self, name, default=notset, skip=False): 1204 """ return command line option value. 1205 1206 :arg name: name of the option. You may also specify 1207 the literal ``--OPT`` option instead of the "dest" option name. 1208 :arg default: default value if no option of that name exists. 1209 :arg skip: if True raise pytest.skip if option does not exists 1210 or has a None value. 1211 """ 1212 name = self._opt2dest.get(name, name) 1213 try: 1214 val = getattr(self.option, name) 1215 if val is None and skip: 1216 raise AttributeError(name) 1217 return val 1218 except AttributeError: 1219 if default is not notset: 1220 return default 1221 if skip: 1222 import pytest 1223 pytest.skip("no %r option found" % (name,)) 1224 raise ValueError("no option named %r" % (name,)) 1225 1226 def getvalue(self, name, path=None): 1227 """ (deprecated, use getoption()) """ 1228 return self.getoption(name) 1229 1230 def getvalueorskip(self, name, path=None): 1231 """ (deprecated, use getoption(skip=True)) """ 1232 return self.getoption(name, skip=True) 1233 1234 1235def exists(path, ignore=EnvironmentError): 1236 try: 1237 return path.check() 1238 except ignore: 1239 return False 1240 1241 1242def getcfg(args, warnfunc=None): 1243 """ 1244 Search the list of arguments for a valid ini-file for pytest, 1245 and return a tuple of (rootdir, inifile, cfg-dict). 1246 1247 note: warnfunc is an optional function used to warn 1248 about ini-files that use deprecated features. 1249 This parameter should be removed when pytest 1250 adopts standard deprecation warnings (#1804). 1251 """ 1252 from _pytest.deprecated import SETUP_CFG_PYTEST 1253 inibasenames = ["pytest.ini", "tox.ini", "setup.cfg"] 1254 args = [x for x in args if not str(x).startswith("-")] 1255 if not args: 1256 args = [py.path.local()] 1257 for arg in args: 1258 arg = py.path.local(arg) 1259 for base in arg.parts(reverse=True): 1260 for inibasename in inibasenames: 1261 p = base.join(inibasename) 1262 if exists(p): 1263 iniconfig = py.iniconfig.IniConfig(p) 1264 if 'pytest' in iniconfig.sections: 1265 if inibasename == 'setup.cfg' and warnfunc: 1266 warnfunc('C1', SETUP_CFG_PYTEST) 1267 return base, p, iniconfig['pytest'] 1268 if inibasename == 'setup.cfg' and 'tool:pytest' in iniconfig.sections: 1269 return base, p, iniconfig['tool:pytest'] 1270 elif inibasename == "pytest.ini": 1271 # allowed to be empty 1272 return base, p, {} 1273 return None, None, None 1274 1275 1276def get_common_ancestor(paths): 1277 common_ancestor = None 1278 for path in paths: 1279 if not path.exists(): 1280 continue 1281 if common_ancestor is None: 1282 common_ancestor = path 1283 else: 1284 if path.relto(common_ancestor) or path == common_ancestor: 1285 continue 1286 elif common_ancestor.relto(path): 1287 common_ancestor = path 1288 else: 1289 shared = path.common(common_ancestor) 1290 if shared is not None: 1291 common_ancestor = shared 1292 if common_ancestor is None: 1293 common_ancestor = py.path.local() 1294 elif common_ancestor.isfile(): 1295 common_ancestor = common_ancestor.dirpath() 1296 return common_ancestor 1297 1298 1299def get_dirs_from_args(args): 1300 def is_option(x): 1301 return str(x).startswith('-') 1302 1303 def get_file_part_from_node_id(x): 1304 return str(x).split('::')[0] 1305 1306 def get_dir_from_path(path): 1307 if path.isdir(): 1308 return path 1309 return py.path.local(path.dirname) 1310 1311 # These look like paths but may not exist 1312 possible_paths = ( 1313 py.path.local(get_file_part_from_node_id(arg)) 1314 for arg in args 1315 if not is_option(arg) 1316 ) 1317 1318 return [ 1319 get_dir_from_path(path) 1320 for path in possible_paths 1321 if path.exists() 1322 ] 1323 1324 1325def determine_setup(inifile, args, warnfunc=None): 1326 dirs = get_dirs_from_args(args) 1327 if inifile: 1328 iniconfig = py.iniconfig.IniConfig(inifile) 1329 try: 1330 inicfg = iniconfig["pytest"] 1331 except KeyError: 1332 inicfg = None 1333 rootdir = get_common_ancestor(dirs) 1334 else: 1335 ancestor = get_common_ancestor(dirs) 1336 rootdir, inifile, inicfg = getcfg([ancestor], warnfunc=warnfunc) 1337 if rootdir is None: 1338 for rootdir in ancestor.parts(reverse=True): 1339 if rootdir.join("setup.py").exists(): 1340 break 1341 else: 1342 rootdir, inifile, inicfg = getcfg(dirs, warnfunc=warnfunc) 1343 if rootdir is None: 1344 rootdir = get_common_ancestor([py.path.local(), ancestor]) 1345 is_fs_root = os.path.splitdrive(str(rootdir))[1] == '/' 1346 if is_fs_root: 1347 rootdir = ancestor 1348 return rootdir, inifile, inicfg or {} 1349 1350 1351def setns(obj, dic): 1352 import pytest 1353 for name, value in dic.items(): 1354 if isinstance(value, dict): 1355 mod = getattr(obj, name, None) 1356 if mod is None: 1357 modname = "pytest.%s" % name 1358 mod = types.ModuleType(modname) 1359 sys.modules[modname] = mod 1360 mod.__all__ = [] 1361 setattr(obj, name, mod) 1362 obj.__all__.append(name) 1363 setns(mod, value) 1364 else: 1365 setattr(obj, name, value) 1366 obj.__all__.append(name) 1367 # if obj != pytest: 1368 # pytest.__all__.append(name) 1369 setattr(pytest, name, value) 1370 1371 1372def create_terminal_writer(config, *args, **kwargs): 1373 """Create a TerminalWriter instance configured according to the options 1374 in the config object. Every code which requires a TerminalWriter object 1375 and has access to a config object should use this function. 1376 """ 1377 tw = py.io.TerminalWriter(*args, **kwargs) 1378 if config.option.color == 'yes': 1379 tw.hasmarkup = True 1380 if config.option.color == 'no': 1381 tw.hasmarkup = False 1382 return tw 1383 1384 1385def _strtobool(val): 1386 """Convert a string representation of truth to true (1) or false (0). 1387 1388 True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values 1389 are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if 1390 'val' is anything else. 1391 1392 .. note:: copied from distutils.util 1393 """ 1394 val = val.lower() 1395 if val in ('y', 'yes', 't', 'true', 'on', '1'): 1396 return 1 1397 elif val in ('n', 'no', 'f', 'false', 'off', '0'): 1398 return 0 1399 else: 1400 raise ValueError("invalid truth value %r" % (val,)) 1401