1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507 |
- # -*- coding: utf-8 -*-
- """ Python test discovery, setup and run of test functions. """
- from __future__ import absolute_import
- from __future__ import division
- from __future__ import print_function
- import collections
- import fnmatch
- import inspect
- import os
- import sys
- import warnings
- from functools import partial
- from textwrap import dedent
- import py
- import six
- import _pytest
- from _pytest import deprecated
- from _pytest import fixtures
- from _pytest import nodes
- from _pytest._code import filter_traceback
- from _pytest.compat import ascii_escaped
- from _pytest.compat import enum
- from _pytest.compat import get_default_arg_names
- from _pytest.compat import get_real_func
- from _pytest.compat import getfslineno
- from _pytest.compat import getimfunc
- from _pytest.compat import getlocation
- from _pytest.compat import is_generator
- from _pytest.compat import isclass
- from _pytest.compat import isfunction
- from _pytest.compat import NOTSET
- from _pytest.compat import REGEX_TYPE
- from _pytest.compat import safe_getattr
- from _pytest.compat import safe_isclass
- from _pytest.compat import safe_str
- from _pytest.compat import STRING_TYPES
- from _pytest.config import hookimpl
- from _pytest.main import FSHookProxy
- from _pytest.mark import MARK_GEN
- from _pytest.mark.structures import get_unpacked_marks
- from _pytest.mark.structures import normalize_mark_list
- from _pytest.outcomes import fail
- from _pytest.outcomes import skip
- from _pytest.pathlib import parts
- from _pytest.warning_types import PytestCollectionWarning
- from _pytest.warning_types import PytestUnhandledCoroutineWarning
- def pyobj_property(name):
- def get(self):
- node = self.getparent(getattr(__import__("pytest"), name))
- if node is not None:
- return node.obj
- doc = "python %s object this node was collected from (can be None)." % (
- name.lower(),
- )
- return property(get, None, None, doc)
- def pytest_addoption(parser):
- group = parser.getgroup("general")
- group.addoption(
- "--fixtures",
- "--funcargs",
- action="store_true",
- dest="showfixtures",
- default=False,
- help="show available fixtures, sorted by plugin appearance "
- "(fixtures with leading '_' are only shown with '-v')",
- )
- group.addoption(
- "--fixtures-per-test",
- action="store_true",
- dest="show_fixtures_per_test",
- default=False,
- help="show fixtures per test",
- )
- parser.addini(
- "python_files",
- type="args",
- # NOTE: default is also used in AssertionRewritingHook.
- default=["test_*.py", "*_test.py"],
- help="glob-style file patterns for Python test module discovery",
- )
- parser.addini(
- "python_classes",
- type="args",
- default=["Test"],
- help="prefixes or glob names for Python test class discovery",
- )
- parser.addini(
- "python_functions",
- type="args",
- default=["test"],
- help="prefixes or glob names for Python test function and method discovery",
- )
- parser.addini(
- "disable_test_id_escaping_and_forfeit_all_rights_to_community_support",
- type="bool",
- default=False,
- help="disable string escape non-ascii characters, might cause unwanted "
- "side effects(use at your own risk)",
- )
- group.addoption(
- "--import-mode",
- default="prepend",
- choices=["prepend", "append"],
- dest="importmode",
- help="prepend/append to sys.path when importing test modules, "
- "default is to prepend.",
- )
- def pytest_cmdline_main(config):
- if config.option.showfixtures:
- showfixtures(config)
- return 0
- if config.option.show_fixtures_per_test:
- show_fixtures_per_test(config)
- return 0
- def pytest_generate_tests(metafunc):
- # those alternative spellings are common - raise a specific error to alert
- # the user
- alt_spellings = ["parameterize", "parametrise", "parameterise"]
- for mark_name in alt_spellings:
- if metafunc.definition.get_closest_marker(mark_name):
- msg = "{0} has '{1}' mark, spelling should be 'parametrize'"
- fail(msg.format(metafunc.function.__name__, mark_name), pytrace=False)
- for marker in metafunc.definition.iter_markers(name="parametrize"):
- metafunc.parametrize(*marker.args, **marker.kwargs)
- def pytest_configure(config):
- config.addinivalue_line(
- "markers",
- "parametrize(argnames, argvalues): call a test function multiple "
- "times passing in different arguments in turn. argvalues generally "
- "needs to be a list of values if argnames specifies only one name "
- "or a list of tuples of values if argnames specifies multiple names. "
- "Example: @parametrize('arg1', [1,2]) would lead to two calls of the "
- "decorated test function, one with arg1=1 and another with arg1=2."
- "see https://docs.pytest.org/en/latest/parametrize.html for more info "
- "and examples.",
- )
- config.addinivalue_line(
- "markers",
- "usefixtures(fixturename1, fixturename2, ...): mark tests as needing "
- "all of the specified fixtures. see "
- "https://docs.pytest.org/en/latest/fixture.html#usefixtures ",
- )
- @hookimpl(trylast=True)
- def pytest_pyfunc_call(pyfuncitem):
- testfunction = pyfuncitem.obj
- iscoroutinefunction = getattr(inspect, "iscoroutinefunction", None)
- if iscoroutinefunction is not None and iscoroutinefunction(testfunction):
- msg = "Coroutine functions are not natively supported and have been skipped.\n"
- msg += "You need to install a suitable plugin for your async framework, for example:\n"
- msg += " - pytest-asyncio\n"
- msg += " - pytest-trio\n"
- msg += " - pytest-tornasync"
- warnings.warn(PytestUnhandledCoroutineWarning(msg.format(pyfuncitem.nodeid)))
- skip(msg="coroutine function and no async plugin installed (see warnings)")
- funcargs = pyfuncitem.funcargs
- testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames}
- testfunction(**testargs)
- return True
- def pytest_collect_file(path, parent):
- ext = path.ext
- if ext == ".py":
- if not parent.session.isinitpath(path):
- if not path_matches_patterns(
- path, parent.config.getini("python_files") + ["__init__.py"]
- ):
- return
- ihook = parent.session.gethookproxy(path)
- return ihook.pytest_pycollect_makemodule(path=path, parent=parent)
- def path_matches_patterns(path, patterns):
- """Returns True if the given py.path.local matches one of the patterns in the list of globs given"""
- return any(path.fnmatch(pattern) for pattern in patterns)
- def pytest_pycollect_makemodule(path, parent):
- if path.basename == "__init__.py":
- return Package(path, parent)
- return Module(path, parent)
- @hookimpl(hookwrapper=True)
- def pytest_pycollect_makeitem(collector, name, obj):
- outcome = yield
- res = outcome.get_result()
- if res is not None:
- return
- # nothing was collected elsewhere, let's do it here
- if safe_isclass(obj):
- if collector.istestclass(obj, name):
- outcome.force_result(Class(name, parent=collector))
- elif collector.istestfunction(obj, name):
- # mock seems to store unbound methods (issue473), normalize it
- obj = getattr(obj, "__func__", obj)
- # We need to try and unwrap the function if it's a functools.partial
- # or a funtools.wrapped.
- # We musn't if it's been wrapped with mock.patch (python 2 only)
- if not (isfunction(obj) or isfunction(get_real_func(obj))):
- filename, lineno = getfslineno(obj)
- warnings.warn_explicit(
- message=PytestCollectionWarning(
- "cannot collect %r because it is not a function." % name
- ),
- category=None,
- filename=str(filename),
- lineno=lineno + 1,
- )
- elif getattr(obj, "__test__", True):
- if is_generator(obj):
- res = Function(name, parent=collector)
- reason = deprecated.YIELD_TESTS.format(name=name)
- res.add_marker(MARK_GEN.xfail(run=False, reason=reason))
- res.warn(PytestCollectionWarning(reason))
- else:
- res = list(collector._genfunctions(name, obj))
- outcome.force_result(res)
- def pytest_make_parametrize_id(config, val, argname=None):
- return None
- class PyobjContext(object):
- module = pyobj_property("Module")
- cls = pyobj_property("Class")
- instance = pyobj_property("Instance")
- class PyobjMixin(PyobjContext):
- _ALLOW_MARKERS = True
- def __init__(self, *k, **kw):
- super(PyobjMixin, self).__init__(*k, **kw)
- @property
- def obj(self):
- """Underlying Python object."""
- obj = getattr(self, "_obj", None)
- if obj is None:
- self._obj = obj = self._getobj()
- # XXX evil hack
- # used to avoid Instance collector marker duplication
- if self._ALLOW_MARKERS:
- self.own_markers.extend(get_unpacked_marks(self.obj))
- return obj
- @obj.setter
- def obj(self, value):
- self._obj = value
- def _getobj(self):
- """Gets the underlying Python object. May be overwritten by subclasses."""
- return getattr(self.parent.obj, self.name)
- def getmodpath(self, stopatmodule=True, includemodule=False):
- """ return python path relative to the containing module. """
- chain = self.listchain()
- chain.reverse()
- parts = []
- for node in chain:
- if isinstance(node, Instance):
- continue
- name = node.name
- if isinstance(node, Module):
- name = os.path.splitext(name)[0]
- if stopatmodule:
- if includemodule:
- parts.append(name)
- break
- parts.append(name)
- parts.reverse()
- s = ".".join(parts)
- return s.replace(".[", "[")
- def reportinfo(self):
- # XXX caching?
- obj = self.obj
- compat_co_firstlineno = getattr(obj, "compat_co_firstlineno", None)
- if isinstance(compat_co_firstlineno, int):
- # nose compatibility
- fspath = sys.modules[obj.__module__].__file__
- if fspath.endswith(".pyc"):
- fspath = fspath[:-1]
- lineno = compat_co_firstlineno
- else:
- fspath, lineno = getfslineno(obj)
- modpath = self.getmodpath()
- assert isinstance(lineno, int)
- return fspath, lineno, modpath
- class PyCollector(PyobjMixin, nodes.Collector):
- def funcnamefilter(self, name):
- return self._matches_prefix_or_glob_option("python_functions", name)
- def isnosetest(self, obj):
- """ Look for the __test__ attribute, which is applied by the
- @nose.tools.istest decorator
- """
- # We explicitly check for "is True" here to not mistakenly treat
- # classes with a custom __getattr__ returning something truthy (like a
- # function) as test classes.
- return safe_getattr(obj, "__test__", False) is True
- def classnamefilter(self, name):
- return self._matches_prefix_or_glob_option("python_classes", name)
- def istestfunction(self, obj, name):
- if self.funcnamefilter(name) or self.isnosetest(obj):
- if isinstance(obj, staticmethod):
- # static methods need to be unwrapped
- obj = safe_getattr(obj, "__func__", False)
- return (
- safe_getattr(obj, "__call__", False)
- and fixtures.getfixturemarker(obj) is None
- )
- else:
- return False
- def istestclass(self, obj, name):
- return self.classnamefilter(name) or self.isnosetest(obj)
- def _matches_prefix_or_glob_option(self, option_name, name):
- """
- checks if the given name matches the prefix or glob-pattern defined
- in ini configuration.
- """
- for option in self.config.getini(option_name):
- if name.startswith(option):
- return True
- # check that name looks like a glob-string before calling fnmatch
- # because this is called for every name in each collected module,
- # and fnmatch is somewhat expensive to call
- elif ("*" in option or "?" in option or "[" in option) and fnmatch.fnmatch(
- name, option
- ):
- return True
- return False
- def collect(self):
- if not getattr(self.obj, "__test__", True):
- return []
- # NB. we avoid random getattrs and peek in the __dict__ instead
- # (XXX originally introduced from a PyPy need, still true?)
- dicts = [getattr(self.obj, "__dict__", {})]
- for basecls in inspect.getmro(self.obj.__class__):
- dicts.append(basecls.__dict__)
- seen = {}
- values = []
- for dic in dicts:
- for name, obj in list(dic.items()):
- if name in seen:
- continue
- seen[name] = True
- res = self._makeitem(name, obj)
- if res is None:
- continue
- if not isinstance(res, list):
- res = [res]
- values.extend(res)
- values.sort(key=lambda item: item.reportinfo()[:2])
- return values
- def _makeitem(self, name, obj):
- # assert self.ihook.fspath == self.fspath, self
- return self.ihook.pytest_pycollect_makeitem(collector=self, name=name, obj=obj)
- def _genfunctions(self, name, funcobj):
- module = self.getparent(Module).obj
- clscol = self.getparent(Class)
- cls = clscol and clscol.obj or None
- fm = self.session._fixturemanager
- definition = FunctionDefinition(name=name, parent=self, callobj=funcobj)
- fixtureinfo = fm.getfixtureinfo(definition, funcobj, cls)
- metafunc = Metafunc(
- definition, fixtureinfo, self.config, cls=cls, module=module
- )
- methods = []
- if hasattr(module, "pytest_generate_tests"):
- methods.append(module.pytest_generate_tests)
- if hasattr(cls, "pytest_generate_tests"):
- methods.append(cls().pytest_generate_tests)
- if methods:
- self.ihook.pytest_generate_tests.call_extra(
- methods, dict(metafunc=metafunc)
- )
- else:
- self.ihook.pytest_generate_tests(metafunc=metafunc)
- if not metafunc._calls:
- yield Function(name, parent=self, fixtureinfo=fixtureinfo)
- else:
- # add funcargs() as fixturedefs to fixtureinfo.arg2fixturedefs
- fixtures.add_funcarg_pseudo_fixture_def(self, metafunc, fm)
- # add_funcarg_pseudo_fixture_def may have shadowed some fixtures
- # with direct parametrization, so make sure we update what the
- # function really needs.
- fixtureinfo.prune_dependency_tree()
- for callspec in metafunc._calls:
- subname = "%s[%s]" % (name, callspec.id)
- yield Function(
- name=subname,
- parent=self,
- callspec=callspec,
- callobj=funcobj,
- fixtureinfo=fixtureinfo,
- keywords={callspec.id: True},
- originalname=name,
- )
- class Module(nodes.File, PyCollector):
- """ Collector for test classes and functions. """
- def _getobj(self):
- return self._importtestmodule()
- def collect(self):
- self._inject_setup_module_fixture()
- self._inject_setup_function_fixture()
- self.session._fixturemanager.parsefactories(self)
- return super(Module, self).collect()
- def _inject_setup_module_fixture(self):
- """Injects a hidden autouse, module scoped fixture into the collected module object
- that invokes setUpModule/tearDownModule if either or both are available.
- Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with
- other fixtures (#517).
- """
- setup_module = _get_non_fixture_func(self.obj, "setUpModule")
- if setup_module is None:
- setup_module = _get_non_fixture_func(self.obj, "setup_module")
- teardown_module = _get_non_fixture_func(self.obj, "tearDownModule")
- if teardown_module is None:
- teardown_module = _get_non_fixture_func(self.obj, "teardown_module")
- if setup_module is None and teardown_module is None:
- return
- @fixtures.fixture(autouse=True, scope="module")
- def xunit_setup_module_fixture(request):
- if setup_module is not None:
- _call_with_optional_argument(setup_module, request.module)
- yield
- if teardown_module is not None:
- _call_with_optional_argument(teardown_module, request.module)
- self.obj.__pytest_setup_module = xunit_setup_module_fixture
- def _inject_setup_function_fixture(self):
- """Injects a hidden autouse, function scoped fixture into the collected module object
- that invokes setup_function/teardown_function if either or both are available.
- Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with
- other fixtures (#517).
- """
- setup_function = _get_non_fixture_func(self.obj, "setup_function")
- teardown_function = _get_non_fixture_func(self.obj, "teardown_function")
- if setup_function is None and teardown_function is None:
- return
- @fixtures.fixture(autouse=True, scope="function")
- def xunit_setup_function_fixture(request):
- if request.instance is not None:
- # in this case we are bound to an instance, so we need to let
- # setup_method handle this
- yield
- return
- if setup_function is not None:
- _call_with_optional_argument(setup_function, request.function)
- yield
- if teardown_function is not None:
- _call_with_optional_argument(teardown_function, request.function)
- self.obj.__pytest_setup_function = xunit_setup_function_fixture
- def _importtestmodule(self):
- # we assume we are only called once per module
- importmode = self.config.getoption("--import-mode")
- try:
- mod = self.fspath.pyimport(ensuresyspath=importmode)
- except SyntaxError:
- raise self.CollectError(
- _pytest._code.ExceptionInfo.from_current().getrepr(style="short")
- )
- except self.fspath.ImportMismatchError:
- e = sys.exc_info()[1]
- raise self.CollectError(
- "import file mismatch:\n"
- "imported module %r has this __file__ attribute:\n"
- " %s\n"
- "which is not the same as the test file we want to collect:\n"
- " %s\n"
- "HINT: remove __pycache__ / .pyc files and/or use a "
- "unique basename for your test file modules" % e.args
- )
- except ImportError:
- from _pytest._code.code import ExceptionInfo
- exc_info = ExceptionInfo.from_current()
- if self.config.getoption("verbose") < 2:
- exc_info.traceback = exc_info.traceback.filter(filter_traceback)
- exc_repr = (
- exc_info.getrepr(style="short")
- if exc_info.traceback
- else exc_info.exconly()
- )
- formatted_tb = safe_str(exc_repr)
- raise self.CollectError(
- "ImportError while importing test module '{fspath}'.\n"
- "Hint: make sure your test modules/packages have valid Python names.\n"
- "Traceback:\n"
- "{traceback}".format(fspath=self.fspath, traceback=formatted_tb)
- )
- except _pytest.runner.Skipped as e:
- if e.allow_module_level:
- raise
- raise self.CollectError(
- "Using pytest.skip outside of a test is not allowed. "
- "To decorate a test function, use the @pytest.mark.skip "
- "or @pytest.mark.skipif decorators instead, and to skip a "
- "module use `pytestmark = pytest.mark.{skip,skipif}."
- )
- self.config.pluginmanager.consider_module(mod)
- return mod
- class Package(Module):
- def __init__(self, fspath, parent=None, config=None, session=None, nodeid=None):
- session = parent.session
- nodes.FSCollector.__init__(
- self, fspath, parent=parent, config=config, session=session, nodeid=nodeid
- )
- self.name = fspath.dirname
- self.trace = session.trace
- self._norecursepatterns = session._norecursepatterns
- self.fspath = fspath
- def setup(self):
- # not using fixtures to call setup_module here because autouse fixtures
- # from packages are not called automatically (#4085)
- setup_module = _get_non_fixture_func(self.obj, "setUpModule")
- if setup_module is None:
- setup_module = _get_non_fixture_func(self.obj, "setup_module")
- if setup_module is not None:
- _call_with_optional_argument(setup_module, self.obj)
- teardown_module = _get_non_fixture_func(self.obj, "tearDownModule")
- if teardown_module is None:
- teardown_module = _get_non_fixture_func(self.obj, "teardown_module")
- if teardown_module is not None:
- func = partial(_call_with_optional_argument, teardown_module, self.obj)
- self.addfinalizer(func)
- def _recurse(self, dirpath):
- if dirpath.basename == "__pycache__":
- return False
- ihook = self.gethookproxy(dirpath.dirpath())
- if ihook.pytest_ignore_collect(path=dirpath, config=self.config):
- return
- for pat in self._norecursepatterns:
- if dirpath.check(fnmatch=pat):
- return False
- ihook = self.gethookproxy(dirpath)
- ihook.pytest_collect_directory(path=dirpath, parent=self)
- return True
- def gethookproxy(self, fspath):
- # check if we have the common case of running
- # hooks with all conftest.py filesall conftest.py
- pm = self.config.pluginmanager
- my_conftestmodules = pm._getconftestmodules(fspath)
- remove_mods = pm._conftest_plugins.difference(my_conftestmodules)
- if remove_mods:
- # one or more conftests are not in use at this fspath
- proxy = FSHookProxy(fspath, pm, remove_mods)
- else:
- # all plugis are active for this fspath
- proxy = self.config.hook
- return proxy
- def _collectfile(self, path, handle_dupes=True):
- assert path.isfile(), "%r is not a file (isdir=%r, exists=%r, islink=%r)" % (
- path,
- path.isdir(),
- path.exists(),
- path.islink(),
- )
- ihook = self.gethookproxy(path)
- if not self.isinitpath(path):
- if ihook.pytest_ignore_collect(path=path, config=self.config):
- return ()
- if handle_dupes:
- keepduplicates = self.config.getoption("keepduplicates")
- if not keepduplicates:
- duplicate_paths = self.config.pluginmanager._duplicatepaths
- if path in duplicate_paths:
- return ()
- else:
- duplicate_paths.add(path)
- if self.fspath == path: # __init__.py
- return [self]
- return ihook.pytest_collect_file(path=path, parent=self)
- def isinitpath(self, path):
- return path in self.session._initialpaths
- def collect(self):
- this_path = self.fspath.dirpath()
- init_module = this_path.join("__init__.py")
- if init_module.check(file=1) and path_matches_patterns(
- init_module, self.config.getini("python_files")
- ):
- yield Module(init_module, self)
- pkg_prefixes = set()
- for path in this_path.visit(rec=self._recurse, bf=True, sort=True):
- # We will visit our own __init__.py file, in which case we skip it.
- is_file = path.isfile()
- if is_file:
- if path.basename == "__init__.py" and path.dirpath() == this_path:
- continue
- parts_ = parts(path.strpath)
- if any(
- pkg_prefix in parts_ and pkg_prefix.join("__init__.py") != path
- for pkg_prefix in pkg_prefixes
- ):
- continue
- if is_file:
- for x in self._collectfile(path):
- yield x
- elif not path.isdir():
- # Broken symlink or invalid/missing file.
- continue
- elif path.join("__init__.py").check(file=1):
- pkg_prefixes.add(path)
- def _get_xunit_setup_teardown(holder, attr_name, param_obj=None):
- """
- Return a callable to perform xunit-style setup or teardown if
- the function exists in the ``holder`` object.
- The ``param_obj`` parameter is the parameter which will be passed to the function
- when the callable is called without arguments, defaults to the ``holder`` object.
- Return ``None`` if a suitable callable is not found.
- """
- # TODO: only needed because of Package!
- param_obj = param_obj if param_obj is not None else holder
- result = _get_non_fixture_func(holder, attr_name)
- if result is not None:
- arg_count = result.__code__.co_argcount
- if inspect.ismethod(result):
- arg_count -= 1
- if arg_count:
- return lambda: result(param_obj)
- else:
- return result
- def _call_with_optional_argument(func, arg):
- """Call the given function with the given argument if func accepts one argument, otherwise
- calls func without arguments"""
- arg_count = func.__code__.co_argcount
- if inspect.ismethod(func):
- arg_count -= 1
- if arg_count:
- func(arg)
- else:
- func()
- def _get_non_fixture_func(obj, name):
- """Return the attribute from the given object to be used as a setup/teardown
- xunit-style function, but only if not marked as a fixture to
- avoid calling it twice.
- """
- meth = getattr(obj, name, None)
- if fixtures.getfixturemarker(meth) is None:
- return meth
- class Class(PyCollector):
- """ Collector for test methods. """
- def collect(self):
- if not safe_getattr(self.obj, "__test__", True):
- return []
- if hasinit(self.obj):
- self.warn(
- PytestCollectionWarning(
- "cannot collect test class %r because it has a "
- "__init__ constructor (from: %s)"
- % (self.obj.__name__, self.parent.nodeid)
- )
- )
- return []
- elif hasnew(self.obj):
- self.warn(
- PytestCollectionWarning(
- "cannot collect test class %r because it has a "
- "__new__ constructor (from: %s)"
- % (self.obj.__name__, self.parent.nodeid)
- )
- )
- return []
- self._inject_setup_class_fixture()
- self._inject_setup_method_fixture()
- return [Instance(name="()", parent=self)]
- def _inject_setup_class_fixture(self):
- """Injects a hidden autouse, class scoped fixture into the collected class object
- that invokes setup_class/teardown_class if either or both are available.
- Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with
- other fixtures (#517).
- """
- setup_class = _get_non_fixture_func(self.obj, "setup_class")
- teardown_class = getattr(self.obj, "teardown_class", None)
- if setup_class is None and teardown_class is None:
- return
- @fixtures.fixture(autouse=True, scope="class")
- def xunit_setup_class_fixture(cls):
- if setup_class is not None:
- func = getimfunc(setup_class)
- _call_with_optional_argument(func, self.obj)
- yield
- if teardown_class is not None:
- func = getimfunc(teardown_class)
- _call_with_optional_argument(func, self.obj)
- self.obj.__pytest_setup_class = xunit_setup_class_fixture
- def _inject_setup_method_fixture(self):
- """Injects a hidden autouse, function scoped fixture into the collected class object
- that invokes setup_method/teardown_method if either or both are available.
- Using a fixture to invoke this methods ensures we play nicely and unsurprisingly with
- other fixtures (#517).
- """
- setup_method = _get_non_fixture_func(self.obj, "setup_method")
- teardown_method = getattr(self.obj, "teardown_method", None)
- if setup_method is None and teardown_method is None:
- return
- @fixtures.fixture(autouse=True, scope="function")
- def xunit_setup_method_fixture(self, request):
- method = request.function
- if setup_method is not None:
- func = getattr(self, "setup_method")
- _call_with_optional_argument(func, method)
- yield
- if teardown_method is not None:
- func = getattr(self, "teardown_method")
- _call_with_optional_argument(func, method)
- self.obj.__pytest_setup_method = xunit_setup_method_fixture
- class Instance(PyCollector):
- _ALLOW_MARKERS = False # hack, destroy later
- # instances share the object with their parents in a way
- # that duplicates markers instances if not taken out
- # can be removed at node structure reorganization time
- def _getobj(self):
- return self.parent.obj()
- def collect(self):
- self.session._fixturemanager.parsefactories(self)
- return super(Instance, self).collect()
- def newinstance(self):
- self.obj = self._getobj()
- return self.obj
- class FunctionMixin(PyobjMixin):
- """ mixin for the code common to Function and Generator.
- """
- def setup(self):
- """ perform setup for this test function. """
- if isinstance(self.parent, Instance):
- self.parent.newinstance()
- self.obj = self._getobj()
- def _prunetraceback(self, excinfo):
- if hasattr(self, "_obj") and not self.config.getoption("fulltrace", False):
- code = _pytest._code.Code(get_real_func(self.obj))
- path, firstlineno = code.path, code.firstlineno
- traceback = excinfo.traceback
- ntraceback = traceback.cut(path=path, firstlineno=firstlineno)
- if ntraceback == traceback:
- ntraceback = ntraceback.cut(path=path)
- if ntraceback == traceback:
- ntraceback = ntraceback.filter(filter_traceback)
- if not ntraceback:
- ntraceback = traceback
- excinfo.traceback = ntraceback.filter()
- # issue364: mark all but first and last frames to
- # only show a single-line message for each frame
- if self.config.getoption("tbstyle", "auto") == "auto":
- if len(excinfo.traceback) > 2:
- for entry in excinfo.traceback[1:-1]:
- entry.set_repr_style("short")
- def repr_failure(self, excinfo, outerr=None):
- assert outerr is None, "XXX outerr usage is deprecated"
- style = self.config.getoption("tbstyle", "auto")
- if style == "auto":
- style = "long"
- return self._repr_failure_py(excinfo, style=style)
- def hasinit(obj):
- init = getattr(obj, "__init__", None)
- if init:
- return init != object.__init__
- def hasnew(obj):
- new = getattr(obj, "__new__", None)
- if new:
- return new != object.__new__
- class CallSpec2(object):
- def __init__(self, metafunc):
- self.metafunc = metafunc
- self.funcargs = {}
- self._idlist = []
- self.params = {}
- self._globalid = NOTSET
- self._globalparam = NOTSET
- self._arg2scopenum = {} # used for sorting parametrized resources
- self.marks = []
- self.indices = {}
- def copy(self):
- cs = CallSpec2(self.metafunc)
- cs.funcargs.update(self.funcargs)
- cs.params.update(self.params)
- cs.marks.extend(self.marks)
- cs.indices.update(self.indices)
- cs._arg2scopenum.update(self._arg2scopenum)
- cs._idlist = list(self._idlist)
- cs._globalid = self._globalid
- cs._globalparam = self._globalparam
- return cs
- def _checkargnotcontained(self, arg):
- if arg in self.params or arg in self.funcargs:
- raise ValueError("duplicate %r" % (arg,))
- def getparam(self, name):
- try:
- return self.params[name]
- except KeyError:
- if self._globalparam is NOTSET:
- raise ValueError(name)
- return self._globalparam
- @property
- def id(self):
- return "-".join(map(safe_str, self._idlist))
- def setmulti2(self, valtypes, argnames, valset, id, marks, scopenum, param_index):
- for arg, val in zip(argnames, valset):
- self._checkargnotcontained(arg)
- valtype_for_arg = valtypes[arg]
- getattr(self, valtype_for_arg)[arg] = val
- self.indices[arg] = param_index
- self._arg2scopenum[arg] = scopenum
- self._idlist.append(id)
- self.marks.extend(normalize_mark_list(marks))
- def setall(self, funcargs, id, param):
- for x in funcargs:
- self._checkargnotcontained(x)
- self.funcargs.update(funcargs)
- if id is not NOTSET:
- self._idlist.append(id)
- if param is not NOTSET:
- assert self._globalparam is NOTSET
- self._globalparam = param
- for arg in funcargs:
- self._arg2scopenum[arg] = fixtures.scopenum_function
- class Metafunc(fixtures.FuncargnamesCompatAttr):
- """
- Metafunc objects are passed to the :func:`pytest_generate_tests <_pytest.hookspec.pytest_generate_tests>` hook.
- They help to inspect a test function and to generate tests according to
- test configuration or values specified in the class or module where a
- test function is defined.
- """
- def __init__(self, definition, fixtureinfo, config, cls=None, module=None):
- assert (
- isinstance(definition, FunctionDefinition)
- or type(definition).__name__ == "DefinitionMock"
- )
- self.definition = definition
- #: access to the :class:`_pytest.config.Config` object for the test session
- self.config = config
- #: the module object where the test function is defined in.
- self.module = module
- #: underlying python test function
- self.function = definition.obj
- #: set of fixture names required by the test function
- self.fixturenames = fixtureinfo.names_closure
- #: class object where the test function is defined in or ``None``.
- self.cls = cls
- self._calls = []
- self._ids = set()
- self._arg2fixturedefs = fixtureinfo.name2fixturedefs
- def parametrize(self, argnames, argvalues, indirect=False, ids=None, scope=None):
- """ Add new invocations to the underlying test function using the list
- of argvalues for the given argnames. Parametrization is performed
- during the collection phase. If you need to setup expensive resources
- see about setting indirect to do it rather at test setup time.
- :arg argnames: a comma-separated string denoting one or more argument
- names, or a list/tuple of argument strings.
- :arg argvalues: The list of argvalues determines how often a
- test is invoked with different argument values. If only one
- argname was specified argvalues is a list of values. If N
- argnames were specified, argvalues must be a list of N-tuples,
- where each tuple-element specifies a value for its respective
- argname.
- :arg indirect: The list of argnames or boolean. A list of arguments'
- names (subset of argnames). If True the list contains all names from
- the argnames. Each argvalue corresponding to an argname in this list will
- be passed as request.param to its respective argname fixture
- function so that it can perform more expensive setups during the
- setup phase of a test rather than at collection time.
- :arg ids: list of string ids, or a callable.
- If strings, each is corresponding to the argvalues so that they are
- part of the test id. If None is given as id of specific test, the
- automatically generated id for that argument will be used.
- If callable, it should take one argument (a single argvalue) and return
- a string or return None. If None, the automatically generated id for that
- argument will be used.
- If no ids are provided they will be generated automatically from
- the argvalues.
- :arg scope: if specified it denotes the scope of the parameters.
- The scope is used for grouping tests by parameter instances.
- It will also override any fixture-function defined scope, allowing
- to set a dynamic scope using test context or configuration.
- """
- from _pytest.fixtures import scope2index
- from _pytest.mark import ParameterSet
- argnames, parameters = ParameterSet._for_parametrize(
- argnames,
- argvalues,
- self.function,
- self.config,
- function_definition=self.definition,
- )
- del argvalues
- if scope is None:
- scope = _find_parametrized_scope(argnames, self._arg2fixturedefs, indirect)
- self._validate_if_using_arg_names(argnames, indirect)
- arg_values_types = self._resolve_arg_value_types(argnames, indirect)
- ids = self._resolve_arg_ids(argnames, ids, parameters, item=self.definition)
- scopenum = scope2index(
- scope, descr="parametrize() call in {}".format(self.function.__name__)
- )
- # create the new calls: if we are parametrize() multiple times (by applying the decorator
- # more than once) then we accumulate those calls generating the cartesian product
- # of all calls
- newcalls = []
- for callspec in self._calls or [CallSpec2(self)]:
- for param_index, (param_id, param_set) in enumerate(zip(ids, parameters)):
- newcallspec = callspec.copy()
- newcallspec.setmulti2(
- arg_values_types,
- argnames,
- param_set.values,
- param_id,
- param_set.marks,
- scopenum,
- param_index,
- )
- newcalls.append(newcallspec)
- self._calls = newcalls
- def _resolve_arg_ids(self, argnames, ids, parameters, item):
- """Resolves the actual ids for the given argnames, based on the ``ids`` parameter given
- to ``parametrize``.
- :param List[str] argnames: list of argument names passed to ``parametrize()``.
- :param ids: the ids parameter of the parametrized call (see docs).
- :param List[ParameterSet] parameters: the list of parameter values, same size as ``argnames``.
- :param Item item: the item that generated this parametrized call.
- :rtype: List[str]
- :return: the list of ids for each argname given
- """
- from _pytest._io.saferepr import saferepr
- idfn = None
- if callable(ids):
- idfn = ids
- ids = None
- if ids:
- func_name = self.function.__name__
- if len(ids) != len(parameters):
- msg = "In {}: {} parameter sets specified, with different number of ids: {}"
- fail(msg.format(func_name, len(parameters), len(ids)), pytrace=False)
- for id_value in ids:
- if id_value is not None and not isinstance(id_value, six.string_types):
- msg = "In {}: ids must be list of strings, found: {} (type: {!r})"
- fail(
- msg.format(func_name, saferepr(id_value), type(id_value)),
- pytrace=False,
- )
- ids = idmaker(argnames, parameters, idfn, ids, self.config, item=item)
- return ids
- def _resolve_arg_value_types(self, argnames, indirect):
- """Resolves if each parametrized argument must be considered a parameter to a fixture or a "funcarg"
- to the function, based on the ``indirect`` parameter of the parametrized() call.
- :param List[str] argnames: list of argument names passed to ``parametrize()``.
- :param indirect: same ``indirect`` parameter of ``parametrize()``.
- :rtype: Dict[str, str]
- A dict mapping each arg name to either:
- * "params" if the argname should be the parameter of a fixture of the same name.
- * "funcargs" if the argname should be a parameter to the parametrized test function.
- """
- valtypes = {}
- if indirect is True:
- valtypes = dict.fromkeys(argnames, "params")
- elif indirect is False:
- valtypes = dict.fromkeys(argnames, "funcargs")
- elif isinstance(indirect, (tuple, list)):
- valtypes = dict.fromkeys(argnames, "funcargs")
- for arg in indirect:
- if arg not in argnames:
- fail(
- "In {}: indirect fixture '{}' doesn't exist".format(
- self.function.__name__, arg
- ),
- pytrace=False,
- )
- valtypes[arg] = "params"
- return valtypes
- def _validate_if_using_arg_names(self, argnames, indirect):
- """
- Check if all argnames are being used, by default values, or directly/indirectly.
- :param List[str] argnames: list of argument names passed to ``parametrize()``.
- :param indirect: same ``indirect`` parameter of ``parametrize()``.
- :raise ValueError: if validation fails.
- """
- default_arg_names = set(get_default_arg_names(self.function))
- func_name = self.function.__name__
- for arg in argnames:
- if arg not in self.fixturenames:
- if arg in default_arg_names:
- fail(
- "In {}: function already takes an argument '{}' with a default value".format(
- func_name, arg
- ),
- pytrace=False,
- )
- else:
- if isinstance(indirect, (tuple, list)):
- name = "fixture" if arg in indirect else "argument"
- else:
- name = "fixture" if indirect else "argument"
- fail(
- "In {}: function uses no {} '{}'".format(func_name, name, arg),
- pytrace=False,
- )
- def _find_parametrized_scope(argnames, arg2fixturedefs, indirect):
- """Find the most appropriate scope for a parametrized call based on its arguments.
- When there's at least one direct argument, always use "function" scope.
- When a test function is parametrized and all its arguments are indirect
- (e.g. fixtures), return the most narrow scope based on the fixtures used.
- Related to issue #1832, based on code posted by @Kingdread.
- """
- from _pytest.fixtures import scopes
- if isinstance(indirect, (list, tuple)):
- all_arguments_are_fixtures = len(indirect) == len(argnames)
- else:
- all_arguments_are_fixtures = bool(indirect)
- if all_arguments_are_fixtures:
- fixturedefs = arg2fixturedefs or {}
- used_scopes = [
- fixturedef[0].scope
- for name, fixturedef in fixturedefs.items()
- if name in argnames
- ]
- if used_scopes:
- # Takes the most narrow scope from used fixtures
- for scope in reversed(scopes):
- if scope in used_scopes:
- return scope
- return "function"
- def _ascii_escaped_by_config(val, config):
- if config is None:
- escape_option = False
- else:
- escape_option = config.getini(
- "disable_test_id_escaping_and_forfeit_all_rights_to_community_support"
- )
- return val if escape_option else ascii_escaped(val)
- def _idval(val, argname, idx, idfn, item, config):
- if idfn:
- try:
- generated_id = idfn(val)
- if generated_id is not None:
- val = generated_id
- except Exception as e:
- # See issue https://github.com/pytest-dev/pytest/issues/2169
- msg = "{}: error raised while trying to determine id of parameter '{}' at position {}\n"
- msg = msg.format(item.nodeid, argname, idx)
- # we only append the exception type and message because on Python 2 reraise does nothing
- msg += " {}: {}\n".format(type(e).__name__, e)
- six.raise_from(ValueError(msg), e)
- elif config:
- hook_id = config.hook.pytest_make_parametrize_id(
- config=config, val=val, argname=argname
- )
- if hook_id:
- return hook_id
- if isinstance(val, STRING_TYPES):
- return _ascii_escaped_by_config(val, config)
- elif val is None or isinstance(val, (float, int, bool)):
- return str(val)
- elif isinstance(val, REGEX_TYPE):
- return ascii_escaped(val.pattern)
- elif enum is not None and isinstance(val, enum.Enum):
- return str(val)
- elif (isclass(val) or isfunction(val)) and hasattr(val, "__name__"):
- return val.__name__
- return str(argname) + str(idx)
- def limit_idval(limit):
- import functools
- names = {}
- limit -= 6
- assert limit > 0
- def decorator(func):
- @functools.wraps(func)
- def wrapper(*args, **kw):
- idval = func(*args, **kw)
- if len(idval) > limit:
- prefix = idval[:limit]
- # There might be same prefix for the different test cases - take item into account
- name = "{}-{}".format(kw.get('item', ''), safe_str(prefix))
- idx = names.setdefault(name, -1) + 1
- names[name] = idx
- idval = "{}-{}".format(safe_str(prefix), idx)
- return idval
- return wrapper
- return decorator
- # XXX limit testnames in the name of sanity and readability
- @limit_idval(limit=500)
- def _idvalset(idx, parameterset, argnames, idfn, ids, item, config):
- if parameterset.id is not None:
- return parameterset.id
- if ids is None or (idx >= len(ids) or ids[idx] is None):
- this_id = [
- _idval(val, argname, idx, idfn, item=item, config=config)
- for val, argname in zip(parameterset.values, argnames)
- ]
- return "-".join(this_id)
- else:
- return _ascii_escaped_by_config(ids[idx], config)
- def idmaker(argnames, parametersets, idfn=None, ids=None, config=None, item=None):
- ids = [
- _idvalset(valindex, parameterset, argnames, idfn, ids, config=config, item=item)
- for valindex, parameterset in enumerate(parametersets)
- ]
- if len(set(ids)) != len(ids):
- # The ids are not unique
- duplicates = [testid for testid in ids if ids.count(testid) > 1]
- counters = collections.defaultdict(lambda: 0)
- for index, testid in enumerate(ids):
- if testid in duplicates:
- ids[index] = testid + str(counters[testid])
- counters[testid] += 1
- return ids
- def show_fixtures_per_test(config):
- from _pytest.main import wrap_session
- return wrap_session(config, _show_fixtures_per_test)
- def _show_fixtures_per_test(config, session):
- import _pytest.config
- session.perform_collect()
- curdir = py.path.local()
- tw = _pytest.config.create_terminal_writer(config)
- verbose = config.getvalue("verbose")
- def get_best_relpath(func):
- loc = getlocation(func, curdir)
- return curdir.bestrelpath(loc)
- def write_fixture(fixture_def):
- argname = fixture_def.argname
- if verbose <= 0 and argname.startswith("_"):
- return
- if verbose > 0:
- bestrel = get_best_relpath(fixture_def.func)
- funcargspec = "{} -- {}".format(argname, bestrel)
- else:
- funcargspec = argname
- tw.line(funcargspec, green=True)
- fixture_doc = fixture_def.func.__doc__
- if fixture_doc:
- write_docstring(tw, fixture_doc)
- else:
- tw.line(" no docstring available", red=True)
- def write_item(item):
- try:
- info = item._fixtureinfo
- except AttributeError:
- # doctests items have no _fixtureinfo attribute
- return
- if not info.name2fixturedefs:
- # this test item does not use any fixtures
- return
- tw.line()
- tw.sep("-", "fixtures used by {}".format(item.name))
- tw.sep("-", "({})".format(get_best_relpath(item.function)))
- # dict key not used in loop but needed for sorting
- for _, fixturedefs in sorted(info.name2fixturedefs.items()):
- assert fixturedefs is not None
- if not fixturedefs:
- continue
- # last item is expected to be the one used by the test item
- write_fixture(fixturedefs[-1])
- for session_item in session.items:
- write_item(session_item)
- def showfixtures(config):
- from _pytest.main import wrap_session
- return wrap_session(config, _showfixtures_main)
- def _showfixtures_main(config, session):
- import _pytest.config
- session.perform_collect()
- curdir = py.path.local()
- tw = _pytest.config.create_terminal_writer(config)
- verbose = config.getvalue("verbose")
- fm = session._fixturemanager
- available = []
- seen = set()
- for argname, fixturedefs in fm._arg2fixturedefs.items():
- assert fixturedefs is not None
- if not fixturedefs:
- continue
- for fixturedef in fixturedefs:
- loc = getlocation(fixturedef.func, curdir)
- if (fixturedef.argname, loc) in seen:
- continue
- seen.add((fixturedef.argname, loc))
- available.append(
- (
- len(fixturedef.baseid),
- fixturedef.func.__module__,
- curdir.bestrelpath(loc),
- fixturedef.argname,
- fixturedef,
- )
- )
- available.sort()
- currentmodule = None
- for baseid, module, bestrel, argname, fixturedef in available:
- if currentmodule != module:
- if not module.startswith("_pytest."):
- tw.line()
- tw.sep("-", "fixtures defined from %s" % (module,))
- currentmodule = module
- if verbose <= 0 and argname[0] == "_":
- continue
- tw.write(argname, green=True)
- if fixturedef.scope != "function":
- tw.write(" [%s scope]" % fixturedef.scope, cyan=True)
- if verbose > 0:
- tw.write(" -- %s" % bestrel, yellow=True)
- tw.write("\n")
- loc = getlocation(fixturedef.func, curdir)
- doc = fixturedef.func.__doc__ or ""
- if doc:
- write_docstring(tw, doc)
- else:
- tw.line(" %s: no docstring available" % (loc,), red=True)
- tw.line()
- def write_docstring(tw, doc, indent=" "):
- doc = doc.rstrip()
- if "\n" in doc:
- firstline, rest = doc.split("\n", 1)
- else:
- firstline, rest = doc, ""
- if firstline.strip():
- tw.line(indent + firstline.strip())
- if rest:
- for line in dedent(rest).split("\n"):
- tw.write(indent + line + "\n")
- class Function(FunctionMixin, nodes.Item, fixtures.FuncargnamesCompatAttr):
- """ a Function Item is responsible for setting up and executing a
- Python test function.
- """
- # disable since functions handle it themselves
- _ALLOW_MARKERS = False
- def __init__(
- self,
- name,
- parent,
- args=None,
- config=None,
- callspec=None,
- callobj=NOTSET,
- keywords=None,
- session=None,
- fixtureinfo=None,
- originalname=None,
- ):
- super(Function, self).__init__(name, parent, config=config, session=session)
- self._args = args
- if callobj is not NOTSET:
- self.obj = callobj
- self.keywords.update(self.obj.__dict__)
- self.own_markers.extend(get_unpacked_marks(self.obj))
- if callspec:
- self.callspec = callspec
- # this is total hostile and a mess
- # keywords are broken by design by now
- # this will be redeemed later
- for mark in callspec.marks:
- # feel free to cry, this was broken for years before
- # and keywords cant fix it per design
- self.keywords[mark.name] = mark
- self.own_markers.extend(normalize_mark_list(callspec.marks))
- if keywords:
- self.keywords.update(keywords)
- # todo: this is a hell of a hack
- # https://github.com/pytest-dev/pytest/issues/4569
- self.keywords.update(
- dict.fromkeys(
- [
- mark.name
- for mark in self.iter_markers()
- if mark.name not in self.keywords
- ],
- True,
- )
- )
- if fixtureinfo is None:
- fixtureinfo = self.session._fixturemanager.getfixtureinfo(
- self, self.obj, self.cls, funcargs=True
- )
- self._fixtureinfo = fixtureinfo
- self.fixturenames = fixtureinfo.names_closure
- self._initrequest()
- #: original function name, without any decorations (for example
- #: parametrization adds a ``"[...]"`` suffix to function names).
- #:
- #: .. versionadded:: 3.0
- self.originalname = originalname
- def _initrequest(self):
- self.funcargs = {}
- self._request = fixtures.FixtureRequest(self)
- @property
- def function(self):
- "underlying python 'function' object"
- return getimfunc(self.obj)
- def _getobj(self):
- name = self.name
- i = name.find("[") # parametrization
- if i != -1:
- name = name[:i]
- return getattr(self.parent.obj, name)
- @property
- def _pyfuncitem(self):
- "(compatonly) for code expecting pytest-2.2 style request objects"
- return self
- def runtest(self):
- """ execute the underlying test function. """
- self.ihook.pytest_pyfunc_call(pyfuncitem=self)
- def setup(self):
- super(Function, self).setup()
- fixtures.fillfixtures(self)
- class FunctionDefinition(Function):
- """
- internal hack until we get actual definition nodes instead of the
- crappy metafunc hack
- """
- def runtest(self):
- raise RuntimeError("function definitions are not supposed to be used")
- setup = runtest
|