зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1748737: Bump attrs to be compatible with mochitest r=ahal
A bunch of modern packages (`pytest`, `twisted`, `automat`) all need `attrs==19.2.0` (or newer). We _could_ bump `attrs` all the way to the modern `21.4.0` version, but I'd like to defer that upgrade risk, since there's a lot of backwards-incompatible changes and deprecations. So, lightly bump it to `19.2.0`. As part of bumping it, `pytest` is no longer compatible. The earliest candidate that seems to be compatible is `pytest` 4.6.6, which boasts in its release notes that it's resolved some deprecation warnings against `attrs>=19.2.0`. Once `pytest` was bumped, it needed a newer version of `pluggy`, which itself has dependencies. Since we're using hashes in `tox_requirements.txt`, all dependencies needed to be hashed as well. Differential Revision: https://phabricator.services.mozilla.com/D135178
This commit is contained in:
Родитель
0116a8d491
Коммит
a37e7812d4
|
@ -1,2 +1,2 @@
|
|||
vendored:third_party/python/glean_parser
|
||||
pypi:pytest==3.6.2
|
||||
pypi:pytest==4.6.6
|
||||
|
|
|
@ -16,9 +16,11 @@ from ._make import (
|
|||
make_class,
|
||||
validate,
|
||||
)
|
||||
from ._version import VersionInfo
|
||||
|
||||
|
||||
__version__ = "19.1.0"
|
||||
__version__ = "19.2.0"
|
||||
__version_info__ = VersionInfo._from_version_string(__version__)
|
||||
|
||||
__title__ = "attrs"
|
||||
__description__ = "Classes Without Boilerplate"
|
||||
|
@ -37,6 +39,7 @@ s = attributes = attrs
|
|||
ib = attr = attrib
|
||||
dataclass = partial(attrs, auto_attribs=True) # happy Easter ;)
|
||||
|
||||
|
||||
__all__ = [
|
||||
"Attribute",
|
||||
"Factory",
|
||||
|
|
|
@ -20,12 +20,27 @@ from . import filters as filters
|
|||
from . import converters as converters
|
||||
from . import validators as validators
|
||||
|
||||
from ._version import VersionInfo
|
||||
|
||||
__version__: str
|
||||
__version_info__: VersionInfo
|
||||
__title__: str
|
||||
__description__: str
|
||||
__url__: str
|
||||
__uri__: str
|
||||
__author__: str
|
||||
__email__: str
|
||||
__license__: str
|
||||
__copyright__: str
|
||||
|
||||
_T = TypeVar("_T")
|
||||
_C = TypeVar("_C", bound=type)
|
||||
|
||||
_ValidatorType = Callable[[Any, Attribute[_T], _T], Any]
|
||||
_ConverterType = Callable[[Any], _T]
|
||||
_FilterType = Callable[[Attribute[_T], _T], bool]
|
||||
_ReprType = Callable[[Any], str]
|
||||
_ReprArgType = Union[bool, _ReprType]
|
||||
# FIXME: in reality, if multiple validators are passed they must be in a list or tuple,
|
||||
# but those are invariant and so would prevent subtypes of _ValidatorType from working
|
||||
# when passed in a list or tuple.
|
||||
|
@ -49,18 +64,16 @@ class Attribute(Generic[_T]):
|
|||
name: str
|
||||
default: Optional[_T]
|
||||
validator: Optional[_ValidatorType[_T]]
|
||||
repr: bool
|
||||
repr: _ReprArgType
|
||||
cmp: bool
|
||||
eq: bool
|
||||
order: bool
|
||||
hash: Optional[bool]
|
||||
init: bool
|
||||
converter: Optional[_ConverterType[_T]]
|
||||
metadata: Dict[Any, Any]
|
||||
type: Optional[Type[_T]]
|
||||
kw_only: bool
|
||||
def __lt__(self, x: Attribute[_T]) -> bool: ...
|
||||
def __le__(self, x: Attribute[_T]) -> bool: ...
|
||||
def __gt__(self, x: Attribute[_T]) -> bool: ...
|
||||
def __ge__(self, x: Attribute[_T]) -> bool: ...
|
||||
|
||||
# NOTE: We had several choices for the annotation to use for type arg:
|
||||
# 1) Type[_T]
|
||||
|
@ -89,16 +102,17 @@ class Attribute(Generic[_T]):
|
|||
def attrib(
|
||||
default: None = ...,
|
||||
validator: None = ...,
|
||||
repr: bool = ...,
|
||||
cmp: bool = ...,
|
||||
repr: _ReprArgType = ...,
|
||||
cmp: Optional[bool] = ...,
|
||||
hash: Optional[bool] = ...,
|
||||
init: bool = ...,
|
||||
convert: None = ...,
|
||||
metadata: Optional[Mapping[Any, Any]] = ...,
|
||||
type: None = ...,
|
||||
converter: None = ...,
|
||||
factory: None = ...,
|
||||
kw_only: bool = ...,
|
||||
eq: Optional[bool] = ...,
|
||||
order: Optional[bool] = ...,
|
||||
) -> Any: ...
|
||||
|
||||
# This form catches an explicit None or no default and infers the type from the other arguments.
|
||||
|
@ -106,16 +120,17 @@ def attrib(
|
|||
def attrib(
|
||||
default: None = ...,
|
||||
validator: Optional[_ValidatorArgType[_T]] = ...,
|
||||
repr: bool = ...,
|
||||
cmp: bool = ...,
|
||||
repr: _ReprArgType = ...,
|
||||
cmp: Optional[bool] = ...,
|
||||
hash: Optional[bool] = ...,
|
||||
init: bool = ...,
|
||||
convert: Optional[_ConverterType[_T]] = ...,
|
||||
metadata: Optional[Mapping[Any, Any]] = ...,
|
||||
type: Optional[Type[_T]] = ...,
|
||||
converter: Optional[_ConverterType[_T]] = ...,
|
||||
factory: Optional[Callable[[], _T]] = ...,
|
||||
kw_only: bool = ...,
|
||||
eq: Optional[bool] = ...,
|
||||
order: Optional[bool] = ...,
|
||||
) -> _T: ...
|
||||
|
||||
# This form catches an explicit default argument.
|
||||
|
@ -123,16 +138,17 @@ def attrib(
|
|||
def attrib(
|
||||
default: _T,
|
||||
validator: Optional[_ValidatorArgType[_T]] = ...,
|
||||
repr: bool = ...,
|
||||
cmp: bool = ...,
|
||||
repr: _ReprArgType = ...,
|
||||
cmp: Optional[bool] = ...,
|
||||
hash: Optional[bool] = ...,
|
||||
init: bool = ...,
|
||||
convert: Optional[_ConverterType[_T]] = ...,
|
||||
metadata: Optional[Mapping[Any, Any]] = ...,
|
||||
type: Optional[Type[_T]] = ...,
|
||||
converter: Optional[_ConverterType[_T]] = ...,
|
||||
factory: Optional[Callable[[], _T]] = ...,
|
||||
kw_only: bool = ...,
|
||||
eq: Optional[bool] = ...,
|
||||
order: Optional[bool] = ...,
|
||||
) -> _T: ...
|
||||
|
||||
# This form covers type=non-Type: e.g. forward references (str), Any
|
||||
|
@ -140,16 +156,17 @@ def attrib(
|
|||
def attrib(
|
||||
default: Optional[_T] = ...,
|
||||
validator: Optional[_ValidatorArgType[_T]] = ...,
|
||||
repr: bool = ...,
|
||||
cmp: bool = ...,
|
||||
repr: _ReprArgType = ...,
|
||||
cmp: Optional[bool] = ...,
|
||||
hash: Optional[bool] = ...,
|
||||
init: bool = ...,
|
||||
convert: Optional[_ConverterType[_T]] = ...,
|
||||
metadata: Optional[Mapping[Any, Any]] = ...,
|
||||
type: object = ...,
|
||||
converter: Optional[_ConverterType[_T]] = ...,
|
||||
factory: Optional[Callable[[], _T]] = ...,
|
||||
kw_only: bool = ...,
|
||||
eq: Optional[bool] = ...,
|
||||
order: Optional[bool] = ...,
|
||||
) -> Any: ...
|
||||
@overload
|
||||
def attrs(
|
||||
|
@ -157,7 +174,7 @@ def attrs(
|
|||
these: Optional[Dict[str, Any]] = ...,
|
||||
repr_ns: Optional[str] = ...,
|
||||
repr: bool = ...,
|
||||
cmp: bool = ...,
|
||||
cmp: Optional[bool] = ...,
|
||||
hash: Optional[bool] = ...,
|
||||
init: bool = ...,
|
||||
slots: bool = ...,
|
||||
|
@ -168,6 +185,8 @@ def attrs(
|
|||
kw_only: bool = ...,
|
||||
cache_hash: bool = ...,
|
||||
auto_exc: bool = ...,
|
||||
eq: Optional[bool] = ...,
|
||||
order: Optional[bool] = ...,
|
||||
) -> _C: ...
|
||||
@overload
|
||||
def attrs(
|
||||
|
@ -175,7 +194,7 @@ def attrs(
|
|||
these: Optional[Dict[str, Any]] = ...,
|
||||
repr_ns: Optional[str] = ...,
|
||||
repr: bool = ...,
|
||||
cmp: bool = ...,
|
||||
cmp: Optional[bool] = ...,
|
||||
hash: Optional[bool] = ...,
|
||||
init: bool = ...,
|
||||
slots: bool = ...,
|
||||
|
@ -186,6 +205,8 @@ def attrs(
|
|||
kw_only: bool = ...,
|
||||
cache_hash: bool = ...,
|
||||
auto_exc: bool = ...,
|
||||
eq: Optional[bool] = ...,
|
||||
order: Optional[bool] = ...,
|
||||
) -> Callable[[_C], _C]: ...
|
||||
|
||||
# TODO: add support for returning NamedTuple from the mypy plugin
|
||||
|
@ -204,7 +225,7 @@ def make_class(
|
|||
bases: Tuple[type, ...] = ...,
|
||||
repr_ns: Optional[str] = ...,
|
||||
repr: bool = ...,
|
||||
cmp: bool = ...,
|
||||
cmp: Optional[bool] = ...,
|
||||
hash: Optional[bool] = ...,
|
||||
init: bool = ...,
|
||||
slots: bool = ...,
|
||||
|
@ -215,6 +236,8 @@ def make_class(
|
|||
kw_only: bool = ...,
|
||||
cache_hash: bool = ...,
|
||||
auto_exc: bool = ...,
|
||||
eq: Optional[bool] = ...,
|
||||
order: Optional[bool] = ...,
|
||||
) -> type: ...
|
||||
|
||||
# _funcs --
|
||||
|
|
|
@ -20,7 +20,7 @@ else:
|
|||
|
||||
if PY2:
|
||||
from UserDict import IterableUserDict
|
||||
from collections import Mapping, Sequence # noqa
|
||||
from collections import Mapping, Sequence
|
||||
|
||||
# We 'bundle' isclass instead of using inspect as importing inspect is
|
||||
# fairly expensive (order of 10-15 ms for a modern machine in 2016)
|
||||
|
@ -106,7 +106,8 @@ else: # Python 3 and later.
|
|||
consequences of not setting the cell on Python 2.
|
||||
"""
|
||||
warnings.warn(
|
||||
"Missing ctypes. Some features like bare super() or accessing "
|
||||
"Running interpreter doesn't sufficiently support code object "
|
||||
"introspection. Some features like bare super() or accessing "
|
||||
"__class__ will not work with slotted classes.",
|
||||
RuntimeWarning,
|
||||
stacklevel=2,
|
||||
|
@ -124,36 +125,106 @@ else: # Python 3 and later.
|
|||
return types.MappingProxyType(dict(d))
|
||||
|
||||
|
||||
def import_ctypes():
|
||||
"""
|
||||
Moved into a function for testability.
|
||||
"""
|
||||
import ctypes
|
||||
|
||||
return ctypes
|
||||
|
||||
|
||||
def make_set_closure_cell():
|
||||
"""Return a function of two arguments (cell, value) which sets
|
||||
the value stored in the closure cell `cell` to `value`.
|
||||
"""
|
||||
Moved into a function for testability.
|
||||
"""
|
||||
# pypy makes this easy. (It also supports the logic below, but
|
||||
# why not do the easy/fast thing?)
|
||||
if PYPY: # pragma: no cover
|
||||
|
||||
def set_closure_cell(cell, value):
|
||||
cell.__setstate__((value,))
|
||||
|
||||
else:
|
||||
try:
|
||||
ctypes = import_ctypes()
|
||||
return set_closure_cell
|
||||
|
||||
set_closure_cell = ctypes.pythonapi.PyCell_Set
|
||||
set_closure_cell.argtypes = (ctypes.py_object, ctypes.py_object)
|
||||
set_closure_cell.restype = ctypes.c_int
|
||||
except Exception:
|
||||
# We try best effort to set the cell, but sometimes it's not
|
||||
# possible. For example on Jython or on GAE.
|
||||
set_closure_cell = just_warn
|
||||
return set_closure_cell
|
||||
# Otherwise gotta do it the hard way.
|
||||
|
||||
# Create a function that will set its first cellvar to `value`.
|
||||
def set_first_cellvar_to(value):
|
||||
x = value
|
||||
return
|
||||
|
||||
# This function will be eliminated as dead code, but
|
||||
# not before its reference to `x` forces `x` to be
|
||||
# represented as a closure cell rather than a local.
|
||||
def force_x_to_be_a_cell(): # pragma: no cover
|
||||
return x
|
||||
|
||||
try:
|
||||
# Extract the code object and make sure our assumptions about
|
||||
# the closure behavior are correct.
|
||||
if PY2:
|
||||
co = set_first_cellvar_to.func_code
|
||||
else:
|
||||
co = set_first_cellvar_to.__code__
|
||||
if co.co_cellvars != ("x",) or co.co_freevars != ():
|
||||
raise AssertionError # pragma: no cover
|
||||
|
||||
# Convert this code object to a code object that sets the
|
||||
# function's first _freevar_ (not cellvar) to the argument.
|
||||
if sys.version_info >= (3, 8):
|
||||
# CPython 3.8+ has an incompatible CodeType signature
|
||||
# (added a posonlyargcount argument) but also added
|
||||
# CodeType.replace() to do this without counting parameters.
|
||||
set_first_freevar_code = co.replace(
|
||||
co_cellvars=co.co_freevars, co_freevars=co.co_cellvars
|
||||
)
|
||||
else:
|
||||
args = [co.co_argcount]
|
||||
if not PY2:
|
||||
args.append(co.co_kwonlyargcount)
|
||||
args.extend(
|
||||
[
|
||||
co.co_nlocals,
|
||||
co.co_stacksize,
|
||||
co.co_flags,
|
||||
co.co_code,
|
||||
co.co_consts,
|
||||
co.co_names,
|
||||
co.co_varnames,
|
||||
co.co_filename,
|
||||
co.co_name,
|
||||
co.co_firstlineno,
|
||||
co.co_lnotab,
|
||||
# These two arguments are reversed:
|
||||
co.co_cellvars,
|
||||
co.co_freevars,
|
||||
]
|
||||
)
|
||||
set_first_freevar_code = types.CodeType(*args)
|
||||
|
||||
def set_closure_cell(cell, value):
|
||||
# Create a function using the set_first_freevar_code,
|
||||
# whose first closure cell is `cell`. Calling it will
|
||||
# change the value of that cell.
|
||||
setter = types.FunctionType(
|
||||
set_first_freevar_code, {}, "setter", (), (cell,)
|
||||
)
|
||||
# And call it to set the cell.
|
||||
setter(value)
|
||||
|
||||
# Make sure it works on this interpreter:
|
||||
def make_func_with_cell():
|
||||
x = None
|
||||
|
||||
def func():
|
||||
return x # pragma: no cover
|
||||
|
||||
return func
|
||||
|
||||
if PY2:
|
||||
cell = make_func_with_cell().func_closure[0]
|
||||
else:
|
||||
cell = make_func_with_cell().__closure__[0]
|
||||
set_closure_cell(cell, 100)
|
||||
if cell.cell_contents != 100:
|
||||
raise AssertionError # pragma: no cover
|
||||
|
||||
except Exception:
|
||||
return just_warn
|
||||
else:
|
||||
return set_closure_cell
|
||||
|
||||
|
||||
set_closure_cell = make_set_closure_cell()
|
||||
|
|
|
@ -24,7 +24,7 @@ def asdict(
|
|||
``attrs``-decorated.
|
||||
:param callable filter: A callable whose return code determines whether an
|
||||
attribute or element is included (``True``) or dropped (``False``). Is
|
||||
called with the :class:`attr.Attribute` as the first argument and the
|
||||
called with the `attr.Attribute` as the first argument and the
|
||||
value as the second argument.
|
||||
:param callable dict_factory: A callable to produce dictionaries from. For
|
||||
example, to produce ordered dictionaries instead of normal Python
|
||||
|
@ -130,7 +130,7 @@ def astuple(
|
|||
``attrs``-decorated.
|
||||
:param callable filter: A callable whose return code determines whether an
|
||||
attribute or element is included (``True``) or dropped (``False``). Is
|
||||
called with the :class:`attr.Attribute` as the first argument and the
|
||||
called with the `attr.Attribute` as the first argument and the
|
||||
value as the second argument.
|
||||
:param callable tuple_factory: A callable to produce tuples from. For
|
||||
example, to produce lists instead of tuples.
|
||||
|
@ -219,7 +219,7 @@ def has(cls):
|
|||
:param type cls: Class to introspect.
|
||||
:raise TypeError: If *cls* is not a class.
|
||||
|
||||
:rtype: :class:`bool`
|
||||
:rtype: bool
|
||||
"""
|
||||
return getattr(cls, "__attrs_attrs__", None) is not None
|
||||
|
||||
|
@ -239,7 +239,7 @@ def assoc(inst, **changes):
|
|||
class.
|
||||
|
||||
.. deprecated:: 17.1.0
|
||||
Use :func:`evolve` instead.
|
||||
Use `evolve` instead.
|
||||
"""
|
||||
import warnings
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,85 @@
|
|||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
from functools import total_ordering
|
||||
|
||||
from ._funcs import astuple
|
||||
from ._make import attrib, attrs
|
||||
|
||||
|
||||
@total_ordering
|
||||
@attrs(eq=False, order=False, slots=True, frozen=True)
|
||||
class VersionInfo(object):
|
||||
"""
|
||||
A version object that can be compared to tuple of length 1--4:
|
||||
|
||||
>>> attr.VersionInfo(19, 1, 0, "final") <= (19, 2)
|
||||
True
|
||||
>>> attr.VersionInfo(19, 1, 0, "final") < (19, 1, 1)
|
||||
True
|
||||
>>> vi = attr.VersionInfo(19, 2, 0, "final")
|
||||
>>> vi < (19, 1, 1)
|
||||
False
|
||||
>>> vi < (19,)
|
||||
False
|
||||
>>> vi == (19, 2,)
|
||||
True
|
||||
>>> vi == (19, 2, 1)
|
||||
False
|
||||
|
||||
.. versionadded:: 19.2
|
||||
"""
|
||||
|
||||
year = attrib(type=int)
|
||||
minor = attrib(type=int)
|
||||
micro = attrib(type=int)
|
||||
releaselevel = attrib(type=str)
|
||||
|
||||
@classmethod
|
||||
def _from_version_string(cls, s):
|
||||
"""
|
||||
Parse *s* and return a _VersionInfo.
|
||||
"""
|
||||
v = s.split(".")
|
||||
if len(v) == 3:
|
||||
v.append("final")
|
||||
|
||||
return cls(
|
||||
year=int(v[0]), minor=int(v[1]), micro=int(v[2]), releaselevel=v[3]
|
||||
)
|
||||
|
||||
def _ensure_tuple(self, other):
|
||||
"""
|
||||
Ensure *other* is a tuple of a valid length.
|
||||
|
||||
Returns a possibly transformed *other* and ourselves as a tuple of
|
||||
the same length as *other*.
|
||||
"""
|
||||
|
||||
if self.__class__ is other.__class__:
|
||||
other = astuple(other)
|
||||
|
||||
if not isinstance(other, tuple):
|
||||
raise NotImplementedError
|
||||
|
||||
if not (1 <= len(other) <= 4):
|
||||
raise NotImplementedError
|
||||
|
||||
return astuple(self)[: len(other)], other
|
||||
|
||||
def __eq__(self, other):
|
||||
try:
|
||||
us, them = self._ensure_tuple(other)
|
||||
except NotImplementedError:
|
||||
return NotImplemented
|
||||
|
||||
return us == them
|
||||
|
||||
def __lt__(self, other):
|
||||
try:
|
||||
us, them = self._ensure_tuple(other)
|
||||
except NotImplementedError:
|
||||
return NotImplemented
|
||||
|
||||
# Since alphabetically "dev0" < "final" < "post1" < "post2", we don't
|
||||
# have to do anything special with releaselevel for now.
|
||||
return us < them
|
|
@ -0,0 +1,9 @@
|
|||
class VersionInfo:
|
||||
@property
|
||||
def year(self) -> int: ...
|
||||
@property
|
||||
def minor(self) -> int: ...
|
||||
@property
|
||||
def micro(self) -> int: ...
|
||||
@property
|
||||
def releaselevel(self) -> str: ...
|
|
@ -32,14 +32,14 @@ def default_if_none(default=NOTHING, factory=None):
|
|||
result of *factory*.
|
||||
|
||||
:param default: Value to be used if ``None`` is passed. Passing an instance
|
||||
of :class:`attr.Factory` is supported, however the ``takes_self`` option
|
||||
of `attr.Factory` is supported, however the ``takes_self`` option
|
||||
is *not*.
|
||||
:param callable factory: A callable that takes not parameters whose result
|
||||
is used if ``None`` is passed.
|
||||
|
||||
:raises TypeError: If **neither** *default* or *factory* is passed.
|
||||
:raises TypeError: If **both** *default* and *factory* are passed.
|
||||
:raises ValueError: If an instance of :class:`attr.Factory` is passed with
|
||||
:raises ValueError: If an instance of `attr.Factory` is passed with
|
||||
``takes_self=True``.
|
||||
|
||||
.. versionadded:: 18.2.0
|
||||
|
|
|
@ -6,7 +6,7 @@ class FrozenInstanceError(AttributeError):
|
|||
A frozen/immutable instance has been attempted to be modified.
|
||||
|
||||
It mirrors the behavior of ``namedtuples`` by using the same error message
|
||||
and subclassing :exc:`AttributeError`.
|
||||
and subclassing `AttributeError`.
|
||||
|
||||
.. versionadded:: 16.1.0
|
||||
"""
|
||||
|
@ -55,3 +55,20 @@ class PythonTooOldError(RuntimeError):
|
|||
|
||||
.. versionadded:: 18.2.0
|
||||
"""
|
||||
|
||||
|
||||
class NotCallableError(TypeError):
|
||||
"""
|
||||
A ``attr.ib()`` requiring a callable has been set with a value
|
||||
that is not callable.
|
||||
|
||||
.. versionadded:: 19.2.0
|
||||
"""
|
||||
|
||||
def __init__(self, msg, value):
|
||||
super(TypeError, self).__init__(msg, value)
|
||||
self.msg = msg
|
||||
self.value = value
|
||||
|
||||
def __str__(self):
|
||||
return str(self.msg)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
from typing import Any
|
||||
|
||||
class FrozenInstanceError(AttributeError):
|
||||
msg: str = ...
|
||||
|
||||
|
@ -5,3 +7,9 @@ class AttrsAttributeNotFoundError(ValueError): ...
|
|||
class NotAnAttrsClassError(ValueError): ...
|
||||
class DefaultAlreadySetError(RuntimeError): ...
|
||||
class UnannotatedAttributeError(RuntimeError): ...
|
||||
class PythonTooOldError(RuntimeError): ...
|
||||
|
||||
class NotCallableError(TypeError):
|
||||
msg: str = ...
|
||||
value: Any = ...
|
||||
def __init__(self, msg: str, value: Any) -> None: ...
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
"""
|
||||
Commonly useful filters for :func:`attr.asdict`.
|
||||
Commonly useful filters for `attr.asdict`.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
@ -23,9 +23,9 @@ def include(*what):
|
|||
Whitelist *what*.
|
||||
|
||||
:param what: What to whitelist.
|
||||
:type what: :class:`list` of :class:`type` or :class:`attr.Attribute`\\ s
|
||||
:type what: `list` of `type` or `attr.Attribute`\\ s
|
||||
|
||||
:rtype: :class:`callable`
|
||||
:rtype: `callable`
|
||||
"""
|
||||
cls, attrs = _split_what(what)
|
||||
|
||||
|
@ -40,9 +40,9 @@ def exclude(*what):
|
|||
Blacklist *what*.
|
||||
|
||||
:param what: What to blacklist.
|
||||
:type what: :class:`list` of classes or :class:`attr.Attribute`\\ s.
|
||||
:type what: `list` of classes or `attr.Attribute`\\ s.
|
||||
|
||||
:rtype: :class:`callable`
|
||||
:rtype: `callable`
|
||||
"""
|
||||
cls, attrs = _split_what(what)
|
||||
|
||||
|
|
|
@ -4,10 +4,23 @@ Commonly useful validators.
|
|||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
import re
|
||||
|
||||
from ._make import _AndValidator, and_, attrib, attrs
|
||||
from .exceptions import NotCallableError
|
||||
|
||||
|
||||
__all__ = ["and_", "in_", "instance_of", "optional", "provides"]
|
||||
__all__ = [
|
||||
"and_",
|
||||
"deep_iterable",
|
||||
"deep_mapping",
|
||||
"in_",
|
||||
"instance_of",
|
||||
"is_callable",
|
||||
"matches_re",
|
||||
"optional",
|
||||
"provides",
|
||||
]
|
||||
|
||||
|
||||
@attrs(repr=False, slots=True, hash=True)
|
||||
|
@ -40,20 +53,92 @@ class _InstanceOfValidator(object):
|
|||
|
||||
def instance_of(type):
|
||||
"""
|
||||
A validator that raises a :exc:`TypeError` if the initializer is called
|
||||
A validator that raises a `TypeError` if the initializer is called
|
||||
with a wrong type for this particular attribute (checks are performed using
|
||||
:func:`isinstance` therefore it's also valid to pass a tuple of types).
|
||||
`isinstance` therefore it's also valid to pass a tuple of types).
|
||||
|
||||
:param type: The type to check for.
|
||||
:type type: type or tuple of types
|
||||
|
||||
:raises TypeError: With a human readable error message, the attribute
|
||||
(of type :class:`attr.Attribute`), the expected type, and the value it
|
||||
(of type `attr.Attribute`), the expected type, and the value it
|
||||
got.
|
||||
"""
|
||||
return _InstanceOfValidator(type)
|
||||
|
||||
|
||||
@attrs(repr=False, frozen=True)
|
||||
class _MatchesReValidator(object):
|
||||
regex = attrib()
|
||||
flags = attrib()
|
||||
match_func = attrib()
|
||||
|
||||
def __call__(self, inst, attr, value):
|
||||
"""
|
||||
We use a callable class to be able to change the ``__repr__``.
|
||||
"""
|
||||
if not self.match_func(value):
|
||||
raise ValueError(
|
||||
"'{name}' must match regex {regex!r}"
|
||||
" ({value!r} doesn't)".format(
|
||||
name=attr.name, regex=self.regex.pattern, value=value
|
||||
),
|
||||
attr,
|
||||
self.regex,
|
||||
value,
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return "<matches_re validator for pattern {regex!r}>".format(
|
||||
regex=self.regex
|
||||
)
|
||||
|
||||
|
||||
def matches_re(regex, flags=0, func=None):
|
||||
r"""
|
||||
A validator that raises `ValueError` if the initializer is called
|
||||
with a string that doesn't match *regex*.
|
||||
|
||||
:param str regex: a regex string to match against
|
||||
:param int flags: flags that will be passed to the underlying re function
|
||||
(default 0)
|
||||
:param callable func: which underlying `re` function to call (options
|
||||
are `re.fullmatch`, `re.search`, `re.match`, default
|
||||
is ``None`` which means either `re.fullmatch` or an emulation of
|
||||
it on Python 2). For performance reasons, they won't be used directly
|
||||
but on a pre-`re.compile`\ ed pattern.
|
||||
|
||||
.. versionadded:: 19.2.0
|
||||
"""
|
||||
fullmatch = getattr(re, "fullmatch", None)
|
||||
valid_funcs = (fullmatch, None, re.search, re.match)
|
||||
if func not in valid_funcs:
|
||||
raise ValueError(
|
||||
"'func' must be one of %s."
|
||||
% (
|
||||
", ".join(
|
||||
sorted(
|
||||
e and e.__name__ or "None" for e in set(valid_funcs)
|
||||
)
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
pattern = re.compile(regex, flags)
|
||||
if func is re.match:
|
||||
match_func = pattern.match
|
||||
elif func is re.search:
|
||||
match_func = pattern.search
|
||||
else:
|
||||
if fullmatch:
|
||||
match_func = pattern.fullmatch
|
||||
else:
|
||||
pattern = re.compile(r"(?:{})\Z".format(regex), flags)
|
||||
match_func = pattern.match
|
||||
|
||||
return _MatchesReValidator(pattern, flags, match_func)
|
||||
|
||||
|
||||
@attrs(repr=False, slots=True, hash=True)
|
||||
class _ProvidesValidator(object):
|
||||
interface = attrib()
|
||||
|
@ -81,7 +166,7 @@ class _ProvidesValidator(object):
|
|||
|
||||
def provides(interface):
|
||||
"""
|
||||
A validator that raises a :exc:`TypeError` if the initializer is called
|
||||
A validator that raises a `TypeError` if the initializer is called
|
||||
with an object that does not provide the requested *interface* (checks are
|
||||
performed using ``interface.providedBy(value)`` (see `zope.interface
|
||||
<https://zopeinterface.readthedocs.io/en/latest/>`_).
|
||||
|
@ -89,7 +174,7 @@ def provides(interface):
|
|||
:param zope.interface.Interface interface: The interface to check for.
|
||||
|
||||
:raises TypeError: With a human readable error message, the attribute
|
||||
(of type :class:`attr.Attribute`), the expected interface, and the
|
||||
(of type `attr.Attribute`), the expected interface, and the
|
||||
value it got.
|
||||
"""
|
||||
return _ProvidesValidator(interface)
|
||||
|
@ -119,7 +204,7 @@ def optional(validator):
|
|||
|
||||
:param validator: A validator (or a list of validators) that is used for
|
||||
non-``None`` values.
|
||||
:type validator: callable or :class:`list` of callables.
|
||||
:type validator: callable or `list` of callables.
|
||||
|
||||
.. versionadded:: 15.1.0
|
||||
.. versionchanged:: 17.1.0 *validator* can be a list of validators.
|
||||
|
@ -154,15 +239,15 @@ class _InValidator(object):
|
|||
|
||||
def in_(options):
|
||||
"""
|
||||
A validator that raises a :exc:`ValueError` if the initializer is called
|
||||
A validator that raises a `ValueError` if the initializer is called
|
||||
with a value that does not belong in the options provided. The check is
|
||||
performed using ``value in options``.
|
||||
|
||||
:param options: Allowed options.
|
||||
:type options: list, tuple, :class:`enum.Enum`, ...
|
||||
:type options: list, tuple, `enum.Enum`, ...
|
||||
|
||||
:raises ValueError: With a human readable error message, the attribute (of
|
||||
type :class:`attr.Attribute`), the expected options, and the value it
|
||||
type `attr.Attribute`), the expected options, and the value it
|
||||
got.
|
||||
|
||||
.. versionadded:: 17.1.0
|
||||
|
@ -177,7 +262,16 @@ class _IsCallableValidator(object):
|
|||
We use a callable class to be able to change the ``__repr__``.
|
||||
"""
|
||||
if not callable(value):
|
||||
raise TypeError("'{name}' must be callable".format(name=attr.name))
|
||||
message = (
|
||||
"'{name}' must be callable "
|
||||
"(got {value!r} that is a {actual!r})."
|
||||
)
|
||||
raise NotCallableError(
|
||||
msg=message.format(
|
||||
name=attr.name, value=value, actual=value.__class__
|
||||
),
|
||||
value=value,
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return "<is_callable validator>"
|
||||
|
@ -185,13 +279,15 @@ class _IsCallableValidator(object):
|
|||
|
||||
def is_callable():
|
||||
"""
|
||||
A validator that raises a :class:`TypeError` if the initializer is called
|
||||
with a value for this particular attribute that is not callable.
|
||||
A validator that raises a `attr.exceptions.NotCallableError` if the
|
||||
initializer is called with a value for this particular attribute
|
||||
that is not callable.
|
||||
|
||||
.. versionadded:: 19.1.0
|
||||
|
||||
:raises TypeError: With a human readable error message containing the
|
||||
attribute (of type :class:`attr.Attribute`) name.
|
||||
:raises `attr.exceptions.NotCallableError`: With a human readable error
|
||||
message containing the attribute (`attr.Attribute`) name,
|
||||
and the value it got.
|
||||
"""
|
||||
return _IsCallableValidator()
|
||||
|
||||
|
|
|
@ -1,24 +1,66 @@
|
|||
from typing import Container, List, Union, TypeVar, Type, Any, Optional, Tuple
|
||||
from typing import (
|
||||
Container,
|
||||
List,
|
||||
Union,
|
||||
TypeVar,
|
||||
Type,
|
||||
Any,
|
||||
Optional,
|
||||
Tuple,
|
||||
Iterable,
|
||||
Mapping,
|
||||
Callable,
|
||||
Match,
|
||||
AnyStr,
|
||||
overload,
|
||||
)
|
||||
from . import _ValidatorType
|
||||
|
||||
_T = TypeVar("_T")
|
||||
_T1 = TypeVar("_T1")
|
||||
_T2 = TypeVar("_T2")
|
||||
_T3 = TypeVar("_T3")
|
||||
_I = TypeVar("_I", bound=Iterable)
|
||||
_K = TypeVar("_K")
|
||||
_V = TypeVar("_V")
|
||||
_M = TypeVar("_M", bound=Mapping)
|
||||
|
||||
# To be more precise on instance_of use some overloads.
|
||||
# If there are more than 3 items in the tuple then we fall back to Any
|
||||
@overload
|
||||
def instance_of(type: Type[_T]) -> _ValidatorType[_T]: ...
|
||||
@overload
|
||||
def instance_of(type: Tuple[Type[_T]]) -> _ValidatorType[_T]: ...
|
||||
@overload
|
||||
def instance_of(
|
||||
type: Union[Tuple[Type[_T], ...], Type[_T]]
|
||||
) -> _ValidatorType[_T]: ...
|
||||
type: Tuple[Type[_T1], Type[_T2]]
|
||||
) -> _ValidatorType[Union[_T1, _T2]]: ...
|
||||
@overload
|
||||
def instance_of(
|
||||
type: Tuple[Type[_T1], Type[_T2], Type[_T3]]
|
||||
) -> _ValidatorType[Union[_T1, _T2, _T3]]: ...
|
||||
@overload
|
||||
def instance_of(type: Tuple[type, ...]) -> _ValidatorType[Any]: ...
|
||||
def provides(interface: Any) -> _ValidatorType[Any]: ...
|
||||
def optional(
|
||||
validator: Union[_ValidatorType[_T], List[_ValidatorType[_T]]]
|
||||
) -> _ValidatorType[Optional[_T]]: ...
|
||||
def in_(options: Container[_T]) -> _ValidatorType[_T]: ...
|
||||
def and_(*validators: _ValidatorType[_T]) -> _ValidatorType[_T]: ...
|
||||
def matches_re(
|
||||
regex: AnyStr,
|
||||
flags: int = ...,
|
||||
func: Optional[
|
||||
Callable[[AnyStr, AnyStr, int], Optional[Match[AnyStr]]]
|
||||
] = ...,
|
||||
) -> _ValidatorType[AnyStr]: ...
|
||||
def deep_iterable(
|
||||
member_validator: _ValidatorType[_T],
|
||||
iterable_validator: Optional[_ValidatorType[_T]],
|
||||
) -> _ValidatorType[_T]: ...
|
||||
iterable_validator: Optional[_ValidatorType[_I]] = ...,
|
||||
) -> _ValidatorType[_I]: ...
|
||||
def deep_mapping(
|
||||
key_validator: _ValidatorType[_T],
|
||||
value_validator: _ValidatorType[_T],
|
||||
mapping_validator: Optional[_ValidatorType[_T]],
|
||||
) -> _ValidatorType[_T]: ...
|
||||
key_validator: _ValidatorType[_K],
|
||||
value_validator: _ValidatorType[_V],
|
||||
mapping_validator: Optional[_ValidatorType[_M]] = ...,
|
||||
) -> _ValidatorType[_M]: ...
|
||||
def is_callable() -> _ValidatorType[_T]: ...
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
attr/__init__.py,sha256=3XomfUfit8bVVEmSf1bRhLnRMPKauPbzFqPUnVRPgXw,1244
|
||||
attr/__init__.pyi,sha256=OON4rNWdgL69frd_WdrxtuQe8CEczl3aFpgifFeESN8,7769
|
||||
attr/_compat.py,sha256=GcjqWHrwUWGVCbDKY7twYt-Rr_4nPJqBnfrf5SeHsIY,4583
|
||||
attr/_config.py,sha256=_KvW0mQdH2PYjHc0YfIUaV_o2pVfM7ziMEYTxwmEhOA,514
|
||||
attr/_funcs.py,sha256=7v3MNMHdOUP2NkiLPwEiWAorBs3uNQq5Rn70Odr5uqo,9725
|
||||
attr/_make.py,sha256=be1PmzR8EDGfVA2Cx6ljsTIuXRxW2tEWPpTqtQXde0Y,68317
|
||||
attr/converters.py,sha256=SFPiz6-hAs2pw3kn7SzkBcdpE9AjW8iT9wjpe2eLDrQ,2155
|
||||
attr/converters.pyi,sha256=wAhCoOT1MFV8t323rpD87O7bxQ8CYLTPiBQd-29BieI,351
|
||||
attr/exceptions.py,sha256=N0WQfKvBVd4GWgDxTbFScg4ajy7-HlyvXiwlSQBA0jA,1272
|
||||
attr/exceptions.pyi,sha256=sq7TbBEGGSf81uFXScW9_aO62vd0v6LAvqz0a8Hrsxw,257
|
||||
attr/filters.py,sha256=s6NrcRWJKlCQauPEH0S4lmgFwlCdUQcHKcNkDHpptN4,1153
|
||||
attr/filters.pyi,sha256=xDpmKQlFdssgxGa5tsl1ADh_3zwAwAT4vUhd8h-8-Tk,214
|
||||
attr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||
attr/validators.py,sha256=ZAf_y5wNHyq2Rdlin_fwplQnU2u5wZnvmYJq1JddPtM,8750
|
||||
attr/validators.pyi,sha256=p2xr2ob8RaKW3PqlKDrQQVAyl8ZH4pNdlZzWXapGPjk,897
|
||||
attrs-19.1.0.dist-info/LICENSE,sha256=v2WaKLSSQGAvVrvfSQy-LsUJsVuY-Z17GaUsdA4yeGM,1082
|
||||
attrs-19.1.0.dist-info/METADATA,sha256=5yXp3BTFGRkY2hQDs18h-2dT7xnSlExRUfxvujCtHTE,10275
|
||||
attrs-19.1.0.dist-info/WHEEL,sha256=_wJFdOYk7i3xxT8ElOkUJvOdOvfNGbR9g-bf6UQT6sU,110
|
||||
attrs-19.1.0.dist-info/top_level.txt,sha256=tlRYMddkRlKPqJ96wP2_j9uEsmcNHgD2SbuWd4CzGVU,5
|
||||
attrs-19.1.0.dist-info/RECORD,,
|
|
@ -1,6 +1,6 @@
|
|||
Metadata-Version: 2.1
|
||||
Name: attrs
|
||||
Version: 19.1.0
|
||||
Version: 19.2.0
|
||||
Summary: Classes Without Boilerplate
|
||||
Home-page: https://www.attrs.org/
|
||||
Author: Hynek Schlawack
|
||||
|
@ -30,11 +30,20 @@ Classifier: Programming Language :: Python :: Implementation :: CPython
|
|||
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
||||
Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
|
||||
Description-Content-Type: text/x-rst
|
||||
Provides-Extra: azure-pipelines
|
||||
Requires-Dist: coverage ; extra == 'azure-pipelines'
|
||||
Requires-Dist: hypothesis ; extra == 'azure-pipelines'
|
||||
Requires-Dist: pympler ; extra == 'azure-pipelines'
|
||||
Requires-Dist: pytest (>=4.3.0) ; extra == 'azure-pipelines'
|
||||
Requires-Dist: six ; extra == 'azure-pipelines'
|
||||
Requires-Dist: zope.interface ; extra == 'azure-pipelines'
|
||||
Requires-Dist: pytest-azurepipelines ; extra == 'azure-pipelines'
|
||||
Provides-Extra: dev
|
||||
Requires-Dist: coverage ; extra == 'dev'
|
||||
Requires-Dist: hypothesis ; extra == 'dev'
|
||||
Requires-Dist: pympler ; extra == 'dev'
|
||||
Requires-Dist: pytest ; extra == 'dev'
|
||||
Requires-Dist: pytest (>=4.3.0) ; extra == 'dev'
|
||||
Requires-Dist: six ; extra == 'dev'
|
||||
Requires-Dist: zope.interface ; extra == 'dev'
|
||||
Requires-Dist: sphinx ; extra == 'dev'
|
||||
|
@ -46,7 +55,7 @@ Provides-Extra: tests
|
|||
Requires-Dist: coverage ; extra == 'tests'
|
||||
Requires-Dist: hypothesis ; extra == 'tests'
|
||||
Requires-Dist: pympler ; extra == 'tests'
|
||||
Requires-Dist: pytest ; extra == 'tests'
|
||||
Requires-Dist: pytest (>=4.3.0) ; extra == 'tests'
|
||||
Requires-Dist: six ; extra == 'tests'
|
||||
Requires-Dist: zope.interface ; extra == 'tests'
|
||||
|
||||
|
@ -61,8 +70,8 @@ Requires-Dist: zope.interface ; extra == 'tests'
|
|||
:target: https://www.attrs.org/en/stable/?badge=stable
|
||||
:alt: Documentation Status
|
||||
|
||||
.. image:: https://travis-ci.org/python-attrs/attrs.svg?branch=master
|
||||
:target: https://travis-ci.org/python-attrs/attrs
|
||||
.. image:: https://attrs.visualstudio.com/attrs/_apis/build/status/python-attrs.attrs?branchName=master
|
||||
:target: https://attrs.visualstudio.com/attrs/_build/latest?definitionId=1&branchName=master
|
||||
:alt: CI Status
|
||||
|
||||
.. image:: https://codecov.io/github/python-attrs/attrs/branch/master/graph/badge.svg
|
||||
|
@ -70,7 +79,7 @@ Requires-Dist: zope.interface ; extra == 'tests'
|
|||
:alt: Test Coverage
|
||||
|
||||
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
|
||||
:target: https://github.com/ambv/black
|
||||
:target: https://github.com/psf/black
|
||||
:alt: Code style: black
|
||||
|
||||
.. teaser-begin
|
||||
|
@ -124,7 +133,7 @@ After *declaring* your attributes ``attrs`` gives you:
|
|||
|
||||
- a concise and explicit overview of the class's attributes,
|
||||
- a nice human-readable ``__repr__``,
|
||||
- a complete set of comparison methods,
|
||||
- a complete set of comparison methods (equality and ordering),
|
||||
- an initializer,
|
||||
- and much more,
|
||||
|
||||
|
@ -153,12 +162,12 @@ Testimonials
|
|||
It exerts a subtle, but positive, design influence in all the codebases I’ve see it used in.
|
||||
|
||||
|
||||
**Kenneth Reitz**, author of `Requests <http://www.python-requests.org/>`_ and Developer Advocate at DigitalOcean, (`on paper no less <https://twitter.com/hynek/status/866817877650751488>`_!):
|
||||
**Kenneth Reitz**, creator of `Requests <https://github.com/psf/requests>`_ (`on paper no less <https://twitter.com/hynek/status/866817877650751488>`_!):
|
||||
|
||||
attrs—classes for humans. I like it.
|
||||
|
||||
|
||||
**Łukasz Langa**, prolific CPython core developer and Production Engineer at Facebook:
|
||||
**Łukasz Langa**, creator of `Black <https://github.com/psf/black>`_, prolific Python core developer, and release manager for Python 3.8 and 3.9:
|
||||
|
||||
I'm increasingly digging your attr.ocity. Good job!
|
||||
|
||||
|
@ -193,44 +202,70 @@ If you'd like to contribute to ``attrs`` you're most welcome and we've written `
|
|||
Release Information
|
||||
===================
|
||||
|
||||
19.1.0 (2019-03-03)
|
||||
19.2.0 (2019-10-01)
|
||||
-------------------
|
||||
|
||||
Backward-incompatible Changes
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- Fixed a bug where deserialized objects with ``cache_hash=True`` could have incorrect hash code values.
|
||||
This change breaks classes with ``cache_hash=True`` when a custom ``__setstate__`` is present.
|
||||
An exception will be thrown when applying the ``attrs`` annotation to such a class.
|
||||
This limitation is tracked in issue `#494 <https://github.com/python-attrs/attrs/issues/494>`_.
|
||||
`#482 <https://github.com/python-attrs/attrs/issues/482>`_
|
||||
- Removed deprecated ``Attribute`` attribute ``convert`` per scheduled removal on 2019/1.
|
||||
This planned deprecation is tracked in issue `#307 <https://github.com/python-attrs/attrs/issues/307>`_.
|
||||
`#504 <https://github.com/python-attrs/attrs/issues/504>`_
|
||||
- ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` do not consider subclasses comparable anymore.
|
||||
|
||||
This has been deprecated since 18.2.0 and was raising a ``DeprecationWarning`` for over a year.
|
||||
`#570 <https://github.com/python-attrs/attrs/issues/570>`_
|
||||
|
||||
|
||||
Deprecations
|
||||
^^^^^^^^^^^^
|
||||
|
||||
- The ``cmp`` argument to ``attr.s()`` and ``attr.ib()`` is now deprecated.
|
||||
|
||||
Please use ``eq`` to add equality methods (``__eq__`` and ``__ne__``) and ``order`` to add ordering methods (``__lt__``, ``__le__``, ``__gt__``, and ``__ge__``) instead – just like with `dataclasses <https://docs.python.org/3/library/dataclasses.html>`_.
|
||||
|
||||
Both are effectively ``True`` by default but it's enough to set ``eq=False`` to disable both at once.
|
||||
Passing ``eq=False, order=True`` explicitly will raise a ``ValueError`` though.
|
||||
|
||||
Since this is arguably a deeper backward-compatibility break, it will have an extended deprecation period until 2021-06-01.
|
||||
After that day, the ``cmp`` argument will be removed.
|
||||
|
||||
``attr.Attribute`` also isn't orderable anymore.
|
||||
`#574 <https://github.com/python-attrs/attrs/issues/574>`_
|
||||
|
||||
|
||||
Changes
|
||||
^^^^^^^
|
||||
|
||||
- Add ``is_callable``, ``deep_iterable``, and ``deep_mapping`` validators.
|
||||
- Updated ``attr.validators.__all__`` to include new validators added in `#425`_.
|
||||
`#517 <https://github.com/python-attrs/attrs/issues/517>`_
|
||||
- Slotted classes now use a pure Python mechanism to rewrite the ``__class__`` cell when rebuilding the class, so ``super()`` works even on environments where ``ctypes`` is not installed.
|
||||
`#522 <https://github.com/python-attrs/attrs/issues/522>`_
|
||||
- When collecting attributes using ``@attr.s(auto_attribs=True)``, attributes with a default of ``None`` are now deleted too.
|
||||
`#523 <https://github.com/python-attrs/attrs/issues/523>`_,
|
||||
`#556 <https://github.com/python-attrs/attrs/issues/556>`_
|
||||
- Fixed ``attr.validators.deep_iterable()`` and ``attr.validators.deep_mapping()`` type stubs.
|
||||
`#533 <https://github.com/python-attrs/attrs/issues/533>`_
|
||||
- ``attr.validators.is_callable()`` validator now raises an exception ``attr.exceptions.NotCallableError``, a subclass of ``TypeError``, informing the received value.
|
||||
`#536 <https://github.com/python-attrs/attrs/issues/536>`_
|
||||
- ``@attr.s(auto_exc=True)`` now generates classes that are hashable by ID, as the documentation always claimed it would.
|
||||
`#543 <https://github.com/python-attrs/attrs/issues/543>`_,
|
||||
`#563 <https://github.com/python-attrs/attrs/issues/563>`_
|
||||
- Added ``attr.validators.matches_re()`` that checks string attributes whether they match a regular expression.
|
||||
`#552 <https://github.com/python-attrs/attrs/issues/552>`_
|
||||
- Keyword-only attributes (``kw_only=True``) and attributes that are excluded from the ``attrs``'s ``__init__`` (``init=False``) now can appear before mandatory attributes.
|
||||
`#559 <https://github.com/python-attrs/attrs/issues/559>`_
|
||||
- The fake filename for generated methods is now more stable.
|
||||
It won't change when you restart the process.
|
||||
`#560 <https://github.com/python-attrs/attrs/issues/560>`_
|
||||
- The value passed to ``@attr.ib(repr=…)`` can now be either a boolean (as before) or a callable.
|
||||
That callable must return a string and is then used for formatting the attribute by the generated ``__repr__()`` method.
|
||||
`#568 <https://github.com/python-attrs/attrs/issues/568>`_
|
||||
- Added ``attr.__version_info__`` that can be used to reliably check the version of ``attrs`` and write forward- and backward-compatible code.
|
||||
Please check out the `section on deprecated APIs <http://www.attrs.org/en/stable/api.html#deprecated-apis>`_ on how to use it.
|
||||
`#580 <https://github.com/python-attrs/attrs/issues/580>`_
|
||||
|
||||
* ``is_callable``: validates that a value is callable
|
||||
* ``deep_iterable``: Allows recursion down into an iterable,
|
||||
applying another validator to every member in the iterable
|
||||
as well as applying an optional validator to the iterable itself.
|
||||
* ``deep_mapping``: Allows recursion down into the items in a mapping object,
|
||||
applying a key validator and a value validator to the key and value in every item.
|
||||
Also applies an optional validator to the mapping object itself.
|
||||
|
||||
You can find them in the ``attr.validators`` package.
|
||||
`#425 <https://github.com/python-attrs/attrs/issues/425>`_
|
||||
- Fixed stub files to prevent errors raised by mypy's ``disallow_any_generics = True`` option.
|
||||
`#443 <https://github.com/python-attrs/attrs/issues/443>`_
|
||||
- Attributes with ``init=False`` now can follow after ``kw_only=True`` attributes.
|
||||
`#450 <https://github.com/python-attrs/attrs/issues/450>`_
|
||||
- ``attrs`` now has first class support for defining exception classes.
|
||||
|
||||
If you define a class using ``@attr.s(auto_exc=True)`` and subclass an exception, the class will behave like a well-behaved exception class including an appropriate ``__str__`` method, and all attributes additionally available in an ``args`` attribute.
|
||||
`#500 <https://github.com/python-attrs/attrs/issues/500>`_
|
||||
- Clarified documentation for hashing to warn that hashable objects should be deeply immutable (in their usage, even if this is not enforced).
|
||||
`#503 <https://github.com/python-attrs/attrs/issues/503>`_
|
||||
.. _`#425`: https://github.com/python-attrs/attrs/issues/425
|
||||
|
||||
`Full changelog <https://www.attrs.org/en/stable/changelog.html>`_.
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
attr/__init__.py,sha256=nRvEecOWLaJsMraOK89f4hMYThTUZHWj1B0jl249M-0,1344
|
||||
attr/__init__.pyi,sha256=5AVtEEzK-g3HO1SUll44hTL8LFoM8TYD7Gn9vEMFGzk,8252
|
||||
attr/_compat.py,sha256=-pJtdtqgCg0K6rH_BWf3wKuTum58GD-WWPclQQ2SUaU,7326
|
||||
attr/_config.py,sha256=_KvW0mQdH2PYjHc0YfIUaV_o2pVfM7ziMEYTxwmEhOA,514
|
||||
attr/_funcs.py,sha256=unAJfNGSTOzxyFzkj7Rs3O1bfsQodmXyir9uZKen-vY,9696
|
||||
attr/_make.py,sha256=4pdTus8d4OkitzlwytTPP7TNLZK6pVIoKg6KdAZMwYQ,70804
|
||||
attr/_version.py,sha256=azMi1lNelb3cJvvYUMXsXVbUANkRzbD5IEiaXVpeVr4,2162
|
||||
attr/_version.pyi,sha256=x_M3L3WuB7r_ULXAWjx959udKQ4HLB8l-hsc1FDGNvk,209
|
||||
attr/converters.py,sha256=5QJRYSXE8G7PW0289y_SPwvvZIcw-nJIuBlfYVdB4BQ,2141
|
||||
attr/converters.pyi,sha256=wAhCoOT1MFV8t323rpD87O7bxQ8CYLTPiBQd-29BieI,351
|
||||
attr/exceptions.py,sha256=hbhOa3b4W8_mRrbj3FsMTR4Bt5xzbJs5xaFTWn8s6h4,1635
|
||||
attr/exceptions.pyi,sha256=4zuaJyl2axxWbqnZgxo_2oTpPNbyowEw3A4hqV5PmAc,458
|
||||
attr/filters.py,sha256=weDxwATsa69T_0bPVjiM1fGsciAMQmwhY5G8Jm5BxuI,1098
|
||||
attr/filters.pyi,sha256=xDpmKQlFdssgxGa5tsl1ADh_3zwAwAT4vUhd8h-8-Tk,214
|
||||
attr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||
attr/validators.py,sha256=8AsxgdDgh3sGPseiUIMPGcTr6PvaDYfH3AK46tsvs8U,11460
|
||||
attr/validators.pyi,sha256=vZgsJqUwrJevh4v_Hd7_RSXqDrBctE6-3AEZ7uYKodo,1868
|
||||
attrs-19.2.0.dist-info/LICENSE,sha256=v2WaKLSSQGAvVrvfSQy-LsUJsVuY-Z17GaUsdA4yeGM,1082
|
||||
attrs-19.2.0.dist-info/METADATA,sha256=qPqvhqvovqyvpsQebMPTXsOi8pv2xuzUDkUzAxz-wvM,12750
|
||||
attrs-19.2.0.dist-info/WHEEL,sha256=8zNYZbwQSXoB9IfXOjPfeNwvAsALAjffgk27FqvCWbo,110
|
||||
attrs-19.2.0.dist-info/top_level.txt,sha256=tlRYMddkRlKPqJ96wP2_j9uEsmcNHgD2SbuWd4CzGVU,5
|
||||
attrs-19.2.0.dist-info/RECORD,,
|
|
@ -1,5 +1,5 @@
|
|||
Wheel-Version: 1.0
|
||||
Generator: bdist_wheel (0.32.3)
|
||||
Generator: bdist_wheel (0.33.6)
|
||||
Root-Is-Purelib: true
|
||||
Tag: py2-none-any
|
||||
Tag: py3-none-any
|
|
@ -1,5 +1,5 @@
|
|||
appdirs==1.4.4
|
||||
attrs==19.1.0
|
||||
attrs==19.2.0
|
||||
blessings==1.7
|
||||
cbor2==4.0.1
|
||||
# Though we don't depend on colorama directly, we need to explicitly
|
||||
|
|
|
@ -50,9 +50,9 @@ async-timeout==3.0.1 \
|
|||
# via
|
||||
# aiohttp
|
||||
# taskcluster
|
||||
attrs==19.1.0 \
|
||||
--hash=sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79 \
|
||||
--hash=sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399
|
||||
attrs==19.2.0 \
|
||||
--hash=sha256:ec20e7a4825331c1b5ebf261d111e16fa9612c1f7a5e1f884f12bd53a664dfd2 \
|
||||
--hash=sha256:f913492e1663d3c36f502e5e9ba6cd13cf19d7fab50aa13239e420fef95e1396
|
||||
# via
|
||||
# -r requirements-mach-vendor-python.in
|
||||
# aiohttp
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
pluggy==0.6.0 --hash=sha256:e160a7fcf25762bb60efc7e171d4497ff1d8d2d75a3d0df7a21b76821ecbf5c5
|
||||
pluggy==0.13.1 --hash=sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d
|
||||
importlib-metadata==0.23 --hash=sha256:d5f18a79777f3aa179c145737780282e27b508fc8fd688cb17c7a813e8bd39af
|
||||
more-itertools==7.2.0 --hash=sha256:92b8c4b06dac4f0611c0729b2f2ede52b2e1bac1ab48f089c7ddc12e26bb60c4
|
||||
zipp==0.6.0 --hash=sha256:f06903e9f1f43b12d371004b4ac7b06ab39a44adc747266928ae6debfa7b3335
|
||||
py==1.5.4 --hash=sha256:e31fb2767eb657cbde86c454f02e99cb846d3cd9d61b318525140214fdc0e98e
|
||||
tox==2.7.0 --hash=sha256:0f37ea637ead4a5bbae91531b0bf8fd327c7152e20255e5960ee180598228d21
|
||||
virtualenv==15.1.0 --hash=sha256:39d88b533b422825d644087a21e78c45cf5af0ef7a99a1fc9fbb7b481e5c85b0
|
||||
|
|
Загрузка…
Ссылка в новой задаче