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