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 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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue