mirror of
https://github.com/SickGear/SickGear.git
synced 2024-12-18 16:53:38 +00:00
Merge branch 'feature/UpdateAttr' into dev
This commit is contained in:
commit
f8188b93f3
11 changed files with 386 additions and 181 deletions
|
@ -1,5 +1,6 @@
|
|||
### 3.29.0 (2023-xx-xx xx:xx:00 UTC)
|
||||
|
||||
* Update attr 22.2.0 (a9960de) to 22.2.0 (683d056)
|
||||
* Update diskcache 5.4.0 (1cb1425) to 5.6.1 (4d30686)
|
||||
* Update filelock 3.9.0 (ce3e891) to 3.11.0 (d3241b9)
|
||||
* Update Msgpack 1.0.4 (b5acfd5) to 1.0.5 (0516c2c)
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
# SPDX-License-Identifier: MIT
|
||||
|
||||
import sys
|
||||
import warnings
|
||||
"""
|
||||
Classes Without Boilerplate
|
||||
"""
|
||||
|
||||
from functools import partial
|
||||
from typing import Callable
|
||||
|
||||
from . import converters, exceptions, filters, setters, validators
|
||||
from ._cmp import cmp_using
|
||||
|
@ -24,30 +26,6 @@ from ._next_gen import define, field, frozen, mutable
|
|||
from ._version_info import VersionInfo
|
||||
|
||||
|
||||
if sys.version_info < (3, 7): # pragma: no cover
|
||||
warnings.warn(
|
||||
"Running attrs on Python 3.6 is deprecated & we intend to drop "
|
||||
"support soon. If that's a problem for you, please let us know why & "
|
||||
"we MAY re-evaluate: <https://github.com/python-attrs/attrs/pull/993>",
|
||||
DeprecationWarning,
|
||||
)
|
||||
|
||||
__version__ = "22.2.0"
|
||||
__version_info__ = VersionInfo._from_version_string(__version__)
|
||||
|
||||
__title__ = "attrs"
|
||||
__description__ = "Classes Without Boilerplate"
|
||||
__url__ = "https://www.attrs.org/"
|
||||
__uri__ = __url__
|
||||
__doc__ = __description__ + " <" + __uri__ + ">"
|
||||
|
||||
__author__ = "Hynek Schlawack"
|
||||
__email__ = "hs@ox.cx"
|
||||
|
||||
__license__ = "MIT"
|
||||
__copyright__ = "Copyright (c) 2015 Hynek Schlawack"
|
||||
|
||||
|
||||
s = attributes = attrs
|
||||
ib = attr = attrib
|
||||
dataclass = partial(attrs, auto_attribs=True) # happy Easter ;)
|
||||
|
@ -91,3 +69,64 @@ __all__ = [
|
|||
"validate",
|
||||
"validators",
|
||||
]
|
||||
|
||||
|
||||
def _make_getattr(mod_name: str) -> Callable:
|
||||
"""
|
||||
Create a metadata proxy for packaging information that uses *mod_name* in
|
||||
its warnings and errors.
|
||||
"""
|
||||
|
||||
def __getattr__(name: str) -> str:
|
||||
dunder_to_metadata = {
|
||||
"__title__": "Name",
|
||||
"__copyright__": "",
|
||||
"__version__": "version",
|
||||
"__version_info__": "version",
|
||||
"__description__": "summary",
|
||||
"__uri__": "",
|
||||
"__url__": "",
|
||||
"__author__": "",
|
||||
"__email__": "",
|
||||
"__license__": "license",
|
||||
}
|
||||
if name not in dunder_to_metadata.keys():
|
||||
raise AttributeError(f"module {mod_name} has no attribute {name}")
|
||||
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
if sys.version_info < (3, 8):
|
||||
from importlib_metadata import metadata
|
||||
else:
|
||||
from importlib.metadata import metadata
|
||||
|
||||
if name != "__version_info__":
|
||||
warnings.warn(
|
||||
f"Accessing {mod_name}.{name} is deprecated and will be "
|
||||
"removed in a future release. Use importlib.metadata directly "
|
||||
"to query for attrs's packaging metadata.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
meta = metadata("attrs")
|
||||
if name == "__license__":
|
||||
return "MIT"
|
||||
elif name == "__copyright__":
|
||||
return "Copyright (c) 2015 Hynek Schlawack"
|
||||
elif name in ("__uri__", "__url__"):
|
||||
return meta["Project-URL"].split(" ", 1)[-1]
|
||||
elif name == "__version_info__":
|
||||
return VersionInfo._from_version_string(meta["version"])
|
||||
elif name == "__author__":
|
||||
return meta["Author-email"].rsplit(" ", 1)[0]
|
||||
elif name == "__email__":
|
||||
return meta["Author-email"].rsplit("<", 1)[1][:-1]
|
||||
|
||||
return meta[dunder_to_metadata[name]]
|
||||
|
||||
return __getattr__
|
||||
|
||||
|
||||
__getattr__ = _make_getattr(__name__)
|
||||
|
|
|
@ -69,6 +69,7 @@ _ValidatorArgType = Union[_ValidatorType[_T], Sequence[_ValidatorType[_T]]]
|
|||
class AttrsInstance(AttrsInstance_, Protocol):
|
||||
pass
|
||||
|
||||
_A = TypeVar("_A", bound=AttrsInstance)
|
||||
# _make --
|
||||
|
||||
class _Nothing(enum.Enum):
|
||||
|
@ -116,6 +117,7 @@ def __dataclass_transform__(
|
|||
eq_default: bool = True,
|
||||
order_default: bool = False,
|
||||
kw_only_default: bool = False,
|
||||
frozen_default: bool = False,
|
||||
field_descriptors: Tuple[Union[type, Callable[..., Any]], ...] = (()),
|
||||
) -> Callable[[_T], _T]: ...
|
||||
|
||||
|
@ -257,6 +259,7 @@ def field(
|
|||
order: Optional[bool] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
alias: Optional[str] = ...,
|
||||
type: Optional[type] = ...,
|
||||
) -> Any: ...
|
||||
|
||||
# This form catches an explicit None or no default and infers the type from the
|
||||
|
@ -277,6 +280,7 @@ def field(
|
|||
order: Optional[_EqOrderType] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
alias: Optional[str] = ...,
|
||||
type: Optional[type] = ...,
|
||||
) -> _T: ...
|
||||
|
||||
# This form catches an explicit default argument.
|
||||
|
@ -296,6 +300,7 @@ def field(
|
|||
order: Optional[_EqOrderType] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
alias: Optional[str] = ...,
|
||||
type: Optional[type] = ...,
|
||||
) -> _T: ...
|
||||
|
||||
# This form covers type=non-Type: e.g. forward references (str), Any
|
||||
|
@ -315,6 +320,7 @@ def field(
|
|||
order: Optional[_EqOrderType] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
alias: Optional[str] = ...,
|
||||
type: Optional[type] = ...,
|
||||
) -> Any: ...
|
||||
@overload
|
||||
@__dataclass_transform__(order_default=True, field_descriptors=(attrib, field))
|
||||
|
@ -426,17 +432,73 @@ def define(
|
|||
) -> Callable[[_C], _C]: ...
|
||||
|
||||
mutable = define
|
||||
frozen = define # they differ only in their defaults
|
||||
|
||||
@overload
|
||||
@__dataclass_transform__(
|
||||
frozen_default=True, field_descriptors=(attrib, field)
|
||||
)
|
||||
def frozen(
|
||||
maybe_cls: _C,
|
||||
*,
|
||||
these: Optional[Dict[str, Any]] = ...,
|
||||
repr: bool = ...,
|
||||
unsafe_hash: Optional[bool] = ...,
|
||||
hash: Optional[bool] = ...,
|
||||
init: bool = ...,
|
||||
slots: bool = ...,
|
||||
frozen: bool = ...,
|
||||
weakref_slot: bool = ...,
|
||||
str: bool = ...,
|
||||
auto_attribs: bool = ...,
|
||||
kw_only: bool = ...,
|
||||
cache_hash: bool = ...,
|
||||
auto_exc: bool = ...,
|
||||
eq: Optional[bool] = ...,
|
||||
order: Optional[bool] = ...,
|
||||
auto_detect: bool = ...,
|
||||
getstate_setstate: Optional[bool] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
field_transformer: Optional[_FieldTransformer] = ...,
|
||||
match_args: bool = ...,
|
||||
) -> _C: ...
|
||||
@overload
|
||||
@__dataclass_transform__(
|
||||
frozen_default=True, field_descriptors=(attrib, field)
|
||||
)
|
||||
def frozen(
|
||||
maybe_cls: None = ...,
|
||||
*,
|
||||
these: Optional[Dict[str, Any]] = ...,
|
||||
repr: bool = ...,
|
||||
unsafe_hash: Optional[bool] = ...,
|
||||
hash: Optional[bool] = ...,
|
||||
init: bool = ...,
|
||||
slots: bool = ...,
|
||||
frozen: bool = ...,
|
||||
weakref_slot: bool = ...,
|
||||
str: bool = ...,
|
||||
auto_attribs: bool = ...,
|
||||
kw_only: bool = ...,
|
||||
cache_hash: bool = ...,
|
||||
auto_exc: bool = ...,
|
||||
eq: Optional[bool] = ...,
|
||||
order: Optional[bool] = ...,
|
||||
auto_detect: bool = ...,
|
||||
getstate_setstate: Optional[bool] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
field_transformer: Optional[_FieldTransformer] = ...,
|
||||
match_args: bool = ...,
|
||||
) -> Callable[[_C], _C]: ...
|
||||
def fields(cls: Type[AttrsInstance]) -> Any: ...
|
||||
def fields_dict(cls: Type[AttrsInstance]) -> Dict[str, Attribute[Any]]: ...
|
||||
def validate(inst: AttrsInstance) -> None: ...
|
||||
def resolve_types(
|
||||
cls: _C,
|
||||
cls: _A,
|
||||
globalns: Optional[Dict[str, Any]] = ...,
|
||||
localns: Optional[Dict[str, Any]] = ...,
|
||||
attribs: Optional[List[Attribute[Any]]] = ...,
|
||||
) -> _C: ...
|
||||
include_extras: bool = ...,
|
||||
) -> _A: ...
|
||||
|
||||
# TODO: add support for returning a proper attrs class from the mypy plugin
|
||||
# we use Any instead of _CountingAttr so that e.g. `make_class('Foo',
|
||||
|
|
|
@ -20,22 +20,22 @@ def cmp_using(
|
|||
class_name="Comparable",
|
||||
):
|
||||
"""
|
||||
Create a class that can be passed into `attr.ib`'s ``eq``, ``order``, and
|
||||
``cmp`` arguments to customize field comparison.
|
||||
Create a class that can be passed into `attrs.field`'s ``eq``, ``order``,
|
||||
and ``cmp`` arguments to customize field comparison.
|
||||
|
||||
The resulting class will have a full set of ordering methods if
|
||||
at least one of ``{lt, le, gt, ge}`` and ``eq`` are provided.
|
||||
The resulting class will have a full set of ordering methods if at least
|
||||
one of ``{lt, le, gt, ge}`` and ``eq`` are provided.
|
||||
|
||||
:param Optional[callable] eq: `callable` used to evaluate equality
|
||||
of two objects.
|
||||
:param Optional[callable] lt: `callable` used to evaluate whether
|
||||
one object is less than another object.
|
||||
:param Optional[callable] le: `callable` used to evaluate whether
|
||||
one object is less than or equal to another object.
|
||||
:param Optional[callable] gt: `callable` used to evaluate whether
|
||||
one object is greater than another object.
|
||||
:param Optional[callable] ge: `callable` used to evaluate whether
|
||||
one object is greater than or equal to another object.
|
||||
:param Optional[callable] eq: `callable` used to evaluate equality of two
|
||||
objects.
|
||||
:param Optional[callable] lt: `callable` used to evaluate whether one
|
||||
object is less than another object.
|
||||
:param Optional[callable] le: `callable` used to evaluate whether one
|
||||
object is less than or equal to another object.
|
||||
:param Optional[callable] gt: `callable` used to evaluate whether one
|
||||
object is greater than another object.
|
||||
:param Optional[callable] ge: `callable` used to evaluate whether one
|
||||
object is greater than or equal to another object.
|
||||
|
||||
:param bool require_same_type: When `True`, equality and ordering methods
|
||||
will return `NotImplemented` if objects are not of the same type.
|
||||
|
|
|
@ -9,9 +9,11 @@ import types
|
|||
import warnings
|
||||
|
||||
from collections.abc import Mapping, Sequence # noqa
|
||||
from typing import _GenericAlias
|
||||
|
||||
|
||||
PYPY = platform.python_implementation() == "PyPy"
|
||||
PY_3_9_PLUS = sys.version_info[:2] >= (3, 9)
|
||||
PY310 = sys.version_info[:2] >= (3, 10)
|
||||
PY_3_12_PLUS = sys.version_info[:2] >= (3, 12)
|
||||
|
||||
|
@ -81,6 +83,13 @@ def make_set_closure_cell():
|
|||
|
||||
# Otherwise gotta do it the hard way.
|
||||
|
||||
try:
|
||||
if sys.version_info >= (3, 8):
|
||||
|
||||
def set_closure_cell(cell, value):
|
||||
cell.cell_contents = value
|
||||
|
||||
else:
|
||||
# Create a function that will set its first cellvar to `value`.
|
||||
def set_first_cellvar_to(value):
|
||||
x = value
|
||||
|
@ -92,7 +101,6 @@ def make_set_closure_cell():
|
|||
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.
|
||||
co = set_first_cellvar_to.__code__
|
||||
|
@ -101,12 +109,6 @@ def make_set_closure_cell():
|
|||
|
||||
# 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):
|
||||
|
||||
def set_closure_cell(cell, value):
|
||||
cell.cell_contents = value
|
||||
|
||||
else:
|
||||
args = [co.co_argcount]
|
||||
args.append(co.co_kwonlyargcount)
|
||||
args.extend(
|
||||
|
@ -174,3 +176,10 @@ set_closure_cell = make_set_closure_cell()
|
|||
# don't have a direct reference to the thread-local in their globals dict.
|
||||
# If they have such a reference, it breaks cloudpickle.
|
||||
repr_context = threading.local()
|
||||
|
||||
|
||||
def get_generic_base(cl):
|
||||
"""If this is a generic class (A[str]), return the generic base for it."""
|
||||
if cl.__class__ is _GenericAlias:
|
||||
return cl.__origin__
|
||||
return None
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
import copy
|
||||
|
||||
from ._compat import PY_3_9_PLUS, get_generic_base
|
||||
from ._make import NOTHING, _obj_setattr, fields
|
||||
from .exceptions import AttrsAttributeNotFoundError
|
||||
|
||||
|
@ -16,13 +17,13 @@ def asdict(
|
|||
value_serializer=None,
|
||||
):
|
||||
"""
|
||||
Return the ``attrs`` attribute values of *inst* as a dict.
|
||||
Return the *attrs* attribute values of *inst* as a dict.
|
||||
|
||||
Optionally recurse into other ``attrs``-decorated classes.
|
||||
Optionally recurse into other *attrs*-decorated classes.
|
||||
|
||||
:param inst: Instance of an ``attrs``-decorated class.
|
||||
:param inst: Instance of an *attrs*-decorated class.
|
||||
:param bool recurse: Recurse into classes that are also
|
||||
``attrs``-decorated.
|
||||
*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 `attrs.Attribute` as the first argument and the
|
||||
|
@ -40,7 +41,7 @@ def asdict(
|
|||
|
||||
:rtype: return type of *dict_factory*
|
||||
|
||||
:raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
|
||||
:raise attrs.exceptions.NotAnAttrsClassError: If *cls* is not an *attrs*
|
||||
class.
|
||||
|
||||
.. versionadded:: 16.0.0 *dict_factory*
|
||||
|
@ -195,13 +196,13 @@ def astuple(
|
|||
retain_collection_types=False,
|
||||
):
|
||||
"""
|
||||
Return the ``attrs`` attribute values of *inst* as a tuple.
|
||||
Return the *attrs* attribute values of *inst* as a tuple.
|
||||
|
||||
Optionally recurse into other ``attrs``-decorated classes.
|
||||
Optionally recurse into other *attrs*-decorated classes.
|
||||
|
||||
:param inst: Instance of an ``attrs``-decorated class.
|
||||
:param inst: Instance of an *attrs*-decorated class.
|
||||
:param bool recurse: Recurse into classes that are also
|
||||
``attrs``-decorated.
|
||||
*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 `attrs.Attribute` as the first argument and the
|
||||
|
@ -215,7 +216,7 @@ def astuple(
|
|||
|
||||
:rtype: return type of *tuple_factory*
|
||||
|
||||
:raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
|
||||
:raise attrs.exceptions.NotAnAttrsClassError: If *cls* is not an *attrs*
|
||||
class.
|
||||
|
||||
.. versionadded:: 16.2.0
|
||||
|
@ -289,28 +290,48 @@ def astuple(
|
|||
|
||||
def has(cls):
|
||||
"""
|
||||
Check whether *cls* is a class with ``attrs`` attributes.
|
||||
Check whether *cls* is a class with *attrs* attributes.
|
||||
|
||||
:param type cls: Class to introspect.
|
||||
:raise TypeError: If *cls* is not a class.
|
||||
|
||||
:rtype: bool
|
||||
"""
|
||||
return getattr(cls, "__attrs_attrs__", None) is not None
|
||||
attrs = getattr(cls, "__attrs_attrs__", None)
|
||||
if attrs is not None:
|
||||
return True
|
||||
|
||||
# No attrs, maybe it's a specialized generic (A[str])?
|
||||
generic_base = get_generic_base(cls)
|
||||
if generic_base is not None:
|
||||
generic_attrs = getattr(generic_base, "__attrs_attrs__", None)
|
||||
if generic_attrs is not None:
|
||||
# Stick it on here for speed next time.
|
||||
cls.__attrs_attrs__ = generic_attrs
|
||||
return generic_attrs is not None
|
||||
return False
|
||||
|
||||
|
||||
def assoc(inst, **changes):
|
||||
"""
|
||||
Copy *inst* and apply *changes*.
|
||||
|
||||
:param inst: Instance of a class with ``attrs`` attributes.
|
||||
This is different from `evolve` that applies the changes to the arguments
|
||||
that create the new instance.
|
||||
|
||||
`evolve`'s behavior is preferable, but there are `edge cases`_ where it
|
||||
doesn't work. Therefore `assoc` is deprecated, but will not be removed.
|
||||
|
||||
.. _`edge cases`: https://github.com/python-attrs/attrs/issues/251
|
||||
|
||||
:param inst: Instance of a class with *attrs* attributes.
|
||||
:param changes: Keyword changes in the new copy.
|
||||
|
||||
:return: A copy of inst with *changes* incorporated.
|
||||
|
||||
:raise attr.exceptions.AttrsAttributeNotFoundError: If *attr_name* couldn't
|
||||
be found on *cls*.
|
||||
:raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
|
||||
:raise attrs.exceptions.AttrsAttributeNotFoundError: If *attr_name*
|
||||
couldn't be found on *cls*.
|
||||
:raise attrs.exceptions.NotAnAttrsClassError: If *cls* is not an *attrs*
|
||||
class.
|
||||
|
||||
.. deprecated:: 17.1.0
|
||||
|
@ -318,13 +339,6 @@ def assoc(inst, **changes):
|
|||
This function will not be removed du to the slightly different approach
|
||||
compared to `attrs.evolve`.
|
||||
"""
|
||||
import warnings
|
||||
|
||||
warnings.warn(
|
||||
"assoc is deprecated and will be removed after 2018/01.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
new = copy.copy(inst)
|
||||
attrs = fields(inst.__class__)
|
||||
for k, v in changes.items():
|
||||
|
@ -337,22 +351,55 @@ def assoc(inst, **changes):
|
|||
return new
|
||||
|
||||
|
||||
def evolve(inst, **changes):
|
||||
def evolve(*args, **changes):
|
||||
"""
|
||||
Create a new instance, based on *inst* with *changes* applied.
|
||||
Create a new instance, based on the first positional argument with
|
||||
*changes* applied.
|
||||
|
||||
:param inst: Instance of a class with ``attrs`` attributes.
|
||||
:param inst: Instance of a class with *attrs* attributes.
|
||||
:param changes: Keyword changes in the new copy.
|
||||
|
||||
:return: A copy of inst with *changes* incorporated.
|
||||
|
||||
:raise TypeError: If *attr_name* couldn't be found in the class
|
||||
``__init__``.
|
||||
:raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
|
||||
:raise attrs.exceptions.NotAnAttrsClassError: If *cls* is not an *attrs*
|
||||
class.
|
||||
|
||||
.. versionadded:: 17.1.0
|
||||
.. deprecated:: 23.1.0
|
||||
It is now deprecated to pass the instance using the keyword argument
|
||||
*inst*. It will raise a warning until at least April 2024, after which
|
||||
it will become an error. Always pass the instance as a positional
|
||||
argument.
|
||||
"""
|
||||
# Try to get instance by positional argument first.
|
||||
# Use changes otherwise and warn it'll break.
|
||||
if args:
|
||||
try:
|
||||
(inst,) = args
|
||||
except ValueError:
|
||||
raise TypeError(
|
||||
f"evolve() takes 1 positional argument, but {len(args)} "
|
||||
"were given"
|
||||
) from None
|
||||
else:
|
||||
try:
|
||||
inst = changes.pop("inst")
|
||||
except KeyError:
|
||||
raise TypeError(
|
||||
"evolve() missing 1 required positional argument: 'inst'"
|
||||
) from None
|
||||
|
||||
import warnings
|
||||
|
||||
warnings.warn(
|
||||
"Passing the instance per keyword argument is deprecated and "
|
||||
"will stop working in, or after, April 2024.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
cls = inst.__class__
|
||||
attrs = fields(cls)
|
||||
for a in attrs:
|
||||
|
@ -366,7 +413,9 @@ def evolve(inst, **changes):
|
|||
return cls(**changes)
|
||||
|
||||
|
||||
def resolve_types(cls, globalns=None, localns=None, attribs=None):
|
||||
def resolve_types(
|
||||
cls, globalns=None, localns=None, attribs=None, include_extras=True
|
||||
):
|
||||
"""
|
||||
Resolve any strings and forward annotations in type annotations.
|
||||
|
||||
|
@ -385,10 +434,14 @@ def resolve_types(cls, globalns=None, localns=None, attribs=None):
|
|||
:param Optional[dict] localns: Dictionary containing local variables.
|
||||
:param Optional[list] attribs: List of attribs for the given class.
|
||||
This is necessary when calling from inside a ``field_transformer``
|
||||
since *cls* is not an ``attrs`` class yet.
|
||||
since *cls* is not an *attrs* class yet.
|
||||
:param bool include_extras: Resolve more accurately, if possible.
|
||||
Pass ``include_extras`` to ``typing.get_hints``, if supported by the
|
||||
typing module. On supported Python versions (3.9+), this resolves the
|
||||
types more accurately.
|
||||
|
||||
:raise TypeError: If *cls* is not a class.
|
||||
:raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
|
||||
:raise attrs.exceptions.NotAnAttrsClassError: If *cls* is not an *attrs*
|
||||
class and you didn't pass any attribs.
|
||||
:raise NameError: If types cannot be resolved because of missing variables.
|
||||
|
||||
|
@ -398,6 +451,7 @@ def resolve_types(cls, globalns=None, localns=None, attribs=None):
|
|||
|
||||
.. versionadded:: 20.1.0
|
||||
.. versionadded:: 21.1.0 *attribs*
|
||||
.. versionadded:: 23.1.0 *include_extras*
|
||||
|
||||
"""
|
||||
# Since calling get_type_hints is expensive we cache whether we've
|
||||
|
@ -405,7 +459,12 @@ def resolve_types(cls, globalns=None, localns=None, attribs=None):
|
|||
if getattr(cls, "__attrs_types_resolved__", None) != cls:
|
||||
import typing
|
||||
|
||||
hints = typing.get_type_hints(cls, globalns=globalns, localns=localns)
|
||||
kwargs = {"globalns": globalns, "localns": localns}
|
||||
|
||||
if PY_3_9_PLUS:
|
||||
kwargs["include_extras"] = include_extras
|
||||
|
||||
hints = typing.get_type_hints(cls, **kwargs)
|
||||
for field in fields(cls) if attribs is None else attribs:
|
||||
if field.name in hints:
|
||||
# Since fields have been frozen we must work around it.
|
||||
|
|
|
@ -12,7 +12,12 @@ from operator import itemgetter
|
|||
# We need to import _compat itself in addition to the _compat members to avoid
|
||||
# having the thread-local in the globals here.
|
||||
from . import _compat, _config, setters
|
||||
from ._compat import PY310, PYPY, _AnnotationExtractor, set_closure_cell
|
||||
from ._compat import (
|
||||
PY310,
|
||||
_AnnotationExtractor,
|
||||
get_generic_base,
|
||||
set_closure_cell,
|
||||
)
|
||||
from .exceptions import (
|
||||
DefaultAlreadySetError,
|
||||
FrozenInstanceError,
|
||||
|
@ -109,9 +114,12 @@ def attrib(
|
|||
.. warning::
|
||||
|
||||
Does *not* do anything unless the class is also decorated with
|
||||
`attr.s`!
|
||||
`attr.s` / `attrs.define` / et cetera!
|
||||
|
||||
:param default: A value that is used if an ``attrs``-generated ``__init__``
|
||||
Please consider using `attrs.field` in new code (``attr.ib`` will *never*
|
||||
go away, though).
|
||||
|
||||
:param default: A value that is used if an *attrs*-generated ``__init__``
|
||||
is used and no value is passed while instantiating or the attribute is
|
||||
excluded using ``init=False``.
|
||||
|
||||
|
@ -130,7 +138,7 @@ def attrib(
|
|||
:param callable factory: Syntactic sugar for
|
||||
``default=attr.Factory(factory)``.
|
||||
|
||||
:param validator: `callable` that is called by ``attrs``-generated
|
||||
:param validator: `callable` that is called by *attrs*-generated
|
||||
``__init__`` methods after the instance has been initialized. They
|
||||
receive the initialized instance, the :func:`~attrs.Attribute`, and the
|
||||
passed value.
|
||||
|
@ -142,7 +150,7 @@ def attrib(
|
|||
all pass.
|
||||
|
||||
Validators can be globally disabled and re-enabled using
|
||||
`get_run_validators`.
|
||||
`attrs.validators.get_disabled` / `attrs.validators.set_disabled`.
|
||||
|
||||
The validator can also be set using decorator notation as shown below.
|
||||
|
||||
|
@ -184,7 +192,7 @@ def attrib(
|
|||
value. In that case this attributed is unconditionally initialized
|
||||
with the specified default value or factory.
|
||||
:param callable converter: `callable` that is called by
|
||||
``attrs``-generated ``__init__`` methods to convert attribute's value
|
||||
*attrs*-generated ``__init__`` methods to convert attribute's value
|
||||
to the desired format. It is given the passed-in value, and the
|
||||
returned value will be used as the new value of the attribute. The
|
||||
value is converted before being passed to the validator, if any.
|
||||
|
@ -197,7 +205,7 @@ def attrib(
|
|||
Regardless of the approach used, the type will be stored on
|
||||
``Attribute.type``.
|
||||
|
||||
Please note that ``attrs`` doesn't do anything with this metadata by
|
||||
Please note that *attrs* doesn't do anything with this metadata by
|
||||
itself. You can use it as part of your own code or for
|
||||
`static type checking <types>`.
|
||||
:param kw_only: Make this attribute keyword-only in the generated
|
||||
|
@ -582,29 +590,20 @@ def _transform_attrs(
|
|||
return _Attributes((AttrsClass(attrs), base_attrs, base_attr_map))
|
||||
|
||||
|
||||
if PYPY:
|
||||
|
||||
def _frozen_setattrs(self, name, value):
|
||||
def _frozen_setattrs(self, name, value):
|
||||
"""
|
||||
Attached to frozen classes as __setattr__.
|
||||
"""
|
||||
if isinstance(self, BaseException) and name in (
|
||||
"__cause__",
|
||||
"__context__",
|
||||
"__traceback__",
|
||||
):
|
||||
BaseException.__setattr__(self, name, value)
|
||||
return
|
||||
|
||||
raise FrozenInstanceError()
|
||||
|
||||
else:
|
||||
|
||||
def _frozen_setattrs(self, name, value):
|
||||
"""
|
||||
Attached to frozen classes as __setattr__.
|
||||
"""
|
||||
raise FrozenInstanceError()
|
||||
|
||||
|
||||
def _frozen_delattrs(self, name):
|
||||
"""
|
||||
|
@ -940,6 +939,12 @@ class _ClassBuilder:
|
|||
Automatically created by attrs.
|
||||
"""
|
||||
__bound_setattr = _obj_setattr.__get__(self)
|
||||
if isinstance(state, tuple):
|
||||
# Backward compatibility with attrs instances pickled with
|
||||
# attrs versions before v22.2.0 which stored tuples.
|
||||
for name, value in zip(state_attr_names, state):
|
||||
__bound_setattr(name, value)
|
||||
else:
|
||||
for name in state_attr_names:
|
||||
if name in state:
|
||||
__bound_setattr(name, state[name])
|
||||
|
@ -1220,12 +1225,15 @@ def attrs(
|
|||
A class decorator that adds :term:`dunder methods` according to the
|
||||
specified attributes using `attr.ib` or the *these* argument.
|
||||
|
||||
Please consider using `attrs.define` / `attrs.frozen` in new code
|
||||
(``attr.s`` will *never* go away, though).
|
||||
|
||||
:param these: A dictionary of name to `attr.ib` mappings. This is
|
||||
useful to avoid the definition of your attributes within the class body
|
||||
because you can't (e.g. if you want to add ``__repr__`` methods to
|
||||
Django models) or don't want to.
|
||||
|
||||
If *these* is not ``None``, ``attrs`` will *not* search the class body
|
||||
If *these* is not ``None``, *attrs* will *not* search the class body
|
||||
for attributes and will *not* remove any attributes from it.
|
||||
|
||||
The order is deduced from the order of the attributes inside *these*.
|
||||
|
@ -1242,14 +1250,14 @@ def attrs(
|
|||
inherited from some base class).
|
||||
|
||||
So for example by implementing ``__eq__`` on a class yourself,
|
||||
``attrs`` will deduce ``eq=False`` and will create *neither*
|
||||
*attrs* will deduce ``eq=False`` and will create *neither*
|
||||
``__eq__`` *nor* ``__ne__`` (but Python classes come with a sensible
|
||||
``__ne__`` by default, so it *should* be enough to only implement
|
||||
``__eq__`` in most cases).
|
||||
|
||||
.. warning::
|
||||
|
||||
If you prevent ``attrs`` from creating the ordering methods for you
|
||||
If you prevent *attrs* from creating the ordering methods for you
|
||||
(``order=False``, e.g. by implementing ``__le__``), it becomes
|
||||
*your* responsibility to make sure its ordering is sound. The best
|
||||
way is to use the `functools.total_ordering` decorator.
|
||||
|
@ -1259,14 +1267,14 @@ def attrs(
|
|||
*cmp*, or *hash* overrides whatever *auto_detect* would determine.
|
||||
|
||||
:param bool repr: Create a ``__repr__`` method with a human readable
|
||||
representation of ``attrs`` attributes..
|
||||
representation of *attrs* attributes..
|
||||
:param bool str: Create a ``__str__`` method that is identical to
|
||||
``__repr__``. This is usually not necessary except for
|
||||
`Exception`\ s.
|
||||
:param Optional[bool] eq: If ``True`` or ``None`` (default), add ``__eq__``
|
||||
and ``__ne__`` methods that check two instances for equality.
|
||||
|
||||
They compare the instances as if they were tuples of their ``attrs``
|
||||
They compare the instances as if they were tuples of their *attrs*
|
||||
attributes if and only if the types of both classes are *identical*!
|
||||
:param Optional[bool] order: If ``True``, add ``__lt__``, ``__le__``,
|
||||
``__gt__``, and ``__ge__`` methods that behave like *eq* above and
|
||||
|
@ -1277,7 +1285,7 @@ def attrs(
|
|||
:param Optional[bool] unsafe_hash: If ``None`` (default), the ``__hash__``
|
||||
method is generated according how *eq* and *frozen* are set.
|
||||
|
||||
1. If *both* are True, ``attrs`` will generate a ``__hash__`` for you.
|
||||
1. If *both* are True, *attrs* will generate a ``__hash__`` for you.
|
||||
2. If *eq* is True and *frozen* is False, ``__hash__`` will be set to
|
||||
None, marking it unhashable (which it is).
|
||||
3. If *eq* is False, ``__hash__`` will be left untouched meaning the
|
||||
|
@ -1285,7 +1293,7 @@ def attrs(
|
|||
``object``, this means it will fall back to id-based hashing.).
|
||||
|
||||
Although not recommended, you can decide for yourself and force
|
||||
``attrs`` to create one (e.g. if the class is immutable even though you
|
||||
*attrs* to create one (e.g. if the class is immutable even though you
|
||||
didn't freeze it programmatically) by passing ``True`` or not. Both of
|
||||
these cases are rather special and should be used carefully.
|
||||
|
||||
|
@ -1296,7 +1304,7 @@ def attrs(
|
|||
:param Optional[bool] hash: Alias for *unsafe_hash*. *unsafe_hash* takes
|
||||
precedence.
|
||||
:param bool init: Create a ``__init__`` method that initializes the
|
||||
``attrs`` attributes. Leading underscores are stripped for the argument
|
||||
*attrs* attributes. Leading underscores are stripped for the argument
|
||||
name. If a ``__attrs_pre_init__`` method exists on the class, it will
|
||||
be called before the class is initialized. If a ``__attrs_post_init__``
|
||||
method exists on the class, it will be called after the class is fully
|
||||
|
@ -1312,7 +1320,7 @@ def attrs(
|
|||
we encourage you to read the :term:`glossary entry <slotted classes>`.
|
||||
:param bool frozen: Make instances immutable after initialization. If
|
||||
someone attempts to modify a frozen instance,
|
||||
`attr.exceptions.FrozenInstanceError` is raised.
|
||||
`attrs.exceptions.FrozenInstanceError` is raised.
|
||||
|
||||
.. note::
|
||||
|
||||
|
@ -1337,7 +1345,7 @@ def attrs(
|
|||
:param bool auto_attribs: If ``True``, collect :pep:`526`-annotated
|
||||
attributes from the class body.
|
||||
|
||||
In this case, you **must** annotate every field. If ``attrs``
|
||||
In this case, you **must** annotate every field. If *attrs*
|
||||
encounters a field that is set to an `attr.ib` but lacks a type
|
||||
annotation, an `attr.exceptions.UnannotatedAttributeError` is
|
||||
raised. Use ``field_name: typing.Any = attr.ib(...)`` if you don't
|
||||
|
@ -1353,9 +1361,9 @@ def attrs(
|
|||
|
||||
.. warning::
|
||||
For features that use the attribute name to create decorators (e.g.
|
||||
`validators <validators>`), you still *must* assign `attr.ib` to
|
||||
them. Otherwise Python will either not find the name or try to use
|
||||
the default value to call e.g. ``validator`` on it.
|
||||
:ref:`validators <validators>`), you still *must* assign `attr.ib`
|
||||
to them. Otherwise Python will either not find the name or try to
|
||||
use the default value to call e.g. ``validator`` on it.
|
||||
|
||||
These errors can be quite confusing and probably the most common bug
|
||||
report on our bug tracker.
|
||||
|
@ -1376,14 +1384,14 @@ def attrs(
|
|||
class:
|
||||
|
||||
- the values for *eq*, *order*, and *hash* are ignored and the
|
||||
instances compare and hash by the instance's ids (N.B. ``attrs`` will
|
||||
instances compare and hash by the instance's ids (N.B. *attrs* will
|
||||
*not* remove existing implementations of ``__hash__`` or the equality
|
||||
methods. It just won't add own ones.),
|
||||
- all attributes that are either passed into ``__init__`` or have a
|
||||
default value are additionally available as a tuple in the ``args``
|
||||
attribute,
|
||||
- the value of *str* is ignored leaving ``__str__`` to base classes.
|
||||
:param bool collect_by_mro: Setting this to `True` fixes the way ``attrs``
|
||||
:param bool collect_by_mro: Setting this to `True` fixes the way *attrs*
|
||||
collects attributes from base classes. The default behavior is
|
||||
incorrect in certain cases of multiple inheritance. It should be on by
|
||||
default but is kept off for backward-compatibility.
|
||||
|
@ -1422,7 +1430,7 @@ def attrs(
|
|||
|
||||
:param Optional[callable] field_transformer:
|
||||
A function that is called with the original class object and all
|
||||
fields right before ``attrs`` finalizes the class. You can use
|
||||
fields right before *attrs* finalizes the class. You can use
|
||||
this, e.g., to automatically add converters or validators to
|
||||
fields based on their types. See `transform-fields` for more details.
|
||||
|
||||
|
@ -1900,7 +1908,7 @@ def _add_repr(cls, ns=None, attrs=None):
|
|||
|
||||
def fields(cls):
|
||||
"""
|
||||
Return the tuple of ``attrs`` attributes for a class.
|
||||
Return the tuple of *attrs* attributes for a class.
|
||||
|
||||
The tuple also allows accessing the fields by their names (see below for
|
||||
examples).
|
||||
|
@ -1908,31 +1916,45 @@ def fields(cls):
|
|||
:param type cls: Class to introspect.
|
||||
|
||||
:raise TypeError: If *cls* is not a class.
|
||||
:raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
|
||||
:raise attrs.exceptions.NotAnAttrsClassError: If *cls* is not an *attrs*
|
||||
class.
|
||||
|
||||
:rtype: tuple (with name accessors) of `attrs.Attribute`
|
||||
|
||||
.. versionchanged:: 16.2.0 Returned tuple allows accessing the fields
|
||||
by name.
|
||||
.. versionchanged:: 23.1.0 Add support for generic classes.
|
||||
"""
|
||||
if not isinstance(cls, type):
|
||||
generic_base = get_generic_base(cls)
|
||||
|
||||
if generic_base is None and not isinstance(cls, type):
|
||||
raise TypeError("Passed object must be a class.")
|
||||
|
||||
attrs = getattr(cls, "__attrs_attrs__", None)
|
||||
|
||||
if attrs is None:
|
||||
if generic_base is not None:
|
||||
attrs = getattr(generic_base, "__attrs_attrs__", None)
|
||||
if attrs is not None:
|
||||
# Even though this is global state, stick it on here to speed
|
||||
# it up. We rely on `cls` being cached for this to be
|
||||
# efficient.
|
||||
cls.__attrs_attrs__ = attrs
|
||||
return attrs
|
||||
raise NotAnAttrsClassError(f"{cls!r} is not an attrs-decorated class.")
|
||||
|
||||
return attrs
|
||||
|
||||
|
||||
def fields_dict(cls):
|
||||
"""
|
||||
Return an ordered dictionary of ``attrs`` attributes for a class, whose
|
||||
Return an ordered dictionary of *attrs* attributes for a class, whose
|
||||
keys are the attribute names.
|
||||
|
||||
:param type cls: Class to introspect.
|
||||
|
||||
:raise TypeError: If *cls* is not a class.
|
||||
:raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs``
|
||||
:raise attrs.exceptions.NotAnAttrsClassError: If *cls* is not an *attrs*
|
||||
class.
|
||||
|
||||
:rtype: dict
|
||||
|
@ -1953,7 +1975,7 @@ def validate(inst):
|
|||
|
||||
Leaves all exceptions through.
|
||||
|
||||
:param inst: Instance of a class with ``attrs`` attributes.
|
||||
:param inst: Instance of a class with *attrs* attributes.
|
||||
"""
|
||||
if _config._run_validators is False:
|
||||
return
|
||||
|
@ -2391,6 +2413,10 @@ class Attribute:
|
|||
"""
|
||||
*Read-only* representation of an attribute.
|
||||
|
||||
.. warning::
|
||||
|
||||
You should never instantiate this class yourself.
|
||||
|
||||
The class has *all* arguments of `attr.ib` (except for ``factory``
|
||||
which is only syntactic sugar for ``default=Factory(...)`` plus the
|
||||
following:
|
||||
|
@ -2536,13 +2562,13 @@ class Attribute:
|
|||
**inst_dict,
|
||||
)
|
||||
|
||||
# Don't use attr.evolve since fields(Attribute) doesn't work
|
||||
# Don't use attrs.evolve since fields(Attribute) doesn't work
|
||||
def evolve(self, **changes):
|
||||
"""
|
||||
Copy *self* and apply *changes*.
|
||||
|
||||
This works similarly to `attr.evolve` but that function does not work
|
||||
with ``Attribute``.
|
||||
This works similarly to `attrs.evolve` but that function does not work
|
||||
with `Attribute`.
|
||||
|
||||
It is mainly meant to be used for `transform-fields`.
|
||||
|
||||
|
@ -2777,10 +2803,6 @@ class Factory:
|
|||
__slots__ = ("factory", "takes_self")
|
||||
|
||||
def __init__(self, factory, takes_self=False):
|
||||
"""
|
||||
`Factory` is part of the default machinery so if we want a default
|
||||
value here, we have to implement it ourselves.
|
||||
"""
|
||||
self.factory = factory
|
||||
self.takes_self = takes_self
|
||||
|
||||
|
@ -2818,13 +2840,13 @@ Factory = _add_hash(_add_eq(_add_repr(Factory, attrs=_f), attrs=_f), attrs=_f)
|
|||
|
||||
|
||||
def make_class(name, attrs, bases=(object,), **attributes_arguments):
|
||||
"""
|
||||
r"""
|
||||
A quick way to create a new class called *name* with *attrs*.
|
||||
|
||||
:param str name: The name for the new class.
|
||||
|
||||
:param attrs: A list of names or a dictionary of mappings of names to
|
||||
attributes.
|
||||
`attr.ib`\ s / `attrs.field`\ s.
|
||||
|
||||
The order is deduced from the order of the names or attributes inside
|
||||
*attrs*. Otherwise the order of the definition of the attributes is
|
||||
|
|
|
@ -46,7 +46,7 @@ def define(
|
|||
match_args=True,
|
||||
):
|
||||
r"""
|
||||
Define an ``attrs`` class.
|
||||
Define an *attrs* class.
|
||||
|
||||
Differences to the classic `attr.s` that it uses underneath:
|
||||
|
||||
|
@ -167,6 +167,7 @@ def field(
|
|||
hash=None,
|
||||
init=True,
|
||||
metadata=None,
|
||||
type=None,
|
||||
converter=None,
|
||||
factory=None,
|
||||
kw_only=False,
|
||||
|
@ -179,6 +180,10 @@ def field(
|
|||
Identical to `attr.ib`, except keyword-only and with some arguments
|
||||
removed.
|
||||
|
||||
.. versionadded:: 22.3.0
|
||||
The *type* parameter has been re-added; mostly for
|
||||
{func}`attrs.make_class`. Please note that type checkers ignore this
|
||||
metadata.
|
||||
.. versionadded:: 20.1.0
|
||||
"""
|
||||
return attrib(
|
||||
|
@ -188,6 +193,7 @@ def field(
|
|||
hash=hash,
|
||||
init=init,
|
||||
metadata=metadata,
|
||||
type=type,
|
||||
converter=converter,
|
||||
factory=factory,
|
||||
kw_only=kw_only,
|
||||
|
|
|
@ -34,7 +34,7 @@ class FrozenAttributeError(FrozenError):
|
|||
|
||||
class AttrsAttributeNotFoundError(ValueError):
|
||||
"""
|
||||
An ``attrs`` function couldn't find an attribute that the user asked for.
|
||||
An *attrs* function couldn't find an attribute that the user asked for.
|
||||
|
||||
.. versionadded:: 16.2.0
|
||||
"""
|
||||
|
@ -42,7 +42,7 @@ class AttrsAttributeNotFoundError(ValueError):
|
|||
|
||||
class NotAnAttrsClassError(ValueError):
|
||||
"""
|
||||
A non-``attrs`` class has been passed into an ``attrs`` function.
|
||||
A non-*attrs* class has been passed into an *attrs* function.
|
||||
|
||||
.. versionadded:: 16.2.0
|
||||
"""
|
||||
|
@ -50,7 +50,7 @@ class NotAnAttrsClassError(ValueError):
|
|||
|
||||
class DefaultAlreadySetError(RuntimeError):
|
||||
"""
|
||||
A default has been set using ``attr.ib()`` and is attempted to be reset
|
||||
A default has been set when defining the field and is attempted to be reset
|
||||
using the decorator.
|
||||
|
||||
.. versionadded:: 17.1.0
|
||||
|
@ -59,8 +59,7 @@ class DefaultAlreadySetError(RuntimeError):
|
|||
|
||||
class UnannotatedAttributeError(RuntimeError):
|
||||
"""
|
||||
A class with ``auto_attribs=True`` has an ``attr.ib()`` without a type
|
||||
annotation.
|
||||
A class with ``auto_attribs=True`` has a field without a type annotation.
|
||||
|
||||
.. versionadded:: 17.3.0
|
||||
"""
|
||||
|
@ -68,7 +67,7 @@ class UnannotatedAttributeError(RuntimeError):
|
|||
|
||||
class PythonTooOldError(RuntimeError):
|
||||
"""
|
||||
It was attempted to use an ``attrs`` feature that requires a newer Python
|
||||
It was attempted to use an *attrs* feature that requires a newer Python
|
||||
version.
|
||||
|
||||
.. versionadded:: 18.2.0
|
||||
|
@ -77,8 +76,8 @@ class PythonTooOldError(RuntimeError):
|
|||
|
||||
class NotCallableError(TypeError):
|
||||
"""
|
||||
A ``attr.ib()`` requiring a callable has been set with a value
|
||||
that is not callable.
|
||||
A field requiring a callable has been set with a value that is not
|
||||
callable.
|
||||
|
||||
.. versionadded:: 19.2.0
|
||||
"""
|
||||
|
|
|
@ -9,6 +9,7 @@ import operator
|
|||
import re
|
||||
|
||||
from contextlib import contextmanager
|
||||
from re import Pattern
|
||||
|
||||
from ._config import get_run_validators, set_run_validators
|
||||
from ._make import _AndValidator, and_, attrib, attrs
|
||||
|
@ -16,12 +17,6 @@ from .converters import default_if_none
|
|||
from .exceptions import NotCallableError
|
||||
|
||||
|
||||
try:
|
||||
Pattern = re.Pattern
|
||||
except AttributeError: # Python <3.7 lacks a Pattern type.
|
||||
Pattern = type(re.compile(""))
|
||||
|
||||
|
||||
__all__ = [
|
||||
"and_",
|
||||
"deep_iterable",
|
||||
|
@ -249,7 +244,17 @@ def provides(interface):
|
|||
:raises TypeError: With a human readable error message, the attribute
|
||||
(of type `attrs.Attribute`), the expected interface, and the
|
||||
value it got.
|
||||
|
||||
.. deprecated:: 23.1.0
|
||||
"""
|
||||
import warnings
|
||||
|
||||
warnings.warn(
|
||||
"attrs's zope-interface support is deprecated and will be removed in, "
|
||||
"or after, April 2024.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
return _ProvidesValidator(interface)
|
||||
|
||||
|
||||
|
@ -275,15 +280,16 @@ def optional(validator):
|
|||
which can be set to ``None`` in addition to satisfying the requirements of
|
||||
the sub-validator.
|
||||
|
||||
:param validator: A validator (or a list of validators) that is used for
|
||||
non-``None`` values.
|
||||
:type validator: callable or `list` of callables.
|
||||
:param Callable | tuple[Callable] | list[Callable] validator: A validator
|
||||
(or validators) that is used for non-``None`` values.
|
||||
|
||||
.. versionadded:: 15.1.0
|
||||
.. versionchanged:: 17.1.0 *validator* can be a list of validators.
|
||||
.. versionchanged:: 23.1.0 *validator* can also be a tuple of validators.
|
||||
"""
|
||||
if isinstance(validator, list):
|
||||
if isinstance(validator, (list, tuple)):
|
||||
return _OptionalValidator(_AndValidator(validator))
|
||||
|
||||
return _OptionalValidator(validator)
|
||||
|
||||
|
||||
|
@ -359,13 +365,13 @@ class _IsCallableValidator:
|
|||
|
||||
def is_callable():
|
||||
"""
|
||||
A validator that raises a `attr.exceptions.NotCallableError` if the
|
||||
A validator that raises a `attrs.exceptions.NotCallableError` if the
|
||||
initializer is called with a value for this particular attribute
|
||||
that is not callable.
|
||||
|
||||
.. versionadded:: 19.1.0
|
||||
|
||||
:raises `attr.exceptions.NotCallableError`: With a human readable error
|
||||
:raises attrs.exceptions.NotCallableError: With a human readable error
|
||||
message containing the attribute (`attrs.Attribute`) name,
|
||||
and the value it got.
|
||||
"""
|
||||
|
|
|
@ -51,7 +51,9 @@ def instance_of(
|
|||
def instance_of(type: Tuple[type, ...]) -> _ValidatorType[Any]: ...
|
||||
def provides(interface: Any) -> _ValidatorType[Any]: ...
|
||||
def optional(
|
||||
validator: Union[_ValidatorType[_T], List[_ValidatorType[_T]]]
|
||||
validator: Union[
|
||||
_ValidatorType[_T], List[_ValidatorType[_T]], Tuple[_ValidatorType[_T]]
|
||||
]
|
||||
) -> _ValidatorType[Optional[_T]]: ...
|
||||
def in_(options: Container[_T]) -> _ValidatorType[_T]: ...
|
||||
def and_(*validators: _ValidatorType[_T]) -> _ValidatorType[_T]: ...
|
||||
|
@ -82,5 +84,5 @@ def not_(
|
|||
validator: _ValidatorType[_T],
|
||||
*,
|
||||
msg: Optional[str] = None,
|
||||
exc_types: Union[Type[Exception], Iterable[Type[Exception]]] = ...
|
||||
exc_types: Union[Type[Exception], Iterable[Type[Exception]]] = ...,
|
||||
) -> _ValidatorType[_T]: ...
|
||||
|
|
Loading…
Reference in a new issue