Don't stop at the first package when looking up package-scoped fixtures.
Example: package1.subpackage1 package1.subpackage2 package1's setup/teardown were executed again when exiting subpackage1 and entering subpackage2.
This commit is contained in:
parent
7d923c389e
commit
c416b1d935
|
@ -2,6 +2,7 @@ from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
import functools
|
import functools
|
||||||
import inspect
|
import inspect
|
||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
import warnings
|
import warnings
|
||||||
from collections import OrderedDict, deque, defaultdict
|
from collections import OrderedDict, deque, defaultdict
|
||||||
|
@ -70,6 +71,17 @@ def scopeproperty(name=None, doc=None):
|
||||||
return decoratescope
|
return decoratescope
|
||||||
|
|
||||||
|
|
||||||
|
def get_scope_package(node, fixturedef):
|
||||||
|
cls = node.Package
|
||||||
|
current = node
|
||||||
|
fixture_package_name = os.path.join(fixturedef.baseid, '__init__.py')
|
||||||
|
while current and type(current) is not cls or \
|
||||||
|
fixture_package_name != current.nodeid:
|
||||||
|
current = current.parent
|
||||||
|
assert current
|
||||||
|
return current
|
||||||
|
|
||||||
|
|
||||||
def get_scope_node(node, scope):
|
def get_scope_node(node, scope):
|
||||||
cls = scopename2class.get(scope)
|
cls = scopename2class.get(scope)
|
||||||
if cls is None:
|
if cls is None:
|
||||||
|
@ -558,7 +570,10 @@ class FixtureRequest(FuncargnamesCompatAttr):
|
||||||
if scope == "function":
|
if scope == "function":
|
||||||
# this might also be a non-function Item despite its attribute name
|
# this might also be a non-function Item despite its attribute name
|
||||||
return self._pyfuncitem
|
return self._pyfuncitem
|
||||||
node = get_scope_node(self._pyfuncitem, scope)
|
if scope == 'package':
|
||||||
|
node = get_scope_package(self._pyfuncitem, self._fixturedef)
|
||||||
|
else:
|
||||||
|
node = get_scope_node(self._pyfuncitem, scope)
|
||||||
if node is None and scope == "class":
|
if node is None and scope == "class":
|
||||||
# fallback to function item itself
|
# fallback to function item itself
|
||||||
node = self._pyfuncitem
|
node = self._pyfuncitem
|
||||||
|
|
|
@ -116,6 +116,7 @@ class Node(object):
|
||||||
Function = _CompatProperty("Function")
|
Function = _CompatProperty("Function")
|
||||||
File = _CompatProperty("File")
|
File = _CompatProperty("File")
|
||||||
Item = _CompatProperty("Item")
|
Item = _CompatProperty("Item")
|
||||||
|
Package = _CompatProperty("Package")
|
||||||
|
|
||||||
def _getcustomclass(self, name):
|
def _getcustomclass(self, name):
|
||||||
maybe_compatprop = getattr(type(self), name)
|
maybe_compatprop = getattr(type(self), name)
|
||||||
|
|
|
@ -13,11 +13,11 @@ from itertools import count
|
||||||
|
|
||||||
import py
|
import py
|
||||||
import six
|
import six
|
||||||
|
from _pytest.main import FSHookProxy
|
||||||
from _pytest.mark import MarkerError
|
from _pytest.mark import MarkerError
|
||||||
from _pytest.config import hookimpl
|
from _pytest.config import hookimpl
|
||||||
|
|
||||||
import _pytest
|
import _pytest
|
||||||
from _pytest.main import Session
|
|
||||||
import pluggy
|
import pluggy
|
||||||
from _pytest import fixtures
|
from _pytest import fixtures
|
||||||
from _pytest import nodes
|
from _pytest import nodes
|
||||||
|
@ -490,7 +490,7 @@ class Module(nodes.File, PyCollector):
|
||||||
self.addfinalizer(teardown_module)
|
self.addfinalizer(teardown_module)
|
||||||
|
|
||||||
|
|
||||||
class Package(Session, Module):
|
class Package(Module):
|
||||||
|
|
||||||
def __init__(self, fspath, parent=None, config=None, session=None, nodeid=None):
|
def __init__(self, fspath, parent=None, config=None, session=None, nodeid=None):
|
||||||
session = parent.session
|
session = parent.session
|
||||||
|
@ -503,7 +503,38 @@ class Package(Session, Module):
|
||||||
for path in list(session.config.pluginmanager._duplicatepaths):
|
for path in list(session.config.pluginmanager._duplicatepaths):
|
||||||
if path.dirname == fspath.dirname and path != fspath:
|
if path.dirname == fspath.dirname and path != fspath:
|
||||||
session.config.pluginmanager._duplicatepaths.remove(path)
|
session.config.pluginmanager._duplicatepaths.remove(path)
|
||||||
pass
|
|
||||||
|
def _recurse(self, path):
|
||||||
|
ihook = self.gethookproxy(path.dirpath())
|
||||||
|
if ihook.pytest_ignore_collect(path=path, config=self.config):
|
||||||
|
return
|
||||||
|
for pat in self._norecursepatterns:
|
||||||
|
if path.check(fnmatch=pat):
|
||||||
|
return False
|
||||||
|
ihook = self.gethookproxy(path)
|
||||||
|
ihook.pytest_collect_directory(path=path, 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):
|
||||||
|
ihook = self.gethookproxy(path)
|
||||||
|
if not self.isinitpath(path):
|
||||||
|
if ihook.pytest_ignore_collect(path=path, config=self.config):
|
||||||
|
return ()
|
||||||
|
return ihook.pytest_collect_file(path=path, parent=self)
|
||||||
|
|
||||||
def isinitpath(self, path):
|
def isinitpath(self, path):
|
||||||
return path in self.session._initialpaths
|
return path in self.session._initialpaths
|
||||||
|
|
Loading…
Reference in New Issue