config.py 44 KB

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