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