Coverage for /usr/local/lib/python3.7/site-packages/_pytest/python.py : 34%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1""" Python test discovery, setup and run of test functions. """
2import enum
3import fnmatch
4import inspect
5import os
6import sys
7import warnings
8from collections import Counter
9from collections import defaultdict
10from collections.abc import Sequence
11from functools import partial
12from textwrap import dedent
13from typing import List
14from typing import Tuple
16import py
18import _pytest
19from _pytest import fixtures
20from _pytest import nodes
21from _pytest._code import filter_traceback
22from _pytest.compat import ascii_escaped
23from _pytest.compat import get_default_arg_names
24from _pytest.compat import get_real_func
25from _pytest.compat import getfslineno
26from _pytest.compat import getimfunc
27from _pytest.compat import getlocation
28from _pytest.compat import is_generator
29from _pytest.compat import iscoroutinefunction
30from _pytest.compat import NOTSET
31from _pytest.compat import REGEX_TYPE
32from _pytest.compat import safe_getattr
33from _pytest.compat import safe_isclass
34from _pytest.compat import STRING_TYPES
35from _pytest.config import hookimpl
36from _pytest.deprecated import FUNCARGNAMES
37from _pytest.main import FSHookProxy
38from _pytest.mark import MARK_GEN
39from _pytest.mark.structures import get_unpacked_marks
40from _pytest.mark.structures import normalize_mark_list
41from _pytest.outcomes import fail
42from _pytest.outcomes import skip
43from _pytest.pathlib import parts
44from _pytest.warning_types import PytestCollectionWarning
45from _pytest.warning_types import PytestUnhandledCoroutineWarning
48def pyobj_property(name):
49 def get(self):
50 node = self.getparent(getattr(__import__("pytest"), name))
51 if node is not None:
52 return node.obj
54 doc = "python {} object this node was collected from (can be None).".format(
55 name.lower()
56 )
57 return property(get, None, None, doc)
60def pytest_addoption(parser):
61 group = parser.getgroup("general")
62 group.addoption(
63 "--fixtures",
64 "--funcargs",
65 action="store_true",
66 dest="showfixtures",
67 default=False,
68 help="show available fixtures, sorted by plugin appearance "
69 "(fixtures with leading '_' are only shown with '-v')",
70 )
71 group.addoption(
72 "--fixtures-per-test",
73 action="store_true",
74 dest="show_fixtures_per_test",
75 default=False,
76 help="show fixtures per test",
77 )
78 parser.addini(
79 "python_files",
80 type="args",
81 # NOTE: default is also used in AssertionRewritingHook.
82 default=["test_*.py", "*_test.py"],
83 help="glob-style file patterns for Python test module discovery",
84 )
85 parser.addini(
86 "python_classes",
87 type="args",
88 default=["Test"],
89 help="prefixes or glob names for Python test class discovery",
90 )
91 parser.addini(
92 "python_functions",
93 type="args",
94 default=["test"],
95 help="prefixes or glob names for Python test function and method discovery",
96 )
97 parser.addini(
98 "disable_test_id_escaping_and_forfeit_all_rights_to_community_support",
99 type="bool",
100 default=False,
101 help="disable string escape non-ascii characters, might cause unwanted "
102 "side effects(use at your own risk)",
103 )
105 group.addoption(
106 "--import-mode",
107 default="prepend",
108 choices=["prepend", "append"],
109 dest="importmode",
110 help="prepend/append to sys.path when importing test modules, "
111 "default is to prepend.",
112 )
115def pytest_cmdline_main(config):
116 if config.option.showfixtures:
117 showfixtures(config)
118 return 0
119 if config.option.show_fixtures_per_test:
120 show_fixtures_per_test(config)
121 return 0
124def pytest_generate_tests(metafunc):
125 for marker in metafunc.definition.iter_markers(name="parametrize"):
126 metafunc.parametrize(*marker.args, **marker.kwargs)
129def pytest_configure(config):
130 config.addinivalue_line(
131 "markers",
132 "parametrize(argnames, argvalues): call a test function multiple "
133 "times passing in different arguments in turn. argvalues generally "
134 "needs to be a list of values if argnames specifies only one name "
135 "or a list of tuples of values if argnames specifies multiple names. "
136 "Example: @parametrize('arg1', [1,2]) would lead to two calls of the "
137 "decorated test function, one with arg1=1 and another with arg1=2."
138 "see https://docs.pytest.org/en/latest/parametrize.html for more info "
139 "and examples.",
140 )
141 config.addinivalue_line(
142 "markers",
143 "usefixtures(fixturename1, fixturename2, ...): mark tests as needing "
144 "all of the specified fixtures. see "
145 "https://docs.pytest.org/en/latest/fixture.html#usefixtures ",
146 )
149@hookimpl(trylast=True)
150def pytest_pyfunc_call(pyfuncitem):
151 def async_warn():
152 msg = "async def functions are not natively supported and have been skipped.\n"
153 msg += "You need to install a suitable plugin for your async framework, for example:\n"
154 msg += " - pytest-asyncio\n"
155 msg += " - pytest-trio\n"
156 msg += " - pytest-tornasync"
157 warnings.warn(PytestUnhandledCoroutineWarning(msg.format(pyfuncitem.nodeid)))
158 skip(msg="async def function and no async plugin installed (see warnings)")
160 testfunction = pyfuncitem.obj
161 if iscoroutinefunction(testfunction) or (
162 sys.version_info >= (3, 6) and inspect.isasyncgenfunction(testfunction)
163 ):
164 async_warn()
165 funcargs = pyfuncitem.funcargs
166 testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames}
167 result = testfunction(**testargs)
168 if hasattr(result, "__await__") or hasattr(result, "__aiter__"):
169 async_warn()
170 return True
173def pytest_collect_file(path, parent):
174 ext = path.ext
175 if ext == ".py":
176 if not parent.session.isinitpath(path):
177 if not path_matches_patterns(
178 path, parent.config.getini("python_files") + ["__init__.py"]
179 ):
180 return
181 ihook = parent.session.gethookproxy(path)
182 return ihook.pytest_pycollect_makemodule(path=path, parent=parent)
185def path_matches_patterns(path, patterns):
186 """Returns True if the given py.path.local matches one of the patterns in the list of globs given"""
187 return any(path.fnmatch(pattern) for pattern in patterns)
190def pytest_pycollect_makemodule(path, parent):
191 if path.basename == "__init__.py":
192 return Package(path, parent)
193 return Module(path, parent)
196@hookimpl(hookwrapper=True)
197def pytest_pycollect_makeitem(collector, name, obj):
198 outcome = yield
199 res = outcome.get_result()
200 if res is not None:
201 return
202 # nothing was collected elsewhere, let's do it here
203 if safe_isclass(obj):
204 if collector.istestclass(obj, name):
205 outcome.force_result(Class(name, parent=collector))
206 elif collector.istestfunction(obj, name):
207 # mock seems to store unbound methods (issue473), normalize it
208 obj = getattr(obj, "__func__", obj)
209 # We need to try and unwrap the function if it's a functools.partial
210 # or a functools.wrapped.
211 # We mustn't if it's been wrapped with mock.patch (python 2 only)
212 if not (inspect.isfunction(obj) or inspect.isfunction(get_real_func(obj))):
213 filename, lineno = getfslineno(obj)
214 warnings.warn_explicit(
215 message=PytestCollectionWarning(
216 "cannot collect %r because it is not a function." % name
217 ),
218 category=None,
219 filename=str(filename),
220 lineno=lineno + 1,
221 )
222 elif getattr(obj, "__test__", True):
223 if is_generator(obj):
224 res = Function(name, parent=collector)
225 reason = "yield tests were removed in pytest 4.0 - {name} will be ignored".format(
226 name=name
227 )
228 res.add_marker(MARK_GEN.xfail(run=False, reason=reason))
229 res.warn(PytestCollectionWarning(reason))
230 else:
231 res = list(collector._genfunctions(name, obj))
232 outcome.force_result(res)
235class PyobjContext:
236 module = pyobj_property("Module")
237 cls = pyobj_property("Class")
238 instance = pyobj_property("Instance")
241class PyobjMixin(PyobjContext):
242 _ALLOW_MARKERS = True
244 @property
245 def obj(self):
246 """Underlying Python object."""
247 obj = getattr(self, "_obj", None)
248 if obj is None:
249 self._obj = obj = self._getobj()
250 # XXX evil hack
251 # used to avoid Instance collector marker duplication
252 if self._ALLOW_MARKERS:
253 self.own_markers.extend(get_unpacked_marks(self.obj))
254 return obj
256 @obj.setter
257 def obj(self, value):
258 self._obj = value
260 def _getobj(self):
261 """Gets the underlying Python object. May be overwritten by subclasses."""
262 return getattr(self.parent.obj, self.name)
264 def getmodpath(self, stopatmodule=True, includemodule=False):
265 """ return python path relative to the containing module. """
266 chain = self.listchain()
267 chain.reverse()
268 parts = []
269 for node in chain:
270 if isinstance(node, Instance):
271 continue
272 name = node.name
273 if isinstance(node, Module):
274 name = os.path.splitext(name)[0]
275 if stopatmodule:
276 if includemodule:
277 parts.append(name)
278 break
279 parts.append(name)
280 parts.reverse()
281 return ".".join(parts)
283 def reportinfo(self) -> Tuple[str, int, str]:
284 # XXX caching?
285 obj = self.obj
286 compat_co_firstlineno = getattr(obj, "compat_co_firstlineno", None)
287 if isinstance(compat_co_firstlineno, int):
288 # nose compatibility
289 fspath = sys.modules[obj.__module__].__file__
290 if fspath.endswith(".pyc"):
291 fspath = fspath[:-1]
292 lineno = compat_co_firstlineno
293 else:
294 fspath, lineno = getfslineno(obj)
295 modpath = self.getmodpath()
296 assert isinstance(lineno, int)
297 return fspath, lineno, modpath
300class PyCollector(PyobjMixin, nodes.Collector):
301 def funcnamefilter(self, name):
302 return self._matches_prefix_or_glob_option("python_functions", name)
304 def isnosetest(self, obj):
305 """ Look for the __test__ attribute, which is applied by the
306 @nose.tools.istest decorator
307 """
308 # We explicitly check for "is True" here to not mistakenly treat
309 # classes with a custom __getattr__ returning something truthy (like a
310 # function) as test classes.
311 return safe_getattr(obj, "__test__", False) is True
313 def classnamefilter(self, name):
314 return self._matches_prefix_or_glob_option("python_classes", name)
316 def istestfunction(self, obj, name):
317 if self.funcnamefilter(name) or self.isnosetest(obj):
318 if isinstance(obj, staticmethod):
319 # static methods need to be unwrapped
320 obj = safe_getattr(obj, "__func__", False)
321 return (
322 safe_getattr(obj, "__call__", False)
323 and fixtures.getfixturemarker(obj) is None
324 )
325 else:
326 return False
328 def istestclass(self, obj, name):
329 return self.classnamefilter(name) or self.isnosetest(obj)
331 def _matches_prefix_or_glob_option(self, option_name, name):
332 """
333 checks if the given name matches the prefix or glob-pattern defined
334 in ini configuration.
335 """
336 for option in self.config.getini(option_name):
337 if name.startswith(option):
338 return True
339 # check that name looks like a glob-string before calling fnmatch
340 # because this is called for every name in each collected module,
341 # and fnmatch is somewhat expensive to call
342 elif ("*" in option or "?" in option or "[" in option) and fnmatch.fnmatch(
343 name, option
344 ):
345 return True
346 return False
348 def collect(self):
349 if not getattr(self.obj, "__test__", True):
350 return []
352 # NB. we avoid random getattrs and peek in the __dict__ instead
353 # (XXX originally introduced from a PyPy need, still true?)
354 dicts = [getattr(self.obj, "__dict__", {})]
355 for basecls in inspect.getmro(self.obj.__class__):
356 dicts.append(basecls.__dict__)
357 seen = {}
358 values = []
359 for dic in dicts:
360 for name, obj in list(dic.items()):
361 if name in seen:
362 continue
363 seen[name] = True
364 res = self._makeitem(name, obj)
365 if res is None:
366 continue
367 if not isinstance(res, list):
368 res = [res]
369 values.extend(res)
370 values.sort(key=lambda item: item.reportinfo()[:2])
371 return values
373 def _makeitem(self, name, obj):
374 # assert self.ihook.fspath == self.fspath, self
375 return self.ihook.pytest_pycollect_makeitem(collector=self, name=name, obj=obj)
377 def _genfunctions(self, name, funcobj):
378 module = self.getparent(Module).obj
379 clscol = self.getparent(Class)
380 cls = clscol and clscol.obj or None
381 fm = self.session._fixturemanager
383 definition = FunctionDefinition(name=name, parent=self, callobj=funcobj)
384 fixtureinfo = fm.getfixtureinfo(definition, funcobj, cls)
386 metafunc = Metafunc(
387 definition, fixtureinfo, self.config, cls=cls, module=module
388 )
389 methods = []
390 if hasattr(module, "pytest_generate_tests"):
391 methods.append(module.pytest_generate_tests)
392 if hasattr(cls, "pytest_generate_tests"):
393 methods.append(cls().pytest_generate_tests)
395 self.ihook.pytest_generate_tests.call_extra(methods, dict(metafunc=metafunc))
397 if not metafunc._calls:
398 yield Function(name, parent=self, fixtureinfo=fixtureinfo)
399 else:
400 # add funcargs() as fixturedefs to fixtureinfo.arg2fixturedefs
401 fixtures.add_funcarg_pseudo_fixture_def(self, metafunc, fm)
403 # add_funcarg_pseudo_fixture_def may have shadowed some fixtures
404 # with direct parametrization, so make sure we update what the
405 # function really needs.
406 fixtureinfo.prune_dependency_tree()
408 for callspec in metafunc._calls:
409 subname = "{}[{}]".format(name, callspec.id)
410 yield Function(
411 name=subname,
412 parent=self,
413 callspec=callspec,
414 callobj=funcobj,
415 fixtureinfo=fixtureinfo,
416 keywords={callspec.id: True},
417 originalname=name,
418 )
421class Module(nodes.File, PyCollector):
422 """ Collector for test classes and functions. """
424 def _getobj(self):
425 return self._importtestmodule()
427 def collect(self):
428 self._inject_setup_module_fixture()
429 self._inject_setup_function_fixture()
430 self.session._fixturemanager.parsefactories(self)
431 return super().collect()
433 def _inject_setup_module_fixture(self):
434 """Injects a hidden autouse, module scoped fixture into the collected module object
435 that invokes setUpModule/tearDownModule if either or both are available.
437 Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with
438 other fixtures (#517).
439 """
440 setup_module = _get_first_non_fixture_func(
441 self.obj, ("setUpModule", "setup_module")
442 )
443 teardown_module = _get_first_non_fixture_func(
444 self.obj, ("tearDownModule", "teardown_module")
445 )
447 if setup_module is None and teardown_module is None:
448 return
450 @fixtures.fixture(autouse=True, scope="module")
451 def xunit_setup_module_fixture(request):
452 if setup_module is not None:
453 _call_with_optional_argument(setup_module, request.module)
454 yield
455 if teardown_module is not None:
456 _call_with_optional_argument(teardown_module, request.module)
458 self.obj.__pytest_setup_module = xunit_setup_module_fixture
460 def _inject_setup_function_fixture(self):
461 """Injects a hidden autouse, function scoped fixture into the collected module object
462 that invokes setup_function/teardown_function if either or both are available.
464 Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with
465 other fixtures (#517).
466 """
467 setup_function = _get_first_non_fixture_func(self.obj, ("setup_function",))
468 teardown_function = _get_first_non_fixture_func(
469 self.obj, ("teardown_function",)
470 )
471 if setup_function is None and teardown_function is None:
472 return
474 @fixtures.fixture(autouse=True, scope="function")
475 def xunit_setup_function_fixture(request):
476 if request.instance is not None:
477 # in this case we are bound to an instance, so we need to let
478 # setup_method handle this
479 yield
480 return
481 if setup_function is not None:
482 _call_with_optional_argument(setup_function, request.function)
483 yield
484 if teardown_function is not None:
485 _call_with_optional_argument(teardown_function, request.function)
487 self.obj.__pytest_setup_function = xunit_setup_function_fixture
489 def _importtestmodule(self):
490 # we assume we are only called once per module
491 importmode = self.config.getoption("--import-mode")
492 try:
493 mod = self.fspath.pyimport(ensuresyspath=importmode)
494 except SyntaxError:
495 raise self.CollectError(
496 _pytest._code.ExceptionInfo.from_current().getrepr(style="short")
497 )
498 except self.fspath.ImportMismatchError:
499 e = sys.exc_info()[1]
500 raise self.CollectError(
501 "import file mismatch:\n"
502 "imported module %r has this __file__ attribute:\n"
503 " %s\n"
504 "which is not the same as the test file we want to collect:\n"
505 " %s\n"
506 "HINT: remove __pycache__ / .pyc files and/or use a "
507 "unique basename for your test file modules" % e.args
508 )
509 except ImportError:
510 from _pytest._code.code import ExceptionInfo
512 exc_info = ExceptionInfo.from_current()
513 if self.config.getoption("verbose") < 2:
514 exc_info.traceback = exc_info.traceback.filter(filter_traceback)
515 exc_repr = (
516 exc_info.getrepr(style="short")
517 if exc_info.traceback
518 else exc_info.exconly()
519 )
520 formatted_tb = str(exc_repr)
521 raise self.CollectError(
522 "ImportError while importing test module '{fspath}'.\n"
523 "Hint: make sure your test modules/packages have valid Python names.\n"
524 "Traceback:\n"
525 "{traceback}".format(fspath=self.fspath, traceback=formatted_tb)
526 )
527 except _pytest.runner.Skipped as e:
528 if e.allow_module_level:
529 raise
530 raise self.CollectError(
531 "Using pytest.skip outside of a test is not allowed. "
532 "To decorate a test function, use the @pytest.mark.skip "
533 "or @pytest.mark.skipif decorators instead, and to skip a "
534 "module use `pytestmark = pytest.mark.{skip,skipif}."
535 )
536 self.config.pluginmanager.consider_module(mod)
537 return mod
540class Package(Module):
541 def __init__(self, fspath, parent=None, config=None, session=None, nodeid=None):
542 session = parent.session
543 nodes.FSCollector.__init__(
544 self, fspath, parent=parent, config=config, session=session, nodeid=nodeid
545 )
546 self.name = fspath.dirname
547 self.trace = session.trace
548 self._norecursepatterns = session._norecursepatterns
549 self.fspath = fspath
551 def setup(self):
552 # not using fixtures to call setup_module here because autouse fixtures
553 # from packages are not called automatically (#4085)
554 setup_module = _get_first_non_fixture_func(
555 self.obj, ("setUpModule", "setup_module")
556 )
557 if setup_module is not None:
558 _call_with_optional_argument(setup_module, self.obj)
560 teardown_module = _get_first_non_fixture_func(
561 self.obj, ("tearDownModule", "teardown_module")
562 )
563 if teardown_module is not None:
564 func = partial(_call_with_optional_argument, teardown_module, self.obj)
565 self.addfinalizer(func)
567 def _recurse(self, dirpath):
568 if dirpath.basename == "__pycache__":
569 return False
570 ihook = self.gethookproxy(dirpath.dirpath())
571 if ihook.pytest_ignore_collect(path=dirpath, config=self.config):
572 return
573 for pat in self._norecursepatterns:
574 if dirpath.check(fnmatch=pat):
575 return False
576 ihook = self.gethookproxy(dirpath)
577 ihook.pytest_collect_directory(path=dirpath, parent=self)
578 return True
580 def gethookproxy(self, fspath):
581 # check if we have the common case of running
582 # hooks with all conftest.py filesall conftest.py
583 pm = self.config.pluginmanager
584 my_conftestmodules = pm._getconftestmodules(fspath)
585 remove_mods = pm._conftest_plugins.difference(my_conftestmodules)
586 if remove_mods:
587 # one or more conftests are not in use at this fspath
588 proxy = FSHookProxy(fspath, pm, remove_mods)
589 else:
590 # all plugins are active for this fspath
591 proxy = self.config.hook
592 return proxy
594 def _collectfile(self, path, handle_dupes=True):
595 assert (
596 path.isfile()
597 ), "{!r} is not a file (isdir={!r}, exists={!r}, islink={!r})".format(
598 path, path.isdir(), path.exists(), path.islink()
599 )
600 ihook = self.gethookproxy(path)
601 if not self.isinitpath(path):
602 if ihook.pytest_ignore_collect(path=path, config=self.config):
603 return ()
605 if handle_dupes:
606 keepduplicates = self.config.getoption("keepduplicates")
607 if not keepduplicates:
608 duplicate_paths = self.config.pluginmanager._duplicatepaths
609 if path in duplicate_paths:
610 return ()
611 else:
612 duplicate_paths.add(path)
614 if self.fspath == path: # __init__.py
615 return [self]
617 return ihook.pytest_collect_file(path=path, parent=self)
619 def isinitpath(self, path):
620 return path in self.session._initialpaths
622 def collect(self):
623 this_path = self.fspath.dirpath()
624 init_module = this_path.join("__init__.py")
625 if init_module.check(file=1) and path_matches_patterns(
626 init_module, self.config.getini("python_files")
627 ):
628 yield Module(init_module, self)
629 pkg_prefixes = set()
630 for path in this_path.visit(rec=self._recurse, bf=True, sort=True):
631 # We will visit our own __init__.py file, in which case we skip it.
632 is_file = path.isfile()
633 if is_file:
634 if path.basename == "__init__.py" and path.dirpath() == this_path:
635 continue
637 parts_ = parts(path.strpath)
638 if any(
639 pkg_prefix in parts_ and pkg_prefix.join("__init__.py") != path
640 for pkg_prefix in pkg_prefixes
641 ):
642 continue
644 if is_file:
645 yield from self._collectfile(path)
646 elif not path.isdir():
647 # Broken symlink or invalid/missing file.
648 continue
649 elif path.join("__init__.py").check(file=1):
650 pkg_prefixes.add(path)
653def _call_with_optional_argument(func, arg):
654 """Call the given function with the given argument if func accepts one argument, otherwise
655 calls func without arguments"""
656 arg_count = func.__code__.co_argcount
657 if inspect.ismethod(func):
658 arg_count -= 1
659 if arg_count:
660 func(arg)
661 else:
662 func()
665def _get_first_non_fixture_func(obj, names):
666 """Return the attribute from the given object to be used as a setup/teardown
667 xunit-style function, but only if not marked as a fixture to
668 avoid calling it twice.
669 """
670 for name in names:
671 meth = getattr(obj, name, None)
672 if meth is not None and fixtures.getfixturemarker(meth) is None:
673 return meth
676class Class(PyCollector):
677 """ Collector for test methods. """
679 def collect(self):
680 if not safe_getattr(self.obj, "__test__", True):
681 return []
682 if hasinit(self.obj):
683 self.warn(
684 PytestCollectionWarning(
685 "cannot collect test class %r because it has a "
686 "__init__ constructor (from: %s)"
687 % (self.obj.__name__, self.parent.nodeid)
688 )
689 )
690 return []
691 elif hasnew(self.obj):
692 self.warn(
693 PytestCollectionWarning(
694 "cannot collect test class %r because it has a "
695 "__new__ constructor (from: %s)"
696 % (self.obj.__name__, self.parent.nodeid)
697 )
698 )
699 return []
701 self._inject_setup_class_fixture()
702 self._inject_setup_method_fixture()
704 return [Instance(name="()", parent=self)]
706 def _inject_setup_class_fixture(self):
707 """Injects a hidden autouse, class scoped fixture into the collected class object
708 that invokes setup_class/teardown_class if either or both are available.
710 Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with
711 other fixtures (#517).
712 """
713 setup_class = _get_first_non_fixture_func(self.obj, ("setup_class",))
714 teardown_class = getattr(self.obj, "teardown_class", None)
715 if setup_class is None and teardown_class is None:
716 return
718 @fixtures.fixture(autouse=True, scope="class")
719 def xunit_setup_class_fixture(cls):
720 if setup_class is not None:
721 func = getimfunc(setup_class)
722 _call_with_optional_argument(func, self.obj)
723 yield
724 if teardown_class is not None:
725 func = getimfunc(teardown_class)
726 _call_with_optional_argument(func, self.obj)
728 self.obj.__pytest_setup_class = xunit_setup_class_fixture
730 def _inject_setup_method_fixture(self):
731 """Injects a hidden autouse, function scoped fixture into the collected class object
732 that invokes setup_method/teardown_method if either or both are available.
734 Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with
735 other fixtures (#517).
736 """
737 setup_method = _get_first_non_fixture_func(self.obj, ("setup_method",))
738 teardown_method = getattr(self.obj, "teardown_method", None)
739 if setup_method is None and teardown_method is None:
740 return
742 @fixtures.fixture(autouse=True, scope="function")
743 def xunit_setup_method_fixture(self, request):
744 method = request.function
745 if setup_method is not None:
746 func = getattr(self, "setup_method")
747 _call_with_optional_argument(func, method)
748 yield
749 if teardown_method is not None:
750 func = getattr(self, "teardown_method")
751 _call_with_optional_argument(func, method)
753 self.obj.__pytest_setup_method = xunit_setup_method_fixture
756class Instance(PyCollector):
757 _ALLOW_MARKERS = False # hack, destroy later
758 # instances share the object with their parents in a way
759 # that duplicates markers instances if not taken out
760 # can be removed at node structure reorganization time
762 def _getobj(self):
763 return self.parent.obj()
765 def collect(self):
766 self.session._fixturemanager.parsefactories(self)
767 return super().collect()
769 def newinstance(self):
770 self.obj = self._getobj()
771 return self.obj
774class FunctionMixin(PyobjMixin):
775 """ mixin for the code common to Function and Generator.
776 """
778 def setup(self):
779 """ perform setup for this test function. """
780 if isinstance(self.parent, Instance):
781 self.parent.newinstance()
782 self.obj = self._getobj()
784 def _prunetraceback(self, excinfo):
785 if hasattr(self, "_obj") and not self.config.getoption("fulltrace", False):
786 code = _pytest._code.Code(get_real_func(self.obj))
787 path, firstlineno = code.path, code.firstlineno
788 traceback = excinfo.traceback
789 ntraceback = traceback.cut(path=path, firstlineno=firstlineno)
790 if ntraceback == traceback:
791 ntraceback = ntraceback.cut(path=path)
792 if ntraceback == traceback:
793 ntraceback = ntraceback.filter(filter_traceback)
794 if not ntraceback:
795 ntraceback = traceback
797 excinfo.traceback = ntraceback.filter()
798 # issue364: mark all but first and last frames to
799 # only show a single-line message for each frame
800 if self.config.getoption("tbstyle", "auto") == "auto":
801 if len(excinfo.traceback) > 2:
802 for entry in excinfo.traceback[1:-1]:
803 entry.set_repr_style("short")
805 def repr_failure(self, excinfo, outerr=None):
806 assert outerr is None, "XXX outerr usage is deprecated"
807 style = self.config.getoption("tbstyle", "auto")
808 if style == "auto":
809 style = "long"
810 return self._repr_failure_py(excinfo, style=style)
813def hasinit(obj):
814 init = getattr(obj, "__init__", None)
815 if init:
816 return init != object.__init__
819def hasnew(obj):
820 new = getattr(obj, "__new__", None)
821 if new:
822 return new != object.__new__
825class CallSpec2:
826 def __init__(self, metafunc):
827 self.metafunc = metafunc
828 self.funcargs = {}
829 self._idlist = []
830 self.params = {}
831 self._globalid = NOTSET
832 self._globalparam = NOTSET
833 self._arg2scopenum = {} # used for sorting parametrized resources
834 self.marks = []
835 self.indices = {}
837 def copy(self):
838 cs = CallSpec2(self.metafunc)
839 cs.funcargs.update(self.funcargs)
840 cs.params.update(self.params)
841 cs.marks.extend(self.marks)
842 cs.indices.update(self.indices)
843 cs._arg2scopenum.update(self._arg2scopenum)
844 cs._idlist = list(self._idlist)
845 cs._globalid = self._globalid
846 cs._globalparam = self._globalparam
847 return cs
849 def _checkargnotcontained(self, arg):
850 if arg in self.params or arg in self.funcargs:
851 raise ValueError("duplicate {!r}".format(arg))
853 def getparam(self, name):
854 try:
855 return self.params[name]
856 except KeyError:
857 if self._globalparam is NOTSET:
858 raise ValueError(name)
859 return self._globalparam
861 @property
862 def id(self):
863 return "-".join(map(str, filter(None, self._idlist)))
865 def setmulti2(self, valtypes, argnames, valset, id, marks, scopenum, param_index):
866 for arg, val in zip(argnames, valset):
867 self._checkargnotcontained(arg)
868 valtype_for_arg = valtypes[arg]
869 getattr(self, valtype_for_arg)[arg] = val
870 self.indices[arg] = param_index
871 self._arg2scopenum[arg] = scopenum
872 self._idlist.append(id)
873 self.marks.extend(normalize_mark_list(marks))
876class Metafunc:
877 """
878 Metafunc objects are passed to the :func:`pytest_generate_tests <_pytest.hookspec.pytest_generate_tests>` hook.
879 They help to inspect a test function and to generate tests according to
880 test configuration or values specified in the class or module where a
881 test function is defined.
882 """
884 def __init__(
885 self,
886 definition: "FunctionDefinition",
887 fixtureinfo,
888 config,
889 cls=None,
890 module=None,
891 ) -> None:
892 self.definition = definition
894 #: access to the :class:`_pytest.config.Config` object for the test session
895 self.config = config
897 #: the module object where the test function is defined in.
898 self.module = module
900 #: underlying python test function
901 self.function = definition.obj
903 #: set of fixture names required by the test function
904 self.fixturenames = fixtureinfo.names_closure
906 #: class object where the test function is defined in or ``None``.
907 self.cls = cls
909 self._calls = [] # type: List[CallSpec2]
910 self._arg2fixturedefs = fixtureinfo.name2fixturedefs
912 @property
913 def funcargnames(self):
914 """ alias attribute for ``fixturenames`` for pre-2.3 compatibility"""
915 warnings.warn(FUNCARGNAMES, stacklevel=2)
916 return self.fixturenames
918 def parametrize(self, argnames, argvalues, indirect=False, ids=None, scope=None):
919 """ Add new invocations to the underlying test function using the list
920 of argvalues for the given argnames. Parametrization is performed
921 during the collection phase. If you need to setup expensive resources
922 see about setting indirect to do it rather at test setup time.
924 :arg argnames: a comma-separated string denoting one or more argument
925 names, or a list/tuple of argument strings.
927 :arg argvalues: The list of argvalues determines how often a
928 test is invoked with different argument values. If only one
929 argname was specified argvalues is a list of values. If N
930 argnames were specified, argvalues must be a list of N-tuples,
931 where each tuple-element specifies a value for its respective
932 argname.
934 :arg indirect: The list of argnames or boolean. A list of arguments'
935 names (subset of argnames). If True the list contains all names from
936 the argnames. Each argvalue corresponding to an argname in this list will
937 be passed as request.param to its respective argname fixture
938 function so that it can perform more expensive setups during the
939 setup phase of a test rather than at collection time.
941 :arg ids: list of string ids, or a callable.
942 If strings, each is corresponding to the argvalues so that they are
943 part of the test id. If None is given as id of specific test, the
944 automatically generated id for that argument will be used.
945 If callable, it should take one argument (a single argvalue) and return
946 a string or return None. If None, the automatically generated id for that
947 argument will be used.
948 If no ids are provided they will be generated automatically from
949 the argvalues.
951 :arg scope: if specified it denotes the scope of the parameters.
952 The scope is used for grouping tests by parameter instances.
953 It will also override any fixture-function defined scope, allowing
954 to set a dynamic scope using test context or configuration.
955 """
956 from _pytest.fixtures import scope2index
957 from _pytest.mark import ParameterSet
959 argnames, parameters = ParameterSet._for_parametrize(
960 argnames,
961 argvalues,
962 self.function,
963 self.config,
964 function_definition=self.definition,
965 )
966 del argvalues
968 if "request" in argnames:
969 fail(
970 "'request' is a reserved name and cannot be used in @pytest.mark.parametrize",
971 pytrace=False,
972 )
974 if scope is None:
975 scope = _find_parametrized_scope(argnames, self._arg2fixturedefs, indirect)
977 self._validate_if_using_arg_names(argnames, indirect)
979 arg_values_types = self._resolve_arg_value_types(argnames, indirect)
981 ids = self._resolve_arg_ids(argnames, ids, parameters, item=self.definition)
983 scopenum = scope2index(
984 scope, descr="parametrize() call in {}".format(self.function.__name__)
985 )
987 # create the new calls: if we are parametrize() multiple times (by applying the decorator
988 # more than once) then we accumulate those calls generating the cartesian product
989 # of all calls
990 newcalls = []
991 for callspec in self._calls or [CallSpec2(self)]:
992 for param_index, (param_id, param_set) in enumerate(zip(ids, parameters)):
993 newcallspec = callspec.copy()
994 newcallspec.setmulti2(
995 arg_values_types,
996 argnames,
997 param_set.values,
998 param_id,
999 param_set.marks,
1000 scopenum,
1001 param_index,
1002 )
1003 newcalls.append(newcallspec)
1004 self._calls = newcalls
1006 def _resolve_arg_ids(self, argnames, ids, parameters, item):
1007 """Resolves the actual ids for the given argnames, based on the ``ids`` parameter given
1008 to ``parametrize``.
1010 :param List[str] argnames: list of argument names passed to ``parametrize()``.
1011 :param ids: the ids parameter of the parametrized call (see docs).
1012 :param List[ParameterSet] parameters: the list of parameter values, same size as ``argnames``.
1013 :param Item item: the item that generated this parametrized call.
1014 :rtype: List[str]
1015 :return: the list of ids for each argname given
1016 """
1017 from _pytest._io.saferepr import saferepr
1019 idfn = None
1020 if callable(ids):
1021 idfn = ids
1022 ids = None
1023 if ids:
1024 func_name = self.function.__name__
1025 if len(ids) != len(parameters):
1026 msg = "In {}: {} parameter sets specified, with different number of ids: {}"
1027 fail(msg.format(func_name, len(parameters), len(ids)), pytrace=False)
1028 for id_value in ids:
1029 if id_value is not None and not isinstance(id_value, str):
1030 msg = "In {}: ids must be list of strings, found: {} (type: {!r})"
1031 fail(
1032 msg.format(func_name, saferepr(id_value), type(id_value)),
1033 pytrace=False,
1034 )
1035 ids = idmaker(argnames, parameters, idfn, ids, self.config, item=item)
1036 return ids
1038 def _resolve_arg_value_types(self, argnames, indirect):
1039 """Resolves if each parametrized argument must be considered a parameter to a fixture or a "funcarg"
1040 to the function, based on the ``indirect`` parameter of the parametrized() call.
1042 :param List[str] argnames: list of argument names passed to ``parametrize()``.
1043 :param indirect: same ``indirect`` parameter of ``parametrize()``.
1044 :rtype: Dict[str, str]
1045 A dict mapping each arg name to either:
1046 * "params" if the argname should be the parameter of a fixture of the same name.
1047 * "funcargs" if the argname should be a parameter to the parametrized test function.
1048 """
1049 if isinstance(indirect, bool):
1050 valtypes = dict.fromkeys(argnames, "params" if indirect else "funcargs")
1051 elif isinstance(indirect, Sequence):
1052 valtypes = dict.fromkeys(argnames, "funcargs")
1053 for arg in indirect:
1054 if arg not in argnames:
1055 fail(
1056 "In {}: indirect fixture '{}' doesn't exist".format(
1057 self.function.__name__, arg
1058 ),
1059 pytrace=False,
1060 )
1061 valtypes[arg] = "params"
1062 else:
1063 fail(
1064 "In {func}: expected Sequence or boolean for indirect, got {type}".format(
1065 type=type(indirect).__name__, func=self.function.__name__
1066 ),
1067 pytrace=False,
1068 )
1069 return valtypes
1071 def _validate_if_using_arg_names(self, argnames, indirect):
1072 """
1073 Check if all argnames are being used, by default values, or directly/indirectly.
1075 :param List[str] argnames: list of argument names passed to ``parametrize()``.
1076 :param indirect: same ``indirect`` parameter of ``parametrize()``.
1077 :raise ValueError: if validation fails.
1078 """
1079 default_arg_names = set(get_default_arg_names(self.function))
1080 func_name = self.function.__name__
1081 for arg in argnames:
1082 if arg not in self.fixturenames:
1083 if arg in default_arg_names:
1084 fail(
1085 "In {}: function already takes an argument '{}' with a default value".format(
1086 func_name, arg
1087 ),
1088 pytrace=False,
1089 )
1090 else:
1091 if isinstance(indirect, (tuple, list)):
1092 name = "fixture" if arg in indirect else "argument"
1093 else:
1094 name = "fixture" if indirect else "argument"
1095 fail(
1096 "In {}: function uses no {} '{}'".format(func_name, name, arg),
1097 pytrace=False,
1098 )
1101def _find_parametrized_scope(argnames, arg2fixturedefs, indirect):
1102 """Find the most appropriate scope for a parametrized call based on its arguments.
1104 When there's at least one direct argument, always use "function" scope.
1106 When a test function is parametrized and all its arguments are indirect
1107 (e.g. fixtures), return the most narrow scope based on the fixtures used.
1109 Related to issue #1832, based on code posted by @Kingdread.
1110 """
1111 from _pytest.fixtures import scopes
1113 if isinstance(indirect, (list, tuple)):
1114 all_arguments_are_fixtures = len(indirect) == len(argnames)
1115 else:
1116 all_arguments_are_fixtures = bool(indirect)
1118 if all_arguments_are_fixtures:
1119 fixturedefs = arg2fixturedefs or {}
1120 used_scopes = [
1121 fixturedef[0].scope
1122 for name, fixturedef in fixturedefs.items()
1123 if name in argnames
1124 ]
1125 if used_scopes:
1126 # Takes the most narrow scope from used fixtures
1127 for scope in reversed(scopes):
1128 if scope in used_scopes:
1129 return scope
1131 return "function"
1134def _ascii_escaped_by_config(val, config):
1135 if config is None:
1136 escape_option = False
1137 else:
1138 escape_option = config.getini(
1139 "disable_test_id_escaping_and_forfeit_all_rights_to_community_support"
1140 )
1141 return val if escape_option else ascii_escaped(val)
1144def _idval(val, argname, idx, idfn, item, config):
1145 if idfn:
1146 try:
1147 generated_id = idfn(val)
1148 if generated_id is not None:
1149 val = generated_id
1150 except Exception as e:
1151 # See issue https://github.com/pytest-dev/pytest/issues/2169
1152 msg = "{}: error raised while trying to determine id of parameter '{}' at position {}\n"
1153 msg = msg.format(item.nodeid, argname, idx)
1154 raise ValueError(msg) from e
1155 elif config:
1156 hook_id = config.hook.pytest_make_parametrize_id(
1157 config=config, val=val, argname=argname
1158 )
1159 if hook_id:
1160 return hook_id
1162 if isinstance(val, STRING_TYPES):
1163 return _ascii_escaped_by_config(val, config)
1164 elif val is None or isinstance(val, (float, int, bool)):
1165 return str(val)
1166 elif isinstance(val, REGEX_TYPE):
1167 return ascii_escaped(val.pattern)
1168 elif isinstance(val, enum.Enum):
1169 return str(val)
1170 elif hasattr(val, "__name__") and isinstance(val.__name__, str):
1171 # name of a class, function, module, etc.
1172 return val.__name__
1173 return str(argname) + str(idx)
1176def _idvalset(idx, parameterset, argnames, idfn, ids, item, config):
1177 if parameterset.id is not None:
1178 return parameterset.id
1179 if ids is None or (idx >= len(ids) or ids[idx] is None):
1180 this_id = [
1181 _idval(val, argname, idx, idfn, item=item, config=config)
1182 for val, argname in zip(parameterset.values, argnames)
1183 ]
1184 return "-".join(this_id)
1185 else:
1186 return _ascii_escaped_by_config(ids[idx], config)
1189def idmaker(argnames, parametersets, idfn=None, ids=None, config=None, item=None):
1190 ids = [
1191 _idvalset(valindex, parameterset, argnames, idfn, ids, config=config, item=item)
1192 for valindex, parameterset in enumerate(parametersets)
1193 ]
1195 # All IDs must be unique!
1196 unique_ids = set(ids)
1197 if len(unique_ids) != len(ids):
1199 # Record the number of occurrences of each test ID
1200 test_id_counts = Counter(ids)
1202 # Map the test ID to its next suffix
1203 test_id_suffixes = defaultdict(int)
1205 # Suffix non-unique IDs to make them unique
1206 for index, test_id in enumerate(ids):
1207 if test_id_counts[test_id] > 1:
1208 ids[index] = "{}{}".format(test_id, test_id_suffixes[test_id])
1209 test_id_suffixes[test_id] += 1
1211 return ids
1214def show_fixtures_per_test(config):
1215 from _pytest.main import wrap_session
1217 return wrap_session(config, _show_fixtures_per_test)
1220def _show_fixtures_per_test(config, session):
1221 import _pytest.config
1223 session.perform_collect()
1224 curdir = py.path.local()
1225 tw = _pytest.config.create_terminal_writer(config)
1226 verbose = config.getvalue("verbose")
1228 def get_best_relpath(func):
1229 loc = getlocation(func, curdir)
1230 return curdir.bestrelpath(loc)
1232 def write_fixture(fixture_def):
1233 argname = fixture_def.argname
1234 if verbose <= 0 and argname.startswith("_"):
1235 return
1236 if verbose > 0:
1237 bestrel = get_best_relpath(fixture_def.func)
1238 funcargspec = "{} -- {}".format(argname, bestrel)
1239 else:
1240 funcargspec = argname
1241 tw.line(funcargspec, green=True)
1242 fixture_doc = fixture_def.func.__doc__
1243 if fixture_doc:
1244 write_docstring(tw, fixture_doc)
1245 else:
1246 tw.line(" no docstring available", red=True)
1248 def write_item(item):
1249 try:
1250 info = item._fixtureinfo
1251 except AttributeError:
1252 # doctests items have no _fixtureinfo attribute
1253 return
1254 if not info.name2fixturedefs:
1255 # this test item does not use any fixtures
1256 return
1257 tw.line()
1258 tw.sep("-", "fixtures used by {}".format(item.name))
1259 tw.sep("-", "({})".format(get_best_relpath(item.function)))
1260 # dict key not used in loop but needed for sorting
1261 for _, fixturedefs in sorted(info.name2fixturedefs.items()):
1262 assert fixturedefs is not None
1263 if not fixturedefs:
1264 continue
1265 # last item is expected to be the one used by the test item
1266 write_fixture(fixturedefs[-1])
1268 for session_item in session.items:
1269 write_item(session_item)
1272def showfixtures(config):
1273 from _pytest.main import wrap_session
1275 return wrap_session(config, _showfixtures_main)
1278def _showfixtures_main(config, session):
1279 import _pytest.config
1281 session.perform_collect()
1282 curdir = py.path.local()
1283 tw = _pytest.config.create_terminal_writer(config)
1284 verbose = config.getvalue("verbose")
1286 fm = session._fixturemanager
1288 available = []
1289 seen = set()
1291 for argname, fixturedefs in fm._arg2fixturedefs.items():
1292 assert fixturedefs is not None
1293 if not fixturedefs:
1294 continue
1295 for fixturedef in fixturedefs:
1296 loc = getlocation(fixturedef.func, curdir)
1297 if (fixturedef.argname, loc) in seen:
1298 continue
1299 seen.add((fixturedef.argname, loc))
1300 available.append(
1301 (
1302 len(fixturedef.baseid),
1303 fixturedef.func.__module__,
1304 curdir.bestrelpath(loc),
1305 fixturedef.argname,
1306 fixturedef,
1307 )
1308 )
1310 available.sort()
1311 currentmodule = None
1312 for baseid, module, bestrel, argname, fixturedef in available:
1313 if currentmodule != module:
1314 if not module.startswith("_pytest."):
1315 tw.line()
1316 tw.sep("-", "fixtures defined from {}".format(module))
1317 currentmodule = module
1318 if verbose <= 0 and argname[0] == "_":
1319 continue
1320 tw.write(argname, green=True)
1321 if fixturedef.scope != "function":
1322 tw.write(" [%s scope]" % fixturedef.scope, cyan=True)
1323 if verbose > 0:
1324 tw.write(" -- %s" % bestrel, yellow=True)
1325 tw.write("\n")
1326 loc = getlocation(fixturedef.func, curdir)
1327 doc = fixturedef.func.__doc__ or ""
1328 if doc:
1329 write_docstring(tw, doc)
1330 else:
1331 tw.line(" {}: no docstring available".format(loc), red=True)
1332 tw.line()
1335def write_docstring(tw, doc, indent=" "):
1336 doc = doc.rstrip()
1337 if "\n" in doc:
1338 firstline, rest = doc.split("\n", 1)
1339 else:
1340 firstline, rest = doc, ""
1342 if firstline.strip():
1343 tw.line(indent + firstline.strip())
1345 if rest:
1346 for line in dedent(rest).split("\n"):
1347 tw.write(indent + line + "\n")
1350class Function(FunctionMixin, nodes.Item):
1351 """ a Function Item is responsible for setting up and executing a
1352 Python test function.
1353 """
1355 # disable since functions handle it themselves
1356 _ALLOW_MARKERS = False
1358 def __init__(
1359 self,
1360 name,
1361 parent,
1362 args=None,
1363 config=None,
1364 callspec=None,
1365 callobj=NOTSET,
1366 keywords=None,
1367 session=None,
1368 fixtureinfo=None,
1369 originalname=None,
1370 ):
1371 super().__init__(name, parent, config=config, session=session)
1372 self._args = args
1373 if callobj is not NOTSET:
1374 self.obj = callobj
1376 self.keywords.update(self.obj.__dict__)
1377 self.own_markers.extend(get_unpacked_marks(self.obj))
1378 if callspec:
1379 self.callspec = callspec
1380 # this is total hostile and a mess
1381 # keywords are broken by design by now
1382 # this will be redeemed later
1383 for mark in callspec.marks:
1384 # feel free to cry, this was broken for years before
1385 # and keywords cant fix it per design
1386 self.keywords[mark.name] = mark
1387 self.own_markers.extend(normalize_mark_list(callspec.marks))
1388 if keywords:
1389 self.keywords.update(keywords)
1391 # todo: this is a hell of a hack
1392 # https://github.com/pytest-dev/pytest/issues/4569
1394 self.keywords.update(
1395 {
1396 mark.name: True
1397 for mark in self.iter_markers()
1398 if mark.name not in self.keywords
1399 }
1400 )
1402 if fixtureinfo is None:
1403 fixtureinfo = self.session._fixturemanager.getfixtureinfo(
1404 self, self.obj, self.cls, funcargs=True
1405 )
1406 self._fixtureinfo = fixtureinfo
1407 self.fixturenames = fixtureinfo.names_closure
1408 self._initrequest()
1410 #: original function name, without any decorations (for example
1411 #: parametrization adds a ``"[...]"`` suffix to function names).
1412 #:
1413 #: .. versionadded:: 3.0
1414 self.originalname = originalname
1416 def _initrequest(self):
1417 self.funcargs = {}
1418 self._request = fixtures.FixtureRequest(self)
1420 @property
1421 def function(self):
1422 "underlying python 'function' object"
1423 return getimfunc(self.obj)
1425 def _getobj(self):
1426 name = self.name
1427 i = name.find("[") # parametrization
1428 if i != -1:
1429 name = name[:i]
1430 return getattr(self.parent.obj, name)
1432 @property
1433 def _pyfuncitem(self):
1434 "(compatonly) for code expecting pytest-2.2 style request objects"
1435 return self
1437 @property
1438 def funcargnames(self):
1439 """ alias attribute for ``fixturenames`` for pre-2.3 compatibility"""
1440 warnings.warn(FUNCARGNAMES, stacklevel=2)
1441 return self.fixturenames
1443 def runtest(self):
1444 """ execute the underlying test function. """
1445 self.ihook.pytest_pyfunc_call(pyfuncitem=self)
1447 def setup(self):
1448 super().setup()
1449 fixtures.fillfixtures(self)
1452class FunctionDefinition(Function):
1453 """
1454 internal hack until we get actual definition nodes instead of the
1455 crappy metafunc hack
1456 """
1458 def runtest(self):
1459 raise RuntimeError("function definitions are not supposed to be used")
1461 setup = runtest