implement Node.path as pathlib.Path
* reorganize lastfailed node sort Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
This commit is contained in:
parent
19a2f7425d
commit
22dad53a24
|
@ -0,0 +1 @@
|
||||||
|
Deprecate ``Node.fspath`` as we plan to move off `py.path.local <https://py.readthedocs.io/en/latest/path.html>`__ and switch to :mod:``pathlib``.
|
|
@ -0,0 +1 @@
|
||||||
|
Implement ``Node.path`` as a ``pathlib.Path``.
|
|
@ -19,6 +19,16 @@ Below is a complete list of all pytest features which are considered deprecated.
|
||||||
:class:`PytestWarning` or subclasses, which can be filtered using :ref:`standard warning filters <warnings>`.
|
:class:`PytestWarning` or subclasses, which can be filtered using :ref:`standard warning filters <warnings>`.
|
||||||
|
|
||||||
|
|
||||||
|
``Node.fspath`` in favor of ``pathlib`` and ``Node.path``
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. deprecated:: 6.3
|
||||||
|
|
||||||
|
As pytest tries to move off `py.path.local <https://py.readthedocs.io/en/latest/path.html>`__ we ported most of the node internals to :mod:`pathlib`.
|
||||||
|
|
||||||
|
Pytest will provide compatibility for quite a while.
|
||||||
|
|
||||||
|
|
||||||
Backward compatibilities in ``Parser.addoption``
|
Backward compatibilities in ``Parser.addoption``
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
|
@ -218,14 +218,17 @@ class LFPluginCollWrapper:
|
||||||
|
|
||||||
# Sort any lf-paths to the beginning.
|
# Sort any lf-paths to the beginning.
|
||||||
lf_paths = self.lfplugin._last_failed_paths
|
lf_paths = self.lfplugin._last_failed_paths
|
||||||
|
|
||||||
res.result = sorted(
|
res.result = sorted(
|
||||||
res.result,
|
res.result,
|
||||||
key=lambda x: 0 if Path(str(x.fspath)) in lf_paths else 1,
|
# use stable sort to priorize last failed
|
||||||
|
key=lambda x: x.path in lf_paths,
|
||||||
|
reverse=True,
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
elif isinstance(collector, Module):
|
elif isinstance(collector, Module):
|
||||||
if Path(str(collector.fspath)) in self.lfplugin._last_failed_paths:
|
if collector.path in self.lfplugin._last_failed_paths:
|
||||||
out = yield
|
out = yield
|
||||||
res = out.get_result()
|
res = out.get_result()
|
||||||
result = res.result
|
result = res.result
|
||||||
|
@ -246,7 +249,7 @@ class LFPluginCollWrapper:
|
||||||
for x in result
|
for x in result
|
||||||
if x.nodeid in lastfailed
|
if x.nodeid in lastfailed
|
||||||
# Include any passed arguments (not trivial to filter).
|
# Include any passed arguments (not trivial to filter).
|
||||||
or session.isinitpath(x.fspath)
|
or session.isinitpath(x.path)
|
||||||
# Keep all sub-collectors.
|
# Keep all sub-collectors.
|
||||||
or isinstance(x, nodes.Collector)
|
or isinstance(x, nodes.Collector)
|
||||||
]
|
]
|
||||||
|
@ -266,7 +269,7 @@ class LFPluginCollSkipfiles:
|
||||||
# test-bearing paths and doesn't try to include the paths of their
|
# test-bearing paths and doesn't try to include the paths of their
|
||||||
# packages, so don't filter them.
|
# packages, so don't filter them.
|
||||||
if isinstance(collector, Module) and not isinstance(collector, Package):
|
if isinstance(collector, Module) and not isinstance(collector, Package):
|
||||||
if Path(str(collector.fspath)) not in self.lfplugin._last_failed_paths:
|
if collector.path not in self.lfplugin._last_failed_paths:
|
||||||
self.lfplugin._skipped_files += 1
|
self.lfplugin._skipped_files += 1
|
||||||
|
|
||||||
return CollectReport(
|
return CollectReport(
|
||||||
|
@ -415,7 +418,7 @@ class NFPlugin:
|
||||||
self.cached_nodeids.update(item.nodeid for item in items)
|
self.cached_nodeids.update(item.nodeid for item in items)
|
||||||
|
|
||||||
def _get_increasing_order(self, items: Iterable[nodes.Item]) -> List[nodes.Item]:
|
def _get_increasing_order(self, items: Iterable[nodes.Item]) -> List[nodes.Item]:
|
||||||
return sorted(items, key=lambda item: item.fspath.mtime(), reverse=True)
|
return sorted(items, key=lambda item: item.path.stat().st_mtime, reverse=True) # type: ignore[no-any-return]
|
||||||
|
|
||||||
def pytest_sessionfinish(self) -> None:
|
def pytest_sessionfinish(self) -> None:
|
||||||
config = self.config
|
config = self.config
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import enum
|
import enum
|
||||||
import functools
|
import functools
|
||||||
import inspect
|
import inspect
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
@ -18,6 +19,7 @@ from typing import TypeVar
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import attr
|
import attr
|
||||||
|
import py
|
||||||
|
|
||||||
from _pytest.outcomes import fail
|
from _pytest.outcomes import fail
|
||||||
from _pytest.outcomes import TEST_OUTCOME
|
from _pytest.outcomes import TEST_OUTCOME
|
||||||
|
@ -30,6 +32,16 @@ if TYPE_CHECKING:
|
||||||
_T = TypeVar("_T")
|
_T = TypeVar("_T")
|
||||||
_S = TypeVar("_S")
|
_S = TypeVar("_S")
|
||||||
|
|
||||||
|
#: constant to prepare valuing py.path.local replacements/lazy proxies later on
|
||||||
|
# intended for removal in pytest 8.0 or 9.0
|
||||||
|
|
||||||
|
LEGACY_PATH = py.path.local
|
||||||
|
|
||||||
|
|
||||||
|
def legacy_path(path: Union[str, "os.PathLike[str]"]) -> LEGACY_PATH:
|
||||||
|
"""Internal wrapper to prepare lazy proxies for py.path.local instances"""
|
||||||
|
return py.path.local(path)
|
||||||
|
|
||||||
|
|
||||||
# fmt: off
|
# fmt: off
|
||||||
# Singleton type for NOTSET, as described in:
|
# Singleton type for NOTSET, as described in:
|
||||||
|
|
|
@ -89,6 +89,12 @@ ARGUMENT_TYPE_STR = UnformattedWarning(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
NODE_FSPATH = UnformattedWarning(
|
||||||
|
PytestDeprecationWarning,
|
||||||
|
"{type}.fspath is deprecated and will be replaced by {type}.path.\n"
|
||||||
|
"see TODO;URL for details on replacing py.path.local with pathlib.Path",
|
||||||
|
)
|
||||||
|
|
||||||
# You want to make some `__init__` or function "private".
|
# You want to make some `__init__` or function "private".
|
||||||
#
|
#
|
||||||
# def my_private_function(some, args):
|
# def my_private_function(some, args):
|
||||||
|
@ -106,6 +112,8 @@ ARGUMENT_TYPE_STR = UnformattedWarning(
|
||||||
#
|
#
|
||||||
# All other calls will get the default _ispytest=False and trigger
|
# All other calls will get the default _ispytest=False and trigger
|
||||||
# the warning (possibly error in the future).
|
# the warning (possibly error in the future).
|
||||||
|
|
||||||
|
|
||||||
def check_ispytest(ispytest: bool) -> None:
|
def check_ispytest(ispytest: bool) -> None:
|
||||||
if not ispytest:
|
if not ispytest:
|
||||||
warn(PRIVATE, stacklevel=3)
|
warn(PRIVATE, stacklevel=3)
|
||||||
|
|
|
@ -30,6 +30,7 @@ from _pytest._code.code import ExceptionInfo
|
||||||
from _pytest._code.code import ReprFileLocation
|
from _pytest._code.code import ReprFileLocation
|
||||||
from _pytest._code.code import TerminalRepr
|
from _pytest._code.code import TerminalRepr
|
||||||
from _pytest._io import TerminalWriter
|
from _pytest._io import TerminalWriter
|
||||||
|
from _pytest.compat import legacy_path
|
||||||
from _pytest.compat import safe_getattr
|
from _pytest.compat import safe_getattr
|
||||||
from _pytest.config import Config
|
from _pytest.config import Config
|
||||||
from _pytest.config.argparsing import Parser
|
from _pytest.config.argparsing import Parser
|
||||||
|
@ -128,10 +129,10 @@ def pytest_collect_file(
|
||||||
config = parent.config
|
config = parent.config
|
||||||
if fspath.suffix == ".py":
|
if fspath.suffix == ".py":
|
||||||
if config.option.doctestmodules and not _is_setup_py(fspath):
|
if config.option.doctestmodules and not _is_setup_py(fspath):
|
||||||
mod: DoctestModule = DoctestModule.from_parent(parent, fspath=path)
|
mod: DoctestModule = DoctestModule.from_parent(parent, path=fspath)
|
||||||
return mod
|
return mod
|
||||||
elif _is_doctest(config, fspath, parent):
|
elif _is_doctest(config, fspath, parent):
|
||||||
txt: DoctestTextfile = DoctestTextfile.from_parent(parent, fspath=path)
|
txt: DoctestTextfile = DoctestTextfile.from_parent(parent, path=fspath)
|
||||||
return txt
|
return txt
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -378,7 +379,7 @@ class DoctestItem(pytest.Item):
|
||||||
|
|
||||||
def reportinfo(self):
|
def reportinfo(self):
|
||||||
assert self.dtest is not None
|
assert self.dtest is not None
|
||||||
return self.fspath, self.dtest.lineno, "[doctest] %s" % self.name
|
return legacy_path(self.path), self.dtest.lineno, "[doctest] %s" % self.name
|
||||||
|
|
||||||
|
|
||||||
def _get_flag_lookup() -> Dict[str, int]:
|
def _get_flag_lookup() -> Dict[str, int]:
|
||||||
|
@ -425,9 +426,9 @@ class DoctestTextfile(pytest.Module):
|
||||||
# Inspired by doctest.testfile; ideally we would use it directly,
|
# Inspired by doctest.testfile; ideally we would use it directly,
|
||||||
# but it doesn't support passing a custom checker.
|
# but it doesn't support passing a custom checker.
|
||||||
encoding = self.config.getini("doctest_encoding")
|
encoding = self.config.getini("doctest_encoding")
|
||||||
text = self.fspath.read_text(encoding)
|
text = self.path.read_text(encoding)
|
||||||
filename = str(self.fspath)
|
filename = str(self.path)
|
||||||
name = self.fspath.basename
|
name = self.path.name
|
||||||
globs = {"__name__": "__main__"}
|
globs = {"__name__": "__main__"}
|
||||||
|
|
||||||
optionflags = get_optionflags(self)
|
optionflags = get_optionflags(self)
|
||||||
|
@ -534,16 +535,16 @@ class DoctestModule(pytest.Module):
|
||||||
self, tests, obj, name, module, source_lines, globs, seen
|
self, tests, obj, name, module, source_lines, globs, seen
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.fspath.basename == "conftest.py":
|
if self.path.name == "conftest.py":
|
||||||
module = self.config.pluginmanager._importconftest(
|
module = self.config.pluginmanager._importconftest(
|
||||||
Path(self.fspath), self.config.getoption("importmode")
|
self.path, self.config.getoption("importmode")
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
module = import_path(self.fspath)
|
module = import_path(self.path)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
if self.config.getvalue("doctest_ignore_import_errors"):
|
if self.config.getvalue("doctest_ignore_import_errors"):
|
||||||
pytest.skip("unable to import module %r" % self.fspath)
|
pytest.skip("unable to import module %r" % self.path)
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
# Uses internal doctest module parsing mechanism.
|
# Uses internal doctest module parsing mechanism.
|
||||||
|
|
|
@ -28,7 +28,6 @@ from typing import TypeVar
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import attr
|
import attr
|
||||||
import py
|
|
||||||
|
|
||||||
import _pytest
|
import _pytest
|
||||||
from _pytest import nodes
|
from _pytest import nodes
|
||||||
|
@ -46,6 +45,8 @@ from _pytest.compat import getfuncargnames
|
||||||
from _pytest.compat import getimfunc
|
from _pytest.compat import getimfunc
|
||||||
from _pytest.compat import getlocation
|
from _pytest.compat import getlocation
|
||||||
from _pytest.compat import is_generator
|
from _pytest.compat import is_generator
|
||||||
|
from _pytest.compat import LEGACY_PATH
|
||||||
|
from _pytest.compat import legacy_path
|
||||||
from _pytest.compat import NOTSET
|
from _pytest.compat import NOTSET
|
||||||
from _pytest.compat import safe_getattr
|
from _pytest.compat import safe_getattr
|
||||||
from _pytest.config import _PluggyPlugin
|
from _pytest.config import _PluggyPlugin
|
||||||
|
@ -53,6 +54,7 @@ from _pytest.config import Config
|
||||||
from _pytest.config.argparsing import Parser
|
from _pytest.config.argparsing import Parser
|
||||||
from _pytest.deprecated import check_ispytest
|
from _pytest.deprecated import check_ispytest
|
||||||
from _pytest.deprecated import FILLFUNCARGS
|
from _pytest.deprecated import FILLFUNCARGS
|
||||||
|
from _pytest.deprecated import NODE_FSPATH
|
||||||
from _pytest.deprecated import YIELD_FIXTURE
|
from _pytest.deprecated import YIELD_FIXTURE
|
||||||
from _pytest.mark import Mark
|
from _pytest.mark import Mark
|
||||||
from _pytest.mark import ParameterSet
|
from _pytest.mark import ParameterSet
|
||||||
|
@ -256,12 +258,12 @@ def get_parametrized_fixture_keys(item: nodes.Item, scopenum: int) -> Iterator[_
|
||||||
if scopenum == 0: # session
|
if scopenum == 0: # session
|
||||||
key: _Key = (argname, param_index)
|
key: _Key = (argname, param_index)
|
||||||
elif scopenum == 1: # package
|
elif scopenum == 1: # package
|
||||||
key = (argname, param_index, item.fspath.dirpath())
|
key = (argname, param_index, item.path.parent)
|
||||||
elif scopenum == 2: # module
|
elif scopenum == 2: # module
|
||||||
key = (argname, param_index, item.fspath)
|
key = (argname, param_index, item.path)
|
||||||
elif scopenum == 3: # class
|
elif scopenum == 3: # class
|
||||||
item_cls = item.cls # type: ignore[attr-defined]
|
item_cls = item.cls # type: ignore[attr-defined]
|
||||||
key = (argname, param_index, item.fspath, item_cls)
|
key = (argname, param_index, item.path, item_cls)
|
||||||
yield key
|
yield key
|
||||||
|
|
||||||
|
|
||||||
|
@ -519,12 +521,17 @@ class FixtureRequest:
|
||||||
return self._pyfuncitem.getparent(_pytest.python.Module).obj
|
return self._pyfuncitem.getparent(_pytest.python.Module).obj
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def fspath(self) -> py.path.local:
|
def fspath(self) -> LEGACY_PATH:
|
||||||
"""The file system path of the test module which collected this test."""
|
"""(deprecated) The file system path of the test module which collected this test."""
|
||||||
|
warnings.warn(NODE_FSPATH.format(type=type(self).__name__), stacklevel=2)
|
||||||
|
return legacy_path(self.path)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def path(self) -> Path:
|
||||||
if self.scope not in ("function", "class", "module", "package"):
|
if self.scope not in ("function", "class", "module", "package"):
|
||||||
raise AttributeError(f"module not available in {self.scope}-scoped context")
|
raise AttributeError(f"module not available in {self.scope}-scoped context")
|
||||||
# TODO: Remove ignore once _pyfuncitem is properly typed.
|
# TODO: Remove ignore once _pyfuncitem is properly typed.
|
||||||
return self._pyfuncitem.fspath # type: ignore
|
return self._pyfuncitem.path # type: ignore
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def keywords(self) -> MutableMapping[str, Any]:
|
def keywords(self) -> MutableMapping[str, Any]:
|
||||||
|
@ -1040,7 +1047,7 @@ class FixtureDef(Generic[_FixtureValue]):
|
||||||
if exc:
|
if exc:
|
||||||
raise exc
|
raise exc
|
||||||
finally:
|
finally:
|
||||||
hook = self._fixturemanager.session.gethookproxy(request.node.fspath)
|
hook = self._fixturemanager.session.gethookproxy(request.node.path)
|
||||||
hook.pytest_fixture_post_finalizer(fixturedef=self, request=request)
|
hook.pytest_fixture_post_finalizer(fixturedef=self, request=request)
|
||||||
# Even if finalization fails, we invalidate the cached fixture
|
# Even if finalization fails, we invalidate the cached fixture
|
||||||
# value and remove all finalizers because they may be bound methods
|
# value and remove all finalizers because they may be bound methods
|
||||||
|
@ -1075,7 +1082,7 @@ class FixtureDef(Generic[_FixtureValue]):
|
||||||
self.finish(request)
|
self.finish(request)
|
||||||
assert self.cached_result is None
|
assert self.cached_result is None
|
||||||
|
|
||||||
hook = self._fixturemanager.session.gethookproxy(request.node.fspath)
|
hook = self._fixturemanager.session.gethookproxy(request.node.path)
|
||||||
result = hook.pytest_fixture_setup(fixturedef=self, request=request)
|
result = hook.pytest_fixture_setup(fixturedef=self, request=request)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@ -1623,6 +1630,11 @@ class FixtureManager:
|
||||||
self._holderobjseen.add(holderobj)
|
self._holderobjseen.add(holderobj)
|
||||||
autousenames = []
|
autousenames = []
|
||||||
for name in dir(holderobj):
|
for name in dir(holderobj):
|
||||||
|
# ugly workaround for one of the fspath deprecated property of node
|
||||||
|
# todo: safely generalize
|
||||||
|
if isinstance(holderobj, nodes.Node) and name == "fspath":
|
||||||
|
continue
|
||||||
|
|
||||||
# The attribute can be an arbitrary descriptor, so the attribute
|
# The attribute can be an arbitrary descriptor, so the attribute
|
||||||
# access below can raise. safe_getatt() ignores such exceptions.
|
# access below can raise. safe_getatt() ignores such exceptions.
|
||||||
obj = safe_getattr(holderobj, name, None)
|
obj = safe_getattr(holderobj, name, None)
|
||||||
|
|
|
@ -464,7 +464,12 @@ class Session(nodes.FSCollector):
|
||||||
|
|
||||||
def __init__(self, config: Config) -> None:
|
def __init__(self, config: Config) -> None:
|
||||||
super().__init__(
|
super().__init__(
|
||||||
config.rootdir, parent=None, config=config, session=self, nodeid=""
|
path=config.rootpath,
|
||||||
|
fspath=config.rootdir,
|
||||||
|
parent=None,
|
||||||
|
config=config,
|
||||||
|
session=self,
|
||||||
|
nodeid="",
|
||||||
)
|
)
|
||||||
self.testsfailed = 0
|
self.testsfailed = 0
|
||||||
self.testscollected = 0
|
self.testscollected = 0
|
||||||
|
@ -688,7 +693,7 @@ class Session(nodes.FSCollector):
|
||||||
if col:
|
if col:
|
||||||
if isinstance(col[0], Package):
|
if isinstance(col[0], Package):
|
||||||
pkg_roots[str(parent)] = col[0]
|
pkg_roots[str(parent)] = col[0]
|
||||||
node_cache1[Path(col[0].fspath)] = [col[0]]
|
node_cache1[col[0].path] = [col[0]]
|
||||||
|
|
||||||
# If it's a directory argument, recurse and look for any Subpackages.
|
# If it's a directory argument, recurse and look for any Subpackages.
|
||||||
# Let the Package collector deal with subnodes, don't collect here.
|
# Let the Package collector deal with subnodes, don't collect here.
|
||||||
|
@ -717,7 +722,7 @@ class Session(nodes.FSCollector):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
for x in self._collectfile(path):
|
for x in self._collectfile(path):
|
||||||
key2 = (type(x), Path(x.fspath))
|
key2 = (type(x), x.path)
|
||||||
if key2 in node_cache2:
|
if key2 in node_cache2:
|
||||||
yield node_cache2[key2]
|
yield node_cache2[key2]
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -23,9 +23,12 @@ from _pytest._code import getfslineno
|
||||||
from _pytest._code.code import ExceptionInfo
|
from _pytest._code.code import ExceptionInfo
|
||||||
from _pytest._code.code import TerminalRepr
|
from _pytest._code.code import TerminalRepr
|
||||||
from _pytest.compat import cached_property
|
from _pytest.compat import cached_property
|
||||||
|
from _pytest.compat import LEGACY_PATH
|
||||||
|
from _pytest.compat import legacy_path
|
||||||
from _pytest.config import Config
|
from _pytest.config import Config
|
||||||
from _pytest.config import ConftestImportFailure
|
from _pytest.config import ConftestImportFailure
|
||||||
from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH
|
from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH
|
||||||
|
from _pytest.deprecated import NODE_FSPATH
|
||||||
from _pytest.mark.structures import Mark
|
from _pytest.mark.structures import Mark
|
||||||
from _pytest.mark.structures import MarkDecorator
|
from _pytest.mark.structures import MarkDecorator
|
||||||
from _pytest.mark.structures import NodeKeywords
|
from _pytest.mark.structures import NodeKeywords
|
||||||
|
@ -79,6 +82,26 @@ def iterparentnodeids(nodeid: str) -> Iterator[str]:
|
||||||
pos = at + len(sep)
|
pos = at + len(sep)
|
||||||
|
|
||||||
|
|
||||||
|
def _imply_path(
|
||||||
|
path: Optional[Path], fspath: Optional[LEGACY_PATH]
|
||||||
|
) -> Tuple[Path, LEGACY_PATH]:
|
||||||
|
if path is not None:
|
||||||
|
if fspath is not None:
|
||||||
|
if Path(fspath) != path:
|
||||||
|
raise ValueError(
|
||||||
|
f"Path({fspath!r}) != {path!r}\n"
|
||||||
|
"if both path and fspath are given they need to be equal"
|
||||||
|
)
|
||||||
|
assert Path(fspath) == path, f"{fspath} != {path}"
|
||||||
|
else:
|
||||||
|
fspath = legacy_path(path)
|
||||||
|
return path, fspath
|
||||||
|
|
||||||
|
else:
|
||||||
|
assert fspath is not None
|
||||||
|
return Path(fspath), fspath
|
||||||
|
|
||||||
|
|
||||||
_NodeType = TypeVar("_NodeType", bound="Node")
|
_NodeType = TypeVar("_NodeType", bound="Node")
|
||||||
|
|
||||||
|
|
||||||
|
@ -110,7 +133,7 @@ class Node(metaclass=NodeMeta):
|
||||||
"parent",
|
"parent",
|
||||||
"config",
|
"config",
|
||||||
"session",
|
"session",
|
||||||
"fspath",
|
"path",
|
||||||
"_nodeid",
|
"_nodeid",
|
||||||
"_store",
|
"_store",
|
||||||
"__dict__",
|
"__dict__",
|
||||||
|
@ -123,6 +146,7 @@ class Node(metaclass=NodeMeta):
|
||||||
config: Optional[Config] = None,
|
config: Optional[Config] = None,
|
||||||
session: "Optional[Session]" = None,
|
session: "Optional[Session]" = None,
|
||||||
fspath: Optional[py.path.local] = None,
|
fspath: Optional[py.path.local] = None,
|
||||||
|
path: Optional[Path] = None,
|
||||||
nodeid: Optional[str] = None,
|
nodeid: Optional[str] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
#: A unique name within the scope of the parent node.
|
#: A unique name within the scope of the parent node.
|
||||||
|
@ -148,7 +172,7 @@ class Node(metaclass=NodeMeta):
|
||||||
self.session = parent.session
|
self.session = parent.session
|
||||||
|
|
||||||
#: Filesystem path where this node was collected from (can be None).
|
#: Filesystem path where this node was collected from (can be None).
|
||||||
self.fspath = fspath or getattr(parent, "fspath", None)
|
self.path = _imply_path(path or getattr(parent, "path", None), fspath=fspath)[0]
|
||||||
|
|
||||||
# The explicit annotation is to avoid publicly exposing NodeKeywords.
|
# The explicit annotation is to avoid publicly exposing NodeKeywords.
|
||||||
#: Keywords/markers collected from all scopes.
|
#: Keywords/markers collected from all scopes.
|
||||||
|
@ -174,6 +198,17 @@ class Node(metaclass=NodeMeta):
|
||||||
# own use. Currently only intended for internal plugins.
|
# own use. Currently only intended for internal plugins.
|
||||||
self._store = Store()
|
self._store = Store()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def fspath(self):
|
||||||
|
"""(deprecated) returns a py.path.local copy of self.path"""
|
||||||
|
warnings.warn(NODE_FSPATH.format(type=type(self).__name__), stacklevel=2)
|
||||||
|
return py.path.local(self.path)
|
||||||
|
|
||||||
|
@fspath.setter
|
||||||
|
def fspath(self, value: py.path.local):
|
||||||
|
warnings.warn(NODE_FSPATH.format(type=type(self).__name__), stacklevel=2)
|
||||||
|
self.path = Path(value)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_parent(cls, parent: "Node", **kw):
|
def from_parent(cls, parent: "Node", **kw):
|
||||||
"""Public constructor for Nodes.
|
"""Public constructor for Nodes.
|
||||||
|
@ -195,7 +230,7 @@ class Node(metaclass=NodeMeta):
|
||||||
@property
|
@property
|
||||||
def ihook(self):
|
def ihook(self):
|
||||||
"""fspath-sensitive hook proxy used to call pytest hooks."""
|
"""fspath-sensitive hook proxy used to call pytest hooks."""
|
||||||
return self.session.gethookproxy(self.fspath)
|
return self.session.gethookproxy(self.path)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return "<{} {}>".format(self.__class__.__name__, getattr(self, "name", None))
|
return "<{} {}>".format(self.__class__.__name__, getattr(self, "name", None))
|
||||||
|
@ -476,9 +511,9 @@ class Collector(Node):
|
||||||
return self._repr_failure_py(excinfo, style=tbstyle)
|
return self._repr_failure_py(excinfo, style=tbstyle)
|
||||||
|
|
||||||
def _prunetraceback(self, excinfo: ExceptionInfo[BaseException]) -> None:
|
def _prunetraceback(self, excinfo: ExceptionInfo[BaseException]) -> None:
|
||||||
if hasattr(self, "fspath"):
|
if hasattr(self, "path"):
|
||||||
traceback = excinfo.traceback
|
traceback = excinfo.traceback
|
||||||
ntraceback = traceback.cut(path=Path(self.fspath))
|
ntraceback = traceback.cut(path=self.path)
|
||||||
if ntraceback == traceback:
|
if ntraceback == traceback:
|
||||||
ntraceback = ntraceback.cut(excludepath=tracebackcutdir)
|
ntraceback = ntraceback.cut(excludepath=tracebackcutdir)
|
||||||
excinfo.traceback = ntraceback.filter()
|
excinfo.traceback = ntraceback.filter()
|
||||||
|
@ -497,36 +532,52 @@ def _check_initialpaths_for_relpath(
|
||||||
class FSCollector(Collector):
|
class FSCollector(Collector):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
fspath: py.path.local,
|
fspath: Optional[py.path.local],
|
||||||
|
path: Optional[Path],
|
||||||
parent=None,
|
parent=None,
|
||||||
config: Optional[Config] = None,
|
config: Optional[Config] = None,
|
||||||
session: Optional["Session"] = None,
|
session: Optional["Session"] = None,
|
||||||
nodeid: Optional[str] = None,
|
nodeid: Optional[str] = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
path, fspath = _imply_path(path, fspath=fspath)
|
||||||
name = fspath.basename
|
name = fspath.basename
|
||||||
if parent is not None:
|
if parent is not None and parent.path != path:
|
||||||
rel = fspath.relto(parent.fspath)
|
try:
|
||||||
if rel:
|
rel = path.relative_to(parent.path)
|
||||||
name = rel
|
except ValueError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
name = str(rel)
|
||||||
name = name.replace(os.sep, SEP)
|
name = name.replace(os.sep, SEP)
|
||||||
self.fspath = fspath
|
self.path = Path(fspath)
|
||||||
|
|
||||||
session = session or parent.session
|
session = session or parent.session
|
||||||
|
|
||||||
if nodeid is None:
|
if nodeid is None:
|
||||||
nodeid = self.fspath.relto(session.config.rootdir)
|
try:
|
||||||
|
nodeid = str(self.path.relative_to(session.config.rootpath))
|
||||||
if not nodeid:
|
except ValueError:
|
||||||
nodeid = _check_initialpaths_for_relpath(session, fspath)
|
nodeid = _check_initialpaths_for_relpath(session, fspath)
|
||||||
|
|
||||||
if nodeid and os.sep != SEP:
|
if nodeid and os.sep != SEP:
|
||||||
nodeid = nodeid.replace(os.sep, SEP)
|
nodeid = nodeid.replace(os.sep, SEP)
|
||||||
|
|
||||||
super().__init__(name, parent, config, session, nodeid=nodeid, fspath=fspath)
|
super().__init__(
|
||||||
|
name, parent, config, session, nodeid=nodeid, fspath=fspath, path=path
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_parent(cls, parent, *, fspath, **kw):
|
def from_parent(
|
||||||
|
cls,
|
||||||
|
parent,
|
||||||
|
*,
|
||||||
|
fspath: Optional[py.path.local] = None,
|
||||||
|
path: Optional[Path] = None,
|
||||||
|
**kw,
|
||||||
|
):
|
||||||
"""The public constructor."""
|
"""The public constructor."""
|
||||||
return super().from_parent(parent=parent, fspath=fspath, **kw)
|
path, fspath = _imply_path(path, fspath=fspath)
|
||||||
|
return super().from_parent(parent=parent, fspath=fspath, path=path, **kw)
|
||||||
|
|
||||||
def gethookproxy(self, fspath: "os.PathLike[str]"):
|
def gethookproxy(self, fspath: "os.PathLike[str]"):
|
||||||
warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2)
|
warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2)
|
||||||
|
|
|
@ -61,6 +61,7 @@ from _pytest.nodes import Item
|
||||||
from _pytest.outcomes import fail
|
from _pytest.outcomes import fail
|
||||||
from _pytest.outcomes import importorskip
|
from _pytest.outcomes import importorskip
|
||||||
from _pytest.outcomes import skip
|
from _pytest.outcomes import skip
|
||||||
|
from _pytest.pathlib import bestrelpath
|
||||||
from _pytest.pathlib import make_numbered_dir
|
from _pytest.pathlib import make_numbered_dir
|
||||||
from _pytest.reports import CollectReport
|
from _pytest.reports import CollectReport
|
||||||
from _pytest.reports import TestReport
|
from _pytest.reports import TestReport
|
||||||
|
@ -976,10 +977,10 @@ class Pytester:
|
||||||
|
|
||||||
:param py.path.local path: Path to the file.
|
:param py.path.local path: Path to the file.
|
||||||
"""
|
"""
|
||||||
path = py.path.local(path)
|
path = Path(path)
|
||||||
config = self.parseconfigure(path)
|
config = self.parseconfigure(path)
|
||||||
session = Session.from_config(config)
|
session = Session.from_config(config)
|
||||||
x = session.fspath.bestrelpath(path)
|
x = bestrelpath(session.path, path)
|
||||||
config.hook.pytest_sessionstart(session=session)
|
config.hook.pytest_sessionstart(session=session)
|
||||||
res = session.perform_collect([x], genitems=False)[0]
|
res = session.perform_collect([x], genitems=False)[0]
|
||||||
config.hook.pytest_sessionfinish(session=session, exitstatus=ExitCode.OK)
|
config.hook.pytest_sessionfinish(session=session, exitstatus=ExitCode.OK)
|
||||||
|
|
|
@ -577,7 +577,7 @@ class Module(nodes.File, PyCollector):
|
||||||
# We assume we are only called once per module.
|
# We assume we are only called once per module.
|
||||||
importmode = self.config.getoption("--import-mode")
|
importmode = self.config.getoption("--import-mode")
|
||||||
try:
|
try:
|
||||||
mod = import_path(self.fspath, mode=importmode)
|
mod = import_path(self.path, mode=importmode)
|
||||||
except SyntaxError as e:
|
except SyntaxError as e:
|
||||||
raise self.CollectError(
|
raise self.CollectError(
|
||||||
ExceptionInfo.from_current().getrepr(style="short")
|
ExceptionInfo.from_current().getrepr(style="short")
|
||||||
|
@ -603,10 +603,10 @@ class Module(nodes.File, PyCollector):
|
||||||
)
|
)
|
||||||
formatted_tb = str(exc_repr)
|
formatted_tb = str(exc_repr)
|
||||||
raise self.CollectError(
|
raise self.CollectError(
|
||||||
"ImportError while importing test module '{fspath}'.\n"
|
"ImportError while importing test module '{path}'.\n"
|
||||||
"Hint: make sure your test modules/packages have valid Python names.\n"
|
"Hint: make sure your test modules/packages have valid Python names.\n"
|
||||||
"Traceback:\n"
|
"Traceback:\n"
|
||||||
"{traceback}".format(fspath=self.fspath, traceback=formatted_tb)
|
"{traceback}".format(path=self.path, traceback=formatted_tb)
|
||||||
) from e
|
) from e
|
||||||
except skip.Exception as e:
|
except skip.Exception as e:
|
||||||
if e.allow_module_level:
|
if e.allow_module_level:
|
||||||
|
@ -624,18 +624,26 @@ class Module(nodes.File, PyCollector):
|
||||||
class Package(Module):
|
class Package(Module):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
fspath: py.path.local,
|
fspath: Optional[py.path.local],
|
||||||
parent: nodes.Collector,
|
parent: nodes.Collector,
|
||||||
# NOTE: following args are unused:
|
# NOTE: following args are unused:
|
||||||
config=None,
|
config=None,
|
||||||
session=None,
|
session=None,
|
||||||
nodeid=None,
|
nodeid=None,
|
||||||
|
path=Optional[Path],
|
||||||
) -> None:
|
) -> None:
|
||||||
# NOTE: Could be just the following, but kept as-is for compat.
|
# NOTE: Could be just the following, but kept as-is for compat.
|
||||||
# nodes.FSCollector.__init__(self, fspath, parent=parent)
|
# nodes.FSCollector.__init__(self, fspath, parent=parent)
|
||||||
|
path, fspath = nodes._imply_path(path, fspath=fspath)
|
||||||
session = parent.session
|
session = parent.session
|
||||||
nodes.FSCollector.__init__(
|
nodes.FSCollector.__init__(
|
||||||
self, fspath, parent=parent, config=config, session=session, nodeid=nodeid
|
self,
|
||||||
|
fspath=fspath,
|
||||||
|
path=path,
|
||||||
|
parent=parent,
|
||||||
|
config=config,
|
||||||
|
session=session,
|
||||||
|
nodeid=nodeid,
|
||||||
)
|
)
|
||||||
self.name = os.path.basename(str(fspath.dirname))
|
self.name = os.path.basename(str(fspath.dirname))
|
||||||
|
|
||||||
|
@ -704,12 +712,12 @@ class Package(Module):
|
||||||
return ihook.pytest_collect_file(fspath=fspath, path=path, parent=self) # type: ignore[no-any-return]
|
return ihook.pytest_collect_file(fspath=fspath, path=path, parent=self) # type: ignore[no-any-return]
|
||||||
|
|
||||||
def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]:
|
def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]:
|
||||||
this_path = Path(self.fspath).parent
|
this_path = self.path.parent
|
||||||
init_module = this_path / "__init__.py"
|
init_module = this_path / "__init__.py"
|
||||||
if init_module.is_file() and path_matches_patterns(
|
if init_module.is_file() and path_matches_patterns(
|
||||||
init_module, self.config.getini("python_files")
|
init_module, self.config.getini("python_files")
|
||||||
):
|
):
|
||||||
yield Module.from_parent(self, fspath=py.path.local(init_module))
|
yield Module.from_parent(self, path=init_module)
|
||||||
pkg_prefixes: Set[Path] = set()
|
pkg_prefixes: Set[Path] = set()
|
||||||
for direntry in visit(str(this_path), recurse=self._recurse):
|
for direntry in visit(str(this_path), recurse=self._recurse):
|
||||||
path = Path(direntry.path)
|
path = Path(direntry.path)
|
||||||
|
|
|
@ -2,3 +2,4 @@
|
||||||
addopts = --strict-markers
|
addopts = --strict-markers
|
||||||
filterwarnings =
|
filterwarnings =
|
||||||
error::pytest.PytestWarning
|
error::pytest.PytestWarning
|
||||||
|
ignore:.*.fspath is deprecated and will be replaced by .*.path.*:pytest.PytestDeprecationWarning
|
||||||
|
|
|
@ -1125,7 +1125,8 @@ class TestReportInfo:
|
||||||
def test_func_reportinfo(self, pytester: Pytester) -> None:
|
def test_func_reportinfo(self, pytester: Pytester) -> None:
|
||||||
item = pytester.getitem("def test_func(): pass")
|
item = pytester.getitem("def test_func(): pass")
|
||||||
fspath, lineno, modpath = item.reportinfo()
|
fspath, lineno, modpath = item.reportinfo()
|
||||||
assert fspath == item.fspath
|
with pytest.warns(DeprecationWarning):
|
||||||
|
assert fspath == item.fspath
|
||||||
assert lineno == 0
|
assert lineno == 0
|
||||||
assert modpath == "test_func"
|
assert modpath == "test_func"
|
||||||
|
|
||||||
|
@ -1140,7 +1141,8 @@ class TestReportInfo:
|
||||||
classcol = pytester.collect_by_name(modcol, "TestClass")
|
classcol = pytester.collect_by_name(modcol, "TestClass")
|
||||||
assert isinstance(classcol, Class)
|
assert isinstance(classcol, Class)
|
||||||
fspath, lineno, msg = classcol.reportinfo()
|
fspath, lineno, msg = classcol.reportinfo()
|
||||||
assert fspath == modcol.fspath
|
with pytest.warns(DeprecationWarning):
|
||||||
|
assert fspath == modcol.fspath
|
||||||
assert lineno == 1
|
assert lineno == 1
|
||||||
assert msg == "TestClass"
|
assert msg == "TestClass"
|
||||||
|
|
||||||
|
|
|
@ -966,7 +966,9 @@ class TestRequestBasic:
|
||||||
modcol = pytester.getmodulecol("def test_somefunc(): pass")
|
modcol = pytester.getmodulecol("def test_somefunc(): pass")
|
||||||
(item,) = pytester.genitems([modcol])
|
(item,) = pytester.genitems([modcol])
|
||||||
req = fixtures.FixtureRequest(item, _ispytest=True)
|
req = fixtures.FixtureRequest(item, _ispytest=True)
|
||||||
assert req.fspath == modcol.fspath
|
assert req.path == modcol.path
|
||||||
|
with pytest.warns(pytest.PytestDeprecationWarning):
|
||||||
|
assert req.fspath == modcol.fspath
|
||||||
|
|
||||||
def test_request_fixturenames(self, pytester: Pytester) -> None:
|
def test_request_fixturenames(self, pytester: Pytester) -> None:
|
||||||
pytester.makepyfile(
|
pytester.makepyfile(
|
||||||
|
|
|
@ -464,13 +464,13 @@ class TestSession:
|
||||||
config = pytester.parseconfig(id)
|
config = pytester.parseconfig(id)
|
||||||
topdir = pytester.path
|
topdir = pytester.path
|
||||||
rcol = Session.from_config(config)
|
rcol = Session.from_config(config)
|
||||||
assert topdir == rcol.fspath
|
assert topdir == rcol.path
|
||||||
# rootid = rcol.nodeid
|
# rootid = rcol.nodeid
|
||||||
# root2 = rcol.perform_collect([rcol.nodeid], genitems=False)[0]
|
# root2 = rcol.perform_collect([rcol.nodeid], genitems=False)[0]
|
||||||
# assert root2 == rcol, rootid
|
# assert root2 == rcol, rootid
|
||||||
colitems = rcol.perform_collect([rcol.nodeid], genitems=False)
|
colitems = rcol.perform_collect([rcol.nodeid], genitems=False)
|
||||||
assert len(colitems) == 1
|
assert len(colitems) == 1
|
||||||
assert colitems[0].fspath == p
|
assert colitems[0].path == p
|
||||||
|
|
||||||
def get_reported_items(self, hookrec: HookRecorder) -> List[Item]:
|
def get_reported_items(self, hookrec: HookRecorder) -> List[Item]:
|
||||||
"""Return pytest.Item instances reported by the pytest_collectreport hook"""
|
"""Return pytest.Item instances reported by the pytest_collectreport hook"""
|
||||||
|
@ -494,10 +494,10 @@ class TestSession:
|
||||||
topdir = pytester.path # noqa
|
topdir = pytester.path # noqa
|
||||||
hookrec.assert_contains(
|
hookrec.assert_contains(
|
||||||
[
|
[
|
||||||
("pytest_collectstart", "collector.fspath == topdir"),
|
("pytest_collectstart", "collector.path == topdir"),
|
||||||
("pytest_make_collect_report", "collector.fspath == topdir"),
|
("pytest_make_collect_report", "collector.path == topdir"),
|
||||||
("pytest_collectstart", "collector.fspath == p"),
|
("pytest_collectstart", "collector.path == p"),
|
||||||
("pytest_make_collect_report", "collector.fspath == p"),
|
("pytest_make_collect_report", "collector.path == p"),
|
||||||
("pytest_pycollect_makeitem", "name == 'test_func'"),
|
("pytest_pycollect_makeitem", "name == 'test_func'"),
|
||||||
("pytest_collectreport", "report.result[0].name == 'test_func'"),
|
("pytest_collectreport", "report.result[0].name == 'test_func'"),
|
||||||
]
|
]
|
||||||
|
@ -547,7 +547,7 @@ class TestSession:
|
||||||
assert len(items) == 2
|
assert len(items) == 2
|
||||||
hookrec.assert_contains(
|
hookrec.assert_contains(
|
||||||
[
|
[
|
||||||
("pytest_collectstart", "collector.fspath == collector.session.fspath"),
|
("pytest_collectstart", "collector.path == collector.session.path"),
|
||||||
(
|
(
|
||||||
"pytest_collectstart",
|
"pytest_collectstart",
|
||||||
"collector.__class__.__name__ == 'SpecialFile'",
|
"collector.__class__.__name__ == 'SpecialFile'",
|
||||||
|
@ -570,7 +570,7 @@ class TestSession:
|
||||||
pprint.pprint(hookrec.calls)
|
pprint.pprint(hookrec.calls)
|
||||||
hookrec.assert_contains(
|
hookrec.assert_contains(
|
||||||
[
|
[
|
||||||
("pytest_collectstart", "collector.fspath == test_aaa"),
|
("pytest_collectstart", "collector.path == test_aaa"),
|
||||||
("pytest_pycollect_makeitem", "name == 'test_func'"),
|
("pytest_pycollect_makeitem", "name == 'test_func'"),
|
||||||
("pytest_collectreport", "report.nodeid.startswith('aaa/test_aaa.py')"),
|
("pytest_collectreport", "report.nodeid.startswith('aaa/test_aaa.py')"),
|
||||||
]
|
]
|
||||||
|
@ -592,10 +592,10 @@ class TestSession:
|
||||||
pprint.pprint(hookrec.calls)
|
pprint.pprint(hookrec.calls)
|
||||||
hookrec.assert_contains(
|
hookrec.assert_contains(
|
||||||
[
|
[
|
||||||
("pytest_collectstart", "collector.fspath == test_aaa"),
|
("pytest_collectstart", "collector.path == test_aaa"),
|
||||||
("pytest_pycollect_makeitem", "name == 'test_func'"),
|
("pytest_pycollect_makeitem", "name == 'test_func'"),
|
||||||
("pytest_collectreport", "report.nodeid == 'aaa/test_aaa.py'"),
|
("pytest_collectreport", "report.nodeid == 'aaa/test_aaa.py'"),
|
||||||
("pytest_collectstart", "collector.fspath == test_bbb"),
|
("pytest_collectstart", "collector.path == test_bbb"),
|
||||||
("pytest_pycollect_makeitem", "name == 'test_func'"),
|
("pytest_pycollect_makeitem", "name == 'test_func'"),
|
||||||
("pytest_collectreport", "report.nodeid == 'bbb/test_bbb.py'"),
|
("pytest_collectreport", "report.nodeid == 'bbb/test_bbb.py'"),
|
||||||
]
|
]
|
||||||
|
@ -609,7 +609,9 @@ class TestSession:
|
||||||
items2, hookrec = pytester.inline_genitems(item.nodeid)
|
items2, hookrec = pytester.inline_genitems(item.nodeid)
|
||||||
(item2,) = items2
|
(item2,) = items2
|
||||||
assert item2.name == item.name
|
assert item2.name == item.name
|
||||||
assert item2.fspath == item.fspath
|
with pytest.warns(DeprecationWarning):
|
||||||
|
assert item2.fspath == item.fspath
|
||||||
|
assert item2.path == item.path
|
||||||
|
|
||||||
def test_find_byid_without_instance_parents(self, pytester: Pytester) -> None:
|
def test_find_byid_without_instance_parents(self, pytester: Pytester) -> None:
|
||||||
p = pytester.makepyfile(
|
p = pytester.makepyfile(
|
||||||
|
@ -1347,14 +1349,10 @@ def test_fscollector_from_parent(pytester: Pytester, request: FixtureRequest) ->
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class MyCollector(pytest.File):
|
class MyCollector(pytest.File):
|
||||||
def __init__(self, fspath, parent, x):
|
def __init__(self, *k, x, **kw):
|
||||||
super().__init__(fspath, parent)
|
super().__init__(*k, **kw)
|
||||||
self.x = x
|
self.x = x
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_parent(cls, parent, *, fspath, x):
|
|
||||||
return super().from_parent(parent=parent, fspath=fspath, x=x)
|
|
||||||
|
|
||||||
collector = MyCollector.from_parent(
|
collector = MyCollector.from_parent(
|
||||||
parent=request.session, fspath=py.path.local(pytester.path) / "foo", x=10
|
parent=request.session, fspath=py.path.local(pytester.path) / "foo", x=10
|
||||||
)
|
)
|
||||||
|
|
|
@ -1048,11 +1048,12 @@ def test_mark_expressions_no_smear(pytester: Pytester) -> None:
|
||||||
# assert skipped_k == failed_k == 0
|
# assert skipped_k == failed_k == 0
|
||||||
|
|
||||||
|
|
||||||
def test_addmarker_order() -> None:
|
def test_addmarker_order(pytester) -> None:
|
||||||
session = mock.Mock()
|
session = mock.Mock()
|
||||||
session.own_markers = []
|
session.own_markers = []
|
||||||
session.parent = None
|
session.parent = None
|
||||||
session.nodeid = ""
|
session.nodeid = ""
|
||||||
|
session.path = pytester.path
|
||||||
node = Node.from_parent(session, name="Test")
|
node = Node.from_parent(session, name="Test")
|
||||||
node.add_marker("foo")
|
node.add_marker("foo")
|
||||||
node.add_marker("bar")
|
node.add_marker("bar")
|
||||||
|
|
|
@ -447,9 +447,9 @@ class TestSessionReports:
|
||||||
assert not rep.skipped
|
assert not rep.skipped
|
||||||
assert rep.passed
|
assert rep.passed
|
||||||
locinfo = rep.location
|
locinfo = rep.location
|
||||||
assert locinfo[0] == col.fspath.basename
|
assert locinfo[0] == col.path.name
|
||||||
assert not locinfo[1]
|
assert not locinfo[1]
|
||||||
assert locinfo[2] == col.fspath.basename
|
assert locinfo[2] == col.path.name
|
||||||
res = rep.result
|
res = rep.result
|
||||||
assert len(res) == 2
|
assert len(res) == 2
|
||||||
assert res[0].name == "test_func1"
|
assert res[0].name == "test_func1"
|
||||||
|
|
|
@ -133,7 +133,7 @@ class TestTerminal:
|
||||||
item.config.pluginmanager.register(tr)
|
item.config.pluginmanager.register(tr)
|
||||||
location = item.reportinfo()
|
location = item.reportinfo()
|
||||||
tr.config.hook.pytest_runtest_logstart(
|
tr.config.hook.pytest_runtest_logstart(
|
||||||
nodeid=item.nodeid, location=location, fspath=str(item.fspath)
|
nodeid=item.nodeid, location=location, fspath=str(item.path)
|
||||||
)
|
)
|
||||||
linecomp.assert_contains_lines(["*test_show_runtest_logstart.py*"])
|
linecomp.assert_contains_lines(["*test_show_runtest_logstart.py*"])
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue