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:
turturica 2018-04-20 15:04:58 -07:00
parent 7d923c389e
commit c416b1d935
3 changed files with 51 additions and 4 deletions

View File

@ -2,6 +2,7 @@ from __future__ import absolute_import, division, print_function
import functools
import inspect
import os
import sys
import warnings
from collections import OrderedDict, deque, defaultdict
@ -70,6 +71,17 @@ def scopeproperty(name=None, doc=None):
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):
cls = scopename2class.get(scope)
if cls is None:
@ -558,7 +570,10 @@ class FixtureRequest(FuncargnamesCompatAttr):
if scope == "function":
# this might also be a non-function Item despite its attribute name
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":
# fallback to function item itself
node = self._pyfuncitem

View File

@ -116,6 +116,7 @@ class Node(object):
Function = _CompatProperty("Function")
File = _CompatProperty("File")
Item = _CompatProperty("Item")
Package = _CompatProperty("Package")
def _getcustomclass(self, name):
maybe_compatprop = getattr(type(self), name)

View File

@ -13,11 +13,11 @@ from itertools import count
import py
import six
from _pytest.main import FSHookProxy
from _pytest.mark import MarkerError
from _pytest.config import hookimpl
import _pytest
from _pytest.main import Session
import pluggy
from _pytest import fixtures
from _pytest import nodes
@ -490,7 +490,7 @@ class Module(nodes.File, PyCollector):
self.addfinalizer(teardown_module)
class Package(Session, Module):
class Package(Module):
def __init__(self, fspath, parent=None, config=None, session=None, nodeid=None):
session = parent.session
@ -503,7 +503,38 @@ class Package(Session, Module):
for path in list(session.config.pluginmanager._duplicatepaths):
if path.dirname == fspath.dirname and path != fspath:
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):
return path in self.session._initialpaths