Drop attrs dependency, use dataclasses instead (#10669)
Since pytest now requires Python>=3.7, we can use the stdlib attrs clone, dataclasses, instead of the OG package. attrs is still somewhat nicer than dataclasses and has some extra functionality, but for pytest usage there's not really a justification IMO to impose the extra dependency on users when a standard alternative exists.
This commit is contained in:
parent
4d4ed42c34
commit
310b67b227
|
@ -0,0 +1 @@
|
||||||
|
pytest no longer depends on the `attrs` package (don't worry, nice diffs for attrs classes are still supported).
|
|
@ -44,7 +44,6 @@ packages =
|
||||||
pytest
|
pytest
|
||||||
py_modules = py
|
py_modules = py
|
||||||
install_requires =
|
install_requires =
|
||||||
attrs>=19.2.0
|
|
||||||
iniconfig
|
iniconfig
|
||||||
packaging
|
packaging
|
||||||
pluggy>=0.12,<2.0
|
pluggy>=0.12,<2.0
|
||||||
|
@ -68,6 +67,7 @@ console_scripts =
|
||||||
[options.extras_require]
|
[options.extras_require]
|
||||||
testing =
|
testing =
|
||||||
argcomplete
|
argcomplete
|
||||||
|
attrs>=19.2.0
|
||||||
hypothesis>=3.56
|
hypothesis>=3.56
|
||||||
mock
|
mock
|
||||||
nose
|
nose
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import ast
|
import ast
|
||||||
|
import dataclasses
|
||||||
import inspect
|
import inspect
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
@ -32,7 +33,6 @@ from typing import TypeVar
|
||||||
from typing import Union
|
from typing import Union
|
||||||
from weakref import ref
|
from weakref import ref
|
||||||
|
|
||||||
import attr
|
|
||||||
import pluggy
|
import pluggy
|
||||||
|
|
||||||
import _pytest
|
import _pytest
|
||||||
|
@ -445,7 +445,7 @@ E = TypeVar("E", bound=BaseException, covariant=True)
|
||||||
|
|
||||||
|
|
||||||
@final
|
@final
|
||||||
@attr.s(repr=False, init=False, auto_attribs=True)
|
@dataclasses.dataclass
|
||||||
class ExceptionInfo(Generic[E]):
|
class ExceptionInfo(Generic[E]):
|
||||||
"""Wraps sys.exc_info() objects and offers help for navigating the traceback."""
|
"""Wraps sys.exc_info() objects and offers help for navigating the traceback."""
|
||||||
|
|
||||||
|
@ -649,12 +649,12 @@ class ExceptionInfo(Generic[E]):
|
||||||
"""
|
"""
|
||||||
if style == "native":
|
if style == "native":
|
||||||
return ReprExceptionInfo(
|
return ReprExceptionInfo(
|
||||||
ReprTracebackNative(
|
reprtraceback=ReprTracebackNative(
|
||||||
traceback.format_exception(
|
traceback.format_exception(
|
||||||
self.type, self.value, self.traceback[0]._rawentry
|
self.type, self.value, self.traceback[0]._rawentry
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
self._getreprcrash(),
|
reprcrash=self._getreprcrash(),
|
||||||
)
|
)
|
||||||
|
|
||||||
fmt = FormattedExcinfo(
|
fmt = FormattedExcinfo(
|
||||||
|
@ -684,7 +684,7 @@ class ExceptionInfo(Generic[E]):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@attr.s(auto_attribs=True)
|
@dataclasses.dataclass
|
||||||
class FormattedExcinfo:
|
class FormattedExcinfo:
|
||||||
"""Presenting information about failing Functions and Generators."""
|
"""Presenting information about failing Functions and Generators."""
|
||||||
|
|
||||||
|
@ -699,8 +699,8 @@ class FormattedExcinfo:
|
||||||
funcargs: bool = False
|
funcargs: bool = False
|
||||||
truncate_locals: bool = True
|
truncate_locals: bool = True
|
||||||
chain: bool = True
|
chain: bool = True
|
||||||
astcache: Dict[Union[str, Path], ast.AST] = attr.ib(
|
astcache: Dict[Union[str, Path], ast.AST] = dataclasses.field(
|
||||||
factory=dict, init=False, repr=False
|
default_factory=dict, init=False, repr=False
|
||||||
)
|
)
|
||||||
|
|
||||||
def _getindent(self, source: "Source") -> int:
|
def _getindent(self, source: "Source") -> int:
|
||||||
|
@ -978,7 +978,7 @@ class FormattedExcinfo:
|
||||||
return ExceptionChainRepr(repr_chain)
|
return ExceptionChainRepr(repr_chain)
|
||||||
|
|
||||||
|
|
||||||
@attr.s(eq=False, auto_attribs=True)
|
@dataclasses.dataclass(eq=False)
|
||||||
class TerminalRepr:
|
class TerminalRepr:
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
# FYI this is called from pytest-xdist's serialization of exception
|
# FYI this is called from pytest-xdist's serialization of exception
|
||||||
|
@ -996,14 +996,14 @@ class TerminalRepr:
|
||||||
|
|
||||||
|
|
||||||
# This class is abstract -- only subclasses are instantiated.
|
# This class is abstract -- only subclasses are instantiated.
|
||||||
@attr.s(eq=False)
|
@dataclasses.dataclass(eq=False)
|
||||||
class ExceptionRepr(TerminalRepr):
|
class ExceptionRepr(TerminalRepr):
|
||||||
# Provided by subclasses.
|
# Provided by subclasses.
|
||||||
reprcrash: Optional["ReprFileLocation"]
|
|
||||||
reprtraceback: "ReprTraceback"
|
reprtraceback: "ReprTraceback"
|
||||||
|
reprcrash: Optional["ReprFileLocation"]
|
||||||
def __attrs_post_init__(self) -> None:
|
sections: List[Tuple[str, str, str]] = dataclasses.field(
|
||||||
self.sections: List[Tuple[str, str, str]] = []
|
init=False, default_factory=list
|
||||||
|
)
|
||||||
|
|
||||||
def addsection(self, name: str, content: str, sep: str = "-") -> None:
|
def addsection(self, name: str, content: str, sep: str = "-") -> None:
|
||||||
self.sections.append((name, content, sep))
|
self.sections.append((name, content, sep))
|
||||||
|
@ -1014,16 +1014,23 @@ class ExceptionRepr(TerminalRepr):
|
||||||
tw.line(content)
|
tw.line(content)
|
||||||
|
|
||||||
|
|
||||||
@attr.s(eq=False, auto_attribs=True)
|
@dataclasses.dataclass(eq=False)
|
||||||
class ExceptionChainRepr(ExceptionRepr):
|
class ExceptionChainRepr(ExceptionRepr):
|
||||||
chain: Sequence[Tuple["ReprTraceback", Optional["ReprFileLocation"], Optional[str]]]
|
chain: Sequence[Tuple["ReprTraceback", Optional["ReprFileLocation"], Optional[str]]]
|
||||||
|
|
||||||
def __attrs_post_init__(self) -> None:
|
def __init__(
|
||||||
super().__attrs_post_init__()
|
self,
|
||||||
|
chain: Sequence[
|
||||||
|
Tuple["ReprTraceback", Optional["ReprFileLocation"], Optional[str]]
|
||||||
|
],
|
||||||
|
) -> None:
|
||||||
# reprcrash and reprtraceback of the outermost (the newest) exception
|
# reprcrash and reprtraceback of the outermost (the newest) exception
|
||||||
# in the chain.
|
# in the chain.
|
||||||
self.reprtraceback = self.chain[-1][0]
|
super().__init__(
|
||||||
self.reprcrash = self.chain[-1][1]
|
reprtraceback=chain[-1][0],
|
||||||
|
reprcrash=chain[-1][1],
|
||||||
|
)
|
||||||
|
self.chain = chain
|
||||||
|
|
||||||
def toterminal(self, tw: TerminalWriter) -> None:
|
def toterminal(self, tw: TerminalWriter) -> None:
|
||||||
for element in self.chain:
|
for element in self.chain:
|
||||||
|
@ -1034,7 +1041,7 @@ class ExceptionChainRepr(ExceptionRepr):
|
||||||
super().toterminal(tw)
|
super().toterminal(tw)
|
||||||
|
|
||||||
|
|
||||||
@attr.s(eq=False, auto_attribs=True)
|
@dataclasses.dataclass(eq=False)
|
||||||
class ReprExceptionInfo(ExceptionRepr):
|
class ReprExceptionInfo(ExceptionRepr):
|
||||||
reprtraceback: "ReprTraceback"
|
reprtraceback: "ReprTraceback"
|
||||||
reprcrash: "ReprFileLocation"
|
reprcrash: "ReprFileLocation"
|
||||||
|
@ -1044,7 +1051,7 @@ class ReprExceptionInfo(ExceptionRepr):
|
||||||
super().toterminal(tw)
|
super().toterminal(tw)
|
||||||
|
|
||||||
|
|
||||||
@attr.s(eq=False, auto_attribs=True)
|
@dataclasses.dataclass(eq=False)
|
||||||
class ReprTraceback(TerminalRepr):
|
class ReprTraceback(TerminalRepr):
|
||||||
reprentries: Sequence[Union["ReprEntry", "ReprEntryNative"]]
|
reprentries: Sequence[Union["ReprEntry", "ReprEntryNative"]]
|
||||||
extraline: Optional[str]
|
extraline: Optional[str]
|
||||||
|
@ -1073,12 +1080,12 @@ class ReprTraceback(TerminalRepr):
|
||||||
|
|
||||||
class ReprTracebackNative(ReprTraceback):
|
class ReprTracebackNative(ReprTraceback):
|
||||||
def __init__(self, tblines: Sequence[str]) -> None:
|
def __init__(self, tblines: Sequence[str]) -> None:
|
||||||
self.style = "native"
|
|
||||||
self.reprentries = [ReprEntryNative(tblines)]
|
self.reprentries = [ReprEntryNative(tblines)]
|
||||||
self.extraline = None
|
self.extraline = None
|
||||||
|
self.style = "native"
|
||||||
|
|
||||||
|
|
||||||
@attr.s(eq=False, auto_attribs=True)
|
@dataclasses.dataclass(eq=False)
|
||||||
class ReprEntryNative(TerminalRepr):
|
class ReprEntryNative(TerminalRepr):
|
||||||
lines: Sequence[str]
|
lines: Sequence[str]
|
||||||
|
|
||||||
|
@ -1088,7 +1095,7 @@ class ReprEntryNative(TerminalRepr):
|
||||||
tw.write("".join(self.lines))
|
tw.write("".join(self.lines))
|
||||||
|
|
||||||
|
|
||||||
@attr.s(eq=False, auto_attribs=True)
|
@dataclasses.dataclass(eq=False)
|
||||||
class ReprEntry(TerminalRepr):
|
class ReprEntry(TerminalRepr):
|
||||||
lines: Sequence[str]
|
lines: Sequence[str]
|
||||||
reprfuncargs: Optional["ReprFuncArgs"]
|
reprfuncargs: Optional["ReprFuncArgs"]
|
||||||
|
@ -1168,12 +1175,15 @@ class ReprEntry(TerminalRepr):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@attr.s(eq=False, auto_attribs=True)
|
@dataclasses.dataclass(eq=False)
|
||||||
class ReprFileLocation(TerminalRepr):
|
class ReprFileLocation(TerminalRepr):
|
||||||
path: str = attr.ib(converter=str)
|
path: str
|
||||||
lineno: int
|
lineno: int
|
||||||
message: str
|
message: str
|
||||||
|
|
||||||
|
def __post_init__(self) -> None:
|
||||||
|
self.path = str(self.path)
|
||||||
|
|
||||||
def toterminal(self, tw: TerminalWriter) -> None:
|
def toterminal(self, tw: TerminalWriter) -> None:
|
||||||
# Filename and lineno output for each entry, using an output format
|
# Filename and lineno output for each entry, using an output format
|
||||||
# that most editors understand.
|
# that most editors understand.
|
||||||
|
@ -1185,7 +1195,7 @@ class ReprFileLocation(TerminalRepr):
|
||||||
tw.line(f":{self.lineno}: {msg}")
|
tw.line(f":{self.lineno}: {msg}")
|
||||||
|
|
||||||
|
|
||||||
@attr.s(eq=False, auto_attribs=True)
|
@dataclasses.dataclass(eq=False)
|
||||||
class ReprLocals(TerminalRepr):
|
class ReprLocals(TerminalRepr):
|
||||||
lines: Sequence[str]
|
lines: Sequence[str]
|
||||||
|
|
||||||
|
@ -1194,7 +1204,7 @@ class ReprLocals(TerminalRepr):
|
||||||
tw.line(indent + line)
|
tw.line(indent + line)
|
||||||
|
|
||||||
|
|
||||||
@attr.s(eq=False, auto_attribs=True)
|
@dataclasses.dataclass(eq=False)
|
||||||
class ReprFuncArgs(TerminalRepr):
|
class ReprFuncArgs(TerminalRepr):
|
||||||
args: Sequence[Tuple[str, object]]
|
args: Sequence[Tuple[str, object]]
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
"""Implementation of the cache provider."""
|
"""Implementation of the cache provider."""
|
||||||
# This plugin was not named "cache" to avoid conflicts with the external
|
# This plugin was not named "cache" to avoid conflicts with the external
|
||||||
# pytest-cache version.
|
# pytest-cache version.
|
||||||
|
import dataclasses
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
@ -12,8 +13,6 @@ from typing import Optional
|
||||||
from typing import Set
|
from typing import Set
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import attr
|
|
||||||
|
|
||||||
from .pathlib import resolve_from_str
|
from .pathlib import resolve_from_str
|
||||||
from .pathlib import rm_rf
|
from .pathlib import rm_rf
|
||||||
from .reports import CollectReport
|
from .reports import CollectReport
|
||||||
|
@ -52,10 +51,12 @@ Signature: 8a477f597d28d172789f06886806bc55
|
||||||
|
|
||||||
|
|
||||||
@final
|
@final
|
||||||
@attr.s(init=False, auto_attribs=True)
|
@dataclasses.dataclass
|
||||||
class Cache:
|
class Cache:
|
||||||
_cachedir: Path = attr.ib(repr=False)
|
"""Instance of the `cache` fixture."""
|
||||||
_config: Config = attr.ib(repr=False)
|
|
||||||
|
_cachedir: Path = dataclasses.field(repr=False)
|
||||||
|
_config: Config = dataclasses.field(repr=False)
|
||||||
|
|
||||||
# Sub-directory under cache-dir for directories created by `mkdir()`.
|
# Sub-directory under cache-dir for directories created by `mkdir()`.
|
||||||
_CACHE_PREFIX_DIRS = "d"
|
_CACHE_PREFIX_DIRS = "d"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
"""Python version compatibility code."""
|
"""Python version compatibility code."""
|
||||||
|
import dataclasses
|
||||||
import enum
|
import enum
|
||||||
import functools
|
import functools
|
||||||
import inspect
|
import inspect
|
||||||
|
@ -17,8 +18,6 @@ from typing import TYPE_CHECKING
|
||||||
from typing import TypeVar
|
from typing import TypeVar
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import attr
|
|
||||||
|
|
||||||
import py
|
import py
|
||||||
|
|
||||||
# fmt: off
|
# fmt: off
|
||||||
|
@ -253,7 +252,7 @@ def ascii_escaped(val: Union[bytes, str]) -> str:
|
||||||
return _translate_non_printable(ret)
|
return _translate_non_printable(ret)
|
||||||
|
|
||||||
|
|
||||||
@attr.s
|
@dataclasses.dataclass
|
||||||
class _PytestWrapper:
|
class _PytestWrapper:
|
||||||
"""Dummy wrapper around a function object for internal use only.
|
"""Dummy wrapper around a function object for internal use only.
|
||||||
|
|
||||||
|
@ -262,7 +261,7 @@ class _PytestWrapper:
|
||||||
decorator to issue warnings when the fixture function is called directly.
|
decorator to issue warnings when the fixture function is called directly.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
obj = attr.ib()
|
obj: Any
|
||||||
|
|
||||||
|
|
||||||
def get_real_func(obj):
|
def get_real_func(obj):
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import argparse
|
import argparse
|
||||||
import collections.abc
|
import collections.abc
|
||||||
import copy
|
import copy
|
||||||
|
import dataclasses
|
||||||
import enum
|
import enum
|
||||||
import glob
|
import glob
|
||||||
import inspect
|
import inspect
|
||||||
|
@ -34,7 +35,6 @@ from typing import Type
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import attr
|
|
||||||
from pluggy import HookimplMarker
|
from pluggy import HookimplMarker
|
||||||
from pluggy import HookspecMarker
|
from pluggy import HookspecMarker
|
||||||
from pluggy import PluginManager
|
from pluggy import PluginManager
|
||||||
|
@ -886,10 +886,6 @@ def _iter_rewritable_modules(package_files: Iterable[str]) -> Iterator[str]:
|
||||||
yield from _iter_rewritable_modules(new_package_files)
|
yield from _iter_rewritable_modules(new_package_files)
|
||||||
|
|
||||||
|
|
||||||
def _args_converter(args: Iterable[str]) -> Tuple[str, ...]:
|
|
||||||
return tuple(args)
|
|
||||||
|
|
||||||
|
|
||||||
@final
|
@final
|
||||||
class Config:
|
class Config:
|
||||||
"""Access to configuration values, pluginmanager and plugin hooks.
|
"""Access to configuration values, pluginmanager and plugin hooks.
|
||||||
|
@ -903,7 +899,7 @@ class Config:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@final
|
@final
|
||||||
@attr.s(frozen=True, auto_attribs=True)
|
@dataclasses.dataclass(frozen=True)
|
||||||
class InvocationParams:
|
class InvocationParams:
|
||||||
"""Holds parameters passed during :func:`pytest.main`.
|
"""Holds parameters passed during :func:`pytest.main`.
|
||||||
|
|
||||||
|
@ -919,13 +915,24 @@ class Config:
|
||||||
Plugins accessing ``InvocationParams`` must be aware of that.
|
Plugins accessing ``InvocationParams`` must be aware of that.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
args: Tuple[str, ...] = attr.ib(converter=_args_converter)
|
args: Tuple[str, ...]
|
||||||
"""The command-line arguments as passed to :func:`pytest.main`."""
|
"""The command-line arguments as passed to :func:`pytest.main`."""
|
||||||
plugins: Optional[Sequence[Union[str, _PluggyPlugin]]]
|
plugins: Optional[Sequence[Union[str, _PluggyPlugin]]]
|
||||||
"""Extra plugins, might be `None`."""
|
"""Extra plugins, might be `None`."""
|
||||||
dir: Path
|
dir: Path
|
||||||
"""The directory from which :func:`pytest.main` was invoked."""
|
"""The directory from which :func:`pytest.main` was invoked."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
args: Iterable[str],
|
||||||
|
plugins: Optional[Sequence[Union[str, _PluggyPlugin]]],
|
||||||
|
dir: Path,
|
||||||
|
) -> None:
|
||||||
|
object.__setattr__(self, "args", tuple(args))
|
||||||
|
object.__setattr__(self, "plugins", plugins)
|
||||||
|
object.__setattr__(self, "dir", dir)
|
||||||
|
|
||||||
class ArgsSource(enum.Enum):
|
class ArgsSource(enum.Enum):
|
||||||
"""Indicates the source of the test arguments.
|
"""Indicates the source of the test arguments.
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import dataclasses
|
||||||
import functools
|
import functools
|
||||||
import inspect
|
import inspect
|
||||||
import os
|
import os
|
||||||
|
@ -28,8 +29,6 @@ from typing import TYPE_CHECKING
|
||||||
from typing import TypeVar
|
from typing import TypeVar
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import attr
|
|
||||||
|
|
||||||
import _pytest
|
import _pytest
|
||||||
from _pytest import nodes
|
from _pytest import nodes
|
||||||
from _pytest._code import getfslineno
|
from _pytest._code import getfslineno
|
||||||
|
@ -103,7 +102,7 @@ _FixtureCachedResult = Union[
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@attr.s(frozen=True, auto_attribs=True)
|
@dataclasses.dataclass(frozen=True)
|
||||||
class PseudoFixtureDef(Generic[FixtureValue]):
|
class PseudoFixtureDef(Generic[FixtureValue]):
|
||||||
cached_result: "_FixtureCachedResult[FixtureValue]"
|
cached_result: "_FixtureCachedResult[FixtureValue]"
|
||||||
_scope: Scope
|
_scope: Scope
|
||||||
|
@ -350,8 +349,10 @@ def get_direct_param_fixture_func(request: "FixtureRequest") -> Any:
|
||||||
return request.param
|
return request.param
|
||||||
|
|
||||||
|
|
||||||
@attr.s(slots=True, auto_attribs=True)
|
@dataclasses.dataclass
|
||||||
class FuncFixtureInfo:
|
class FuncFixtureInfo:
|
||||||
|
__slots__ = ("argnames", "initialnames", "names_closure", "name2fixturedefs")
|
||||||
|
|
||||||
# Original function argument names.
|
# Original function argument names.
|
||||||
argnames: Tuple[str, ...]
|
argnames: Tuple[str, ...]
|
||||||
# Argnames that function immediately requires. These include argnames +
|
# Argnames that function immediately requires. These include argnames +
|
||||||
|
@ -1181,19 +1182,21 @@ def wrap_function_to_error_out_if_called_directly(
|
||||||
|
|
||||||
|
|
||||||
@final
|
@final
|
||||||
@attr.s(frozen=True, auto_attribs=True)
|
@dataclasses.dataclass(frozen=True)
|
||||||
class FixtureFunctionMarker:
|
class FixtureFunctionMarker:
|
||||||
scope: "Union[_ScopeName, Callable[[str, Config], _ScopeName]]"
|
scope: "Union[_ScopeName, Callable[[str, Config], _ScopeName]]"
|
||||||
params: Optional[Tuple[object, ...]] = attr.ib(converter=_params_converter)
|
params: Optional[Tuple[object, ...]]
|
||||||
autouse: bool = False
|
autouse: bool = False
|
||||||
ids: Optional[
|
ids: Optional[
|
||||||
Union[Tuple[Optional[object], ...], Callable[[Any], Optional[object]]]
|
Union[Tuple[Optional[object], ...], Callable[[Any], Optional[object]]]
|
||||||
] = attr.ib(
|
] = None
|
||||||
default=None,
|
|
||||||
converter=_ensure_immutable_ids,
|
|
||||||
)
|
|
||||||
name: Optional[str] = None
|
name: Optional[str] = None
|
||||||
|
|
||||||
|
_ispytest: dataclasses.InitVar[bool] = False
|
||||||
|
|
||||||
|
def __post_init__(self, _ispytest: bool) -> None:
|
||||||
|
check_ispytest(_ispytest)
|
||||||
|
|
||||||
def __call__(self, function: FixtureFunction) -> FixtureFunction:
|
def __call__(self, function: FixtureFunction) -> FixtureFunction:
|
||||||
if inspect.isclass(function):
|
if inspect.isclass(function):
|
||||||
raise ValueError("class fixtures not supported (maybe in the future)")
|
raise ValueError("class fixtures not supported (maybe in the future)")
|
||||||
|
@ -1313,10 +1316,11 @@ def fixture( # noqa: F811
|
||||||
"""
|
"""
|
||||||
fixture_marker = FixtureFunctionMarker(
|
fixture_marker = FixtureFunctionMarker(
|
||||||
scope=scope,
|
scope=scope,
|
||||||
params=params,
|
params=tuple(params) if params is not None else None,
|
||||||
autouse=autouse,
|
autouse=autouse,
|
||||||
ids=ids,
|
ids=None if ids is None else ids if callable(ids) else tuple(ids),
|
||||||
name=name,
|
name=name,
|
||||||
|
_ispytest=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Direct decoration.
|
# Direct decoration.
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
"""Add backward compatibility support for the legacy py path type."""
|
"""Add backward compatibility support for the legacy py path type."""
|
||||||
|
import dataclasses
|
||||||
import shlex
|
import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
@ -7,7 +8,6 @@ from typing import Optional
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import attr
|
|
||||||
from iniconfig import SectionWrapper
|
from iniconfig import SectionWrapper
|
||||||
|
|
||||||
from _pytest.cacheprovider import Cache
|
from _pytest.cacheprovider import Cache
|
||||||
|
@ -268,7 +268,7 @@ class LegacyTestdirPlugin:
|
||||||
|
|
||||||
|
|
||||||
@final
|
@final
|
||||||
@attr.s(init=False, auto_attribs=True)
|
@dataclasses.dataclass
|
||||||
class TempdirFactory:
|
class TempdirFactory:
|
||||||
"""Backward compatibility wrapper that implements :class:`py.path.local`
|
"""Backward compatibility wrapper that implements :class:`py.path.local`
|
||||||
for :class:`TempPathFactory`.
|
for :class:`TempPathFactory`.
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
"""Core implementation of the testing process: init, session, runtest loop."""
|
"""Core implementation of the testing process: init, session, runtest loop."""
|
||||||
import argparse
|
import argparse
|
||||||
|
import dataclasses
|
||||||
import fnmatch
|
import fnmatch
|
||||||
import functools
|
import functools
|
||||||
import importlib
|
import importlib
|
||||||
|
@ -19,8 +20,6 @@ from typing import Type
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import attr
|
|
||||||
|
|
||||||
import _pytest._code
|
import _pytest._code
|
||||||
from _pytest import nodes
|
from _pytest import nodes
|
||||||
from _pytest.compat import final
|
from _pytest.compat import final
|
||||||
|
@ -442,8 +441,10 @@ class Failed(Exception):
|
||||||
"""Signals a stop as failed test run."""
|
"""Signals a stop as failed test run."""
|
||||||
|
|
||||||
|
|
||||||
@attr.s(slots=True, auto_attribs=True)
|
@dataclasses.dataclass
|
||||||
class _bestrelpath_cache(Dict[Path, str]):
|
class _bestrelpath_cache(Dict[Path, str]):
|
||||||
|
__slots__ = ("path",)
|
||||||
|
|
||||||
path: Path
|
path: Path
|
||||||
|
|
||||||
def __missing__(self, path: Path) -> str:
|
def __missing__(self, path: Path) -> str:
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
"""Generic mechanism for marking and selecting python functions."""
|
"""Generic mechanism for marking and selecting python functions."""
|
||||||
|
import dataclasses
|
||||||
from typing import AbstractSet
|
from typing import AbstractSet
|
||||||
from typing import Collection
|
from typing import Collection
|
||||||
from typing import List
|
from typing import List
|
||||||
|
@ -6,8 +7,6 @@ from typing import Optional
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import attr
|
|
||||||
|
|
||||||
from .expression import Expression
|
from .expression import Expression
|
||||||
from .expression import ParseError
|
from .expression import ParseError
|
||||||
from .structures import EMPTY_PARAMETERSET_OPTION
|
from .structures import EMPTY_PARAMETERSET_OPTION
|
||||||
|
@ -130,7 +129,7 @@ def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@attr.s(slots=True, auto_attribs=True)
|
@dataclasses.dataclass
|
||||||
class KeywordMatcher:
|
class KeywordMatcher:
|
||||||
"""A matcher for keywords.
|
"""A matcher for keywords.
|
||||||
|
|
||||||
|
@ -145,6 +144,8 @@ class KeywordMatcher:
|
||||||
any item, as well as names directly assigned to test functions.
|
any item, as well as names directly assigned to test functions.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
__slots__ = ("_names",)
|
||||||
|
|
||||||
_names: AbstractSet[str]
|
_names: AbstractSet[str]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -201,13 +202,15 @@ def deselect_by_keyword(items: "List[Item]", config: Config) -> None:
|
||||||
items[:] = remaining
|
items[:] = remaining
|
||||||
|
|
||||||
|
|
||||||
@attr.s(slots=True, auto_attribs=True)
|
@dataclasses.dataclass
|
||||||
class MarkMatcher:
|
class MarkMatcher:
|
||||||
"""A matcher for markers which are present.
|
"""A matcher for markers which are present.
|
||||||
|
|
||||||
Tries to match on any marker names, attached to the given colitem.
|
Tries to match on any marker names, attached to the given colitem.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
__slots__ = ("own_mark_names",)
|
||||||
|
|
||||||
own_mark_names: AbstractSet[str]
|
own_mark_names: AbstractSet[str]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
@ -15,6 +15,7 @@ The semantics are:
|
||||||
- or/and/not evaluate according to the usual boolean semantics.
|
- or/and/not evaluate according to the usual boolean semantics.
|
||||||
"""
|
"""
|
||||||
import ast
|
import ast
|
||||||
|
import dataclasses
|
||||||
import enum
|
import enum
|
||||||
import re
|
import re
|
||||||
import types
|
import types
|
||||||
|
@ -25,8 +26,6 @@ from typing import NoReturn
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from typing import Sequence
|
from typing import Sequence
|
||||||
|
|
||||||
import attr
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"Expression",
|
"Expression",
|
||||||
|
@ -44,8 +43,9 @@ class TokenType(enum.Enum):
|
||||||
EOF = "end of input"
|
EOF = "end of input"
|
||||||
|
|
||||||
|
|
||||||
@attr.s(frozen=True, slots=True, auto_attribs=True)
|
@dataclasses.dataclass(frozen=True)
|
||||||
class Token:
|
class Token:
|
||||||
|
__slots__ = ("type", "value", "pos")
|
||||||
type: TokenType
|
type: TokenType
|
||||||
value: str
|
value: str
|
||||||
pos: int
|
pos: int
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import collections.abc
|
import collections.abc
|
||||||
|
import dataclasses
|
||||||
import inspect
|
import inspect
|
||||||
import warnings
|
import warnings
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
@ -20,8 +21,6 @@ from typing import TYPE_CHECKING
|
||||||
from typing import TypeVar
|
from typing import TypeVar
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import attr
|
|
||||||
|
|
||||||
from .._code import getfslineno
|
from .._code import getfslineno
|
||||||
from ..compat import ascii_escaped
|
from ..compat import ascii_escaped
|
||||||
from ..compat import final
|
from ..compat import final
|
||||||
|
@ -191,8 +190,10 @@ class ParameterSet(NamedTuple):
|
||||||
|
|
||||||
|
|
||||||
@final
|
@final
|
||||||
@attr.s(frozen=True, init=False, auto_attribs=True)
|
@dataclasses.dataclass(frozen=True)
|
||||||
class Mark:
|
class Mark:
|
||||||
|
"""A pytest mark."""
|
||||||
|
|
||||||
#: Name of the mark.
|
#: Name of the mark.
|
||||||
name: str
|
name: str
|
||||||
#: Positional arguments of the mark decorator.
|
#: Positional arguments of the mark decorator.
|
||||||
|
@ -201,9 +202,11 @@ class Mark:
|
||||||
kwargs: Mapping[str, Any]
|
kwargs: Mapping[str, Any]
|
||||||
|
|
||||||
#: Source Mark for ids with parametrize Marks.
|
#: Source Mark for ids with parametrize Marks.
|
||||||
_param_ids_from: Optional["Mark"] = attr.ib(default=None, repr=False)
|
_param_ids_from: Optional["Mark"] = dataclasses.field(default=None, repr=False)
|
||||||
#: Resolved/generated ids with parametrize Marks.
|
#: Resolved/generated ids with parametrize Marks.
|
||||||
_param_ids_generated: Optional[Sequence[str]] = attr.ib(default=None, repr=False)
|
_param_ids_generated: Optional[Sequence[str]] = dataclasses.field(
|
||||||
|
default=None, repr=False
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
@ -261,7 +264,7 @@ class Mark:
|
||||||
Markable = TypeVar("Markable", bound=Union[Callable[..., object], type])
|
Markable = TypeVar("Markable", bound=Union[Callable[..., object], type])
|
||||||
|
|
||||||
|
|
||||||
@attr.s(init=False, auto_attribs=True)
|
@dataclasses.dataclass
|
||||||
class MarkDecorator:
|
class MarkDecorator:
|
||||||
"""A decorator for applying a mark on test functions and classes.
|
"""A decorator for applying a mark on test functions and classes.
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
"""Python test discovery, setup and run of test functions."""
|
"""Python test discovery, setup and run of test functions."""
|
||||||
|
import dataclasses
|
||||||
import enum
|
import enum
|
||||||
import fnmatch
|
import fnmatch
|
||||||
import inspect
|
import inspect
|
||||||
|
@ -27,8 +28,6 @@ from typing import Tuple
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import attr
|
|
||||||
|
|
||||||
import _pytest
|
import _pytest
|
||||||
from _pytest import fixtures
|
from _pytest import fixtures
|
||||||
from _pytest import nodes
|
from _pytest import nodes
|
||||||
|
@ -956,10 +955,20 @@ def hasnew(obj: object) -> bool:
|
||||||
|
|
||||||
|
|
||||||
@final
|
@final
|
||||||
@attr.s(frozen=True, auto_attribs=True, slots=True)
|
@dataclasses.dataclass(frozen=True)
|
||||||
class IdMaker:
|
class IdMaker:
|
||||||
"""Make IDs for a parametrization."""
|
"""Make IDs for a parametrization."""
|
||||||
|
|
||||||
|
__slots__ = (
|
||||||
|
"argnames",
|
||||||
|
"parametersets",
|
||||||
|
"idfn",
|
||||||
|
"ids",
|
||||||
|
"config",
|
||||||
|
"nodeid",
|
||||||
|
"func_name",
|
||||||
|
)
|
||||||
|
|
||||||
# The argnames of the parametrization.
|
# The argnames of the parametrization.
|
||||||
argnames: Sequence[str]
|
argnames: Sequence[str]
|
||||||
# The ParameterSets of the parametrization.
|
# The ParameterSets of the parametrization.
|
||||||
|
@ -1109,7 +1118,7 @@ class IdMaker:
|
||||||
|
|
||||||
|
|
||||||
@final
|
@final
|
||||||
@attr.s(frozen=True, slots=True, auto_attribs=True)
|
@dataclasses.dataclass(frozen=True)
|
||||||
class CallSpec2:
|
class CallSpec2:
|
||||||
"""A planned parameterized invocation of a test function.
|
"""A planned parameterized invocation of a test function.
|
||||||
|
|
||||||
|
@ -1120,18 +1129,18 @@ class CallSpec2:
|
||||||
|
|
||||||
# arg name -> arg value which will be passed to the parametrized test
|
# arg name -> arg value which will be passed to the parametrized test
|
||||||
# function (direct parameterization).
|
# function (direct parameterization).
|
||||||
funcargs: Dict[str, object] = attr.Factory(dict)
|
funcargs: Dict[str, object] = dataclasses.field(default_factory=dict)
|
||||||
# arg name -> arg value which will be passed to a fixture of the same name
|
# arg name -> arg value which will be passed to a fixture of the same name
|
||||||
# (indirect parametrization).
|
# (indirect parametrization).
|
||||||
params: Dict[str, object] = attr.Factory(dict)
|
params: Dict[str, object] = dataclasses.field(default_factory=dict)
|
||||||
# arg name -> arg index.
|
# arg name -> arg index.
|
||||||
indices: Dict[str, int] = attr.Factory(dict)
|
indices: Dict[str, int] = dataclasses.field(default_factory=dict)
|
||||||
# Used for sorting parametrized resources.
|
# Used for sorting parametrized resources.
|
||||||
_arg2scope: Dict[str, Scope] = attr.Factory(dict)
|
_arg2scope: Dict[str, Scope] = dataclasses.field(default_factory=dict)
|
||||||
# Parts which will be added to the item's name in `[..]` separated by "-".
|
# Parts which will be added to the item's name in `[..]` separated by "-".
|
||||||
_idlist: List[str] = attr.Factory(list)
|
_idlist: List[str] = dataclasses.field(default_factory=list)
|
||||||
# Marks which will be applied to the item.
|
# Marks which will be applied to the item.
|
||||||
marks: List[Mark] = attr.Factory(list)
|
marks: List[Mark] = dataclasses.field(default_factory=list)
|
||||||
|
|
||||||
def setmulti(
|
def setmulti(
|
||||||
self,
|
self,
|
||||||
|
@ -1163,9 +1172,9 @@ class CallSpec2:
|
||||||
return CallSpec2(
|
return CallSpec2(
|
||||||
funcargs=funcargs,
|
funcargs=funcargs,
|
||||||
params=params,
|
params=params,
|
||||||
arg2scope=arg2scope,
|
|
||||||
indices=indices,
|
indices=indices,
|
||||||
idlist=[*self._idlist, id],
|
_arg2scope=arg2scope,
|
||||||
|
_idlist=[*self._idlist, id],
|
||||||
marks=[*self.marks, *normalize_mark_list(marks)],
|
marks=[*self.marks, *normalize_mark_list(marks)],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import dataclasses
|
||||||
import os
|
import os
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
from pprint import pprint
|
from pprint import pprint
|
||||||
|
@ -16,8 +17,6 @@ from typing import TYPE_CHECKING
|
||||||
from typing import TypeVar
|
from typing import TypeVar
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import attr
|
|
||||||
|
|
||||||
from _pytest._code.code import ExceptionChainRepr
|
from _pytest._code.code import ExceptionChainRepr
|
||||||
from _pytest._code.code import ExceptionInfo
|
from _pytest._code.code import ExceptionInfo
|
||||||
from _pytest._code.code import ExceptionRepr
|
from _pytest._code.code import ExceptionRepr
|
||||||
|
@ -459,15 +458,15 @@ def _report_to_json(report: BaseReport) -> Dict[str, Any]:
|
||||||
def serialize_repr_entry(
|
def serialize_repr_entry(
|
||||||
entry: Union[ReprEntry, ReprEntryNative]
|
entry: Union[ReprEntry, ReprEntryNative]
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
data = attr.asdict(entry)
|
data = dataclasses.asdict(entry)
|
||||||
for key, value in data.items():
|
for key, value in data.items():
|
||||||
if hasattr(value, "__dict__"):
|
if hasattr(value, "__dict__"):
|
||||||
data[key] = attr.asdict(value)
|
data[key] = dataclasses.asdict(value)
|
||||||
entry_data = {"type": type(entry).__name__, "data": data}
|
entry_data = {"type": type(entry).__name__, "data": data}
|
||||||
return entry_data
|
return entry_data
|
||||||
|
|
||||||
def serialize_repr_traceback(reprtraceback: ReprTraceback) -> Dict[str, Any]:
|
def serialize_repr_traceback(reprtraceback: ReprTraceback) -> Dict[str, Any]:
|
||||||
result = attr.asdict(reprtraceback)
|
result = dataclasses.asdict(reprtraceback)
|
||||||
result["reprentries"] = [
|
result["reprentries"] = [
|
||||||
serialize_repr_entry(x) for x in reprtraceback.reprentries
|
serialize_repr_entry(x) for x in reprtraceback.reprentries
|
||||||
]
|
]
|
||||||
|
@ -477,7 +476,7 @@ def _report_to_json(report: BaseReport) -> Dict[str, Any]:
|
||||||
reprcrash: Optional[ReprFileLocation],
|
reprcrash: Optional[ReprFileLocation],
|
||||||
) -> Optional[Dict[str, Any]]:
|
) -> Optional[Dict[str, Any]]:
|
||||||
if reprcrash is not None:
|
if reprcrash is not None:
|
||||||
return attr.asdict(reprcrash)
|
return dataclasses.asdict(reprcrash)
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -594,7 +593,10 @@ def _report_kwargs_from_json(reportdict: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
ExceptionChainRepr, ReprExceptionInfo
|
ExceptionChainRepr, ReprExceptionInfo
|
||||||
] = ExceptionChainRepr(chain)
|
] = ExceptionChainRepr(chain)
|
||||||
else:
|
else:
|
||||||
exception_info = ReprExceptionInfo(reprtraceback, reprcrash)
|
exception_info = ReprExceptionInfo(
|
||||||
|
reprtraceback=reprtraceback,
|
||||||
|
reprcrash=reprcrash,
|
||||||
|
)
|
||||||
|
|
||||||
for section in reportdict["longrepr"]["sections"]:
|
for section in reportdict["longrepr"]["sections"]:
|
||||||
exception_info.addsection(*section)
|
exception_info.addsection(*section)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
"""Basic collect and runtest protocol implementations."""
|
"""Basic collect and runtest protocol implementations."""
|
||||||
import bdb
|
import bdb
|
||||||
|
import dataclasses
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
|
@ -14,8 +15,6 @@ from typing import TYPE_CHECKING
|
||||||
from typing import TypeVar
|
from typing import TypeVar
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import attr
|
|
||||||
|
|
||||||
from .reports import BaseReport
|
from .reports import BaseReport
|
||||||
from .reports import CollectErrorRepr
|
from .reports import CollectErrorRepr
|
||||||
from .reports import CollectReport
|
from .reports import CollectReport
|
||||||
|
@ -268,7 +267,7 @@ TResult = TypeVar("TResult", covariant=True)
|
||||||
|
|
||||||
|
|
||||||
@final
|
@final
|
||||||
@attr.s(repr=False, init=False, auto_attribs=True)
|
@dataclasses.dataclass
|
||||||
class CallInfo(Generic[TResult]):
|
class CallInfo(Generic[TResult]):
|
||||||
"""Result/Exception info of a function invocation."""
|
"""Result/Exception info of a function invocation."""
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
"""Support for skip/xfail functions and markers."""
|
"""Support for skip/xfail functions and markers."""
|
||||||
|
import dataclasses
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import sys
|
import sys
|
||||||
|
@ -9,8 +10,6 @@ from typing import Optional
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
from typing import Type
|
from typing import Type
|
||||||
|
|
||||||
import attr
|
|
||||||
|
|
||||||
from _pytest.config import Config
|
from _pytest.config import Config
|
||||||
from _pytest.config import hookimpl
|
from _pytest.config import hookimpl
|
||||||
from _pytest.config.argparsing import Parser
|
from _pytest.config.argparsing import Parser
|
||||||
|
@ -157,7 +156,7 @@ def evaluate_condition(item: Item, mark: Mark, condition: object) -> Tuple[bool,
|
||||||
return result, reason
|
return result, reason
|
||||||
|
|
||||||
|
|
||||||
@attr.s(slots=True, frozen=True, auto_attribs=True)
|
@dataclasses.dataclass(frozen=True)
|
||||||
class Skip:
|
class Skip:
|
||||||
"""The result of evaluate_skip_marks()."""
|
"""The result of evaluate_skip_marks()."""
|
||||||
|
|
||||||
|
@ -192,10 +191,12 @@ def evaluate_skip_marks(item: Item) -> Optional[Skip]:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@attr.s(slots=True, frozen=True, auto_attribs=True)
|
@dataclasses.dataclass(frozen=True)
|
||||||
class Xfail:
|
class Xfail:
|
||||||
"""The result of evaluate_xfail_marks()."""
|
"""The result of evaluate_xfail_marks()."""
|
||||||
|
|
||||||
|
__slots__ = ("reason", "run", "strict", "raises")
|
||||||
|
|
||||||
reason: str
|
reason: str
|
||||||
run: bool
|
run: bool
|
||||||
strict: bool
|
strict: bool
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
This is a good source for looking at the various reporting hooks.
|
This is a good source for looking at the various reporting hooks.
|
||||||
"""
|
"""
|
||||||
import argparse
|
import argparse
|
||||||
|
import dataclasses
|
||||||
import datetime
|
import datetime
|
||||||
import inspect
|
import inspect
|
||||||
import platform
|
import platform
|
||||||
|
@ -27,7 +28,6 @@ from typing import Tuple
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import attr
|
|
||||||
import pluggy
|
import pluggy
|
||||||
|
|
||||||
import _pytest._version
|
import _pytest._version
|
||||||
|
@ -287,7 +287,7 @@ def pytest_report_teststatus(report: BaseReport) -> Tuple[str, str, str]:
|
||||||
return outcome, letter, outcome.upper()
|
return outcome, letter, outcome.upper()
|
||||||
|
|
||||||
|
|
||||||
@attr.s(auto_attribs=True)
|
@dataclasses.dataclass
|
||||||
class WarningReport:
|
class WarningReport:
|
||||||
"""Simple structure to hold warnings information captured by ``pytest_warning_recorded``.
|
"""Simple structure to hold warnings information captured by ``pytest_warning_recorded``.
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
"""Support for providing temporary directories to test functions."""
|
"""Support for providing temporary directories to test functions."""
|
||||||
|
import dataclasses
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from shutil import rmtree
|
from shutil import rmtree
|
||||||
|
from typing import Any
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from typing import Generator
|
from typing import Generator
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
@ -21,7 +23,6 @@ if TYPE_CHECKING:
|
||||||
RetentionType = Literal["all", "failed", "none"]
|
RetentionType = Literal["all", "failed", "none"]
|
||||||
|
|
||||||
|
|
||||||
import attr
|
|
||||||
from _pytest.config.argparsing import Parser
|
from _pytest.config.argparsing import Parser
|
||||||
|
|
||||||
from .pathlib import LOCK_TIMEOUT
|
from .pathlib import LOCK_TIMEOUT
|
||||||
|
@ -42,18 +43,19 @@ tmppath_result_key = StashKey[Dict[str, bool]]()
|
||||||
|
|
||||||
|
|
||||||
@final
|
@final
|
||||||
@attr.s(init=False)
|
@dataclasses.dataclass
|
||||||
class TempPathFactory:
|
class TempPathFactory:
|
||||||
"""Factory for temporary directories under the common base temp directory.
|
"""Factory for temporary directories under the common base temp directory.
|
||||||
|
|
||||||
The base directory can be configured using the ``--basetemp`` option.
|
The base directory can be configured using the ``--basetemp`` option.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_given_basetemp = attr.ib(type=Optional[Path])
|
_given_basetemp: Optional[Path]
|
||||||
_trace = attr.ib()
|
# pluggy TagTracerSub, not currently exposed, so Any.
|
||||||
_basetemp = attr.ib(type=Optional[Path])
|
_trace: Any
|
||||||
_retention_count = attr.ib(type=int)
|
_basetemp: Optional[Path]
|
||||||
_retention_policy = attr.ib(type="RetentionType")
|
_retention_count: int
|
||||||
|
_retention_policy: "RetentionType"
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import dataclasses
|
||||||
import inspect
|
import inspect
|
||||||
import warnings
|
import warnings
|
||||||
from types import FunctionType
|
from types import FunctionType
|
||||||
|
@ -6,8 +7,6 @@ from typing import Generic
|
||||||
from typing import Type
|
from typing import Type
|
||||||
from typing import TypeVar
|
from typing import TypeVar
|
||||||
|
|
||||||
import attr
|
|
||||||
|
|
||||||
from _pytest.compat import final
|
from _pytest.compat import final
|
||||||
|
|
||||||
|
|
||||||
|
@ -130,7 +129,7 @@ _W = TypeVar("_W", bound=PytestWarning)
|
||||||
|
|
||||||
|
|
||||||
@final
|
@final
|
||||||
@attr.s(auto_attribs=True)
|
@dataclasses.dataclass
|
||||||
class UnformattedWarning(Generic[_W]):
|
class UnformattedWarning(Generic[_W]):
|
||||||
"""A warning meant to be formatted during runtime.
|
"""A warning meant to be formatted during runtime.
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
|
import dataclasses
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import types
|
import types
|
||||||
|
|
||||||
import attr
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest.compat import importlib_metadata
|
from _pytest.compat import importlib_metadata
|
||||||
from _pytest.config import ExitCode
|
from _pytest.config import ExitCode
|
||||||
|
@ -115,11 +114,11 @@ class TestGeneralUsage:
|
||||||
|
|
||||||
loaded = []
|
loaded = []
|
||||||
|
|
||||||
@attr.s
|
@dataclasses.dataclass
|
||||||
class DummyEntryPoint:
|
class DummyEntryPoint:
|
||||||
name = attr.ib()
|
name: str
|
||||||
module = attr.ib()
|
module: str
|
||||||
group = "pytest11"
|
group: str = "pytest11"
|
||||||
|
|
||||||
def load(self):
|
def load(self):
|
||||||
__import__(self.module)
|
__import__(self.module)
|
||||||
|
@ -132,10 +131,10 @@ class TestGeneralUsage:
|
||||||
DummyEntryPoint("mycov", "mycov_module"),
|
DummyEntryPoint("mycov", "mycov_module"),
|
||||||
]
|
]
|
||||||
|
|
||||||
@attr.s
|
@dataclasses.dataclass
|
||||||
class DummyDist:
|
class DummyDist:
|
||||||
entry_points = attr.ib()
|
entry_points: object
|
||||||
files = ()
|
files: object = ()
|
||||||
|
|
||||||
def my_dists():
|
def my_dists():
|
||||||
return (DummyDist(entry_points),)
|
return (DummyDist(entry_points),)
|
||||||
|
@ -1037,14 +1036,14 @@ def test_fixture_values_leak(pytester: Pytester) -> None:
|
||||||
"""
|
"""
|
||||||
pytester.makepyfile(
|
pytester.makepyfile(
|
||||||
"""
|
"""
|
||||||
import attr
|
import dataclasses
|
||||||
import gc
|
import gc
|
||||||
import pytest
|
import pytest
|
||||||
import weakref
|
import weakref
|
||||||
|
|
||||||
@attr.s
|
@dataclasses.dataclass
|
||||||
class SomeObj(object):
|
class SomeObj:
|
||||||
name = attr.ib()
|
name: str
|
||||||
|
|
||||||
fix_of_test1_ref = None
|
fix_of_test1_ref = None
|
||||||
session_ref = None
|
session_ref = None
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import dataclasses
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
from typing import List
|
from typing import List
|
||||||
|
@ -192,20 +193,18 @@ def mock_timing(monkeypatch: MonkeyPatch):
|
||||||
Time is static, and only advances through `sleep` calls, thus tests might sleep over large
|
Time is static, and only advances through `sleep` calls, thus tests might sleep over large
|
||||||
numbers and obtain accurate time() calls at the end, making tests reliable and instant.
|
numbers and obtain accurate time() calls at the end, making tests reliable and instant.
|
||||||
"""
|
"""
|
||||||
import attr
|
|
||||||
|
|
||||||
@attr.s
|
@dataclasses.dataclass
|
||||||
class MockTiming:
|
class MockTiming:
|
||||||
|
_current_time: float = 1590150050.0
|
||||||
|
|
||||||
_current_time = attr.ib(default=1590150050.0)
|
def sleep(self, seconds: float) -> None:
|
||||||
|
|
||||||
def sleep(self, seconds):
|
|
||||||
self._current_time += seconds
|
self._current_time += seconds
|
||||||
|
|
||||||
def time(self):
|
def time(self) -> float:
|
||||||
return self._current_time
|
return self._current_time
|
||||||
|
|
||||||
def patch(self):
|
def patch(self) -> None:
|
||||||
from _pytest import timing
|
from _pytest import timing
|
||||||
|
|
||||||
monkeypatch.setattr(timing, "sleep", self.sleep)
|
monkeypatch.setattr(timing, "sleep", self.sleep)
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import dataclasses
|
||||||
import itertools
|
import itertools
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
@ -12,7 +13,6 @@ from typing import Sequence
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import attr
|
|
||||||
import hypothesis
|
import hypothesis
|
||||||
from hypothesis import strategies
|
from hypothesis import strategies
|
||||||
|
|
||||||
|
@ -39,14 +39,14 @@ class TestMetafunc:
|
||||||
def __init__(self, names):
|
def __init__(self, names):
|
||||||
self.names_closure = names
|
self.names_closure = names
|
||||||
|
|
||||||
@attr.s
|
@dataclasses.dataclass
|
||||||
class DefinitionMock(python.FunctionDefinition):
|
class DefinitionMock(python.FunctionDefinition):
|
||||||
obj = attr.ib()
|
_nodeid: str
|
||||||
_nodeid = attr.ib()
|
obj: object
|
||||||
|
|
||||||
names = getfuncargnames(func)
|
names = getfuncargnames(func)
|
||||||
fixtureinfo: Any = FuncFixtureInfoMock(names)
|
fixtureinfo: Any = FuncFixtureInfoMock(names)
|
||||||
definition: Any = DefinitionMock._create(func, "mock::nodeid")
|
definition: Any = DefinitionMock._create(obj=func, _nodeid="mock::nodeid")
|
||||||
return python.Metafunc(definition, fixtureinfo, config, _ispytest=True)
|
return python.Metafunc(definition, fixtureinfo, config, _ispytest=True)
|
||||||
|
|
||||||
def test_no_funcargs(self) -> None:
|
def test_no_funcargs(self) -> None:
|
||||||
|
@ -140,9 +140,9 @@ class TestMetafunc:
|
||||||
"""Unit test for _find_parametrized_scope (#3941)."""
|
"""Unit test for _find_parametrized_scope (#3941)."""
|
||||||
from _pytest.python import _find_parametrized_scope
|
from _pytest.python import _find_parametrized_scope
|
||||||
|
|
||||||
@attr.s
|
@dataclasses.dataclass
|
||||||
class DummyFixtureDef:
|
class DummyFixtureDef:
|
||||||
_scope = attr.ib()
|
_scope: Scope
|
||||||
|
|
||||||
fixtures_defs = cast(
|
fixtures_defs = cast(
|
||||||
Dict[str, Sequence[fixtures.FixtureDef[object]]],
|
Dict[str, Sequence[fixtures.FixtureDef[object]]],
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import dataclasses
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
@ -10,8 +11,6 @@ from typing import Tuple
|
||||||
from typing import Type
|
from typing import Type
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import attr
|
|
||||||
|
|
||||||
import _pytest._code
|
import _pytest._code
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest.compat import importlib_metadata
|
from _pytest.compat import importlib_metadata
|
||||||
|
@ -423,11 +422,11 @@ class TestParseIni:
|
||||||
This test installs a mock "myplugin-1.5" which is used in the parametrized test cases.
|
This test installs a mock "myplugin-1.5" which is used in the parametrized test cases.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@attr.s
|
@dataclasses.dataclass
|
||||||
class DummyEntryPoint:
|
class DummyEntryPoint:
|
||||||
name = attr.ib()
|
name: str
|
||||||
module = attr.ib()
|
module: str
|
||||||
group = "pytest11"
|
group: str = "pytest11"
|
||||||
|
|
||||||
def load(self):
|
def load(self):
|
||||||
__import__(self.module)
|
__import__(self.module)
|
||||||
|
@ -437,11 +436,11 @@ class TestParseIni:
|
||||||
DummyEntryPoint("myplugin1", "myplugin1_module"),
|
DummyEntryPoint("myplugin1", "myplugin1_module"),
|
||||||
]
|
]
|
||||||
|
|
||||||
@attr.s
|
@dataclasses.dataclass
|
||||||
class DummyDist:
|
class DummyDist:
|
||||||
entry_points = attr.ib()
|
entry_points: object
|
||||||
files = ()
|
files: object = ()
|
||||||
version = plugin_version
|
version: str = plugin_version
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def metadata(self):
|
def metadata(self):
|
||||||
|
|
|
@ -518,10 +518,10 @@ class TestImportLibMode:
|
||||||
fn1.write_text(
|
fn1.write_text(
|
||||||
dedent(
|
dedent(
|
||||||
"""
|
"""
|
||||||
import attr
|
import dataclasses
|
||||||
import pickle
|
import pickle
|
||||||
|
|
||||||
@attr.s(auto_attribs=True)
|
@dataclasses.dataclass
|
||||||
class Data:
|
class Data:
|
||||||
x: int = 42
|
x: int = 42
|
||||||
"""
|
"""
|
||||||
|
@ -533,10 +533,10 @@ class TestImportLibMode:
|
||||||
fn2.write_text(
|
fn2.write_text(
|
||||||
dedent(
|
dedent(
|
||||||
"""
|
"""
|
||||||
import attr
|
import dataclasses
|
||||||
import pickle
|
import pickle
|
||||||
|
|
||||||
@attr.s(auto_attribs=True)
|
@dataclasses.dataclass
|
||||||
class Data:
|
class Data:
|
||||||
x: str = ""
|
x: str = ""
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import dataclasses
|
||||||
import os
|
import os
|
||||||
import stat
|
import stat
|
||||||
import sys
|
import sys
|
||||||
|
@ -6,8 +7,7 @@ from pathlib import Path
|
||||||
from typing import Callable
|
from typing import Callable
|
||||||
from typing import cast
|
from typing import cast
|
||||||
from typing import List
|
from typing import List
|
||||||
|
from typing import Union
|
||||||
import attr
|
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest import pathlib
|
from _pytest import pathlib
|
||||||
|
@ -31,9 +31,9 @@ def test_tmp_path_fixture(pytester: Pytester) -> None:
|
||||||
results.stdout.fnmatch_lines(["*1 passed*"])
|
results.stdout.fnmatch_lines(["*1 passed*"])
|
||||||
|
|
||||||
|
|
||||||
@attr.s
|
@dataclasses.dataclass
|
||||||
class FakeConfig:
|
class FakeConfig:
|
||||||
basetemp = attr.ib()
|
basetemp: Union[str, Path]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def trace(self):
|
def trace(self):
|
||||||
|
@ -56,7 +56,7 @@ class FakeConfig:
|
||||||
|
|
||||||
|
|
||||||
class TestTmpPathHandler:
|
class TestTmpPathHandler:
|
||||||
def test_mktemp(self, tmp_path):
|
def test_mktemp(self, tmp_path: Path) -> None:
|
||||||
config = cast(Config, FakeConfig(tmp_path))
|
config = cast(Config, FakeConfig(tmp_path))
|
||||||
t = TempPathFactory.from_config(config, _ispytest=True)
|
t = TempPathFactory.from_config(config, _ispytest=True)
|
||||||
tmp = t.mktemp("world")
|
tmp = t.mktemp("world")
|
||||||
|
@ -67,7 +67,9 @@ class TestTmpPathHandler:
|
||||||
assert str(tmp2.relative_to(t.getbasetemp())).startswith("this")
|
assert str(tmp2.relative_to(t.getbasetemp())).startswith("this")
|
||||||
assert tmp2 != tmp
|
assert tmp2 != tmp
|
||||||
|
|
||||||
def test_tmppath_relative_basetemp_absolute(self, tmp_path, monkeypatch):
|
def test_tmppath_relative_basetemp_absolute(
|
||||||
|
self, tmp_path: Path, monkeypatch: MonkeyPatch
|
||||||
|
) -> None:
|
||||||
"""#4425"""
|
"""#4425"""
|
||||||
monkeypatch.chdir(tmp_path)
|
monkeypatch.chdir(tmp_path)
|
||||||
config = cast(Config, FakeConfig("hello"))
|
config = cast(Config, FakeConfig("hello"))
|
||||||
|
|
Loading…
Reference in New Issue