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:
Ran Benita 2023-01-20 11:13:36 +02:00 committed by GitHub
parent 4d4ed42c34
commit 310b67b227
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 199 additions and 159 deletions

View File

@ -0,0 +1 @@
pytest no longer depends on the `attrs` package (don't worry, nice diffs for attrs classes are still supported).

View File

@ -44,7 +44,6 @@ packages =
pytest
py_modules = py
install_requires =
attrs>=19.2.0
iniconfig
packaging
pluggy>=0.12,<2.0
@ -68,6 +67,7 @@ console_scripts =
[options.extras_require]
testing =
argcomplete
attrs>=19.2.0
hypothesis>=3.56
mock
nose

View File

@ -1,4 +1,5 @@
import ast
import dataclasses
import inspect
import os
import re
@ -32,7 +33,6 @@ from typing import TypeVar
from typing import Union
from weakref import ref
import attr
import pluggy
import _pytest
@ -445,7 +445,7 @@ E = TypeVar("E", bound=BaseException, covariant=True)
@final
@attr.s(repr=False, init=False, auto_attribs=True)
@dataclasses.dataclass
class ExceptionInfo(Generic[E]):
"""Wraps sys.exc_info() objects and offers help for navigating the traceback."""
@ -649,12 +649,12 @@ class ExceptionInfo(Generic[E]):
"""
if style == "native":
return ReprExceptionInfo(
ReprTracebackNative(
reprtraceback=ReprTracebackNative(
traceback.format_exception(
self.type, self.value, self.traceback[0]._rawentry
)
),
self._getreprcrash(),
reprcrash=self._getreprcrash(),
)
fmt = FormattedExcinfo(
@ -684,7 +684,7 @@ class ExceptionInfo(Generic[E]):
return True
@attr.s(auto_attribs=True)
@dataclasses.dataclass
class FormattedExcinfo:
"""Presenting information about failing Functions and Generators."""
@ -699,8 +699,8 @@ class FormattedExcinfo:
funcargs: bool = False
truncate_locals: bool = True
chain: bool = True
astcache: Dict[Union[str, Path], ast.AST] = attr.ib(
factory=dict, init=False, repr=False
astcache: Dict[Union[str, Path], ast.AST] = dataclasses.field(
default_factory=dict, init=False, repr=False
)
def _getindent(self, source: "Source") -> int:
@ -978,7 +978,7 @@ class FormattedExcinfo:
return ExceptionChainRepr(repr_chain)
@attr.s(eq=False, auto_attribs=True)
@dataclasses.dataclass(eq=False)
class TerminalRepr:
def __str__(self) -> str:
# 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.
@attr.s(eq=False)
@dataclasses.dataclass(eq=False)
class ExceptionRepr(TerminalRepr):
# Provided by subclasses.
reprcrash: Optional["ReprFileLocation"]
reprtraceback: "ReprTraceback"
def __attrs_post_init__(self) -> None:
self.sections: List[Tuple[str, str, str]] = []
reprcrash: Optional["ReprFileLocation"]
sections: List[Tuple[str, str, str]] = dataclasses.field(
init=False, default_factory=list
)
def addsection(self, name: str, content: str, sep: str = "-") -> None:
self.sections.append((name, content, sep))
@ -1014,16 +1014,23 @@ class ExceptionRepr(TerminalRepr):
tw.line(content)
@attr.s(eq=False, auto_attribs=True)
@dataclasses.dataclass(eq=False)
class ExceptionChainRepr(ExceptionRepr):
chain: Sequence[Tuple["ReprTraceback", Optional["ReprFileLocation"], Optional[str]]]
def __attrs_post_init__(self) -> None:
super().__attrs_post_init__()
def __init__(
self,
chain: Sequence[
Tuple["ReprTraceback", Optional["ReprFileLocation"], Optional[str]]
],
) -> None:
# reprcrash and reprtraceback of the outermost (the newest) exception
# in the chain.
self.reprtraceback = self.chain[-1][0]
self.reprcrash = self.chain[-1][1]
super().__init__(
reprtraceback=chain[-1][0],
reprcrash=chain[-1][1],
)
self.chain = chain
def toterminal(self, tw: TerminalWriter) -> None:
for element in self.chain:
@ -1034,7 +1041,7 @@ class ExceptionChainRepr(ExceptionRepr):
super().toterminal(tw)
@attr.s(eq=False, auto_attribs=True)
@dataclasses.dataclass(eq=False)
class ReprExceptionInfo(ExceptionRepr):
reprtraceback: "ReprTraceback"
reprcrash: "ReprFileLocation"
@ -1044,7 +1051,7 @@ class ReprExceptionInfo(ExceptionRepr):
super().toterminal(tw)
@attr.s(eq=False, auto_attribs=True)
@dataclasses.dataclass(eq=False)
class ReprTraceback(TerminalRepr):
reprentries: Sequence[Union["ReprEntry", "ReprEntryNative"]]
extraline: Optional[str]
@ -1073,12 +1080,12 @@ class ReprTraceback(TerminalRepr):
class ReprTracebackNative(ReprTraceback):
def __init__(self, tblines: Sequence[str]) -> None:
self.style = "native"
self.reprentries = [ReprEntryNative(tblines)]
self.extraline = None
self.style = "native"
@attr.s(eq=False, auto_attribs=True)
@dataclasses.dataclass(eq=False)
class ReprEntryNative(TerminalRepr):
lines: Sequence[str]
@ -1088,7 +1095,7 @@ class ReprEntryNative(TerminalRepr):
tw.write("".join(self.lines))
@attr.s(eq=False, auto_attribs=True)
@dataclasses.dataclass(eq=False)
class ReprEntry(TerminalRepr):
lines: Sequence[str]
reprfuncargs: Optional["ReprFuncArgs"]
@ -1168,12 +1175,15 @@ class ReprEntry(TerminalRepr):
)
@attr.s(eq=False, auto_attribs=True)
@dataclasses.dataclass(eq=False)
class ReprFileLocation(TerminalRepr):
path: str = attr.ib(converter=str)
path: str
lineno: int
message: str
def __post_init__(self) -> None:
self.path = str(self.path)
def toterminal(self, tw: TerminalWriter) -> None:
# Filename and lineno output for each entry, using an output format
# that most editors understand.
@ -1185,7 +1195,7 @@ class ReprFileLocation(TerminalRepr):
tw.line(f":{self.lineno}: {msg}")
@attr.s(eq=False, auto_attribs=True)
@dataclasses.dataclass(eq=False)
class ReprLocals(TerminalRepr):
lines: Sequence[str]
@ -1194,7 +1204,7 @@ class ReprLocals(TerminalRepr):
tw.line(indent + line)
@attr.s(eq=False, auto_attribs=True)
@dataclasses.dataclass(eq=False)
class ReprFuncArgs(TerminalRepr):
args: Sequence[Tuple[str, object]]

View File

@ -1,6 +1,7 @@
"""Implementation of the cache provider."""
# This plugin was not named "cache" to avoid conflicts with the external
# pytest-cache version.
import dataclasses
import json
import os
from pathlib import Path
@ -12,8 +13,6 @@ from typing import Optional
from typing import Set
from typing import Union
import attr
from .pathlib import resolve_from_str
from .pathlib import rm_rf
from .reports import CollectReport
@ -52,10 +51,12 @@ Signature: 8a477f597d28d172789f06886806bc55
@final
@attr.s(init=False, auto_attribs=True)
@dataclasses.dataclass
class Cache:
_cachedir: Path = attr.ib(repr=False)
_config: Config = attr.ib(repr=False)
"""Instance of the `cache` fixture."""
_cachedir: Path = dataclasses.field(repr=False)
_config: Config = dataclasses.field(repr=False)
# Sub-directory under cache-dir for directories created by `mkdir()`.
_CACHE_PREFIX_DIRS = "d"

View File

@ -1,4 +1,5 @@
"""Python version compatibility code."""
import dataclasses
import enum
import functools
import inspect
@ -17,8 +18,6 @@ from typing import TYPE_CHECKING
from typing import TypeVar
from typing import Union
import attr
import py
# fmt: off
@ -253,7 +252,7 @@ def ascii_escaped(val: Union[bytes, str]) -> str:
return _translate_non_printable(ret)
@attr.s
@dataclasses.dataclass
class _PytestWrapper:
"""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.
"""
obj = attr.ib()
obj: Any
def get_real_func(obj):

View File

@ -2,6 +2,7 @@
import argparse
import collections.abc
import copy
import dataclasses
import enum
import glob
import inspect
@ -34,7 +35,6 @@ from typing import Type
from typing import TYPE_CHECKING
from typing import Union
import attr
from pluggy import HookimplMarker
from pluggy import HookspecMarker
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)
def _args_converter(args: Iterable[str]) -> Tuple[str, ...]:
return tuple(args)
@final
class Config:
"""Access to configuration values, pluginmanager and plugin hooks.
@ -903,7 +899,7 @@ class Config:
"""
@final
@attr.s(frozen=True, auto_attribs=True)
@dataclasses.dataclass(frozen=True)
class InvocationParams:
"""Holds parameters passed during :func:`pytest.main`.
@ -919,13 +915,24 @@ class Config:
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`."""
plugins: Optional[Sequence[Union[str, _PluggyPlugin]]]
"""Extra plugins, might be `None`."""
dir: Path
"""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):
"""Indicates the source of the test arguments.

View File

@ -1,3 +1,4 @@
import dataclasses
import functools
import inspect
import os
@ -28,8 +29,6 @@ from typing import TYPE_CHECKING
from typing import TypeVar
from typing import Union
import attr
import _pytest
from _pytest import nodes
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]):
cached_result: "_FixtureCachedResult[FixtureValue]"
_scope: Scope
@ -350,8 +349,10 @@ def get_direct_param_fixture_func(request: "FixtureRequest") -> Any:
return request.param
@attr.s(slots=True, auto_attribs=True)
@dataclasses.dataclass
class FuncFixtureInfo:
__slots__ = ("argnames", "initialnames", "names_closure", "name2fixturedefs")
# Original function argument names.
argnames: Tuple[str, ...]
# Argnames that function immediately requires. These include argnames +
@ -1181,19 +1182,21 @@ def wrap_function_to_error_out_if_called_directly(
@final
@attr.s(frozen=True, auto_attribs=True)
@dataclasses.dataclass(frozen=True)
class FixtureFunctionMarker:
scope: "Union[_ScopeName, Callable[[str, Config], _ScopeName]]"
params: Optional[Tuple[object, ...]] = attr.ib(converter=_params_converter)
params: Optional[Tuple[object, ...]]
autouse: bool = False
ids: Optional[
Union[Tuple[Optional[object], ...], Callable[[Any], Optional[object]]]
] = attr.ib(
default=None,
converter=_ensure_immutable_ids,
)
] = 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:
if inspect.isclass(function):
raise ValueError("class fixtures not supported (maybe in the future)")
@ -1313,10 +1316,11 @@ def fixture( # noqa: F811
"""
fixture_marker = FixtureFunctionMarker(
scope=scope,
params=params,
params=tuple(params) if params is not None else None,
autouse=autouse,
ids=ids,
ids=None if ids is None else ids if callable(ids) else tuple(ids),
name=name,
_ispytest=True,
)
# Direct decoration.

View File

@ -1,4 +1,5 @@
"""Add backward compatibility support for the legacy py path type."""
import dataclasses
import shlex
import subprocess
from pathlib import Path
@ -7,7 +8,6 @@ from typing import Optional
from typing import TYPE_CHECKING
from typing import Union
import attr
from iniconfig import SectionWrapper
from _pytest.cacheprovider import Cache
@ -268,7 +268,7 @@ class LegacyTestdirPlugin:
@final
@attr.s(init=False, auto_attribs=True)
@dataclasses.dataclass
class TempdirFactory:
"""Backward compatibility wrapper that implements :class:`py.path.local`
for :class:`TempPathFactory`.

View File

@ -1,5 +1,6 @@
"""Core implementation of the testing process: init, session, runtest loop."""
import argparse
import dataclasses
import fnmatch
import functools
import importlib
@ -19,8 +20,6 @@ from typing import Type
from typing import TYPE_CHECKING
from typing import Union
import attr
import _pytest._code
from _pytest import nodes
from _pytest.compat import final
@ -442,8 +441,10 @@ class Failed(Exception):
"""Signals a stop as failed test run."""
@attr.s(slots=True, auto_attribs=True)
@dataclasses.dataclass
class _bestrelpath_cache(Dict[Path, str]):
__slots__ = ("path",)
path: Path
def __missing__(self, path: Path) -> str:

View File

@ -1,4 +1,5 @@
"""Generic mechanism for marking and selecting python functions."""
import dataclasses
from typing import AbstractSet
from typing import Collection
from typing import List
@ -6,8 +7,6 @@ from typing import Optional
from typing import TYPE_CHECKING
from typing import Union
import attr
from .expression import Expression
from .expression import ParseError
from .structures import EMPTY_PARAMETERSET_OPTION
@ -130,7 +129,7 @@ def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]:
return None
@attr.s(slots=True, auto_attribs=True)
@dataclasses.dataclass
class KeywordMatcher:
"""A matcher for keywords.
@ -145,6 +144,8 @@ class KeywordMatcher:
any item, as well as names directly assigned to test functions.
"""
__slots__ = ("_names",)
_names: AbstractSet[str]
@classmethod
@ -201,13 +202,15 @@ def deselect_by_keyword(items: "List[Item]", config: Config) -> None:
items[:] = remaining
@attr.s(slots=True, auto_attribs=True)
@dataclasses.dataclass
class MarkMatcher:
"""A matcher for markers which are present.
Tries to match on any marker names, attached to the given colitem.
"""
__slots__ = ("own_mark_names",)
own_mark_names: AbstractSet[str]
@classmethod

View File

@ -15,6 +15,7 @@ The semantics are:
- or/and/not evaluate according to the usual boolean semantics.
"""
import ast
import dataclasses
import enum
import re
import types
@ -25,8 +26,6 @@ from typing import NoReturn
from typing import Optional
from typing import Sequence
import attr
__all__ = [
"Expression",
@ -44,8 +43,9 @@ class TokenType(enum.Enum):
EOF = "end of input"
@attr.s(frozen=True, slots=True, auto_attribs=True)
@dataclasses.dataclass(frozen=True)
class Token:
__slots__ = ("type", "value", "pos")
type: TokenType
value: str
pos: int

View File

@ -1,4 +1,5 @@
import collections.abc
import dataclasses
import inspect
import warnings
from typing import Any
@ -20,8 +21,6 @@ from typing import TYPE_CHECKING
from typing import TypeVar
from typing import Union
import attr
from .._code import getfslineno
from ..compat import ascii_escaped
from ..compat import final
@ -191,8 +190,10 @@ class ParameterSet(NamedTuple):
@final
@attr.s(frozen=True, init=False, auto_attribs=True)
@dataclasses.dataclass(frozen=True)
class Mark:
"""A pytest mark."""
#: Name of the mark.
name: str
#: Positional arguments of the mark decorator.
@ -201,9 +202,11 @@ class Mark:
kwargs: Mapping[str, Any]
#: 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.
_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__(
self,
@ -261,7 +264,7 @@ class Mark:
Markable = TypeVar("Markable", bound=Union[Callable[..., object], type])
@attr.s(init=False, auto_attribs=True)
@dataclasses.dataclass
class MarkDecorator:
"""A decorator for applying a mark on test functions and classes.

View File

@ -1,4 +1,5 @@
"""Python test discovery, setup and run of test functions."""
import dataclasses
import enum
import fnmatch
import inspect
@ -27,8 +28,6 @@ from typing import Tuple
from typing import TYPE_CHECKING
from typing import Union
import attr
import _pytest
from _pytest import fixtures
from _pytest import nodes
@ -956,10 +955,20 @@ def hasnew(obj: object) -> bool:
@final
@attr.s(frozen=True, auto_attribs=True, slots=True)
@dataclasses.dataclass(frozen=True)
class IdMaker:
"""Make IDs for a parametrization."""
__slots__ = (
"argnames",
"parametersets",
"idfn",
"ids",
"config",
"nodeid",
"func_name",
)
# The argnames of the parametrization.
argnames: Sequence[str]
# The ParameterSets of the parametrization.
@ -1109,7 +1118,7 @@ class IdMaker:
@final
@attr.s(frozen=True, slots=True, auto_attribs=True)
@dataclasses.dataclass(frozen=True)
class CallSpec2:
"""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
# 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
# (indirect parametrization).
params: Dict[str, object] = attr.Factory(dict)
params: Dict[str, object] = dataclasses.field(default_factory=dict)
# 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.
_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 "-".
_idlist: List[str] = attr.Factory(list)
_idlist: List[str] = dataclasses.field(default_factory=list)
# Marks which will be applied to the item.
marks: List[Mark] = attr.Factory(list)
marks: List[Mark] = dataclasses.field(default_factory=list)
def setmulti(
self,
@ -1163,9 +1172,9 @@ class CallSpec2:
return CallSpec2(
funcargs=funcargs,
params=params,
arg2scope=arg2scope,
indices=indices,
idlist=[*self._idlist, id],
_arg2scope=arg2scope,
_idlist=[*self._idlist, id],
marks=[*self.marks, *normalize_mark_list(marks)],
)

View File

@ -1,3 +1,4 @@
import dataclasses
import os
from io import StringIO
from pprint import pprint
@ -16,8 +17,6 @@ from typing import TYPE_CHECKING
from typing import TypeVar
from typing import Union
import attr
from _pytest._code.code import ExceptionChainRepr
from _pytest._code.code import ExceptionInfo
from _pytest._code.code import ExceptionRepr
@ -459,15 +458,15 @@ def _report_to_json(report: BaseReport) -> Dict[str, Any]:
def serialize_repr_entry(
entry: Union[ReprEntry, ReprEntryNative]
) -> Dict[str, Any]:
data = attr.asdict(entry)
data = dataclasses.asdict(entry)
for key, value in data.items():
if hasattr(value, "__dict__"):
data[key] = attr.asdict(value)
data[key] = dataclasses.asdict(value)
entry_data = {"type": type(entry).__name__, "data": data}
return entry_data
def serialize_repr_traceback(reprtraceback: ReprTraceback) -> Dict[str, Any]:
result = attr.asdict(reprtraceback)
result = dataclasses.asdict(reprtraceback)
result["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],
) -> Optional[Dict[str, Any]]:
if reprcrash is not None:
return attr.asdict(reprcrash)
return dataclasses.asdict(reprcrash)
else:
return None
@ -594,7 +593,10 @@ def _report_kwargs_from_json(reportdict: Dict[str, Any]) -> Dict[str, Any]:
ExceptionChainRepr, ReprExceptionInfo
] = ExceptionChainRepr(chain)
else:
exception_info = ReprExceptionInfo(reprtraceback, reprcrash)
exception_info = ReprExceptionInfo(
reprtraceback=reprtraceback,
reprcrash=reprcrash,
)
for section in reportdict["longrepr"]["sections"]:
exception_info.addsection(*section)

View File

@ -1,5 +1,6 @@
"""Basic collect and runtest protocol implementations."""
import bdb
import dataclasses
import os
import sys
from typing import Callable
@ -14,8 +15,6 @@ from typing import TYPE_CHECKING
from typing import TypeVar
from typing import Union
import attr
from .reports import BaseReport
from .reports import CollectErrorRepr
from .reports import CollectReport
@ -268,7 +267,7 @@ TResult = TypeVar("TResult", covariant=True)
@final
@attr.s(repr=False, init=False, auto_attribs=True)
@dataclasses.dataclass
class CallInfo(Generic[TResult]):
"""Result/Exception info of a function invocation."""

View File

@ -1,4 +1,5 @@
"""Support for skip/xfail functions and markers."""
import dataclasses
import os
import platform
import sys
@ -9,8 +10,6 @@ from typing import Optional
from typing import Tuple
from typing import Type
import attr
from _pytest.config import Config
from _pytest.config import hookimpl
from _pytest.config.argparsing import Parser
@ -157,7 +156,7 @@ def evaluate_condition(item: Item, mark: Mark, condition: object) -> Tuple[bool,
return result, reason
@attr.s(slots=True, frozen=True, auto_attribs=True)
@dataclasses.dataclass(frozen=True)
class Skip:
"""The result of evaluate_skip_marks()."""
@ -192,10 +191,12 @@ def evaluate_skip_marks(item: Item) -> Optional[Skip]:
return None
@attr.s(slots=True, frozen=True, auto_attribs=True)
@dataclasses.dataclass(frozen=True)
class Xfail:
"""The result of evaluate_xfail_marks()."""
__slots__ = ("reason", "run", "strict", "raises")
reason: str
run: bool
strict: bool

View File

@ -3,6 +3,7 @@
This is a good source for looking at the various reporting hooks.
"""
import argparse
import dataclasses
import datetime
import inspect
import platform
@ -27,7 +28,6 @@ from typing import Tuple
from typing import TYPE_CHECKING
from typing import Union
import attr
import pluggy
import _pytest._version
@ -287,7 +287,7 @@ def pytest_report_teststatus(report: BaseReport) -> Tuple[str, str, str]:
return outcome, letter, outcome.upper()
@attr.s(auto_attribs=True)
@dataclasses.dataclass
class WarningReport:
"""Simple structure to hold warnings information captured by ``pytest_warning_recorded``.

View File

@ -1,10 +1,12 @@
"""Support for providing temporary directories to test functions."""
import dataclasses
import os
import re
import sys
import tempfile
from pathlib import Path
from shutil import rmtree
from typing import Any
from typing import Dict
from typing import Generator
from typing import Optional
@ -21,7 +23,6 @@ if TYPE_CHECKING:
RetentionType = Literal["all", "failed", "none"]
import attr
from _pytest.config.argparsing import Parser
from .pathlib import LOCK_TIMEOUT
@ -42,18 +43,19 @@ tmppath_result_key = StashKey[Dict[str, bool]]()
@final
@attr.s(init=False)
@dataclasses.dataclass
class TempPathFactory:
"""Factory for temporary directories under the common base temp directory.
The base directory can be configured using the ``--basetemp`` option.
"""
_given_basetemp = attr.ib(type=Optional[Path])
_trace = attr.ib()
_basetemp = attr.ib(type=Optional[Path])
_retention_count = attr.ib(type=int)
_retention_policy = attr.ib(type="RetentionType")
_given_basetemp: Optional[Path]
# pluggy TagTracerSub, not currently exposed, so Any.
_trace: Any
_basetemp: Optional[Path]
_retention_count: int
_retention_policy: "RetentionType"
def __init__(
self,

View File

@ -1,3 +1,4 @@
import dataclasses
import inspect
import warnings
from types import FunctionType
@ -6,8 +7,6 @@ from typing import Generic
from typing import Type
from typing import TypeVar
import attr
from _pytest.compat import final
@ -130,7 +129,7 @@ _W = TypeVar("_W", bound=PytestWarning)
@final
@attr.s(auto_attribs=True)
@dataclasses.dataclass
class UnformattedWarning(Generic[_W]):
"""A warning meant to be formatted during runtime.

View File

@ -1,9 +1,8 @@
import dataclasses
import os
import sys
import types
import attr
import pytest
from _pytest.compat import importlib_metadata
from _pytest.config import ExitCode
@ -115,11 +114,11 @@ class TestGeneralUsage:
loaded = []
@attr.s
@dataclasses.dataclass
class DummyEntryPoint:
name = attr.ib()
module = attr.ib()
group = "pytest11"
name: str
module: str
group: str = "pytest11"
def load(self):
__import__(self.module)
@ -132,10 +131,10 @@ class TestGeneralUsage:
DummyEntryPoint("mycov", "mycov_module"),
]
@attr.s
@dataclasses.dataclass
class DummyDist:
entry_points = attr.ib()
files = ()
entry_points: object
files: object = ()
def my_dists():
return (DummyDist(entry_points),)
@ -1037,14 +1036,14 @@ def test_fixture_values_leak(pytester: Pytester) -> None:
"""
pytester.makepyfile(
"""
import attr
import dataclasses
import gc
import pytest
import weakref
@attr.s
class SomeObj(object):
name = attr.ib()
@dataclasses.dataclass
class SomeObj:
name: str
fix_of_test1_ref = None
session_ref = None

View File

@ -1,3 +1,4 @@
import dataclasses
import re
import sys
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
numbers and obtain accurate time() calls at the end, making tests reliable and instant.
"""
import attr
@attr.s
@dataclasses.dataclass
class MockTiming:
_current_time: float = 1590150050.0
_current_time = attr.ib(default=1590150050.0)
def sleep(self, seconds):
def sleep(self, seconds: float) -> None:
self._current_time += seconds
def time(self):
def time(self) -> float:
return self._current_time
def patch(self):
def patch(self) -> None:
from _pytest import timing
monkeypatch.setattr(timing, "sleep", self.sleep)

View File

@ -1,3 +1,4 @@
import dataclasses
import itertools
import re
import sys
@ -12,7 +13,6 @@ from typing import Sequence
from typing import Tuple
from typing import Union
import attr
import hypothesis
from hypothesis import strategies
@ -39,14 +39,14 @@ class TestMetafunc:
def __init__(self, names):
self.names_closure = names
@attr.s
@dataclasses.dataclass
class DefinitionMock(python.FunctionDefinition):
obj = attr.ib()
_nodeid = attr.ib()
_nodeid: str
obj: object
names = getfuncargnames(func)
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)
def test_no_funcargs(self) -> None:
@ -140,9 +140,9 @@ class TestMetafunc:
"""Unit test for _find_parametrized_scope (#3941)."""
from _pytest.python import _find_parametrized_scope
@attr.s
@dataclasses.dataclass
class DummyFixtureDef:
_scope = attr.ib()
_scope: Scope
fixtures_defs = cast(
Dict[str, Sequence[fixtures.FixtureDef[object]]],

View File

@ -1,3 +1,4 @@
import dataclasses
import os
import re
import sys
@ -10,8 +11,6 @@ from typing import Tuple
from typing import Type
from typing import Union
import attr
import _pytest._code
import pytest
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.
"""
@attr.s
@dataclasses.dataclass
class DummyEntryPoint:
name = attr.ib()
module = attr.ib()
group = "pytest11"
name: str
module: str
group: str = "pytest11"
def load(self):
__import__(self.module)
@ -437,11 +436,11 @@ class TestParseIni:
DummyEntryPoint("myplugin1", "myplugin1_module"),
]
@attr.s
@dataclasses.dataclass
class DummyDist:
entry_points = attr.ib()
files = ()
version = plugin_version
entry_points: object
files: object = ()
version: str = plugin_version
@property
def metadata(self):

View File

@ -518,10 +518,10 @@ class TestImportLibMode:
fn1.write_text(
dedent(
"""
import attr
import dataclasses
import pickle
@attr.s(auto_attribs=True)
@dataclasses.dataclass
class Data:
x: int = 42
"""
@ -533,10 +533,10 @@ class TestImportLibMode:
fn2.write_text(
dedent(
"""
import attr
import dataclasses
import pickle
@attr.s(auto_attribs=True)
@dataclasses.dataclass
class Data:
x: str = ""
"""

View File

@ -1,3 +1,4 @@
import dataclasses
import os
import stat
import sys
@ -6,8 +7,7 @@ from pathlib import Path
from typing import Callable
from typing import cast
from typing import List
import attr
from typing import Union
import pytest
from _pytest import pathlib
@ -31,9 +31,9 @@ def test_tmp_path_fixture(pytester: Pytester) -> None:
results.stdout.fnmatch_lines(["*1 passed*"])
@attr.s
@dataclasses.dataclass
class FakeConfig:
basetemp = attr.ib()
basetemp: Union[str, Path]
@property
def trace(self):
@ -56,7 +56,7 @@ class FakeConfig:
class TestTmpPathHandler:
def test_mktemp(self, tmp_path):
def test_mktemp(self, tmp_path: Path) -> None:
config = cast(Config, FakeConfig(tmp_path))
t = TempPathFactory.from_config(config, _ispytest=True)
tmp = t.mktemp("world")
@ -67,7 +67,9 @@ class TestTmpPathHandler:
assert str(tmp2.relative_to(t.getbasetemp())).startswith("this")
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"""
monkeypatch.chdir(tmp_path)
config = cast(Config, FakeConfig("hello"))