__init__.py 41 KB

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