Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/_pytest/python.py : 20%

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 itertools
6import os
7import sys
8import typing
9import warnings
10from collections import Counter
11from collections import defaultdict
12from collections.abc import Sequence
13from functools import partial
14from typing import Any
15from typing import Callable
16from typing import Dict
17from typing import Generator
18from typing import Iterable
19from typing import Iterator
20from typing import List
21from typing import Mapping
22from typing import Optional
23from typing import Set
24from typing import Tuple
25from typing import Union
27import py
29import _pytest
30from _pytest import fixtures
31from _pytest import nodes
32from _pytest._code import filter_traceback
33from _pytest._code import getfslineno
34from _pytest._code.code import ExceptionInfo
35from _pytest._io import TerminalWriter
36from _pytest._io.saferepr import saferepr
37from _pytest.compat import ascii_escaped
38from _pytest.compat import get_default_arg_names
39from _pytest.compat import get_real_func
40from _pytest.compat import getimfunc
41from _pytest.compat import getlocation
42from _pytest.compat import is_async_function
43from _pytest.compat import is_generator
44from _pytest.compat import NOTSET
45from _pytest.compat import REGEX_TYPE
46from _pytest.compat import safe_getattr
47from _pytest.compat import safe_isclass
48from _pytest.compat import STRING_TYPES
49from _pytest.compat import TYPE_CHECKING
50from _pytest.config import Config
51from _pytest.config import ExitCode
52from _pytest.config import hookimpl
53from _pytest.config.argparsing import Parser
54from _pytest.deprecated import FUNCARGNAMES
55from _pytest.fixtures import FuncFixtureInfo
56from _pytest.main import Session
57from _pytest.mark import MARK_GEN
58from _pytest.mark import ParameterSet
59from _pytest.mark.structures import get_unpacked_marks
60from _pytest.mark.structures import Mark
61from _pytest.mark.structures import MarkDecorator
62from _pytest.mark.structures import normalize_mark_list
63from _pytest.outcomes import fail
64from _pytest.outcomes import skip
65from _pytest.pathlib import import_path
66from _pytest.pathlib import ImportPathMismatchError
67from _pytest.pathlib import parts
68from _pytest.reports import TerminalRepr
69from _pytest.warning_types import PytestCollectionWarning
70from _pytest.warning_types import PytestUnhandledCoroutineWarning
72if TYPE_CHECKING:
73 from typing import Type
74 from typing_extensions import Literal
75 from _pytest.fixtures import _Scope
78def pytest_addoption(parser: Parser) -> None:
79 group = parser.getgroup("general")
80 group.addoption(
81 "--fixtures",
82 "--funcargs",
83 action="store_true",
84 dest="showfixtures",
85 default=False,
86 help="show available fixtures, sorted by plugin appearance "
87 "(fixtures with leading '_' are only shown with '-v')",
88 )
89 group.addoption(
90 "--fixtures-per-test",
91 action="store_true",
92 dest="show_fixtures_per_test",
93 default=False,
94 help="show fixtures per test",
95 )
96 parser.addini(
97 "python_files",
98 type="args",
99 # NOTE: default is also used in AssertionRewritingHook.
100 default=["test_*.py", "*_test.py"],
101 help="glob-style file patterns for Python test module discovery",
102 )
103 parser.addini(
104 "python_classes",
105 type="args",
106 default=["Test"],
107 help="prefixes or glob names for Python test class discovery",
108 )
109 parser.addini(
110 "python_functions",
111 type="args",
112 default=["test"],
113 help="prefixes or glob names for Python test function and method discovery",
114 )
115 parser.addini(
116 "disable_test_id_escaping_and_forfeit_all_rights_to_community_support",
117 type="bool",
118 default=False,
119 help="disable string escape non-ascii characters, might cause unwanted "
120 "side effects(use at your own risk)",
121 )
124def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]:
125 if config.option.showfixtures:
126 showfixtures(config)
127 return 0
128 if config.option.show_fixtures_per_test:
129 show_fixtures_per_test(config)
130 return 0
131 return None
134def pytest_generate_tests(metafunc: "Metafunc") -> None:
135 for marker in metafunc.definition.iter_markers(name="parametrize"):
136 # TODO: Fix this type-ignore (overlapping kwargs).
137 metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) # type: ignore[misc]
140def pytest_configure(config: Config) -> None:
141 config.addinivalue_line(
142 "markers",
143 "parametrize(argnames, argvalues): call a test function multiple "
144 "times passing in different arguments in turn. argvalues generally "
145 "needs to be a list of values if argnames specifies only one name "
146 "or a list of tuples of values if argnames specifies multiple names. "
147 "Example: @parametrize('arg1', [1,2]) would lead to two calls of the "
148 "decorated test function, one with arg1=1 and another with arg1=2."
149 "see https://docs.pytest.org/en/stable/parametrize.html for more info "
150 "and examples.",
151 )
152 config.addinivalue_line(
153 "markers",
154 "usefixtures(fixturename1, fixturename2, ...): mark tests as needing "
155 "all of the specified fixtures. see "
156 "https://docs.pytest.org/en/stable/fixture.html#usefixtures ",
157 )
160def async_warn_and_skip(nodeid: str) -> None:
161 msg = "async def functions are not natively supported and have been skipped.\n"
162 msg += (
163 "You need to install a suitable plugin for your async framework, for example:\n"
164 )
165 msg += " - pytest-asyncio\n"
166 msg += " - pytest-trio\n"
167 msg += " - pytest-tornasync\n"
168 msg += " - pytest-twisted"
169 warnings.warn(PytestUnhandledCoroutineWarning(msg.format(nodeid)))
170 skip(msg="async def function and no async plugin installed (see warnings)")
173@hookimpl(trylast=True)
174def pytest_pyfunc_call(pyfuncitem: "Function") -> Optional[object]:
175 testfunction = pyfuncitem.obj
176 if is_async_function(testfunction):
177 async_warn_and_skip(pyfuncitem.nodeid)
178 funcargs = pyfuncitem.funcargs
179 testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames}
180 result = testfunction(**testargs)
181 if hasattr(result, "__await__") or hasattr(result, "__aiter__"):
182 async_warn_and_skip(pyfuncitem.nodeid)
183 return True
186def pytest_collect_file(path: py.path.local, parent) -> Optional["Module"]:
187 ext = path.ext
188 if ext == ".py":
189 if not parent.session.isinitpath(path):
190 if not path_matches_patterns(
191 path, parent.config.getini("python_files") + ["__init__.py"]
192 ):
193 return None
194 ihook = parent.session.gethookproxy(path)
195 module = ihook.pytest_pycollect_makemodule(
196 path=path, parent=parent
197 ) # type: Module
198 return module
199 return None
202def path_matches_patterns(path: py.path.local, patterns: Iterable[str]) -> bool:
203 """Returns True if path matches any of the patterns in the list of globs given."""
204 return any(path.fnmatch(pattern) for pattern in patterns)
207def pytest_pycollect_makemodule(path: py.path.local, parent) -> "Module":
208 if path.basename == "__init__.py":
209 pkg = Package.from_parent(parent, fspath=path) # type: Package
210 return pkg
211 mod = Module.from_parent(parent, fspath=path) # type: Module
212 return mod
215@hookimpl(trylast=True)
216def pytest_pycollect_makeitem(collector: "PyCollector", name: str, obj: object):
217 # nothing was collected elsewhere, let's do it here
218 if safe_isclass(obj):
219 if collector.istestclass(obj, name):
220 return Class.from_parent(collector, name=name, obj=obj)
221 elif collector.istestfunction(obj, name):
222 # mock seems to store unbound methods (issue473), normalize it
223 obj = getattr(obj, "__func__", obj)
224 # We need to try and unwrap the function if it's a functools.partial
225 # or a functools.wrapped.
226 # We mustn't if it's been wrapped with mock.patch (python 2 only)
227 if not (inspect.isfunction(obj) or inspect.isfunction(get_real_func(obj))):
228 filename, lineno = getfslineno(obj)
229 warnings.warn_explicit(
230 message=PytestCollectionWarning(
231 "cannot collect %r because it is not a function." % name
232 ),
233 category=None,
234 filename=str(filename),
235 lineno=lineno + 1,
236 )
237 elif getattr(obj, "__test__", True):
238 if is_generator(obj):
239 res = Function.from_parent(collector, name=name)
240 reason = "yield tests were removed in pytest 4.0 - {name} will be ignored".format(
241 name=name
242 )
243 res.add_marker(MARK_GEN.xfail(run=False, reason=reason))
244 res.warn(PytestCollectionWarning(reason))
245 else:
246 res = list(collector._genfunctions(name, obj))
247 return res
250class PyobjMixin:
251 _ALLOW_MARKERS = True
253 # Function and attributes that the mixin needs (for type-checking only).
254 if TYPE_CHECKING:
255 name = "" # type: str
256 parent = None # type: Optional[nodes.Node]
257 own_markers = [] # type: List[Mark]
259 def getparent(self, cls: Type[nodes._NodeType]) -> Optional[nodes._NodeType]:
260 ...
262 def listchain(self) -> List[nodes.Node]:
263 ...
265 @property
266 def module(self):
267 """Python module object this node was collected from (can be None)."""
268 node = self.getparent(Module)
269 return node.obj if node is not None else None
271 @property
272 def cls(self):
273 """Python class object this node was collected from (can be None)."""
274 node = self.getparent(Class)
275 return node.obj if node is not None else None
277 @property
278 def instance(self):
279 """Python instance object this node was collected from (can be None)."""
280 node = self.getparent(Instance)
281 return node.obj if node is not None else None
283 @property
284 def obj(self):
285 """Underlying Python object."""
286 obj = getattr(self, "_obj", None)
287 if obj is None:
288 self._obj = obj = self._getobj()
289 # XXX evil hack
290 # used to avoid Instance collector marker duplication
291 if self._ALLOW_MARKERS:
292 self.own_markers.extend(get_unpacked_marks(self.obj))
293 return obj
295 @obj.setter
296 def obj(self, value):
297 self._obj = value
299 def _getobj(self):
300 """Gets the underlying Python object. May be overwritten by subclasses."""
301 # TODO: Improve the type of `parent` such that assert/ignore aren't needed.
302 assert self.parent is not None
303 obj = self.parent.obj # type: ignore[attr-defined]
304 return getattr(obj, self.name)
306 def getmodpath(self, stopatmodule: bool = True, includemodule: bool = False) -> str:
307 """ return python path relative to the containing module. """
308 chain = self.listchain()
309 chain.reverse()
310 parts = []
311 for node in chain:
312 if isinstance(node, Instance):
313 continue
314 name = node.name
315 if isinstance(node, Module):
316 name = os.path.splitext(name)[0]
317 if stopatmodule:
318 if includemodule:
319 parts.append(name)
320 break
321 parts.append(name)
322 parts.reverse()
323 return ".".join(parts)
325 def reportinfo(self) -> Tuple[Union[py.path.local, str], int, str]:
326 # XXX caching?
327 obj = self.obj
328 compat_co_firstlineno = getattr(obj, "compat_co_firstlineno", None)
329 if isinstance(compat_co_firstlineno, int):
330 # nose compatibility
331 file_path = sys.modules[obj.__module__].__file__
332 if file_path.endswith(".pyc"):
333 file_path = file_path[:-1]
334 fspath = file_path # type: Union[py.path.local, str]
335 lineno = compat_co_firstlineno
336 else:
337 fspath, lineno = getfslineno(obj)
338 modpath = self.getmodpath()
339 assert isinstance(lineno, int)
340 return fspath, lineno, modpath
343class PyCollector(PyobjMixin, nodes.Collector):
344 def funcnamefilter(self, name: str) -> bool:
345 return self._matches_prefix_or_glob_option("python_functions", name)
347 def isnosetest(self, obj: object) -> bool:
348 """ Look for the __test__ attribute, which is applied by the
349 @nose.tools.istest decorator
350 """
351 # We explicitly check for "is True" here to not mistakenly treat
352 # classes with a custom __getattr__ returning something truthy (like a
353 # function) as test classes.
354 return safe_getattr(obj, "__test__", False) is True
356 def classnamefilter(self, name: str) -> bool:
357 return self._matches_prefix_or_glob_option("python_classes", name)
359 def istestfunction(self, obj: object, name: str) -> bool:
360 if self.funcnamefilter(name) or self.isnosetest(obj):
361 if isinstance(obj, staticmethod):
362 # static methods need to be unwrapped
363 obj = safe_getattr(obj, "__func__", False)
364 return (
365 safe_getattr(obj, "__call__", False)
366 and fixtures.getfixturemarker(obj) is None
367 )
368 else:
369 return False
371 def istestclass(self, obj: object, name: str) -> bool:
372 return self.classnamefilter(name) or self.isnosetest(obj)
374 def _matches_prefix_or_glob_option(self, option_name: str, name: str) -> bool:
375 """
376 checks if the given name matches the prefix or glob-pattern defined
377 in ini configuration.
378 """
379 for option in self.config.getini(option_name):
380 if name.startswith(option):
381 return True
382 # check that name looks like a glob-string before calling fnmatch
383 # because this is called for every name in each collected module,
384 # and fnmatch is somewhat expensive to call
385 elif ("*" in option or "?" in option or "[" in option) and fnmatch.fnmatch(
386 name, option
387 ):
388 return True
389 return False
391 def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]:
392 if not getattr(self.obj, "__test__", True):
393 return []
395 # NB. we avoid random getattrs and peek in the __dict__ instead
396 # (XXX originally introduced from a PyPy need, still true?)
397 dicts = [getattr(self.obj, "__dict__", {})]
398 for basecls in self.obj.__class__.__mro__:
399 dicts.append(basecls.__dict__)
400 seen = set() # type: Set[str]
401 values = [] # type: List[Union[nodes.Item, nodes.Collector]]
402 for dic in dicts:
403 # Note: seems like the dict can change during iteration -
404 # be careful not to remove the list() without consideration.
405 for name, obj in list(dic.items()):
406 if name in seen:
407 continue
408 seen.add(name)
409 res = self._makeitem(name, obj)
410 if res is None:
411 continue
412 if not isinstance(res, list):
413 res = [res]
414 values.extend(res)
416 def sort_key(item):
417 fspath, lineno, _ = item.reportinfo()
418 return (str(fspath), lineno)
420 values.sort(key=sort_key)
421 return values
423 def _makeitem(
424 self, name: str, obj: object
425 ) -> Union[
426 None, nodes.Item, nodes.Collector, List[Union[nodes.Item, nodes.Collector]]
427 ]:
428 # assert self.ihook.fspath == self.fspath, self
429 item = self.ihook.pytest_pycollect_makeitem(
430 collector=self, name=name, obj=obj
431 ) # type: Union[None, nodes.Item, nodes.Collector, List[Union[nodes.Item, nodes.Collector]]]
432 return item
434 def _genfunctions(self, name: str, funcobj) -> Iterator["Function"]:
435 modulecol = self.getparent(Module)
436 assert modulecol is not None
437 module = modulecol.obj
438 clscol = self.getparent(Class)
439 cls = clscol and clscol.obj or None
440 fm = self.session._fixturemanager
442 definition = FunctionDefinition.from_parent(self, name=name, callobj=funcobj)
443 fixtureinfo = definition._fixtureinfo
445 metafunc = Metafunc(
446 definition, fixtureinfo, self.config, cls=cls, module=module
447 )
448 methods = []
449 if hasattr(module, "pytest_generate_tests"):
450 methods.append(module.pytest_generate_tests)
451 if cls is not None and hasattr(cls, "pytest_generate_tests"):
452 methods.append(cls().pytest_generate_tests)
454 self.ihook.pytest_generate_tests.call_extra(methods, dict(metafunc=metafunc))
456 if not metafunc._calls:
457 yield Function.from_parent(self, name=name, fixtureinfo=fixtureinfo)
458 else:
459 # add funcargs() as fixturedefs to fixtureinfo.arg2fixturedefs
460 fixtures.add_funcarg_pseudo_fixture_def(self, metafunc, fm)
462 # add_funcarg_pseudo_fixture_def may have shadowed some fixtures
463 # with direct parametrization, so make sure we update what the
464 # function really needs.
465 fixtureinfo.prune_dependency_tree()
467 for callspec in metafunc._calls:
468 subname = "{}[{}]".format(name, callspec.id)
469 yield Function.from_parent(
470 self,
471 name=subname,
472 callspec=callspec,
473 callobj=funcobj,
474 fixtureinfo=fixtureinfo,
475 keywords={callspec.id: True},
476 originalname=name,
477 )
480class Module(nodes.File, PyCollector):
481 """ Collector for test classes and functions. """
483 def _getobj(self):
484 return self._importtestmodule()
486 def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]:
487 self._inject_setup_module_fixture()
488 self._inject_setup_function_fixture()
489 self.session._fixturemanager.parsefactories(self)
490 return super().collect()
492 def _inject_setup_module_fixture(self) -> None:
493 """Injects a hidden autouse, module scoped fixture into the collected module object
494 that invokes setUpModule/tearDownModule if either or both are available.
496 Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with
497 other fixtures (#517).
498 """
499 setup_module = _get_first_non_fixture_func(
500 self.obj, ("setUpModule", "setup_module")
501 )
502 teardown_module = _get_first_non_fixture_func(
503 self.obj, ("tearDownModule", "teardown_module")
504 )
506 if setup_module is None and teardown_module is None:
507 return
509 @fixtures.fixture(autouse=True, scope="module")
510 def xunit_setup_module_fixture(request) -> Generator[None, None, None]:
511 if setup_module is not None:
512 _call_with_optional_argument(setup_module, request.module)
513 yield
514 if teardown_module is not None:
515 _call_with_optional_argument(teardown_module, request.module)
517 self.obj.__pytest_setup_module = xunit_setup_module_fixture
519 def _inject_setup_function_fixture(self) -> None:
520 """Injects a hidden autouse, function scoped fixture into the collected module object
521 that invokes setup_function/teardown_function if either or both are available.
523 Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with
524 other fixtures (#517).
525 """
526 setup_function = _get_first_non_fixture_func(self.obj, ("setup_function",))
527 teardown_function = _get_first_non_fixture_func(
528 self.obj, ("teardown_function",)
529 )
530 if setup_function is None and teardown_function is None:
531 return
533 @fixtures.fixture(autouse=True, scope="function")
534 def xunit_setup_function_fixture(request) -> Generator[None, None, None]:
535 if request.instance is not None:
536 # in this case we are bound to an instance, so we need to let
537 # setup_method handle this
538 yield
539 return
540 if setup_function is not None:
541 _call_with_optional_argument(setup_function, request.function)
542 yield
543 if teardown_function is not None:
544 _call_with_optional_argument(teardown_function, request.function)
546 self.obj.__pytest_setup_function = xunit_setup_function_fixture
548 def _importtestmodule(self):
549 # we assume we are only called once per module
550 importmode = self.config.getoption("--import-mode")
551 try:
552 mod = import_path(self.fspath, mode=importmode)
553 except SyntaxError as e:
554 raise self.CollectError(
555 ExceptionInfo.from_current().getrepr(style="short")
556 ) from e
557 except ImportPathMismatchError as e:
558 raise self.CollectError(
559 "import file mismatch:\n"
560 "imported module %r has this __file__ attribute:\n"
561 " %s\n"
562 "which is not the same as the test file we want to collect:\n"
563 " %s\n"
564 "HINT: remove __pycache__ / .pyc files and/or use a "
565 "unique basename for your test file modules" % e.args
566 ) from e
567 except ImportError as e:
568 exc_info = ExceptionInfo.from_current()
569 if self.config.getoption("verbose") < 2:
570 exc_info.traceback = exc_info.traceback.filter(filter_traceback)
571 exc_repr = (
572 exc_info.getrepr(style="short")
573 if exc_info.traceback
574 else exc_info.exconly()
575 )
576 formatted_tb = str(exc_repr)
577 raise self.CollectError(
578 "ImportError while importing test module '{fspath}'.\n"
579 "Hint: make sure your test modules/packages have valid Python names.\n"
580 "Traceback:\n"
581 "{traceback}".format(fspath=self.fspath, traceback=formatted_tb)
582 ) from e
583 except _pytest.runner.Skipped as e:
584 if e.allow_module_level:
585 raise
586 raise self.CollectError(
587 "Using pytest.skip outside of a test is not allowed. "
588 "To decorate a test function, use the @pytest.mark.skip "
589 "or @pytest.mark.skipif decorators instead, and to skip a "
590 "module use `pytestmark = pytest.mark.{skip,skipif}."
591 ) from e
592 self.config.pluginmanager.consider_module(mod)
593 return mod
596class Package(Module):
597 def __init__(
598 self,
599 fspath: py.path.local,
600 parent: nodes.Collector,
601 # NOTE: following args are unused:
602 config=None,
603 session=None,
604 nodeid=None,
605 ) -> None:
606 # NOTE: could be just the following, but kept as-is for compat.
607 # nodes.FSCollector.__init__(self, fspath, parent=parent)
608 session = parent.session
609 nodes.FSCollector.__init__(
610 self, fspath, parent=parent, config=config, session=session, nodeid=nodeid
611 )
612 self.name = os.path.basename(str(fspath.dirname))
614 def setup(self) -> None:
615 # not using fixtures to call setup_module here because autouse fixtures
616 # from packages are not called automatically (#4085)
617 setup_module = _get_first_non_fixture_func(
618 self.obj, ("setUpModule", "setup_module")
619 )
620 if setup_module is not None:
621 _call_with_optional_argument(setup_module, self.obj)
623 teardown_module = _get_first_non_fixture_func(
624 self.obj, ("tearDownModule", "teardown_module")
625 )
626 if teardown_module is not None:
627 func = partial(_call_with_optional_argument, teardown_module, self.obj)
628 self.addfinalizer(func)
630 def gethookproxy(self, fspath: py.path.local):
631 return super()._gethookproxy(fspath)
633 def isinitpath(self, path: py.path.local) -> bool:
634 return path in self.session._initialpaths
636 def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]:
637 this_path = self.fspath.dirpath()
638 init_module = this_path.join("__init__.py")
639 if init_module.check(file=1) and path_matches_patterns(
640 init_module, self.config.getini("python_files")
641 ):
642 yield Module.from_parent(self, fspath=init_module)
643 pkg_prefixes = set() # type: Set[py.path.local]
644 for path in this_path.visit(rec=self._recurse, bf=True, sort=True):
645 # We will visit our own __init__.py file, in which case we skip it.
646 is_file = path.isfile()
647 if is_file:
648 if path.basename == "__init__.py" and path.dirpath() == this_path:
649 continue
651 parts_ = parts(path.strpath)
652 if any(
653 str(pkg_prefix) in parts_ and pkg_prefix.join("__init__.py") != path
654 for pkg_prefix in pkg_prefixes
655 ):
656 continue
658 if is_file:
659 yield from self._collectfile(path)
660 elif not path.isdir():
661 # Broken symlink or invalid/missing file.
662 continue
663 elif path.join("__init__.py").check(file=1):
664 pkg_prefixes.add(path)
667def _call_with_optional_argument(func, arg) -> None:
668 """Call the given function with the given argument if func accepts one argument, otherwise
669 calls func without arguments"""
670 arg_count = func.__code__.co_argcount
671 if inspect.ismethod(func):
672 arg_count -= 1
673 if arg_count:
674 func(arg)
675 else:
676 func()
679def _get_first_non_fixture_func(obj: object, names: Iterable[str]):
680 """Return the attribute from the given object to be used as a setup/teardown
681 xunit-style function, but only if not marked as a fixture to
682 avoid calling it twice.
683 """
684 for name in names:
685 meth = getattr(obj, name, None)
686 if meth is not None and fixtures.getfixturemarker(meth) is None:
687 return meth
690class Class(PyCollector):
691 """ Collector for test methods. """
693 @classmethod
694 def from_parent(cls, parent, *, name, obj=None):
695 """
696 The public constructor
697 """
698 return super().from_parent(name=name, parent=parent)
700 def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]:
701 if not safe_getattr(self.obj, "__test__", True):
702 return []
703 if hasinit(self.obj):
704 assert self.parent is not None
705 self.warn(
706 PytestCollectionWarning(
707 "cannot collect test class %r because it has a "
708 "__init__ constructor (from: %s)"
709 % (self.obj.__name__, self.parent.nodeid)
710 )
711 )
712 return []
713 elif hasnew(self.obj):
714 assert self.parent is not None
715 self.warn(
716 PytestCollectionWarning(
717 "cannot collect test class %r because it has a "
718 "__new__ constructor (from: %s)"
719 % (self.obj.__name__, self.parent.nodeid)
720 )
721 )
722 return []
724 self._inject_setup_class_fixture()
725 self._inject_setup_method_fixture()
727 return [Instance.from_parent(self, name="()")]
729 def _inject_setup_class_fixture(self) -> None:
730 """Injects a hidden autouse, class scoped fixture into the collected class object
731 that invokes setup_class/teardown_class if either or both are available.
733 Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with
734 other fixtures (#517).
735 """
736 setup_class = _get_first_non_fixture_func(self.obj, ("setup_class",))
737 teardown_class = getattr(self.obj, "teardown_class", None)
738 if setup_class is None and teardown_class is None:
739 return
741 @fixtures.fixture(autouse=True, scope="class")
742 def xunit_setup_class_fixture(cls) -> Generator[None, None, None]:
743 if setup_class is not None:
744 func = getimfunc(setup_class)
745 _call_with_optional_argument(func, self.obj)
746 yield
747 if teardown_class is not None:
748 func = getimfunc(teardown_class)
749 _call_with_optional_argument(func, self.obj)
751 self.obj.__pytest_setup_class = xunit_setup_class_fixture
753 def _inject_setup_method_fixture(self) -> None:
754 """Injects a hidden autouse, function scoped fixture into the collected class object
755 that invokes setup_method/teardown_method if either or both are available.
757 Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with
758 other fixtures (#517).
759 """
760 setup_method = _get_first_non_fixture_func(self.obj, ("setup_method",))
761 teardown_method = getattr(self.obj, "teardown_method", None)
762 if setup_method is None and teardown_method is None:
763 return
765 @fixtures.fixture(autouse=True, scope="function")
766 def xunit_setup_method_fixture(self, request) -> Generator[None, None, None]:
767 method = request.function
768 if setup_method is not None:
769 func = getattr(self, "setup_method")
770 _call_with_optional_argument(func, method)
771 yield
772 if teardown_method is not None:
773 func = getattr(self, "teardown_method")
774 _call_with_optional_argument(func, method)
776 self.obj.__pytest_setup_method = xunit_setup_method_fixture
779class Instance(PyCollector):
780 _ALLOW_MARKERS = False # hack, destroy later
781 # instances share the object with their parents in a way
782 # that duplicates markers instances if not taken out
783 # can be removed at node structure reorganization time
785 def _getobj(self):
786 # TODO: Improve the type of `parent` such that assert/ignore aren't needed.
787 assert self.parent is not None
788 obj = self.parent.obj # type: ignore[attr-defined]
789 return obj()
791 def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]:
792 self.session._fixturemanager.parsefactories(self)
793 return super().collect()
795 def newinstance(self):
796 self.obj = self._getobj()
797 return self.obj
800def hasinit(obj: object) -> bool:
801 init = getattr(obj, "__init__", None) # type: object
802 if init:
803 return init != object.__init__
804 return False
807def hasnew(obj: object) -> bool:
808 new = getattr(obj, "__new__", None) # type: object
809 if new:
810 return new != object.__new__
811 return False
814class CallSpec2:
815 def __init__(self, metafunc: "Metafunc") -> None:
816 self.metafunc = metafunc
817 self.funcargs = {} # type: Dict[str, object]
818 self._idlist = [] # type: List[str]
819 self.params = {} # type: Dict[str, object]
820 # Used for sorting parametrized resources.
821 self._arg2scopenum = {} # type: Dict[str, int]
822 self.marks = [] # type: List[Mark]
823 self.indices = {} # type: Dict[str, int]
825 def copy(self) -> "CallSpec2":
826 cs = CallSpec2(self.metafunc)
827 cs.funcargs.update(self.funcargs)
828 cs.params.update(self.params)
829 cs.marks.extend(self.marks)
830 cs.indices.update(self.indices)
831 cs._arg2scopenum.update(self._arg2scopenum)
832 cs._idlist = list(self._idlist)
833 return cs
835 def _checkargnotcontained(self, arg: str) -> None:
836 if arg in self.params or arg in self.funcargs:
837 raise ValueError("duplicate {!r}".format(arg))
839 def getparam(self, name: str) -> object:
840 try:
841 return self.params[name]
842 except KeyError as e:
843 raise ValueError(name) from e
845 @property
846 def id(self) -> str:
847 return "-".join(map(str, self._idlist))
849 def setmulti2(
850 self,
851 valtypes: Mapping[str, "Literal['params', 'funcargs']"],
852 argnames: typing.Sequence[str],
853 valset: Iterable[object],
854 id: str,
855 marks: Iterable[Union[Mark, MarkDecorator]],
856 scopenum: int,
857 param_index: int,
858 ) -> None:
859 for arg, val in zip(argnames, valset):
860 self._checkargnotcontained(arg)
861 valtype_for_arg = valtypes[arg]
862 if valtype_for_arg == "params":
863 self.params[arg] = val
864 elif valtype_for_arg == "funcargs":
865 self.funcargs[arg] = val
866 else: # pragma: no cover
867 assert False, "Unhandled valtype for arg: {}".format(valtype_for_arg)
868 self.indices[arg] = param_index
869 self._arg2scopenum[arg] = scopenum
870 self._idlist.append(id)
871 self.marks.extend(normalize_mark_list(marks))
874class Metafunc:
875 """
876 Metafunc objects are passed to the :func:`pytest_generate_tests <_pytest.hookspec.pytest_generate_tests>` hook.
877 They help to inspect a test function and to generate tests according to
878 test configuration or values specified in the class or module where a
879 test function is defined.
880 """
882 def __init__(
883 self,
884 definition: "FunctionDefinition",
885 fixtureinfo: fixtures.FuncFixtureInfo,
886 config: Config,
887 cls=None,
888 module=None,
889 ) -> None:
890 self.definition = definition
892 #: access to the :class:`_pytest.config.Config` object for the test session
893 self.config = config
895 #: the module object where the test function is defined in.
896 self.module = module
898 #: underlying python test function
899 self.function = definition.obj
901 #: set of fixture names required by the test function
902 self.fixturenames = fixtureinfo.names_closure
904 #: class object where the test function is defined in or ``None``.
905 self.cls = cls
907 self._calls = [] # type: List[CallSpec2]
908 self._arg2fixturedefs = fixtureinfo.name2fixturedefs
910 @property
911 def funcargnames(self) -> List[str]:
912 """ alias attribute for ``fixturenames`` for pre-2.3 compatibility"""
913 warnings.warn(FUNCARGNAMES, stacklevel=2)
914 return self.fixturenames
916 def parametrize(
917 self,
918 argnames: Union[str, List[str], Tuple[str, ...]],
919 argvalues: Iterable[Union[ParameterSet, typing.Sequence[object], object]],
920 indirect: Union[bool, typing.Sequence[str]] = False,
921 ids: Optional[
922 Union[
923 Iterable[Union[None, str, float, int, bool]],
924 Callable[[Any], Optional[object]],
925 ]
926 ] = None,
927 scope: "Optional[_Scope]" = None,
928 *,
929 _param_mark: Optional[Mark] = None
930 ) -> None:
931 """ Add new invocations to the underlying test function using the list
932 of argvalues for the given argnames. Parametrization is performed
933 during the collection phase. If you need to setup expensive resources
934 see about setting indirect to do it rather at test setup time.
936 :arg argnames: a comma-separated string denoting one or more argument
937 names, or a list/tuple of argument strings.
939 :arg argvalues: The list of argvalues determines how often a
940 test is invoked with different argument values. If only one
941 argname was specified argvalues is a list of values. If N
942 argnames were specified, argvalues must be a list of N-tuples,
943 where each tuple-element specifies a value for its respective
944 argname.
946 :arg indirect: The list of argnames or boolean. A list of arguments'
947 names (subset of argnames). If True the list contains all names from
948 the argnames. Each argvalue corresponding to an argname in this list will
949 be passed as request.param to its respective argname fixture
950 function so that it can perform more expensive setups during the
951 setup phase of a test rather than at collection time.
953 :arg ids: sequence of (or generator for) ids for ``argvalues``,
954 or a callable to return part of the id for each argvalue.
956 With sequences (and generators like ``itertools.count()``) the
957 returned ids should be of type ``string``, ``int``, ``float``,
958 ``bool``, or ``None``.
959 They are mapped to the corresponding index in ``argvalues``.
960 ``None`` means to use the auto-generated id.
962 If it is a callable it will be called for each entry in
963 ``argvalues``, and the return value is used as part of the
964 auto-generated id for the whole set (where parts are joined with
965 dashes ("-")).
966 This is useful to provide more specific ids for certain items, e.g.
967 dates. Returning ``None`` will use an auto-generated id.
969 If no ids are provided they will be generated automatically from
970 the argvalues.
972 :arg scope: if specified it denotes the scope of the parameters.
973 The scope is used for grouping tests by parameter instances.
974 It will also override any fixture-function defined scope, allowing
975 to set a dynamic scope using test context or configuration.
976 """
977 from _pytest.fixtures import scope2index
979 argnames, parameters = ParameterSet._for_parametrize(
980 argnames,
981 argvalues,
982 self.function,
983 self.config,
984 nodeid=self.definition.nodeid,
985 )
986 del argvalues
988 if "request" in argnames:
989 fail(
990 "'request' is a reserved name and cannot be used in @pytest.mark.parametrize",
991 pytrace=False,
992 )
994 if scope is None:
995 scope = _find_parametrized_scope(argnames, self._arg2fixturedefs, indirect)
997 self._validate_if_using_arg_names(argnames, indirect)
999 arg_values_types = self._resolve_arg_value_types(argnames, indirect)
1001 # Use any already (possibly) generated ids with parametrize Marks.
1002 if _param_mark and _param_mark._param_ids_from:
1003 generated_ids = _param_mark._param_ids_from._param_ids_generated
1004 if generated_ids is not None:
1005 ids = generated_ids
1007 ids = self._resolve_arg_ids(
1008 argnames, ids, parameters, nodeid=self.definition.nodeid
1009 )
1011 # Store used (possibly generated) ids with parametrize Marks.
1012 if _param_mark and _param_mark._param_ids_from and generated_ids is None:
1013 object.__setattr__(_param_mark._param_ids_from, "_param_ids_generated", ids)
1015 scopenum = scope2index(
1016 scope, descr="parametrize() call in {}".format(self.function.__name__)
1017 )
1019 # create the new calls: if we are parametrize() multiple times (by applying the decorator
1020 # more than once) then we accumulate those calls generating the cartesian product
1021 # of all calls
1022 newcalls = []
1023 for callspec in self._calls or [CallSpec2(self)]:
1024 for param_index, (param_id, param_set) in enumerate(zip(ids, parameters)):
1025 newcallspec = callspec.copy()
1026 newcallspec.setmulti2(
1027 arg_values_types,
1028 argnames,
1029 param_set.values,
1030 param_id,
1031 param_set.marks,
1032 scopenum,
1033 param_index,
1034 )
1035 newcalls.append(newcallspec)
1036 self._calls = newcalls
1038 def _resolve_arg_ids(
1039 self,
1040 argnames: typing.Sequence[str],
1041 ids: Optional[
1042 Union[
1043 Iterable[Union[None, str, float, int, bool]],
1044 Callable[[Any], Optional[object]],
1045 ]
1046 ],
1047 parameters: typing.Sequence[ParameterSet],
1048 nodeid: str,
1049 ) -> List[str]:
1050 """Resolves the actual ids for the given argnames, based on the ``ids`` parameter given
1051 to ``parametrize``.
1053 :param List[str] argnames: list of argument names passed to ``parametrize()``.
1054 :param ids: the ids parameter of the parametrized call (see docs).
1055 :param List[ParameterSet] parameters: the list of parameter values, same size as ``argnames``.
1056 :param str str: the nodeid of the item that generated this parametrized call.
1057 :rtype: List[str]
1058 :return: the list of ids for each argname given
1059 """
1060 if ids is None:
1061 idfn = None
1062 ids_ = None
1063 elif callable(ids):
1064 idfn = ids
1065 ids_ = None
1066 else:
1067 idfn = None
1068 ids_ = self._validate_ids(ids, parameters, self.function.__name__)
1069 return idmaker(argnames, parameters, idfn, ids_, self.config, nodeid=nodeid)
1071 def _validate_ids(
1072 self,
1073 ids: Iterable[Union[None, str, float, int, bool]],
1074 parameters: typing.Sequence[ParameterSet],
1075 func_name: str,
1076 ) -> List[Union[None, str]]:
1077 try:
1078 num_ids = len(ids) # type: ignore[arg-type]
1079 except TypeError:
1080 try:
1081 iter(ids)
1082 except TypeError as e:
1083 raise TypeError("ids must be a callable or an iterable") from e
1084 num_ids = len(parameters)
1086 # num_ids == 0 is a special case: https://github.com/pytest-dev/pytest/issues/1849
1087 if num_ids != len(parameters) and num_ids != 0:
1088 msg = "In {}: {} parameter sets specified, with different number of ids: {}"
1089 fail(msg.format(func_name, len(parameters), num_ids), pytrace=False)
1091 new_ids = []
1092 for idx, id_value in enumerate(itertools.islice(ids, num_ids)):
1093 if id_value is None or isinstance(id_value, str):
1094 new_ids.append(id_value)
1095 elif isinstance(id_value, (float, int, bool)):
1096 new_ids.append(str(id_value))
1097 else:
1098 msg = "In {}: ids must be list of string/float/int/bool, found: {} (type: {!r}) at index {}"
1099 fail(
1100 msg.format(func_name, saferepr(id_value), type(id_value), idx),
1101 pytrace=False,
1102 )
1103 return new_ids
1105 def _resolve_arg_value_types(
1106 self,
1107 argnames: typing.Sequence[str],
1108 indirect: Union[bool, typing.Sequence[str]],
1109 ) -> Dict[str, "Literal['params', 'funcargs']"]:
1110 """Resolves if each parametrized argument must be considered a parameter to a fixture or a "funcarg"
1111 to the function, based on the ``indirect`` parameter of the parametrized() call.
1113 :param List[str] argnames: list of argument names passed to ``parametrize()``.
1114 :param indirect: same ``indirect`` parameter of ``parametrize()``.
1115 :rtype: Dict[str, str]
1116 A dict mapping each arg name to either:
1117 * "params" if the argname should be the parameter of a fixture of the same name.
1118 * "funcargs" if the argname should be a parameter to the parametrized test function.
1119 """
1120 if isinstance(indirect, bool):
1121 valtypes = dict.fromkeys(
1122 argnames, "params" if indirect else "funcargs"
1123 ) # type: Dict[str, Literal["params", "funcargs"]]
1124 elif isinstance(indirect, Sequence):
1125 valtypes = dict.fromkeys(argnames, "funcargs")
1126 for arg in indirect:
1127 if arg not in argnames:
1128 fail(
1129 "In {}: indirect fixture '{}' doesn't exist".format(
1130 self.function.__name__, arg
1131 ),
1132 pytrace=False,
1133 )
1134 valtypes[arg] = "params"
1135 else:
1136 fail(
1137 "In {func}: expected Sequence or boolean for indirect, got {type}".format(
1138 type=type(indirect).__name__, func=self.function.__name__
1139 ),
1140 pytrace=False,
1141 )
1142 return valtypes
1144 def _validate_if_using_arg_names(
1145 self,
1146 argnames: typing.Sequence[str],
1147 indirect: Union[bool, typing.Sequence[str]],
1148 ) -> None:
1149 """
1150 Check if all argnames are being used, by default values, or directly/indirectly.
1152 :param List[str] argnames: list of argument names passed to ``parametrize()``.
1153 :param indirect: same ``indirect`` parameter of ``parametrize()``.
1154 :raise ValueError: if validation fails.
1155 """
1156 default_arg_names = set(get_default_arg_names(self.function))
1157 func_name = self.function.__name__
1158 for arg in argnames:
1159 if arg not in self.fixturenames:
1160 if arg in default_arg_names:
1161 fail(
1162 "In {}: function already takes an argument '{}' with a default value".format(
1163 func_name, arg
1164 ),
1165 pytrace=False,
1166 )
1167 else:
1168 if isinstance(indirect, Sequence):
1169 name = "fixture" if arg in indirect else "argument"
1170 else:
1171 name = "fixture" if indirect else "argument"
1172 fail(
1173 "In {}: function uses no {} '{}'".format(func_name, name, arg),
1174 pytrace=False,
1175 )
1178def _find_parametrized_scope(
1179 argnames: typing.Sequence[str],
1180 arg2fixturedefs: Mapping[str, typing.Sequence[fixtures.FixtureDef]],
1181 indirect: Union[bool, typing.Sequence[str]],
1182) -> "fixtures._Scope":
1183 """Find the most appropriate scope for a parametrized call based on its arguments.
1185 When there's at least one direct argument, always use "function" scope.
1187 When a test function is parametrized and all its arguments are indirect
1188 (e.g. fixtures), return the most narrow scope based on the fixtures used.
1190 Related to issue #1832, based on code posted by @Kingdread.
1191 """
1192 if isinstance(indirect, Sequence):
1193 all_arguments_are_fixtures = len(indirect) == len(argnames)
1194 else:
1195 all_arguments_are_fixtures = bool(indirect)
1197 if all_arguments_are_fixtures:
1198 fixturedefs = arg2fixturedefs or {}
1199 used_scopes = [
1200 fixturedef[0].scope
1201 for name, fixturedef in fixturedefs.items()
1202 if name in argnames
1203 ]
1204 if used_scopes:
1205 # Takes the most narrow scope from used fixtures
1206 for scope in reversed(fixtures.scopes):
1207 if scope in used_scopes:
1208 return scope
1210 return "function"
1213def _ascii_escaped_by_config(val: Union[str, bytes], config: Optional[Config]) -> str:
1214 if config is None:
1215 escape_option = False
1216 else:
1217 escape_option = config.getini(
1218 "disable_test_id_escaping_and_forfeit_all_rights_to_community_support"
1219 )
1220 # TODO: If escaping is turned off and the user passes bytes,
1221 # will return a bytes. For now we ignore this but the
1222 # code *probably* doesn't handle this case.
1223 return val if escape_option else ascii_escaped(val) # type: ignore
1226def _idval(
1227 val: object,
1228 argname: str,
1229 idx: int,
1230 idfn: Optional[Callable[[Any], Optional[object]]],
1231 nodeid: Optional[str],
1232 config: Optional[Config],
1233) -> str:
1234 if idfn:
1235 try:
1236 generated_id = idfn(val)
1237 if generated_id is not None:
1238 val = generated_id
1239 except Exception as e:
1240 prefix = "{}: ".format(nodeid) if nodeid is not None else ""
1241 msg = "error raised while trying to determine id of parameter '{}' at position {}"
1242 msg = prefix + msg.format(argname, idx)
1243 raise ValueError(msg) from e
1244 elif config:
1245 hook_id = config.hook.pytest_make_parametrize_id(
1246 config=config, val=val, argname=argname
1247 ) # type: Optional[str]
1248 if hook_id:
1249 return hook_id
1251 if isinstance(val, STRING_TYPES):
1252 return _ascii_escaped_by_config(val, config)
1253 elif val is None or isinstance(val, (float, int, bool)):
1254 return str(val)
1255 elif isinstance(val, REGEX_TYPE):
1256 return ascii_escaped(val.pattern)
1257 elif val is NOTSET:
1258 # Fallback to default. Note that NOTSET is an enum.Enum.
1259 pass
1260 elif isinstance(val, enum.Enum):
1261 return str(val)
1262 elif isinstance(getattr(val, "__name__", None), str):
1263 # name of a class, function, module, etc.
1264 name = getattr(val, "__name__") # type: str
1265 return name
1266 return str(argname) + str(idx)
1269def _idvalset(
1270 idx: int,
1271 parameterset: ParameterSet,
1272 argnames: Iterable[str],
1273 idfn: Optional[Callable[[Any], Optional[object]]],
1274 ids: Optional[List[Union[None, str]]],
1275 nodeid: Optional[str],
1276 config: Optional[Config],
1277) -> str:
1278 if parameterset.id is not None:
1279 return parameterset.id
1280 id = None if ids is None or idx >= len(ids) else ids[idx]
1281 if id is None:
1282 this_id = [
1283 _idval(val, argname, idx, idfn, nodeid=nodeid, config=config)
1284 for val, argname in zip(parameterset.values, argnames)
1285 ]
1286 return "-".join(this_id)
1287 else:
1288 return _ascii_escaped_by_config(id, config)
1291def idmaker(
1292 argnames: Iterable[str],
1293 parametersets: Iterable[ParameterSet],
1294 idfn: Optional[Callable[[Any], Optional[object]]] = None,
1295 ids: Optional[List[Union[None, str]]] = None,
1296 config: Optional[Config] = None,
1297 nodeid: Optional[str] = None,
1298) -> List[str]:
1299 resolved_ids = [
1300 _idvalset(
1301 valindex, parameterset, argnames, idfn, ids, config=config, nodeid=nodeid
1302 )
1303 for valindex, parameterset in enumerate(parametersets)
1304 ]
1306 # All IDs must be unique!
1307 unique_ids = set(resolved_ids)
1308 if len(unique_ids) != len(resolved_ids):
1310 # Record the number of occurrences of each test ID
1311 test_id_counts = Counter(resolved_ids)
1313 # Map the test ID to its next suffix
1314 test_id_suffixes = defaultdict(int) # type: Dict[str, int]
1316 # Suffix non-unique IDs to make them unique
1317 for index, test_id in enumerate(resolved_ids):
1318 if test_id_counts[test_id] > 1:
1319 resolved_ids[index] = "{}{}".format(test_id, test_id_suffixes[test_id])
1320 test_id_suffixes[test_id] += 1
1322 return resolved_ids
1325def show_fixtures_per_test(config):
1326 from _pytest.main import wrap_session
1328 return wrap_session(config, _show_fixtures_per_test)
1331def _show_fixtures_per_test(config: Config, session: Session) -> None:
1332 import _pytest.config
1334 session.perform_collect()
1335 curdir = py.path.local()
1336 tw = _pytest.config.create_terminal_writer(config)
1337 verbose = config.getvalue("verbose")
1339 def get_best_relpath(func):
1340 loc = getlocation(func, curdir)
1341 return curdir.bestrelpath(py.path.local(loc))
1343 def write_fixture(fixture_def: fixtures.FixtureDef[object]) -> None:
1344 argname = fixture_def.argname
1345 if verbose <= 0 and argname.startswith("_"):
1346 return
1347 if verbose > 0:
1348 bestrel = get_best_relpath(fixture_def.func)
1349 funcargspec = "{} -- {}".format(argname, bestrel)
1350 else:
1351 funcargspec = argname
1352 tw.line(funcargspec, green=True)
1353 fixture_doc = inspect.getdoc(fixture_def.func)
1354 if fixture_doc:
1355 write_docstring(tw, fixture_doc)
1356 else:
1357 tw.line(" no docstring available", red=True)
1359 def write_item(item: nodes.Item) -> None:
1360 # Not all items have _fixtureinfo attribute.
1361 info = getattr(item, "_fixtureinfo", None) # type: Optional[FuncFixtureInfo]
1362 if info is None or not info.name2fixturedefs:
1363 # This test item does not use any fixtures.
1364 return
1365 tw.line()
1366 tw.sep("-", "fixtures used by {}".format(item.name))
1367 # TODO: Fix this type ignore.
1368 tw.sep("-", "({})".format(get_best_relpath(item.function))) # type: ignore[attr-defined]
1369 # dict key not used in loop but needed for sorting
1370 for _, fixturedefs in sorted(info.name2fixturedefs.items()):
1371 assert fixturedefs is not None
1372 if not fixturedefs:
1373 continue
1374 # last item is expected to be the one used by the test item
1375 write_fixture(fixturedefs[-1])
1377 for session_item in session.items:
1378 write_item(session_item)
1381def showfixtures(config: Config) -> Union[int, ExitCode]:
1382 from _pytest.main import wrap_session
1384 return wrap_session(config, _showfixtures_main)
1387def _showfixtures_main(config: Config, session: Session) -> None:
1388 import _pytest.config
1390 session.perform_collect()
1391 curdir = py.path.local()
1392 tw = _pytest.config.create_terminal_writer(config)
1393 verbose = config.getvalue("verbose")
1395 fm = session._fixturemanager
1397 available = []
1398 seen = set() # type: Set[Tuple[str, str]]
1400 for argname, fixturedefs in fm._arg2fixturedefs.items():
1401 assert fixturedefs is not None
1402 if not fixturedefs:
1403 continue
1404 for fixturedef in fixturedefs:
1405 loc = getlocation(fixturedef.func, curdir)
1406 if (fixturedef.argname, loc) in seen:
1407 continue
1408 seen.add((fixturedef.argname, loc))
1409 available.append(
1410 (
1411 len(fixturedef.baseid),
1412 fixturedef.func.__module__,
1413 curdir.bestrelpath(py.path.local(loc)),
1414 fixturedef.argname,
1415 fixturedef,
1416 )
1417 )
1419 available.sort()
1420 currentmodule = None
1421 for baseid, module, bestrel, argname, fixturedef in available:
1422 if currentmodule != module:
1423 if not module.startswith("_pytest."):
1424 tw.line()
1425 tw.sep("-", "fixtures defined from {}".format(module))
1426 currentmodule = module
1427 if verbose <= 0 and argname[0] == "_":
1428 continue
1429 tw.write(argname, green=True)
1430 if fixturedef.scope != "function":
1431 tw.write(" [%s scope]" % fixturedef.scope, cyan=True)
1432 if verbose > 0:
1433 tw.write(" -- %s" % bestrel, yellow=True)
1434 tw.write("\n")
1435 loc = getlocation(fixturedef.func, curdir)
1436 doc = inspect.getdoc(fixturedef.func)
1437 if doc:
1438 write_docstring(tw, doc)
1439 else:
1440 tw.line(" {}: no docstring available".format(loc), red=True)
1441 tw.line()
1444def write_docstring(tw: TerminalWriter, doc: str, indent: str = " ") -> None:
1445 for line in doc.split("\n"):
1446 tw.line(indent + line)
1449class Function(PyobjMixin, nodes.Item):
1450 """ a Function Item is responsible for setting up and executing a
1451 Python test function.
1452 """
1454 # disable since functions handle it themselves
1455 _ALLOW_MARKERS = False
1457 def __init__(
1458 self,
1459 name: str,
1460 parent,
1461 config: Optional[Config] = None,
1462 callspec: Optional[CallSpec2] = None,
1463 callobj=NOTSET,
1464 keywords=None,
1465 session: Optional[Session] = None,
1466 fixtureinfo: Optional[FuncFixtureInfo] = None,
1467 originalname: Optional[str] = None,
1468 ) -> None:
1469 """
1470 param name: the full function name, including any decorations like those
1471 added by parametrization (``my_func[my_param]``).
1472 param parent: the parent Node.
1473 param config: the pytest Config object
1474 param callspec: if given, this is function has been parametrized and the callspec contains
1475 meta information about the parametrization.
1476 param callobj: if given, the object which will be called when the Function is invoked,
1477 otherwise the callobj will be obtained from ``parent`` using ``originalname``
1478 param keywords: keywords bound to the function object for "-k" matching.
1479 param session: the pytest Session object
1480 param fixtureinfo: fixture information already resolved at this fixture node.
1481 param originalname:
1482 The attribute name to use for accessing the underlying function object.
1483 Defaults to ``name``. Set this if name is different from the original name,
1484 for example when it contains decorations like those added by parametrization
1485 (``my_func[my_param]``).
1486 """
1487 super().__init__(name, parent, config=config, session=session)
1489 if callobj is not NOTSET:
1490 self.obj = callobj
1492 #: Original function name, without any decorations (for example
1493 #: parametrization adds a ``"[...]"`` suffix to function names), used to access
1494 #: the underlying function object from ``parent`` (in case ``callobj`` is not given
1495 #: explicitly).
1496 #:
1497 #: .. versionadded:: 3.0
1498 self.originalname = originalname or name
1500 # note: when FunctionDefinition is introduced, we should change ``originalname``
1501 # to a readonly property that returns FunctionDefinition.name
1503 self.keywords.update(self.obj.__dict__)
1504 self.own_markers.extend(get_unpacked_marks(self.obj))
1505 if callspec:
1506 self.callspec = callspec
1507 # this is total hostile and a mess
1508 # keywords are broken by design by now
1509 # this will be redeemed later
1510 for mark in callspec.marks:
1511 # feel free to cry, this was broken for years before
1512 # and keywords cant fix it per design
1513 self.keywords[mark.name] = mark
1514 self.own_markers.extend(normalize_mark_list(callspec.marks))
1515 if keywords:
1516 self.keywords.update(keywords)
1518 # todo: this is a hell of a hack
1519 # https://github.com/pytest-dev/pytest/issues/4569
1521 self.keywords.update(
1522 {
1523 mark.name: True
1524 for mark in self.iter_markers()
1525 if mark.name not in self.keywords
1526 }
1527 )
1529 if fixtureinfo is None:
1530 fixtureinfo = self.session._fixturemanager.getfixtureinfo(
1531 self, self.obj, self.cls, funcargs=True
1532 )
1533 self._fixtureinfo = fixtureinfo # type: FuncFixtureInfo
1534 self.fixturenames = fixtureinfo.names_closure
1535 self._initrequest()
1537 @classmethod
1538 def from_parent(cls, parent, **kw): # todo: determine sound type limitations
1539 """
1540 The public constructor
1541 """
1542 return super().from_parent(parent=parent, **kw)
1544 def _initrequest(self) -> None:
1545 self.funcargs = {} # type: Dict[str, object]
1546 self._request = fixtures.FixtureRequest(self)
1548 @property
1549 def function(self):
1550 "underlying python 'function' object"
1551 return getimfunc(self.obj)
1553 def _getobj(self):
1554 assert self.parent is not None
1555 return getattr(self.parent.obj, self.originalname) # type: ignore[attr-defined]
1557 @property
1558 def _pyfuncitem(self):
1559 "(compatonly) for code expecting pytest-2.2 style request objects"
1560 return self
1562 @property
1563 def funcargnames(self) -> List[str]:
1564 """ alias attribute for ``fixturenames`` for pre-2.3 compatibility"""
1565 warnings.warn(FUNCARGNAMES, stacklevel=2)
1566 return self.fixturenames
1568 def runtest(self) -> None:
1569 """ execute the underlying test function. """
1570 self.ihook.pytest_pyfunc_call(pyfuncitem=self)
1572 def setup(self) -> None:
1573 if isinstance(self.parent, Instance):
1574 self.parent.newinstance()
1575 self.obj = self._getobj()
1576 self._request._fillfixtures()
1578 def _prunetraceback(self, excinfo: ExceptionInfo) -> None:
1579 if hasattr(self, "_obj") and not self.config.getoption("fulltrace", False):
1580 code = _pytest._code.Code(get_real_func(self.obj))
1581 path, firstlineno = code.path, code.firstlineno
1582 traceback = excinfo.traceback
1583 ntraceback = traceback.cut(path=path, firstlineno=firstlineno)
1584 if ntraceback == traceback:
1585 ntraceback = ntraceback.cut(path=path)
1586 if ntraceback == traceback:
1587 ntraceback = ntraceback.filter(filter_traceback)
1588 if not ntraceback:
1589 ntraceback = traceback
1591 excinfo.traceback = ntraceback.filter()
1592 # issue364: mark all but first and last frames to
1593 # only show a single-line message for each frame
1594 if self.config.getoption("tbstyle", "auto") == "auto":
1595 if len(excinfo.traceback) > 2:
1596 for entry in excinfo.traceback[1:-1]:
1597 entry.set_repr_style("short")
1599 # TODO: Type ignored -- breaks Liskov Substitution.
1600 def repr_failure( # type: ignore[override]
1601 self, excinfo: ExceptionInfo[BaseException],
1602 ) -> Union[str, TerminalRepr]:
1603 style = self.config.getoption("tbstyle", "auto")
1604 if style == "auto":
1605 style = "long"
1606 return self._repr_failure_py(excinfo, style=style)
1609class FunctionDefinition(Function):
1610 """
1611 internal hack until we get actual definition nodes instead of the
1612 crappy metafunc hack
1613 """
1615 def runtest(self) -> None:
1616 raise RuntimeError("function definitions are not supposed to be used")
1618 setup = runtest