Merge pull request #9161 from bluetech/attrs-auto

Use attr.s(auto_attribs=True) in more places
This commit is contained in:
Ran Benita 2021-10-04 21:59:52 +03:00 committed by GitHub
commit 483f239d01
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 103 additions and 109 deletions

View File

@ -1,3 +1,4 @@
import ast
import inspect import inspect
import re import re
import sys import sys
@ -12,6 +13,7 @@ from types import FrameType
from types import TracebackType from types import TracebackType
from typing import Any from typing import Any
from typing import Callable from typing import Callable
from typing import ClassVar
from typing import Dict from typing import Dict
from typing import Generic from typing import Generic
from typing import Iterable from typing import Iterable
@ -238,7 +240,9 @@ class TracebackEntry:
def getfirstlinesource(self) -> int: def getfirstlinesource(self) -> int:
return self.frame.code.firstlineno return self.frame.code.firstlineno
def getsource(self, astcache=None) -> Optional["Source"]: def getsource(
self, astcache: Optional[Dict[Union[str, Path], ast.AST]] = None
) -> Optional["Source"]:
"""Return failing source code.""" """Return failing source code."""
# we use the passed in astcache to not reparse asttrees # we use the passed in astcache to not reparse asttrees
# within exception info printing # within exception info printing
@ -258,7 +262,7 @@ class TracebackEntry:
except SyntaxError: except SyntaxError:
end = self.lineno + 1 end = self.lineno + 1
else: else:
if key is not None: if key is not None and astcache is not None:
astcache[key] = astnode astcache[key] = astnode
return source[start:end] return source[start:end]
@ -435,15 +439,15 @@ E = TypeVar("E", bound=BaseException, covariant=True)
@final @final
@attr.s(repr=False, init=False) @attr.s(repr=False, init=False, auto_attribs=True)
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."""
_assert_start_repr = "AssertionError('assert " _assert_start_repr: ClassVar = "AssertionError('assert "
_excinfo = attr.ib(type=Optional[Tuple[Type["E"], "E", TracebackType]]) _excinfo: Optional[Tuple[Type["E"], "E", TracebackType]]
_striptext = attr.ib(type=str) _striptext: str
_traceback = attr.ib(type=Optional[Traceback]) _traceback: Optional[Traceback]
def __init__( def __init__(
self, self,
@ -673,22 +677,24 @@ class ExceptionInfo(Generic[E]):
return True return True
@attr.s @attr.s(auto_attribs=True)
class FormattedExcinfo: class FormattedExcinfo:
"""Presenting information about failing Functions and Generators.""" """Presenting information about failing Functions and Generators."""
# for traceback entries # for traceback entries
flow_marker = ">" flow_marker: ClassVar = ">"
fail_marker = "E" fail_marker: ClassVar = "E"
showlocals = attr.ib(type=bool, default=False) showlocals: bool = False
style = attr.ib(type="_TracebackStyle", default="long") style: "_TracebackStyle" = "long"
abspath = attr.ib(type=bool, default=True) abspath: bool = True
tbfilter = attr.ib(type=bool, default=True) tbfilter: bool = True
funcargs = attr.ib(type=bool, default=False) funcargs: bool = False
truncate_locals = attr.ib(type=bool, default=True) truncate_locals: bool = True
chain = attr.ib(type=bool, default=True) chain: bool = True
astcache = attr.ib(default=attr.Factory(dict), init=False, repr=False) astcache: Dict[Union[str, Path], ast.AST] = attr.ib(
factory=dict, init=False, repr=False
)
def _getindent(self, source: "Source") -> int: def _getindent(self, source: "Source") -> int:
# Figure out indent for the given source. # Figure out indent for the given source.
@ -951,7 +957,7 @@ class FormattedExcinfo:
return ExceptionChainRepr(repr_chain) return ExceptionChainRepr(repr_chain)
@attr.s(eq=False) @attr.s(eq=False, auto_attribs=True)
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
@ -987,13 +993,9 @@ class ExceptionRepr(TerminalRepr):
tw.line(content) tw.line(content)
@attr.s(eq=False) @attr.s(eq=False, auto_attribs=True)
class ExceptionChainRepr(ExceptionRepr): class ExceptionChainRepr(ExceptionRepr):
chain = attr.ib( chain: Sequence[Tuple["ReprTraceback", Optional["ReprFileLocation"], Optional[str]]]
type=Sequence[
Tuple["ReprTraceback", Optional["ReprFileLocation"], Optional[str]]
]
)
def __attrs_post_init__(self) -> None: def __attrs_post_init__(self) -> None:
super().__attrs_post_init__() super().__attrs_post_init__()
@ -1011,23 +1013,23 @@ class ExceptionChainRepr(ExceptionRepr):
super().toterminal(tw) super().toterminal(tw)
@attr.s(eq=False) @attr.s(eq=False, auto_attribs=True)
class ReprExceptionInfo(ExceptionRepr): class ReprExceptionInfo(ExceptionRepr):
reprtraceback = attr.ib(type="ReprTraceback") reprtraceback: "ReprTraceback"
reprcrash = attr.ib(type="ReprFileLocation") reprcrash: "ReprFileLocation"
def toterminal(self, tw: TerminalWriter) -> None: def toterminal(self, tw: TerminalWriter) -> None:
self.reprtraceback.toterminal(tw) self.reprtraceback.toterminal(tw)
super().toterminal(tw) super().toterminal(tw)
@attr.s(eq=False) @attr.s(eq=False, auto_attribs=True)
class ReprTraceback(TerminalRepr): class ReprTraceback(TerminalRepr):
reprentries = attr.ib(type=Sequence[Union["ReprEntry", "ReprEntryNative"]]) reprentries: Sequence[Union["ReprEntry", "ReprEntryNative"]]
extraline = attr.ib(type=Optional[str]) extraline: Optional[str]
style = attr.ib(type="_TracebackStyle") style: "_TracebackStyle"
entrysep = "_ " entrysep: ClassVar = "_ "
def toterminal(self, tw: TerminalWriter) -> None: def toterminal(self, tw: TerminalWriter) -> None:
# The entries might have different styles. # The entries might have different styles.
@ -1055,22 +1057,23 @@ class ReprTracebackNative(ReprTraceback):
self.extraline = None self.extraline = None
@attr.s(eq=False) @attr.s(eq=False, auto_attribs=True)
class ReprEntryNative(TerminalRepr): class ReprEntryNative(TerminalRepr):
lines = attr.ib(type=Sequence[str]) lines: Sequence[str]
style: "_TracebackStyle" = "native"
style: ClassVar["_TracebackStyle"] = "native"
def toterminal(self, tw: TerminalWriter) -> None: def toterminal(self, tw: TerminalWriter) -> None:
tw.write("".join(self.lines)) tw.write("".join(self.lines))
@attr.s(eq=False) @attr.s(eq=False, auto_attribs=True)
class ReprEntry(TerminalRepr): class ReprEntry(TerminalRepr):
lines = attr.ib(type=Sequence[str]) lines: Sequence[str]
reprfuncargs = attr.ib(type=Optional["ReprFuncArgs"]) reprfuncargs: Optional["ReprFuncArgs"]
reprlocals = attr.ib(type=Optional["ReprLocals"]) reprlocals: Optional["ReprLocals"]
reprfileloc = attr.ib(type=Optional["ReprFileLocation"]) reprfileloc: Optional["ReprFileLocation"]
style = attr.ib(type="_TracebackStyle") style: "_TracebackStyle"
def _write_entry_lines(self, tw: TerminalWriter) -> None: def _write_entry_lines(self, tw: TerminalWriter) -> None:
"""Write the source code portions of a list of traceback entries with syntax highlighting. """Write the source code portions of a list of traceback entries with syntax highlighting.
@ -1144,11 +1147,11 @@ class ReprEntry(TerminalRepr):
) )
@attr.s(eq=False) @attr.s(eq=False, auto_attribs=True)
class ReprFileLocation(TerminalRepr): class ReprFileLocation(TerminalRepr):
path = attr.ib(type=str, converter=str) path: str = attr.ib(converter=str)
lineno = attr.ib(type=int) lineno: int
message = attr.ib(type=str) message: str
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
@ -1161,18 +1164,18 @@ class ReprFileLocation(TerminalRepr):
tw.line(f":{self.lineno}: {msg}") tw.line(f":{self.lineno}: {msg}")
@attr.s(eq=False) @attr.s(eq=False, auto_attribs=True)
class ReprLocals(TerminalRepr): class ReprLocals(TerminalRepr):
lines = attr.ib(type=Sequence[str]) lines: Sequence[str]
def toterminal(self, tw: TerminalWriter, indent="") -> None: def toterminal(self, tw: TerminalWriter, indent="") -> None:
for line in self.lines: for line in self.lines:
tw.line(indent + line) tw.line(indent + line)
@attr.s(eq=False) @attr.s(eq=False, auto_attribs=True)
class ReprFuncArgs(TerminalRepr): class ReprFuncArgs(TerminalRepr):
args = attr.ib(type=Sequence[Tuple[str, object]]) args: Sequence[Tuple[str, object]]
def toterminal(self, tw: TerminalWriter) -> None: def toterminal(self, tw: TerminalWriter) -> None:
if self.args: if self.args:

View File

@ -55,10 +55,10 @@ Signature: 8a477f597d28d172789f06886806bc55
@final @final
@attr.s(init=False) @attr.s(init=False, auto_attribs=True)
class Cache: class Cache:
_cachedir = attr.ib(type=Path, repr=False) _cachedir: Path = attr.ib(repr=False)
_config = attr.ib(type=Config, repr=False) _config: Config = attr.ib(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"

View File

@ -858,7 +858,7 @@ class Config:
""" """
@final @final
@attr.s(frozen=True) @attr.s(frozen=True, auto_attribs=True)
class InvocationParams: class InvocationParams:
"""Holds parameters passed during :func:`pytest.main`. """Holds parameters passed during :func:`pytest.main`.
@ -874,21 +874,12 @@ class Config:
Plugins accessing ``InvocationParams`` must be aware of that. Plugins accessing ``InvocationParams`` must be aware of that.
""" """
args = attr.ib(type=Tuple[str, ...], converter=_args_converter) args: Tuple[str, ...] = attr.ib(converter=_args_converter)
"""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]]]
:type: Tuple[str, ...] """Extra plugins, might be `None`."""
""" dir: Path
plugins = attr.ib(type=Optional[Sequence[Union[str, _PluggyPlugin]]]) """The directory from which :func:`pytest.main` was invoked."""
"""Extra plugins, might be `None`.
:type: Optional[Sequence[Union[str, plugin]]]
"""
dir = attr.ib(type=Path)
"""The directory from which :func:`pytest.main` was invoked.
:type: pathlib.Path
"""
def __init__( def __init__(
self, self,

View File

@ -393,16 +393,16 @@ def get_direct_param_fixture_func(request):
return request.param return request.param
@attr.s(slots=True) @attr.s(slots=True, auto_attribs=True)
class FuncFixtureInfo: class FuncFixtureInfo:
# Original function argument names. # Original function argument names.
argnames = attr.ib(type=Tuple[str, ...]) argnames: Tuple[str, ...]
# Argnames that function immediately requires. These include argnames + # Argnames that function immediately requires. These include argnames +
# fixture names specified via usefixtures and via autouse=True in fixture # fixture names specified via usefixtures and via autouse=True in fixture
# definitions. # definitions.
initialnames = attr.ib(type=Tuple[str, ...]) initialnames: Tuple[str, ...]
names_closure = attr.ib(type=List[str]) names_closure: List[str]
name2fixturedefs = attr.ib(type=Dict[str, Sequence["FixtureDef[Any]"]]) name2fixturedefs: Dict[str, Sequence["FixtureDef[Any]"]]
def prune_dependency_tree(self) -> None: def prune_dependency_tree(self) -> None:
"""Recompute names_closure from initialnames and name2fixturedefs. """Recompute names_closure from initialnames and name2fixturedefs.
@ -1187,11 +1187,11 @@ def wrap_function_to_error_out_if_called_directly(
@final @final
@attr.s(frozen=True) @attr.s(frozen=True, auto_attribs=True)
class FixtureFunctionMarker: class FixtureFunctionMarker:
scope: "Union[_ScopeName, Callable[[str, Config], _ScopeName]]" = attr.ib() scope: "Union[_ScopeName, Callable[[str, Config], _ScopeName]]"
params: Optional[Tuple[object, ...]] = attr.ib(converter=_params_converter) params: Optional[Tuple[object, ...]] = attr.ib(converter=_params_converter)
autouse: bool = attr.ib(default=False) autouse: bool = False
ids: Union[ ids: Union[
Tuple[Union[None, str, float, int, bool], ...], Tuple[Union[None, str, float, int, bool], ...],
Callable[[Any], Optional[object]], Callable[[Any], Optional[object]],
@ -1199,7 +1199,7 @@ class FixtureFunctionMarker:
default=None, default=None,
converter=_ensure_immutable_ids, converter=_ensure_immutable_ids,
) )
name: Optional[str] = attr.ib(default=None) name: Optional[str] = None
def __call__(self, function: FixtureFunction) -> FixtureFunction: def __call__(self, function: FixtureFunction) -> FixtureFunction:
if inspect.isclass(function): if inspect.isclass(function):

View File

@ -442,9 +442,9 @@ class Failed(Exception):
"""Signals a stop as failed test run.""" """Signals a stop as failed test run."""
@attr.s @attr.s(slots=True, auto_attribs=True)
class _bestrelpath_cache(Dict[Path, str]): class _bestrelpath_cache(Dict[Path, str]):
path = attr.ib(type=Path) path: Path
def __missing__(self, path: Path) -> str: def __missing__(self, path: Path) -> str:
r = bestrelpath(self.path, path) r = bestrelpath(self.path, path)

View File

@ -133,7 +133,7 @@ def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]:
return None return None
@attr.s(slots=True) @attr.s(slots=True, auto_attribs=True)
class KeywordMatcher: class KeywordMatcher:
"""A matcher for keywords. """A matcher for keywords.
@ -148,7 +148,7 @@ class KeywordMatcher:
any item, as well as names directly assigned to test functions. any item, as well as names directly assigned to test functions.
""" """
_names = attr.ib(type=AbstractSet[str]) _names: AbstractSet[str]
@classmethod @classmethod
def from_item(cls, item: "Item") -> "KeywordMatcher": def from_item(cls, item: "Item") -> "KeywordMatcher":
@ -217,17 +217,17 @@ def deselect_by_keyword(items: "List[Item]", config: Config) -> None:
items[:] = remaining items[:] = remaining
@attr.s(slots=True) @attr.s(slots=True, auto_attribs=True)
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.
""" """
own_mark_names = attr.ib() own_mark_names: AbstractSet[str]
@classmethod @classmethod
def from_item(cls, item) -> "MarkMatcher": def from_item(cls, item: "Item") -> "MarkMatcher":
mark_names = {mark.name for mark in item.iter_markers()} mark_names = {mark.name for mark in item.iter_markers()}
return cls(mark_names) return cls(mark_names)

View File

@ -47,11 +47,11 @@ class TokenType(enum.Enum):
EOF = "end of input" EOF = "end of input"
@attr.s(frozen=True, slots=True) @attr.s(frozen=True, slots=True, auto_attribs=True)
class Token: class Token:
type = attr.ib(type=TokenType) type: TokenType
value = attr.ib(type=str) value: str
pos = attr.ib(type=int) pos: int
class ParseError(Exception): class ParseError(Exception):

View File

@ -33,7 +33,6 @@ from typing import TYPE_CHECKING
from typing import Union from typing import Union
from weakref import WeakKeyDictionary from weakref import WeakKeyDictionary
import attr
from iniconfig import IniConfig from iniconfig import IniConfig
from iniconfig import SectionWrapper from iniconfig import SectionWrapper
@ -1518,7 +1517,6 @@ class LineComp:
@final @final
@attr.s(repr=False, str=False, init=False)
class Testdir: class Testdir:
""" """
Similar to :class:`Pytester`, but this class works with legacy legacy_path objects instead. Similar to :class:`Pytester`, but this class works with legacy legacy_path objects instead.

View File

@ -157,11 +157,11 @@ def evaluate_condition(item: Item, mark: Mark, condition: object) -> Tuple[bool,
return result, reason return result, reason
@attr.s(slots=True, frozen=True) @attr.s(slots=True, frozen=True, auto_attribs=True)
class Skip: class Skip:
"""The result of evaluate_skip_marks().""" """The result of evaluate_skip_marks()."""
reason = attr.ib(type=str, default="unconditional skip") reason: str = "unconditional skip"
def evaluate_skip_marks(item: Item) -> Optional[Skip]: def evaluate_skip_marks(item: Item) -> Optional[Skip]:
@ -192,14 +192,14 @@ def evaluate_skip_marks(item: Item) -> Optional[Skip]:
return None return None
@attr.s(slots=True, frozen=True) @attr.s(slots=True, frozen=True, auto_attribs=True)
class Xfail: class Xfail:
"""The result of evaluate_xfail_marks().""" """The result of evaluate_xfail_marks()."""
reason = attr.ib(type=str) reason: str
run = attr.ib(type=bool) run: bool
strict = attr.ib(type=bool) strict: bool
raises = attr.ib(type=Optional[Tuple[Type[BaseException], ...]]) raises: Optional[Tuple[Type[BaseException], ...]]
def evaluate_xfail_marks(item: Item) -> Optional[Xfail]: def evaluate_xfail_marks(item: Item) -> Optional[Xfail]:

View File

@ -14,6 +14,7 @@ from pathlib import Path
from typing import Any from typing import Any
from typing import Callable from typing import Callable
from typing import cast from typing import cast
from typing import ClassVar
from typing import Dict from typing import Dict
from typing import Generator from typing import Generator
from typing import List from typing import List
@ -279,7 +280,7 @@ def pytest_report_teststatus(report: BaseReport) -> Tuple[str, str, str]:
return outcome, letter, outcome.upper() return outcome, letter, outcome.upper()
@attr.s @attr.s(auto_attribs=True)
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``.
@ -291,10 +292,11 @@ class WarningReport:
File system location of the source of the warning (see ``get_location``). File system location of the source of the warning (see ``get_location``).
""" """
message = attr.ib(type=str) message: str
nodeid = attr.ib(type=Optional[str], default=None) nodeid: Optional[str] = None
fslocation = attr.ib(type=Optional[Tuple[str, int]], default=None) fslocation: Optional[Tuple[str, int]] = None
count_towards_summary = True
count_towards_summary: ClassVar = True
def get_location(self, config: Config) -> Optional[str]: def get_location(self, config: Config) -> Optional[str]:
"""Return the more user-friendly information about the location of a warning, or None.""" """Return the more user-friendly information about the location of a warning, or None."""

View File

@ -158,12 +158,12 @@ class TempPathFactory:
@final @final
@attr.s(init=False) @attr.s(init=False, auto_attribs=True)
class TempdirFactory: class TempdirFactory:
"""Backward compatibility wrapper that implements :class:``_pytest.compat.LEGACY_PATH`` """Backward compatibility wrapper that implements :class:``_pytest.compat.LEGACY_PATH``
for :class:``TempPathFactory``.""" for :class:``TempPathFactory``."""
_tmppath_factory = attr.ib(type=TempPathFactory) _tmppath_factory: TempPathFactory
def __init__( def __init__(
self, tmppath_factory: TempPathFactory, *, _ispytest: bool = False self, tmppath_factory: TempPathFactory, *, _ispytest: bool = False

View File

@ -116,7 +116,7 @@ _W = TypeVar("_W", bound=PytestWarning)
@final @final
@attr.s @attr.s(auto_attribs=True)
class UnformattedWarning(Generic[_W]): class UnformattedWarning(Generic[_W]):
"""A warning meant to be formatted during runtime. """A warning meant to be formatted during runtime.
@ -124,8 +124,8 @@ class UnformattedWarning(Generic[_W]):
as opposed to a direct message. as opposed to a direct message.
""" """
category = attr.ib(type=Type["_W"]) category: Type["_W"]
template = attr.ib(type=str) template: str
def format(self, **kwargs: Any) -> _W: def format(self, **kwargs: Any) -> _W:
"""Return an instance of the warning category, formatted with given kwargs.""" """Return an instance of the warning category, formatted with given kwargs."""

View File

@ -455,7 +455,7 @@ class TestFormattedExcinfo:
pass pass
""" """
).strip() ).strip()
pr.flow_marker = "|" pr.flow_marker = "|" # type: ignore[misc]
lines = pr.get_source(source, 0) lines = pr.get_source(source, 0)
assert len(lines) == 2 assert len(lines) == 2
assert lines[0] == "| def f(x):" assert lines[0] == "| def f(x):"