Merge branch 'master' into master-to-features
This commit is contained in:
commit
a435faad5c
|
@ -849,7 +849,7 @@ Running this test will *skip* the invocation of ``data_set`` with value ``2``:
|
||||||
Modularity: using fixtures from a fixture function
|
Modularity: using fixtures from a fixture function
|
||||||
----------------------------------------------------------
|
----------------------------------------------------------
|
||||||
|
|
||||||
You can not only use fixtures in test functions but fixture functions
|
In addition to using fixtures in test functions, fixture functions
|
||||||
can use other fixtures themselves. This contributes to a modular design
|
can use other fixtures themselves. This contributes to a modular design
|
||||||
of your fixtures and allows re-use of framework-specific fixtures across
|
of your fixtures and allows re-use of framework-specific fixtures across
|
||||||
many projects. As a simple example, we can extend the previous example
|
many projects. As a simple example, we can extend the previous example
|
||||||
|
|
|
@ -61,7 +61,9 @@ def parse_changelog(tag_name):
|
||||||
|
|
||||||
|
|
||||||
def convert_rst_to_md(text):
|
def convert_rst_to_md(text):
|
||||||
return pypandoc.convert_text(text, "md", format="rst", extra_args=["--wrap=none"])
|
return pypandoc.convert_text(
|
||||||
|
text, "md", format="rst", extra_args=["--wrap=preserve"]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def main(argv):
|
def main(argv):
|
||||||
|
|
|
@ -14,5 +14,5 @@ python -m coverage combine
|
||||||
python -m coverage xml
|
python -m coverage xml
|
||||||
python -m coverage report -m
|
python -m coverage report -m
|
||||||
# Set --connect-timeout to work around https://github.com/curl/curl/issues/4461
|
# Set --connect-timeout to work around https://github.com/curl/curl/issues/4461
|
||||||
curl -S -L --connect-timeout 5 --retry 6 --retry-connrefused -s https://codecov.io/bash -o codecov-upload.sh
|
curl -S -L --connect-timeout 5 --retry 6 -s https://codecov.io/bash -o codecov-upload.sh
|
||||||
bash codecov-upload.sh -Z -X fix -f coverage.xml "$@"
|
bash codecov-upload.sh -Z -X fix -f coverage.xml "$@"
|
||||||
|
|
|
@ -7,6 +7,10 @@ from typing import Optional
|
||||||
from _pytest.assertion import rewrite
|
from _pytest.assertion import rewrite
|
||||||
from _pytest.assertion import truncate
|
from _pytest.assertion import truncate
|
||||||
from _pytest.assertion import util
|
from _pytest.assertion import util
|
||||||
|
from _pytest.compat import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from _pytest.main import Session
|
||||||
|
|
||||||
|
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
|
@ -91,7 +95,7 @@ def install_importhook(config):
|
||||||
return hook
|
return hook
|
||||||
|
|
||||||
|
|
||||||
def pytest_collection(session):
|
def pytest_collection(session: "Session") -> None:
|
||||||
# this hook is only called when test modules are collected
|
# this hook is only called when test modules are collected
|
||||||
# so for example not in the master process of pytest-xdist
|
# so for example not in the master process of pytest-xdist
|
||||||
# (which does not collect test modules)
|
# (which does not collect test modules)
|
||||||
|
|
|
@ -144,12 +144,12 @@ def getfuncargnames(
|
||||||
the case of cls, the function is a static method.
|
the case of cls, the function is a static method.
|
||||||
|
|
||||||
The name parameter should be the original name in which the function was collected.
|
The name parameter should be the original name in which the function was collected.
|
||||||
|
|
||||||
@RonnyPfannschmidt: This function should be refactored when we
|
|
||||||
revisit fixtures. The fixture mechanism should ask the node for
|
|
||||||
the fixture names, and not try to obtain directly from the
|
|
||||||
function object well after collection has occurred.
|
|
||||||
"""
|
"""
|
||||||
|
# TODO(RonnyPfannschmidt): This function should be refactored when we
|
||||||
|
# revisit fixtures. The fixture mechanism should ask the node for
|
||||||
|
# the fixture names, and not try to obtain directly from the
|
||||||
|
# function object well after collection has occurred.
|
||||||
|
|
||||||
# The parameters attribute of a Signature object contains an
|
# The parameters attribute of a Signature object contains an
|
||||||
# ordered mapping of parameter names to Parameter instances. This
|
# ordered mapping of parameter names to Parameter instances. This
|
||||||
# creates a tuple of the names of the parameters that don't have
|
# creates a tuple of the names of the parameters that don't have
|
||||||
|
|
|
@ -31,6 +31,7 @@ from _pytest.compat import safe_getattr
|
||||||
from _pytest.compat import TYPE_CHECKING
|
from _pytest.compat import TYPE_CHECKING
|
||||||
from _pytest.deprecated import FIXTURE_POSITIONAL_ARGUMENTS
|
from _pytest.deprecated import FIXTURE_POSITIONAL_ARGUMENTS
|
||||||
from _pytest.deprecated import FUNCARGNAMES
|
from _pytest.deprecated import FUNCARGNAMES
|
||||||
|
from _pytest.mark import ParameterSet
|
||||||
from _pytest.outcomes import fail
|
from _pytest.outcomes import fail
|
||||||
from _pytest.outcomes import TEST_OUTCOME
|
from _pytest.outcomes import TEST_OUTCOME
|
||||||
|
|
||||||
|
@ -1262,8 +1263,6 @@ class FixtureManager:
|
||||||
This things are done later as well when dealing with parametrization
|
This things are done later as well when dealing with parametrization
|
||||||
so this could be improved
|
so this could be improved
|
||||||
"""
|
"""
|
||||||
from _pytest.mark import ParameterSet
|
|
||||||
|
|
||||||
parametrize_argnames = []
|
parametrize_argnames = []
|
||||||
for marker in node.iter_markers(name="parametrize"):
|
for marker in node.iter_markers(name="parametrize"):
|
||||||
if not marker.kwargs.get("indirect", False):
|
if not marker.kwargs.get("indirect", False):
|
||||||
|
|
|
@ -1,6 +1,14 @@
|
||||||
""" hook specifications for pytest plugins, invoked from main.py and builtin plugins. """
|
""" hook specifications for pytest plugins, invoked from main.py and builtin plugins. """
|
||||||
|
from typing import Any
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from pluggy import HookspecMarker
|
from pluggy import HookspecMarker
|
||||||
|
|
||||||
|
from _pytest.compat import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from _pytest.main import Session
|
||||||
|
|
||||||
|
|
||||||
hookspec = HookspecMarker("pytest")
|
hookspec = HookspecMarker("pytest")
|
||||||
|
|
||||||
|
@ -158,7 +166,7 @@ def pytest_load_initial_conftests(early_config, parser, args):
|
||||||
|
|
||||||
|
|
||||||
@hookspec(firstresult=True)
|
@hookspec(firstresult=True)
|
||||||
def pytest_collection(session):
|
def pytest_collection(session: "Session") -> Optional[Any]:
|
||||||
"""Perform the collection protocol for the given session.
|
"""Perform the collection protocol for the given session.
|
||||||
|
|
||||||
Stops at first non-None result, see :ref:`firstresult`.
|
Stops at first non-None result, see :ref:`firstresult`.
|
||||||
|
|
|
@ -5,6 +5,7 @@ from contextlib import contextmanager
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from typing import AbstractSet
|
from typing import AbstractSet
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
from typing import Generator
|
||||||
from typing import List
|
from typing import List
|
||||||
from typing import Mapping
|
from typing import Mapping
|
||||||
|
|
||||||
|
@ -597,7 +598,7 @@ class LoggingPlugin:
|
||||||
) is not None or self._config.getini("log_cli")
|
) is not None or self._config.getini("log_cli")
|
||||||
|
|
||||||
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
|
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
|
||||||
def pytest_collection(self):
|
def pytest_collection(self) -> Generator[None, None, None]:
|
||||||
with self.live_logs_context():
|
with self.live_logs_context():
|
||||||
if self.log_cli_handler:
|
if self.log_cli_handler:
|
||||||
self.log_cli_handler.set_when("collection")
|
self.log_cli_handler.set_when("collection")
|
||||||
|
|
|
@ -8,8 +8,10 @@ import sys
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from typing import FrozenSet
|
from typing import FrozenSet
|
||||||
from typing import List
|
from typing import List
|
||||||
|
from typing import Optional
|
||||||
from typing import Sequence
|
from typing import Sequence
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
import attr
|
import attr
|
||||||
import py
|
import py
|
||||||
|
@ -253,7 +255,7 @@ def pytest_cmdline_main(config):
|
||||||
return wrap_session(config, _main)
|
return wrap_session(config, _main)
|
||||||
|
|
||||||
|
|
||||||
def _main(config, session):
|
def _main(config: Config, session: "Session") -> Optional[Union[int, ExitCode]]:
|
||||||
""" default command line protocol for initialization, session,
|
""" default command line protocol for initialization, session,
|
||||||
running tests and reporting. """
|
running tests and reporting. """
|
||||||
config.hook.pytest_collection(session=session)
|
config.hook.pytest_collection(session=session)
|
||||||
|
@ -263,6 +265,7 @@ def _main(config, session):
|
||||||
return ExitCode.TESTS_FAILED
|
return ExitCode.TESTS_FAILED
|
||||||
elif session.testscollected == 0:
|
elif session.testscollected == 0:
|
||||||
return ExitCode.NO_TESTS_COLLECTED
|
return ExitCode.NO_TESTS_COLLECTED
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def pytest_collection(session):
|
def pytest_collection(session):
|
||||||
|
@ -352,18 +355,6 @@ def pytest_collection_modifyitems(items, config):
|
||||||
items[:] = remaining
|
items[:] = remaining
|
||||||
|
|
||||||
|
|
||||||
class FSHookProxy:
|
|
||||||
def __init__(self, fspath, pm, remove_mods):
|
|
||||||
self.fspath = fspath
|
|
||||||
self.pm = pm
|
|
||||||
self.remove_mods = remove_mods
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
|
||||||
x = self.pm.subset_hook_caller(name, remove_plugins=self.remove_mods)
|
|
||||||
self.__dict__[name] = x
|
|
||||||
return x
|
|
||||||
|
|
||||||
|
|
||||||
class NoMatch(Exception):
|
class NoMatch(Exception):
|
||||||
""" raised if matching cannot locate a matching names. """
|
""" raised if matching cannot locate a matching names. """
|
||||||
|
|
||||||
|
@ -405,7 +396,6 @@ class Session(nodes.FSCollector):
|
||||||
self.shouldstop = False
|
self.shouldstop = False
|
||||||
self.shouldfail = False
|
self.shouldfail = False
|
||||||
self.trace = config.trace.root.get("collection")
|
self.trace = config.trace.root.get("collection")
|
||||||
self._norecursepatterns = config.getini("norecursedirs")
|
|
||||||
self.startdir = config.invocation_dir
|
self.startdir = config.invocation_dir
|
||||||
self._initialpaths = frozenset() # type: FrozenSet[py.path.local]
|
self._initialpaths = frozenset() # type: FrozenSet[py.path.local]
|
||||||
|
|
||||||
|
@ -466,19 +456,8 @@ class Session(nodes.FSCollector):
|
||||||
def isinitpath(self, path):
|
def isinitpath(self, path):
|
||||||
return path in self._initialpaths
|
return path in self._initialpaths
|
||||||
|
|
||||||
def gethookproxy(self, fspath):
|
def gethookproxy(self, fspath: py.path.local):
|
||||||
# check if we have the common case of running
|
return super()._gethookproxy(fspath)
|
||||||
# hooks with all conftest.py files
|
|
||||||
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 plugins are active for this fspath
|
|
||||||
proxy = self.config.hook
|
|
||||||
return proxy
|
|
||||||
|
|
||||||
def perform_collect(self, args=None, genitems=True):
|
def perform_collect(self, args=None, genitems=True):
|
||||||
hook = self.config.hook
|
hook = self.config.hook
|
||||||
|
@ -643,19 +622,6 @@ class Session(nodes.FSCollector):
|
||||||
|
|
||||||
return ihook.pytest_collect_file(path=path, parent=self)
|
return ihook.pytest_collect_file(path=path, parent=self)
|
||||||
|
|
||||||
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 False
|
|
||||||
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
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _visit_filter(f):
|
def _visit_filter(f):
|
||||||
return f.check(file=1)
|
return f.check(file=1)
|
||||||
|
|
|
@ -19,6 +19,7 @@ from _pytest.compat import cached_property
|
||||||
from _pytest.compat import getfslineno
|
from _pytest.compat import getfslineno
|
||||||
from _pytest.compat import TYPE_CHECKING
|
from _pytest.compat import TYPE_CHECKING
|
||||||
from _pytest.config import Config
|
from _pytest.config import Config
|
||||||
|
from _pytest.config import PytestPluginManager
|
||||||
from _pytest.deprecated import NODE_USE_FROM_PARENT
|
from _pytest.deprecated import NODE_USE_FROM_PARENT
|
||||||
from _pytest.fixtures import FixtureDef
|
from _pytest.fixtures import FixtureDef
|
||||||
from _pytest.fixtures import FixtureLookupError
|
from _pytest.fixtures import FixtureLookupError
|
||||||
|
@ -423,6 +424,20 @@ def _check_initialpaths_for_relpath(session, fspath):
|
||||||
return fspath.relto(initial_path)
|
return fspath.relto(initial_path)
|
||||||
|
|
||||||
|
|
||||||
|
class FSHookProxy:
|
||||||
|
def __init__(
|
||||||
|
self, fspath: py.path.local, pm: PytestPluginManager, remove_mods
|
||||||
|
) -> None:
|
||||||
|
self.fspath = fspath
|
||||||
|
self.pm = pm
|
||||||
|
self.remove_mods = remove_mods
|
||||||
|
|
||||||
|
def __getattr__(self, name: str):
|
||||||
|
x = self.pm.subset_hook_caller(name, remove_plugins=self.remove_mods)
|
||||||
|
self.__dict__[name] = x
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
class FSCollector(Collector):
|
class FSCollector(Collector):
|
||||||
def __init__(
|
def __init__(
|
||||||
self, fspath: py.path.local, parent=None, config=None, session=None, nodeid=None
|
self, fspath: py.path.local, parent=None, config=None, session=None, nodeid=None
|
||||||
|
@ -447,6 +462,8 @@ class FSCollector(Collector):
|
||||||
|
|
||||||
super().__init__(name, parent, config, session, nodeid=nodeid, fspath=fspath)
|
super().__init__(name, parent, config, session, nodeid=nodeid, fspath=fspath)
|
||||||
|
|
||||||
|
self._norecursepatterns = self.config.getini("norecursedirs")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_parent(cls, parent, *, fspath):
|
def from_parent(cls, parent, *, fspath):
|
||||||
"""
|
"""
|
||||||
|
@ -454,6 +471,33 @@ class FSCollector(Collector):
|
||||||
"""
|
"""
|
||||||
return super().from_parent(parent=parent, fspath=fspath)
|
return super().from_parent(parent=parent, fspath=fspath)
|
||||||
|
|
||||||
|
def _gethookproxy(self, fspath: py.path.local):
|
||||||
|
# check if we have the common case of running
|
||||||
|
# hooks with all conftest.py files
|
||||||
|
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 plugins are active for this fspath
|
||||||
|
proxy = self.config.hook
|
||||||
|
return proxy
|
||||||
|
|
||||||
|
def _recurse(self, dirpath: py.path.local) -> bool:
|
||||||
|
if dirpath.basename == "__pycache__":
|
||||||
|
return False
|
||||||
|
ihook = self._gethookproxy(dirpath.dirpath())
|
||||||
|
if ihook.pytest_ignore_collect(path=dirpath, config=self.config):
|
||||||
|
return False
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
class File(FSCollector):
|
class File(FSCollector):
|
||||||
""" base class for collecting tests from a file. """
|
""" base class for collecting tests from a file. """
|
||||||
|
|
|
@ -36,7 +36,6 @@ from _pytest.compat import safe_isclass
|
||||||
from _pytest.compat import STRING_TYPES
|
from _pytest.compat import STRING_TYPES
|
||||||
from _pytest.config import hookimpl
|
from _pytest.config import hookimpl
|
||||||
from _pytest.deprecated import FUNCARGNAMES
|
from _pytest.deprecated import FUNCARGNAMES
|
||||||
from _pytest.main import FSHookProxy
|
|
||||||
from _pytest.mark import MARK_GEN
|
from _pytest.mark import MARK_GEN
|
||||||
from _pytest.mark.structures import get_unpacked_marks
|
from _pytest.mark.structures import get_unpacked_marks
|
||||||
from _pytest.mark.structures import Mark
|
from _pytest.mark.structures import Mark
|
||||||
|
@ -149,27 +148,30 @@ def pytest_configure(config):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@hookimpl(trylast=True)
|
def async_warn(nodeid: str) -> None:
|
||||||
def pytest_pyfunc_call(pyfuncitem):
|
msg = "async def functions are not natively supported and have been skipped.\n"
|
||||||
def async_warn():
|
msg += (
|
||||||
msg = "async def functions are not natively supported and have been skipped.\n"
|
"You need to install a suitable plugin for your async framework, for example:\n"
|
||||||
msg += "You need to install a suitable plugin for your async framework, for example:\n"
|
)
|
||||||
msg += " - pytest-asyncio\n"
|
msg += " - pytest-asyncio\n"
|
||||||
msg += " - pytest-trio\n"
|
msg += " - pytest-trio\n"
|
||||||
msg += " - pytest-tornasync"
|
msg += " - pytest-tornasync"
|
||||||
warnings.warn(PytestUnhandledCoroutineWarning(msg.format(pyfuncitem.nodeid)))
|
warnings.warn(PytestUnhandledCoroutineWarning(msg.format(nodeid)))
|
||||||
skip(msg="async def function and no async plugin installed (see warnings)")
|
skip(msg="async def function and no async plugin installed (see warnings)")
|
||||||
|
|
||||||
|
|
||||||
|
@hookimpl(trylast=True)
|
||||||
|
def pytest_pyfunc_call(pyfuncitem: "Function"):
|
||||||
testfunction = pyfuncitem.obj
|
testfunction = pyfuncitem.obj
|
||||||
if iscoroutinefunction(testfunction) or (
|
if iscoroutinefunction(testfunction) or (
|
||||||
sys.version_info >= (3, 6) and inspect.isasyncgenfunction(testfunction)
|
sys.version_info >= (3, 6) and inspect.isasyncgenfunction(testfunction)
|
||||||
):
|
):
|
||||||
async_warn()
|
async_warn(pyfuncitem.nodeid)
|
||||||
funcargs = pyfuncitem.funcargs
|
funcargs = pyfuncitem.funcargs
|
||||||
testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames}
|
testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames}
|
||||||
result = testfunction(**testargs)
|
result = testfunction(**testargs)
|
||||||
if hasattr(result, "__await__") or hasattr(result, "__aiter__"):
|
if hasattr(result, "__await__") or hasattr(result, "__aiter__"):
|
||||||
async_warn()
|
async_warn(pyfuncitem.nodeid)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -547,15 +549,23 @@ class Module(nodes.File, PyCollector):
|
||||||
|
|
||||||
|
|
||||||
class Package(Module):
|
class Package(Module):
|
||||||
def __init__(self, fspath, parent=None, config=None, session=None, nodeid=None):
|
def __init__(
|
||||||
|
self,
|
||||||
|
fspath: py.path.local,
|
||||||
|
parent: nodes.Collector,
|
||||||
|
# NOTE: following args are unused:
|
||||||
|
config=None,
|
||||||
|
session=None,
|
||||||
|
nodeid=None,
|
||||||
|
) -> None:
|
||||||
|
# NOTE: could be just the following, but kept as-is for compat.
|
||||||
|
# nodes.FSCollector.__init__(self, fspath, parent=parent)
|
||||||
session = parent.session
|
session = parent.session
|
||||||
nodes.FSCollector.__init__(
|
nodes.FSCollector.__init__(
|
||||||
self, fspath, parent=parent, config=config, session=session, nodeid=nodeid
|
self, fspath, parent=parent, config=config, session=session, nodeid=nodeid
|
||||||
)
|
)
|
||||||
|
|
||||||
self.name = fspath.dirname
|
self.name = fspath.dirname
|
||||||
self.trace = session.trace
|
|
||||||
self._norecursepatterns = session._norecursepatterns
|
|
||||||
self.fspath = fspath
|
|
||||||
|
|
||||||
def setup(self):
|
def setup(self):
|
||||||
# not using fixtures to call setup_module here because autouse fixtures
|
# not using fixtures to call setup_module here because autouse fixtures
|
||||||
|
@ -573,32 +583,8 @@ class Package(Module):
|
||||||
func = partial(_call_with_optional_argument, teardown_module, self.obj)
|
func = partial(_call_with_optional_argument, teardown_module, self.obj)
|
||||||
self.addfinalizer(func)
|
self.addfinalizer(func)
|
||||||
|
|
||||||
def _recurse(self, dirpath):
|
def gethookproxy(self, fspath: py.path.local):
|
||||||
if dirpath.basename == "__pycache__":
|
return super()._gethookproxy(fspath)
|
||||||
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 plugins are active for this fspath
|
|
||||||
proxy = self.config.hook
|
|
||||||
return proxy
|
|
||||||
|
|
||||||
def _collectfile(self, path, handle_dupes=True):
|
def _collectfile(self, path, handle_dupes=True):
|
||||||
assert (
|
assert (
|
||||||
|
|
|
@ -521,7 +521,7 @@ class TerminalReporter:
|
||||||
# py < 1.6.0
|
# py < 1.6.0
|
||||||
return self._tw.chars_on_current_line
|
return self._tw.chars_on_current_line
|
||||||
|
|
||||||
def pytest_collection(self):
|
def pytest_collection(self) -> None:
|
||||||
if self.isatty:
|
if self.isatty:
|
||||||
if self.config.option.verbose >= 0:
|
if self.config.option.verbose >= 0:
|
||||||
self.write("collecting ... ", bold=True)
|
self.write("collecting ... ", bold=True)
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import sys
|
import sys
|
||||||
import warnings
|
import warnings
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
from typing import Generator
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
from _pytest.main import Session
|
||||||
|
|
||||||
|
|
||||||
def _setoption(wmod, arg):
|
def _setoption(wmod, arg):
|
||||||
|
@ -117,7 +119,7 @@ def pytest_runtest_protocol(item):
|
||||||
|
|
||||||
|
|
||||||
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
|
@pytest.hookimpl(hookwrapper=True, tryfirst=True)
|
||||||
def pytest_collection(session):
|
def pytest_collection(session: Session) -> Generator[None, None, None]:
|
||||||
config = session.config
|
config = session.config
|
||||||
with catch_warnings_for_item(
|
with catch_warnings_for_item(
|
||||||
config=config, ihook=config.hook, when="collect", item=None
|
config=config, ihook=config.hook, when="collect", item=None
|
||||||
|
|
2
tox.ini
2
tox.ini
|
@ -147,7 +147,7 @@ commands = python scripts/publish-gh-release-notes.py {posargs}
|
||||||
|
|
||||||
[pytest]
|
[pytest]
|
||||||
minversion = 2.0
|
minversion = 2.0
|
||||||
addopts = -ra -p pytester --strict-markers
|
addopts = -rfEX -p pytester --strict-markers
|
||||||
rsyncdirs = tox.ini doc src testing
|
rsyncdirs = tox.ini doc src testing
|
||||||
python_files = test_*.py *_test.py testing/*/*.py
|
python_files = test_*.py *_test.py testing/*/*.py
|
||||||
python_classes = Test Acceptance
|
python_classes = Test Acceptance
|
||||||
|
|
Loading…
Reference in New Issue