зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1832629 - Bump attrs to 23.1.0 r=jmaher
Differential Revision: https://phabricator.services.mozilla.com/D177802
This commit is contained in:
Родитель
cb091a1db0
Коммит
317bc33be3
|
@ -1,10 +1,16 @@
|
|||
from __future__ import absolute_import, division, print_function
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
"""
|
||||
Classes Without Boilerplate
|
||||
"""
|
||||
|
||||
from functools import partial
|
||||
from typing import Callable
|
||||
|
||||
from . import converters, exceptions, filters, validators
|
||||
from . import converters, exceptions, filters, setters, validators
|
||||
from ._cmp import cmp_using
|
||||
from ._config import get_run_validators, set_run_validators
|
||||
from ._funcs import asdict, assoc, astuple, evolve, has
|
||||
from ._funcs import asdict, assoc, astuple, evolve, has, resolve_types
|
||||
from ._make import (
|
||||
NOTHING,
|
||||
Attribute,
|
||||
|
@ -16,23 +22,8 @@ from ._make import (
|
|||
make_class,
|
||||
validate,
|
||||
)
|
||||
from ._version import VersionInfo
|
||||
|
||||
|
||||
__version__ = "19.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"
|
||||
from ._next_gen import define, field, frozen, mutable
|
||||
from ._version_info import VersionInfo
|
||||
|
||||
|
||||
s = attributes = attrs
|
||||
|
@ -40,8 +31,13 @@ ib = attr = attrib
|
|||
dataclass = partial(attrs, auto_attribs=True) # happy Easter ;)
|
||||
|
||||
|
||||
class AttrsInstance:
|
||||
pass
|
||||
|
||||
|
||||
__all__ = [
|
||||
"Attribute",
|
||||
"AttrsInstance",
|
||||
"Factory",
|
||||
"NOTHING",
|
||||
"asdict",
|
||||
|
@ -51,18 +47,86 @@ __all__ = [
|
|||
"attrib",
|
||||
"attributes",
|
||||
"attrs",
|
||||
"cmp_using",
|
||||
"converters",
|
||||
"define",
|
||||
"evolve",
|
||||
"exceptions",
|
||||
"field",
|
||||
"fields",
|
||||
"fields_dict",
|
||||
"filters",
|
||||
"frozen",
|
||||
"get_run_validators",
|
||||
"has",
|
||||
"ib",
|
||||
"make_class",
|
||||
"mutable",
|
||||
"resolve_types",
|
||||
"s",
|
||||
"set_run_validators",
|
||||
"setters",
|
||||
"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__)
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
import enum
|
||||
import sys
|
||||
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Dict,
|
||||
Generic,
|
||||
List,
|
||||
Optional,
|
||||
Sequence,
|
||||
Mapping,
|
||||
Optional,
|
||||
Protocol,
|
||||
Sequence,
|
||||
Tuple,
|
||||
Type,
|
||||
TypeVar,
|
||||
|
@ -15,12 +19,19 @@ from typing import (
|
|||
)
|
||||
|
||||
# `import X as X` is required to make these public
|
||||
from . import converters as converters
|
||||
from . import exceptions as exceptions
|
||||
from . import filters as filters
|
||||
from . import converters as converters
|
||||
from . import setters as setters
|
||||
from . import validators as validators
|
||||
from ._cmp import cmp_using as cmp_using
|
||||
from ._typing_compat import AttrsInstance_
|
||||
from ._version_info import VersionInfo
|
||||
|
||||
from ._version import VersionInfo
|
||||
if sys.version_info >= (3, 10):
|
||||
from typing import TypeGuard
|
||||
else:
|
||||
from typing_extensions import TypeGuard
|
||||
|
||||
__version__: str
|
||||
__version_info__: VersionInfo
|
||||
|
@ -36,50 +47,104 @@ __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]
|
||||
_EqOrderType = Union[bool, Callable[[Any], Any]]
|
||||
_ValidatorType = Callable[[Any, "Attribute[_T]", _T], Any]
|
||||
_ConverterType = Callable[[Any], Any]
|
||||
_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.
|
||||
_OnSetAttrType = Callable[[Any, "Attribute[Any]", Any], Any]
|
||||
_OnSetAttrArgType = Union[
|
||||
_OnSetAttrType, List[_OnSetAttrType], setters._NoOpType
|
||||
]
|
||||
_FieldTransformer = Callable[
|
||||
[type, List["Attribute[Any]"]], List["Attribute[Any]"]
|
||||
]
|
||||
# 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.
|
||||
_ValidatorArgType = Union[_ValidatorType[_T], Sequence[_ValidatorType[_T]]]
|
||||
|
||||
# We subclass this here to keep the protocol's qualified name clean.
|
||||
class AttrsInstance(AttrsInstance_, Protocol):
|
||||
pass
|
||||
|
||||
_A = TypeVar("_A", bound=AttrsInstance)
|
||||
# _make --
|
||||
|
||||
NOTHING: object
|
||||
class _Nothing(enum.Enum):
|
||||
NOTHING = enum.auto()
|
||||
|
||||
# NOTE: Factory lies about its return type to make this possible: `x: List[int] = Factory(list)`
|
||||
NOTHING = _Nothing.NOTHING
|
||||
|
||||
# NOTE: Factory lies about its return type to make this possible:
|
||||
# `x: List[int] # = Factory(list)`
|
||||
# Work around mypy issue #4554 in the common case by using an overload.
|
||||
@overload
|
||||
def Factory(factory: Callable[[], _T]) -> _T: ...
|
||||
@overload
|
||||
def Factory(
|
||||
factory: Union[Callable[[Any], _T], Callable[[], _T]],
|
||||
takes_self: bool = ...,
|
||||
) -> _T: ...
|
||||
if sys.version_info >= (3, 8):
|
||||
from typing import Literal
|
||||
@overload
|
||||
def Factory(factory: Callable[[], _T]) -> _T: ...
|
||||
@overload
|
||||
def Factory(
|
||||
factory: Callable[[Any], _T],
|
||||
takes_self: Literal[True],
|
||||
) -> _T: ...
|
||||
@overload
|
||||
def Factory(
|
||||
factory: Callable[[], _T],
|
||||
takes_self: Literal[False],
|
||||
) -> _T: ...
|
||||
|
||||
else:
|
||||
@overload
|
||||
def Factory(factory: Callable[[], _T]) -> _T: ...
|
||||
@overload
|
||||
def Factory(
|
||||
factory: Union[Callable[[Any], _T], Callable[[], _T]],
|
||||
takes_self: bool = ...,
|
||||
) -> _T: ...
|
||||
|
||||
# Static type inference support via __dataclass_transform__ implemented as per:
|
||||
# https://github.com/microsoft/pyright/blob/1.1.135/specs/dataclass_transforms.md
|
||||
# This annotation must be applied to all overloads of "define" and "attrs"
|
||||
#
|
||||
# NOTE: This is a typing construct and does not exist at runtime. Extensions
|
||||
# wrapping attrs decorators should declare a separate __dataclass_transform__
|
||||
# signature in the extension module using the specification linked above to
|
||||
# provide pyright support.
|
||||
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]: ...
|
||||
|
||||
class Attribute(Generic[_T]):
|
||||
name: str
|
||||
default: Optional[_T]
|
||||
validator: Optional[_ValidatorType[_T]]
|
||||
repr: _ReprArgType
|
||||
cmp: bool
|
||||
eq: bool
|
||||
order: bool
|
||||
cmp: _EqOrderType
|
||||
eq: _EqOrderType
|
||||
order: _EqOrderType
|
||||
hash: Optional[bool]
|
||||
init: bool
|
||||
converter: Optional[_ConverterType[_T]]
|
||||
converter: Optional[_ConverterType]
|
||||
metadata: Dict[Any, Any]
|
||||
type: Optional[Type[_T]]
|
||||
kw_only: bool
|
||||
on_setattr: _OnSetAttrType
|
||||
alias: Optional[str]
|
||||
|
||||
def evolve(self, **changes: Any) -> "Attribute[Any]": ...
|
||||
|
||||
# NOTE: We had several choices for the annotation to use for type arg:
|
||||
# 1) Type[_T]
|
||||
# - Pros: Handles simple cases correctly
|
||||
# - Cons: Might produce less informative errors in the case of conflicting TypeVars
|
||||
# e.g. `attr.ib(default='bad', type=int)`
|
||||
# - Cons: Might produce less informative errors in the case of conflicting
|
||||
# TypeVars e.g. `attr.ib(default='bad', type=int)`
|
||||
# 2) Callable[..., _T]
|
||||
# - Pros: Better error messages than #1 for conflicting TypeVars
|
||||
# - Cons: Terrible error messages for validator checks.
|
||||
|
@ -97,13 +162,14 @@ class Attribute(Generic[_T]):
|
|||
# This makes this type of assignments possible:
|
||||
# x: int = attr(8)
|
||||
#
|
||||
# This form catches explicit None or no default but with no other arguments returns Any.
|
||||
# This form catches explicit None or no default but with no other arguments
|
||||
# returns Any.
|
||||
@overload
|
||||
def attrib(
|
||||
default: None = ...,
|
||||
validator: None = ...,
|
||||
repr: _ReprArgType = ...,
|
||||
cmp: Optional[bool] = ...,
|
||||
cmp: Optional[_EqOrderType] = ...,
|
||||
hash: Optional[bool] = ...,
|
||||
init: bool = ...,
|
||||
metadata: Optional[Mapping[Any, Any]] = ...,
|
||||
|
@ -111,26 +177,31 @@ def attrib(
|
|||
converter: None = ...,
|
||||
factory: None = ...,
|
||||
kw_only: bool = ...,
|
||||
eq: Optional[bool] = ...,
|
||||
order: Optional[bool] = ...,
|
||||
eq: Optional[_EqOrderType] = ...,
|
||||
order: Optional[_EqOrderType] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
alias: Optional[str] = ...,
|
||||
) -> Any: ...
|
||||
|
||||
# This form catches an explicit None or no default and infers the type from the other arguments.
|
||||
# This form catches an explicit None or no default and infers the type from the
|
||||
# other arguments.
|
||||
@overload
|
||||
def attrib(
|
||||
default: None = ...,
|
||||
validator: Optional[_ValidatorArgType[_T]] = ...,
|
||||
repr: _ReprArgType = ...,
|
||||
cmp: Optional[bool] = ...,
|
||||
cmp: Optional[_EqOrderType] = ...,
|
||||
hash: Optional[bool] = ...,
|
||||
init: bool = ...,
|
||||
metadata: Optional[Mapping[Any, Any]] = ...,
|
||||
type: Optional[Type[_T]] = ...,
|
||||
converter: Optional[_ConverterType[_T]] = ...,
|
||||
converter: Optional[_ConverterType] = ...,
|
||||
factory: Optional[Callable[[], _T]] = ...,
|
||||
kw_only: bool = ...,
|
||||
eq: Optional[bool] = ...,
|
||||
order: Optional[bool] = ...,
|
||||
eq: Optional[_EqOrderType] = ...,
|
||||
order: Optional[_EqOrderType] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
alias: Optional[str] = ...,
|
||||
) -> _T: ...
|
||||
|
||||
# This form catches an explicit default argument.
|
||||
|
@ -139,16 +210,18 @@ def attrib(
|
|||
default: _T,
|
||||
validator: Optional[_ValidatorArgType[_T]] = ...,
|
||||
repr: _ReprArgType = ...,
|
||||
cmp: Optional[bool] = ...,
|
||||
cmp: Optional[_EqOrderType] = ...,
|
||||
hash: Optional[bool] = ...,
|
||||
init: bool = ...,
|
||||
metadata: Optional[Mapping[Any, Any]] = ...,
|
||||
type: Optional[Type[_T]] = ...,
|
||||
converter: Optional[_ConverterType[_T]] = ...,
|
||||
converter: Optional[_ConverterType] = ...,
|
||||
factory: Optional[Callable[[], _T]] = ...,
|
||||
kw_only: bool = ...,
|
||||
eq: Optional[bool] = ...,
|
||||
order: Optional[bool] = ...,
|
||||
eq: Optional[_EqOrderType] = ...,
|
||||
order: Optional[_EqOrderType] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
alias: Optional[str] = ...,
|
||||
) -> _T: ...
|
||||
|
||||
# This form covers type=non-Type: e.g. forward references (str), Any
|
||||
|
@ -157,24 +230,106 @@ def attrib(
|
|||
default: Optional[_T] = ...,
|
||||
validator: Optional[_ValidatorArgType[_T]] = ...,
|
||||
repr: _ReprArgType = ...,
|
||||
cmp: Optional[bool] = ...,
|
||||
cmp: Optional[_EqOrderType] = ...,
|
||||
hash: Optional[bool] = ...,
|
||||
init: bool = ...,
|
||||
metadata: Optional[Mapping[Any, Any]] = ...,
|
||||
type: object = ...,
|
||||
converter: Optional[_ConverterType[_T]] = ...,
|
||||
converter: Optional[_ConverterType] = ...,
|
||||
factory: Optional[Callable[[], _T]] = ...,
|
||||
kw_only: bool = ...,
|
||||
eq: Optional[_EqOrderType] = ...,
|
||||
order: Optional[_EqOrderType] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
alias: Optional[str] = ...,
|
||||
) -> Any: ...
|
||||
@overload
|
||||
def field(
|
||||
*,
|
||||
default: None = ...,
|
||||
validator: None = ...,
|
||||
repr: _ReprArgType = ...,
|
||||
hash: Optional[bool] = ...,
|
||||
init: bool = ...,
|
||||
metadata: Optional[Mapping[Any, Any]] = ...,
|
||||
converter: None = ...,
|
||||
factory: None = ...,
|
||||
kw_only: bool = ...,
|
||||
eq: Optional[bool] = ...,
|
||||
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
|
||||
# other arguments.
|
||||
@overload
|
||||
def field(
|
||||
*,
|
||||
default: None = ...,
|
||||
validator: Optional[_ValidatorArgType[_T]] = ...,
|
||||
repr: _ReprArgType = ...,
|
||||
hash: Optional[bool] = ...,
|
||||
init: bool = ...,
|
||||
metadata: Optional[Mapping[Any, Any]] = ...,
|
||||
converter: Optional[_ConverterType] = ...,
|
||||
factory: Optional[Callable[[], _T]] = ...,
|
||||
kw_only: bool = ...,
|
||||
eq: Optional[_EqOrderType] = ...,
|
||||
order: Optional[_EqOrderType] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
alias: Optional[str] = ...,
|
||||
type: Optional[type] = ...,
|
||||
) -> _T: ...
|
||||
|
||||
# This form catches an explicit default argument.
|
||||
@overload
|
||||
def field(
|
||||
*,
|
||||
default: _T,
|
||||
validator: Optional[_ValidatorArgType[_T]] = ...,
|
||||
repr: _ReprArgType = ...,
|
||||
hash: Optional[bool] = ...,
|
||||
init: bool = ...,
|
||||
metadata: Optional[Mapping[Any, Any]] = ...,
|
||||
converter: Optional[_ConverterType] = ...,
|
||||
factory: Optional[Callable[[], _T]] = ...,
|
||||
kw_only: bool = ...,
|
||||
eq: Optional[_EqOrderType] = ...,
|
||||
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
|
||||
@overload
|
||||
def field(
|
||||
*,
|
||||
default: Optional[_T] = ...,
|
||||
validator: Optional[_ValidatorArgType[_T]] = ...,
|
||||
repr: _ReprArgType = ...,
|
||||
hash: Optional[bool] = ...,
|
||||
init: bool = ...,
|
||||
metadata: Optional[Mapping[Any, Any]] = ...,
|
||||
converter: Optional[_ConverterType] = ...,
|
||||
factory: Optional[Callable[[], _T]] = ...,
|
||||
kw_only: bool = ...,
|
||||
eq: Optional[_EqOrderType] = ...,
|
||||
order: Optional[_EqOrderType] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
alias: Optional[str] = ...,
|
||||
type: Optional[type] = ...,
|
||||
) -> Any: ...
|
||||
@overload
|
||||
@__dataclass_transform__(order_default=True, field_descriptors=(attrib, field))
|
||||
def attrs(
|
||||
maybe_cls: _C,
|
||||
these: Optional[Dict[str, Any]] = ...,
|
||||
repr_ns: Optional[str] = ...,
|
||||
repr: bool = ...,
|
||||
cmp: Optional[bool] = ...,
|
||||
cmp: Optional[_EqOrderType] = ...,
|
||||
hash: Optional[bool] = ...,
|
||||
init: bool = ...,
|
||||
slots: bool = ...,
|
||||
|
@ -185,16 +340,52 @@ def attrs(
|
|||
kw_only: bool = ...,
|
||||
cache_hash: bool = ...,
|
||||
auto_exc: bool = ...,
|
||||
eq: Optional[bool] = ...,
|
||||
order: Optional[bool] = ...,
|
||||
eq: Optional[_EqOrderType] = ...,
|
||||
order: Optional[_EqOrderType] = ...,
|
||||
auto_detect: bool = ...,
|
||||
collect_by_mro: bool = ...,
|
||||
getstate_setstate: Optional[bool] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
field_transformer: Optional[_FieldTransformer] = ...,
|
||||
match_args: bool = ...,
|
||||
unsafe_hash: Optional[bool] = ...,
|
||||
) -> _C: ...
|
||||
@overload
|
||||
@__dataclass_transform__(order_default=True, field_descriptors=(attrib, field))
|
||||
def attrs(
|
||||
maybe_cls: None = ...,
|
||||
these: Optional[Dict[str, Any]] = ...,
|
||||
repr_ns: Optional[str] = ...,
|
||||
repr: bool = ...,
|
||||
cmp: Optional[bool] = ...,
|
||||
cmp: Optional[_EqOrderType] = ...,
|
||||
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[_EqOrderType] = ...,
|
||||
order: Optional[_EqOrderType] = ...,
|
||||
auto_detect: bool = ...,
|
||||
collect_by_mro: bool = ...,
|
||||
getstate_setstate: Optional[bool] = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
field_transformer: Optional[_FieldTransformer] = ...,
|
||||
match_args: bool = ...,
|
||||
unsafe_hash: Optional[bool] = ...,
|
||||
) -> Callable[[_C], _C]: ...
|
||||
@overload
|
||||
@__dataclass_transform__(field_descriptors=(attrib, field))
|
||||
def define(
|
||||
maybe_cls: _C,
|
||||
*,
|
||||
these: Optional[Dict[str, Any]] = ...,
|
||||
repr: bool = ...,
|
||||
unsafe_hash: Optional[bool] = ...,
|
||||
hash: Optional[bool] = ...,
|
||||
init: bool = ...,
|
||||
slots: bool = ...,
|
||||
|
@ -207,25 +398,118 @@ def attrs(
|
|||
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__(field_descriptors=(attrib, field))
|
||||
def define(
|
||||
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]: ...
|
||||
|
||||
# TODO: add support for returning NamedTuple from the mypy plugin
|
||||
class _Fields(Tuple[Attribute[Any], ...]):
|
||||
def __getattr__(self, name: str) -> Attribute[Any]: ...
|
||||
mutable = define
|
||||
|
||||
def fields(cls: type) -> _Fields: ...
|
||||
def fields_dict(cls: type) -> Dict[str, Attribute[Any]]: ...
|
||||
def validate(inst: Any) -> None: ...
|
||||
@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: _A,
|
||||
globalns: Optional[Dict[str, Any]] = ...,
|
||||
localns: Optional[Dict[str, Any]] = ...,
|
||||
attribs: Optional[List[Attribute[Any]]] = ...,
|
||||
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', [attr.ib()])` is valid
|
||||
# we use Any instead of _CountingAttr so that e.g. `make_class('Foo',
|
||||
# [attr.ib()])` is valid
|
||||
def make_class(
|
||||
name: str,
|
||||
attrs: Union[List[str], Tuple[str, ...], Dict[str, Any]],
|
||||
bases: Tuple[type, ...] = ...,
|
||||
repr_ns: Optional[str] = ...,
|
||||
repr: bool = ...,
|
||||
cmp: Optional[bool] = ...,
|
||||
cmp: Optional[_EqOrderType] = ...,
|
||||
hash: Optional[bool] = ...,
|
||||
init: bool = ...,
|
||||
slots: bool = ...,
|
||||
|
@ -236,33 +520,42 @@ def make_class(
|
|||
kw_only: bool = ...,
|
||||
cache_hash: bool = ...,
|
||||
auto_exc: bool = ...,
|
||||
eq: Optional[bool] = ...,
|
||||
order: Optional[bool] = ...,
|
||||
eq: Optional[_EqOrderType] = ...,
|
||||
order: Optional[_EqOrderType] = ...,
|
||||
collect_by_mro: bool = ...,
|
||||
on_setattr: Optional[_OnSetAttrArgType] = ...,
|
||||
field_transformer: Optional[_FieldTransformer] = ...,
|
||||
) -> type: ...
|
||||
|
||||
# _funcs --
|
||||
|
||||
# TODO: add support for returning TypedDict from the mypy plugin
|
||||
# FIXME: asdict/astuple do not honor their factory args. waiting on one of these:
|
||||
# FIXME: asdict/astuple do not honor their factory args. Waiting on one of
|
||||
# these:
|
||||
# https://github.com/python/mypy/issues/4236
|
||||
# https://github.com/python/typing/issues/253
|
||||
# XXX: remember to fix attrs.asdict/astuple too!
|
||||
def asdict(
|
||||
inst: Any,
|
||||
inst: AttrsInstance,
|
||||
recurse: bool = ...,
|
||||
filter: Optional[_FilterType[Any]] = ...,
|
||||
dict_factory: Type[Mapping[Any, Any]] = ...,
|
||||
retain_collection_types: bool = ...,
|
||||
value_serializer: Optional[
|
||||
Callable[[type, Attribute[Any], Any], Any]
|
||||
] = ...,
|
||||
tuple_keys: Optional[bool] = ...,
|
||||
) -> Dict[str, Any]: ...
|
||||
|
||||
# TODO: add support for returning NamedTuple from the mypy plugin
|
||||
def astuple(
|
||||
inst: Any,
|
||||
inst: AttrsInstance,
|
||||
recurse: bool = ...,
|
||||
filter: Optional[_FilterType[Any]] = ...,
|
||||
tuple_factory: Type[Sequence[Any]] = ...,
|
||||
retain_collection_types: bool = ...,
|
||||
) -> Tuple[Any, ...]: ...
|
||||
def has(cls: type) -> bool: ...
|
||||
def has(cls: type) -> TypeGuard[Type[AttrsInstance]]: ...
|
||||
def assoc(inst: _T, **changes: Any) -> _T: ...
|
||||
def evolve(inst: _T, **changes: Any) -> _T: ...
|
||||
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
# SPDX-License-Identifier: MIT
|
||||
|
||||
|
||||
import functools
|
||||
import types
|
||||
|
||||
from ._make import _make_ne
|
||||
|
||||
|
||||
_operation_names = {"eq": "==", "lt": "<", "le": "<=", "gt": ">", "ge": ">="}
|
||||
|
||||
|
||||
def cmp_using(
|
||||
eq=None,
|
||||
lt=None,
|
||||
le=None,
|
||||
gt=None,
|
||||
ge=None,
|
||||
require_same_type=True,
|
||||
class_name="Comparable",
|
||||
):
|
||||
"""
|
||||
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.
|
||||
|
||||
: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.
|
||||
|
||||
:param Optional[str] class_name: Name of class. Defaults to 'Comparable'.
|
||||
|
||||
See `comparison` for more details.
|
||||
|
||||
.. versionadded:: 21.1.0
|
||||
"""
|
||||
|
||||
body = {
|
||||
"__slots__": ["value"],
|
||||
"__init__": _make_init(),
|
||||
"_requirements": [],
|
||||
"_is_comparable_to": _is_comparable_to,
|
||||
}
|
||||
|
||||
# Add operations.
|
||||
num_order_functions = 0
|
||||
has_eq_function = False
|
||||
|
||||
if eq is not None:
|
||||
has_eq_function = True
|
||||
body["__eq__"] = _make_operator("eq", eq)
|
||||
body["__ne__"] = _make_ne()
|
||||
|
||||
if lt is not None:
|
||||
num_order_functions += 1
|
||||
body["__lt__"] = _make_operator("lt", lt)
|
||||
|
||||
if le is not None:
|
||||
num_order_functions += 1
|
||||
body["__le__"] = _make_operator("le", le)
|
||||
|
||||
if gt is not None:
|
||||
num_order_functions += 1
|
||||
body["__gt__"] = _make_operator("gt", gt)
|
||||
|
||||
if ge is not None:
|
||||
num_order_functions += 1
|
||||
body["__ge__"] = _make_operator("ge", ge)
|
||||
|
||||
type_ = types.new_class(
|
||||
class_name, (object,), {}, lambda ns: ns.update(body)
|
||||
)
|
||||
|
||||
# Add same type requirement.
|
||||
if require_same_type:
|
||||
type_._requirements.append(_check_same_type)
|
||||
|
||||
# Add total ordering if at least one operation was defined.
|
||||
if 0 < num_order_functions < 4:
|
||||
if not has_eq_function:
|
||||
# functools.total_ordering requires __eq__ to be defined,
|
||||
# so raise early error here to keep a nice stack.
|
||||
raise ValueError(
|
||||
"eq must be define is order to complete ordering from "
|
||||
"lt, le, gt, ge."
|
||||
)
|
||||
type_ = functools.total_ordering(type_)
|
||||
|
||||
return type_
|
||||
|
||||
|
||||
def _make_init():
|
||||
"""
|
||||
Create __init__ method.
|
||||
"""
|
||||
|
||||
def __init__(self, value):
|
||||
"""
|
||||
Initialize object with *value*.
|
||||
"""
|
||||
self.value = value
|
||||
|
||||
return __init__
|
||||
|
||||
|
||||
def _make_operator(name, func):
|
||||
"""
|
||||
Create operator method.
|
||||
"""
|
||||
|
||||
def method(self, other):
|
||||
if not self._is_comparable_to(other):
|
||||
return NotImplemented
|
||||
|
||||
result = func(self.value, other.value)
|
||||
if result is NotImplemented:
|
||||
return NotImplemented
|
||||
|
||||
return result
|
||||
|
||||
method.__name__ = f"__{name}__"
|
||||
method.__doc__ = (
|
||||
f"Return a {_operation_names[name]} b. Computed by attrs."
|
||||
)
|
||||
|
||||
return method
|
||||
|
||||
|
||||
def _is_comparable_to(self, other):
|
||||
"""
|
||||
Check whether `other` is comparable to `self`.
|
||||
"""
|
||||
for func in self._requirements:
|
||||
if not func(self, other):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def _check_same_type(self, other):
|
||||
"""
|
||||
Return True if *self* and *other* are of the same type, False otherwise.
|
||||
"""
|
||||
return other.value.__class__ is self.value.__class__
|
|
@ -0,0 +1,13 @@
|
|||
from typing import Any, Callable, Optional, Type
|
||||
|
||||
_CompareWithType = Callable[[Any, Any], bool]
|
||||
|
||||
def cmp_using(
|
||||
eq: Optional[_CompareWithType] = ...,
|
||||
lt: Optional[_CompareWithType] = ...,
|
||||
le: Optional[_CompareWithType] = ...,
|
||||
gt: Optional[_CompareWithType] = ...,
|
||||
ge: Optional[_CompareWithType] = ...,
|
||||
require_same_type: bool = ...,
|
||||
class_name: str = ...,
|
||||
) -> Type: ...
|
|
@ -1,128 +1,71 @@
|
|||
from __future__ import absolute_import, division, print_function
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
|
||||
import inspect
|
||||
import platform
|
||||
import sys
|
||||
import threading
|
||||
import types
|
||||
import warnings
|
||||
|
||||
from collections.abc import Mapping, Sequence # noqa
|
||||
from typing import _GenericAlias
|
||||
|
||||
|
||||
PY2 = sys.version_info[0] == 2
|
||||
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)
|
||||
|
||||
|
||||
if PYPY or sys.version_info[:2] >= (3, 6):
|
||||
ordered_dict = dict
|
||||
else:
|
||||
from collections import OrderedDict
|
||||
|
||||
ordered_dict = OrderedDict
|
||||
def just_warn(*args, **kw):
|
||||
warnings.warn(
|
||||
"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,
|
||||
)
|
||||
|
||||
|
||||
if PY2:
|
||||
from UserDict import IterableUserDict
|
||||
from collections import Mapping, Sequence
|
||||
class _AnnotationExtractor:
|
||||
"""
|
||||
Extract type annotations from a callable, returning None whenever there
|
||||
is none.
|
||||
"""
|
||||
|
||||
# We 'bundle' isclass instead of using inspect as importing inspect is
|
||||
# fairly expensive (order of 10-15 ms for a modern machine in 2016)
|
||||
def isclass(klass):
|
||||
return isinstance(klass, (type, types.ClassType))
|
||||
__slots__ = ["sig"]
|
||||
|
||||
# TYPE is used in exceptions, repr(int) is different on Python 2 and 3.
|
||||
TYPE = "type"
|
||||
def __init__(self, callable):
|
||||
try:
|
||||
self.sig = inspect.signature(callable)
|
||||
except (ValueError, TypeError): # inspect failed
|
||||
self.sig = None
|
||||
|
||||
def iteritems(d):
|
||||
return d.iteritems()
|
||||
|
||||
# Python 2 is bereft of a read-only dict proxy, so we make one!
|
||||
class ReadOnlyDict(IterableUserDict):
|
||||
def get_first_param_type(self):
|
||||
"""
|
||||
Best-effort read-only dict wrapper.
|
||||
Return the type annotation of the first argument if it's not empty.
|
||||
"""
|
||||
if not self.sig:
|
||||
return None
|
||||
|
||||
def __setitem__(self, key, val):
|
||||
# We gently pretend we're a Python 3 mappingproxy.
|
||||
raise TypeError(
|
||||
"'mappingproxy' object does not support item assignment"
|
||||
)
|
||||
params = list(self.sig.parameters.values())
|
||||
if params and params[0].annotation is not inspect.Parameter.empty:
|
||||
return params[0].annotation
|
||||
|
||||
def update(self, _):
|
||||
# We gently pretend we're a Python 3 mappingproxy.
|
||||
raise AttributeError(
|
||||
"'mappingproxy' object has no attribute 'update'"
|
||||
)
|
||||
return None
|
||||
|
||||
def __delitem__(self, _):
|
||||
# We gently pretend we're a Python 3 mappingproxy.
|
||||
raise TypeError(
|
||||
"'mappingproxy' object does not support item deletion"
|
||||
)
|
||||
|
||||
def clear(self):
|
||||
# We gently pretend we're a Python 3 mappingproxy.
|
||||
raise AttributeError(
|
||||
"'mappingproxy' object has no attribute 'clear'"
|
||||
)
|
||||
|
||||
def pop(self, key, default=None):
|
||||
# We gently pretend we're a Python 3 mappingproxy.
|
||||
raise AttributeError(
|
||||
"'mappingproxy' object has no attribute 'pop'"
|
||||
)
|
||||
|
||||
def popitem(self):
|
||||
# We gently pretend we're a Python 3 mappingproxy.
|
||||
raise AttributeError(
|
||||
"'mappingproxy' object has no attribute 'popitem'"
|
||||
)
|
||||
|
||||
def setdefault(self, key, default=None):
|
||||
# We gently pretend we're a Python 3 mappingproxy.
|
||||
raise AttributeError(
|
||||
"'mappingproxy' object has no attribute 'setdefault'"
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
# Override to be identical to the Python 3 version.
|
||||
return "mappingproxy(" + repr(self.data) + ")"
|
||||
|
||||
def metadata_proxy(d):
|
||||
res = ReadOnlyDict()
|
||||
res.data.update(d) # We blocked update, so we have to do it like this.
|
||||
return res
|
||||
|
||||
def just_warn(*args, **kw): # pragma: nocover
|
||||
def get_return_type(self):
|
||||
"""
|
||||
We only warn on Python 3 because we are not aware of any concrete
|
||||
consequences of not setting the cell on Python 2.
|
||||
Return the return type if it's not empty.
|
||||
"""
|
||||
if (
|
||||
self.sig
|
||||
and self.sig.return_annotation is not inspect.Signature.empty
|
||||
):
|
||||
return self.sig.return_annotation
|
||||
|
||||
|
||||
else: # Python 3 and later.
|
||||
from collections.abc import Mapping, Sequence # noqa
|
||||
|
||||
def just_warn(*args, **kw):
|
||||
"""
|
||||
We only warn on Python 3 because we are not aware of any concrete
|
||||
consequences of not setting the cell on Python 2.
|
||||
"""
|
||||
warnings.warn(
|
||||
"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,
|
||||
)
|
||||
|
||||
def isclass(klass):
|
||||
return isinstance(klass, type)
|
||||
|
||||
TYPE = "class"
|
||||
|
||||
def iteritems(d):
|
||||
return d.items()
|
||||
|
||||
def metadata_proxy(d):
|
||||
return types.MappingProxyType(dict(d))
|
||||
return None
|
||||
|
||||
|
||||
def make_set_closure_cell():
|
||||
|
@ -131,7 +74,7 @@ def make_set_closure_cell():
|
|||
"""
|
||||
# pypy makes this easy. (It also supports the logic below, but
|
||||
# why not do the easy/fast thing?)
|
||||
if PYPY: # pragma: no cover
|
||||
if PYPY:
|
||||
|
||||
def set_closure_cell(cell, value):
|
||||
cell.__setstate__((value,))
|
||||
|
@ -140,40 +83,34 @@ def make_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
|
||||
)
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
# Extract the code object and make sure our assumptions about
|
||||
# the closure behavior are correct.
|
||||
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.
|
||||
args = [co.co_argcount]
|
||||
if not PY2:
|
||||
args.append(co.co_kwonlyargcount)
|
||||
args.append(co.co_kwonlyargcount)
|
||||
args.extend(
|
||||
[
|
||||
co.co_nlocals,
|
||||
|
@ -194,15 +131,15 @@ def make_set_closure_cell():
|
|||
)
|
||||
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)
|
||||
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():
|
||||
|
@ -213,10 +150,7 @@ def make_set_closure_cell():
|
|||
|
||||
return func
|
||||
|
||||
if PY2:
|
||||
cell = make_func_with_cell().func_closure[0]
|
||||
else:
|
||||
cell = make_func_with_cell().__closure__[0]
|
||||
cell = make_func_with_cell().__closure__[0]
|
||||
set_closure_cell(cell, 100)
|
||||
if cell.cell_contents != 100:
|
||||
raise AssertionError # pragma: no cover
|
||||
|
@ -228,3 +162,24 @@ def make_set_closure_cell():
|
|||
|
||||
|
||||
set_closure_cell = make_set_closure_cell()
|
||||
|
||||
# Thread-local global to track attrs instances which are already being repr'd.
|
||||
# This is needed because there is no other (thread-safe) way to pass info
|
||||
# about the instances that are already being repr'd through the call stack
|
||||
# in order to ensure we don't perform infinite recursion.
|
||||
#
|
||||
# For instance, if an instance contains a dict which contains that instance,
|
||||
# we need to know that we're already repr'ing the outside instance from within
|
||||
# the dict's repr() call.
|
||||
#
|
||||
# This lives here rather than in _make.py so that the functions in _make.py
|
||||
# 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
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from __future__ import absolute_import, division, print_function
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
|
||||
__all__ = ["set_run_validators", "get_run_validators"]
|
||||
|
@ -9,6 +9,10 @@ _run_validators = True
|
|||
def set_run_validators(run):
|
||||
"""
|
||||
Set whether or not validators are run. By default, they are run.
|
||||
|
||||
.. deprecated:: 21.3.0 It will not be removed, but it also will not be
|
||||
moved to new ``attrs`` namespace. Use `attrs.validators.set_disabled()`
|
||||
instead.
|
||||
"""
|
||||
if not isinstance(run, bool):
|
||||
raise TypeError("'run' must be bool.")
|
||||
|
@ -19,5 +23,9 @@ def set_run_validators(run):
|
|||
def get_run_validators():
|
||||
"""
|
||||
Return whether or not validators are run.
|
||||
|
||||
.. deprecated:: 21.3.0 It will not be removed, but it also will not be
|
||||
moved to new ``attrs`` namespace. Use `attrs.validators.get_disabled()`
|
||||
instead.
|
||||
"""
|
||||
return _run_validators
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
from __future__ import absolute_import, division, print_function
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
|
||||
import copy
|
||||
|
||||
from ._compat import iteritems
|
||||
from ._compat import PY_3_9_PLUS, get_generic_base
|
||||
from ._make import NOTHING, _obj_setattr, fields
|
||||
from .exceptions import AttrsAttributeNotFoundError
|
||||
|
||||
|
@ -13,18 +14,19 @@ def asdict(
|
|||
filter=None,
|
||||
dict_factory=dict,
|
||||
retain_collection_types=False,
|
||||
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 `attr.Attribute` as the first argument and the
|
||||
called with the `attrs.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
|
||||
|
@ -32,14 +34,21 @@ def asdict(
|
|||
:param bool retain_collection_types: Do not convert to ``list`` when
|
||||
encountering an attribute whose type is ``tuple`` or ``set``. Only
|
||||
meaningful if ``recurse`` is ``True``.
|
||||
:param Optional[callable] value_serializer: A hook that is called for every
|
||||
attribute or dict key/value. It receives the current instance, field
|
||||
and value and must return the (updated) value. The hook is run *after*
|
||||
the optional *filter* has been applied.
|
||||
|
||||
: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*
|
||||
.. versionadded:: 16.1.0 *retain_collection_types*
|
||||
.. versionadded:: 20.3.0 *value_serializer*
|
||||
.. versionadded:: 21.3.0 If a dict has a collection for a key, it is
|
||||
serialized as a tuple.
|
||||
"""
|
||||
attrs = fields(inst.__class__)
|
||||
rv = dict_factory()
|
||||
|
@ -47,17 +56,31 @@ def asdict(
|
|||
v = getattr(inst, a.name)
|
||||
if filter is not None and not filter(a, v):
|
||||
continue
|
||||
|
||||
if value_serializer is not None:
|
||||
v = value_serializer(inst, a, v)
|
||||
|
||||
if recurse is True:
|
||||
if has(v.__class__):
|
||||
rv[a.name] = asdict(
|
||||
v, True, filter, dict_factory, retain_collection_types
|
||||
v,
|
||||
recurse=True,
|
||||
filter=filter,
|
||||
dict_factory=dict_factory,
|
||||
retain_collection_types=retain_collection_types,
|
||||
value_serializer=value_serializer,
|
||||
)
|
||||
elif isinstance(v, (tuple, list, set)):
|
||||
elif isinstance(v, (tuple, list, set, frozenset)):
|
||||
cf = v.__class__ if retain_collection_types is True else list
|
||||
rv[a.name] = cf(
|
||||
[
|
||||
_asdict_anything(
|
||||
i, filter, dict_factory, retain_collection_types
|
||||
i,
|
||||
is_key=False,
|
||||
filter=filter,
|
||||
dict_factory=dict_factory,
|
||||
retain_collection_types=retain_collection_types,
|
||||
value_serializer=value_serializer,
|
||||
)
|
||||
for i in v
|
||||
]
|
||||
|
@ -67,13 +90,23 @@ def asdict(
|
|||
rv[a.name] = df(
|
||||
(
|
||||
_asdict_anything(
|
||||
kk, filter, df, retain_collection_types
|
||||
kk,
|
||||
is_key=True,
|
||||
filter=filter,
|
||||
dict_factory=df,
|
||||
retain_collection_types=retain_collection_types,
|
||||
value_serializer=value_serializer,
|
||||
),
|
||||
_asdict_anything(
|
||||
vv, filter, df, retain_collection_types
|
||||
vv,
|
||||
is_key=False,
|
||||
filter=filter,
|
||||
dict_factory=df,
|
||||
retain_collection_types=retain_collection_types,
|
||||
value_serializer=value_serializer,
|
||||
),
|
||||
)
|
||||
for kk, vv in iteritems(v)
|
||||
for kk, vv in v.items()
|
||||
)
|
||||
else:
|
||||
rv[a.name] = v
|
||||
|
@ -82,19 +115,44 @@ def asdict(
|
|||
return rv
|
||||
|
||||
|
||||
def _asdict_anything(val, filter, dict_factory, retain_collection_types):
|
||||
def _asdict_anything(
|
||||
val,
|
||||
is_key,
|
||||
filter,
|
||||
dict_factory,
|
||||
retain_collection_types,
|
||||
value_serializer,
|
||||
):
|
||||
"""
|
||||
``asdict`` only works on attrs instances, this works on anything.
|
||||
"""
|
||||
if getattr(val.__class__, "__attrs_attrs__", None) is not None:
|
||||
# Attrs class.
|
||||
rv = asdict(val, True, filter, dict_factory, retain_collection_types)
|
||||
elif isinstance(val, (tuple, list, set)):
|
||||
cf = val.__class__ if retain_collection_types is True else list
|
||||
rv = asdict(
|
||||
val,
|
||||
recurse=True,
|
||||
filter=filter,
|
||||
dict_factory=dict_factory,
|
||||
retain_collection_types=retain_collection_types,
|
||||
value_serializer=value_serializer,
|
||||
)
|
||||
elif isinstance(val, (tuple, list, set, frozenset)):
|
||||
if retain_collection_types is True:
|
||||
cf = val.__class__
|
||||
elif is_key:
|
||||
cf = tuple
|
||||
else:
|
||||
cf = list
|
||||
|
||||
rv = cf(
|
||||
[
|
||||
_asdict_anything(
|
||||
i, filter, dict_factory, retain_collection_types
|
||||
i,
|
||||
is_key=False,
|
||||
filter=filter,
|
||||
dict_factory=dict_factory,
|
||||
retain_collection_types=retain_collection_types,
|
||||
value_serializer=value_serializer,
|
||||
)
|
||||
for i in val
|
||||
]
|
||||
|
@ -103,13 +161,30 @@ def _asdict_anything(val, filter, dict_factory, retain_collection_types):
|
|||
df = dict_factory
|
||||
rv = df(
|
||||
(
|
||||
_asdict_anything(kk, filter, df, retain_collection_types),
|
||||
_asdict_anything(vv, filter, df, retain_collection_types),
|
||||
_asdict_anything(
|
||||
kk,
|
||||
is_key=True,
|
||||
filter=filter,
|
||||
dict_factory=df,
|
||||
retain_collection_types=retain_collection_types,
|
||||
value_serializer=value_serializer,
|
||||
),
|
||||
_asdict_anything(
|
||||
vv,
|
||||
is_key=False,
|
||||
filter=filter,
|
||||
dict_factory=df,
|
||||
retain_collection_types=retain_collection_types,
|
||||
value_serializer=value_serializer,
|
||||
),
|
||||
)
|
||||
for kk, vv in iteritems(val)
|
||||
for kk, vv in val.items()
|
||||
)
|
||||
else:
|
||||
rv = val
|
||||
if value_serializer is not None:
|
||||
rv = value_serializer(None, None, rv)
|
||||
|
||||
return rv
|
||||
|
||||
|
||||
|
@ -121,16 +196,16 @@ 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 `attr.Attribute` as the first argument and the
|
||||
called with the `attrs.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.
|
||||
|
@ -141,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
|
||||
|
@ -164,7 +239,7 @@ def astuple(
|
|||
retain_collection_types=retain,
|
||||
)
|
||||
)
|
||||
elif isinstance(v, (tuple, list, set)):
|
||||
elif isinstance(v, (tuple, list, set, frozenset)):
|
||||
cf = v.__class__ if retain is True else list
|
||||
rv.append(
|
||||
cf(
|
||||
|
@ -202,89 +277,201 @@ def astuple(
|
|||
if has(vv.__class__)
|
||||
else vv,
|
||||
)
|
||||
for kk, vv in iteritems(v)
|
||||
for kk, vv in v.items()
|
||||
)
|
||||
)
|
||||
else:
|
||||
rv.append(v)
|
||||
else:
|
||||
rv.append(v)
|
||||
|
||||
return rv if tuple_factory is list else tuple_factory(rv)
|
||||
|
||||
|
||||
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
|
||||
Use `evolve` instead.
|
||||
Use `attrs.evolve` instead if you can.
|
||||
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 iteritems(changes):
|
||||
for k, v in changes.items():
|
||||
a = getattr(attrs, k, NOTHING)
|
||||
if a is NOTHING:
|
||||
raise AttrsAttributeNotFoundError(
|
||||
"{k} is not an attrs attribute on {cl}.".format(
|
||||
k=k, cl=new.__class__
|
||||
)
|
||||
f"{k} is not an attrs attribute on {new.__class__}."
|
||||
)
|
||||
_obj_setattr(new, k, v)
|
||||
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
|
||||
.. 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:
|
||||
if not a.init:
|
||||
continue
|
||||
attr_name = a.name # To deal with private attributes.
|
||||
init_name = attr_name if attr_name[0] != "_" else attr_name[1:]
|
||||
init_name = a.alias
|
||||
if init_name not in changes:
|
||||
changes[init_name] = getattr(inst, attr_name)
|
||||
|
||||
return cls(**changes)
|
||||
|
||||
|
||||
def resolve_types(
|
||||
cls, globalns=None, localns=None, attribs=None, include_extras=True
|
||||
):
|
||||
"""
|
||||
Resolve any strings and forward annotations in type annotations.
|
||||
|
||||
This is only required if you need concrete types in `Attribute`'s *type*
|
||||
field. In other words, you don't need to resolve your types if you only
|
||||
use them for static type checking.
|
||||
|
||||
With no arguments, names will be looked up in the module in which the class
|
||||
was created. If this is not what you want, e.g. if the name only exists
|
||||
inside a method, you may pass *globalns* or *localns* to specify other
|
||||
dictionaries in which to look up these names. See the docs of
|
||||
`typing.get_type_hints` for more details.
|
||||
|
||||
:param type cls: Class to resolve.
|
||||
:param Optional[dict] globalns: Dictionary containing global variables.
|
||||
: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.
|
||||
: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 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.
|
||||
|
||||
:returns: *cls* so you can use this function also as a class decorator.
|
||||
Please note that you have to apply it **after** `attrs.define`. That
|
||||
means the decorator has to come in the line **before** `attrs.define`.
|
||||
|
||||
.. 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
|
||||
# done it already.
|
||||
if getattr(cls, "__attrs_types_resolved__", None) != cls:
|
||||
import typing
|
||||
|
||||
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.
|
||||
_obj_setattr(field, "type", hints[field.name])
|
||||
# We store the class we resolved so that subclasses know they haven't
|
||||
# been resolved.
|
||||
cls.__attrs_types_resolved__ = cls
|
||||
|
||||
# Return the class so you can use it as a decorator too.
|
||||
return cls
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,232 @@
|
|||
# SPDX-License-Identifier: MIT
|
||||
|
||||
"""
|
||||
These are keyword-only APIs that call `attr.s` and `attr.ib` with different
|
||||
default values.
|
||||
"""
|
||||
|
||||
|
||||
from functools import partial
|
||||
|
||||
from . import setters
|
||||
from ._funcs import asdict as _asdict
|
||||
from ._funcs import astuple as _astuple
|
||||
from ._make import (
|
||||
NOTHING,
|
||||
_frozen_setattrs,
|
||||
_ng_default_on_setattr,
|
||||
attrib,
|
||||
attrs,
|
||||
)
|
||||
from .exceptions import UnannotatedAttributeError
|
||||
|
||||
|
||||
def define(
|
||||
maybe_cls=None,
|
||||
*,
|
||||
these=None,
|
||||
repr=None,
|
||||
unsafe_hash=None,
|
||||
hash=None,
|
||||
init=None,
|
||||
slots=True,
|
||||
frozen=False,
|
||||
weakref_slot=True,
|
||||
str=False,
|
||||
auto_attribs=None,
|
||||
kw_only=False,
|
||||
cache_hash=False,
|
||||
auto_exc=True,
|
||||
eq=None,
|
||||
order=False,
|
||||
auto_detect=True,
|
||||
getstate_setstate=None,
|
||||
on_setattr=None,
|
||||
field_transformer=None,
|
||||
match_args=True,
|
||||
):
|
||||
r"""
|
||||
Define an *attrs* class.
|
||||
|
||||
Differences to the classic `attr.s` that it uses underneath:
|
||||
|
||||
- Automatically detect whether or not *auto_attribs* should be `True` (c.f.
|
||||
*auto_attribs* parameter).
|
||||
- If *frozen* is `False`, run converters and validators when setting an
|
||||
attribute by default.
|
||||
- *slots=True*
|
||||
|
||||
.. caution::
|
||||
|
||||
Usually this has only upsides and few visible effects in everyday
|
||||
programming. But it *can* lead to some suprising behaviors, so please
|
||||
make sure to read :term:`slotted classes`.
|
||||
- *auto_exc=True*
|
||||
- *auto_detect=True*
|
||||
- *order=False*
|
||||
- Some options that were only relevant on Python 2 or were kept around for
|
||||
backwards-compatibility have been removed.
|
||||
|
||||
Please note that these are all defaults and you can change them as you
|
||||
wish.
|
||||
|
||||
:param Optional[bool] auto_attribs: If set to `True` or `False`, it behaves
|
||||
exactly like `attr.s`. If left `None`, `attr.s` will try to guess:
|
||||
|
||||
1. If any attributes are annotated and no unannotated `attrs.fields`\ s
|
||||
are found, it assumes *auto_attribs=True*.
|
||||
2. Otherwise it assumes *auto_attribs=False* and tries to collect
|
||||
`attrs.fields`\ s.
|
||||
|
||||
For now, please refer to `attr.s` for the rest of the parameters.
|
||||
|
||||
.. versionadded:: 20.1.0
|
||||
.. versionchanged:: 21.3.0 Converters are also run ``on_setattr``.
|
||||
.. versionadded:: 22.2.0
|
||||
*unsafe_hash* as an alias for *hash* (for :pep:`681` compliance).
|
||||
"""
|
||||
|
||||
def do_it(cls, auto_attribs):
|
||||
return attrs(
|
||||
maybe_cls=cls,
|
||||
these=these,
|
||||
repr=repr,
|
||||
hash=hash,
|
||||
unsafe_hash=unsafe_hash,
|
||||
init=init,
|
||||
slots=slots,
|
||||
frozen=frozen,
|
||||
weakref_slot=weakref_slot,
|
||||
str=str,
|
||||
auto_attribs=auto_attribs,
|
||||
kw_only=kw_only,
|
||||
cache_hash=cache_hash,
|
||||
auto_exc=auto_exc,
|
||||
eq=eq,
|
||||
order=order,
|
||||
auto_detect=auto_detect,
|
||||
collect_by_mro=True,
|
||||
getstate_setstate=getstate_setstate,
|
||||
on_setattr=on_setattr,
|
||||
field_transformer=field_transformer,
|
||||
match_args=match_args,
|
||||
)
|
||||
|
||||
def wrap(cls):
|
||||
"""
|
||||
Making this a wrapper ensures this code runs during class creation.
|
||||
|
||||
We also ensure that frozen-ness of classes is inherited.
|
||||
"""
|
||||
nonlocal frozen, on_setattr
|
||||
|
||||
had_on_setattr = on_setattr not in (None, setters.NO_OP)
|
||||
|
||||
# By default, mutable classes convert & validate on setattr.
|
||||
if frozen is False and on_setattr is None:
|
||||
on_setattr = _ng_default_on_setattr
|
||||
|
||||
# However, if we subclass a frozen class, we inherit the immutability
|
||||
# and disable on_setattr.
|
||||
for base_cls in cls.__bases__:
|
||||
if base_cls.__setattr__ is _frozen_setattrs:
|
||||
if had_on_setattr:
|
||||
raise ValueError(
|
||||
"Frozen classes can't use on_setattr "
|
||||
"(frozen-ness was inherited)."
|
||||
)
|
||||
|
||||
on_setattr = setters.NO_OP
|
||||
break
|
||||
|
||||
if auto_attribs is not None:
|
||||
return do_it(cls, auto_attribs)
|
||||
|
||||
try:
|
||||
return do_it(cls, True)
|
||||
except UnannotatedAttributeError:
|
||||
return do_it(cls, False)
|
||||
|
||||
# maybe_cls's type depends on the usage of the decorator. It's a class
|
||||
# if it's used as `@attrs` but ``None`` if used as `@attrs()`.
|
||||
if maybe_cls is None:
|
||||
return wrap
|
||||
else:
|
||||
return wrap(maybe_cls)
|
||||
|
||||
|
||||
mutable = define
|
||||
frozen = partial(define, frozen=True, on_setattr=None)
|
||||
|
||||
|
||||
def field(
|
||||
*,
|
||||
default=NOTHING,
|
||||
validator=None,
|
||||
repr=True,
|
||||
hash=None,
|
||||
init=True,
|
||||
metadata=None,
|
||||
type=None,
|
||||
converter=None,
|
||||
factory=None,
|
||||
kw_only=False,
|
||||
eq=None,
|
||||
order=None,
|
||||
on_setattr=None,
|
||||
alias=None,
|
||||
):
|
||||
"""
|
||||
Identical to `attr.ib`, except keyword-only and with some arguments
|
||||
removed.
|
||||
|
||||
.. versionadded:: 23.1.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(
|
||||
default=default,
|
||||
validator=validator,
|
||||
repr=repr,
|
||||
hash=hash,
|
||||
init=init,
|
||||
metadata=metadata,
|
||||
type=type,
|
||||
converter=converter,
|
||||
factory=factory,
|
||||
kw_only=kw_only,
|
||||
eq=eq,
|
||||
order=order,
|
||||
on_setattr=on_setattr,
|
||||
alias=alias,
|
||||
)
|
||||
|
||||
|
||||
def asdict(inst, *, recurse=True, filter=None, value_serializer=None):
|
||||
"""
|
||||
Same as `attr.asdict`, except that collections types are always retained
|
||||
and dict is always used as *dict_factory*.
|
||||
|
||||
.. versionadded:: 21.3.0
|
||||
"""
|
||||
return _asdict(
|
||||
inst=inst,
|
||||
recurse=recurse,
|
||||
filter=filter,
|
||||
value_serializer=value_serializer,
|
||||
retain_collection_types=True,
|
||||
)
|
||||
|
||||
|
||||
def astuple(inst, *, recurse=True, filter=None):
|
||||
"""
|
||||
Same as `attr.astuple`, except that collections types are always retained
|
||||
and `tuple` is always used as the *tuple_factory*.
|
||||
|
||||
.. versionadded:: 21.3.0
|
||||
"""
|
||||
return _astuple(
|
||||
inst=inst, recurse=recurse, filter=filter, retain_collection_types=True
|
||||
)
|
|
@ -0,0 +1,15 @@
|
|||
from typing import Any, ClassVar, Protocol
|
||||
|
||||
# MYPY is a special constant in mypy which works the same way as `TYPE_CHECKING`.
|
||||
MYPY = False
|
||||
|
||||
if MYPY:
|
||||
# A protocol to be able to statically accept an attrs class.
|
||||
class AttrsInstance_(Protocol):
|
||||
__attrs_attrs__: ClassVar[Any]
|
||||
|
||||
else:
|
||||
# For type checkers without plug-in support use an empty protocol that
|
||||
# will (hopefully) be combined into a union.
|
||||
class AttrsInstance_(Protocol):
|
||||
pass
|
|
@ -1,4 +1,5 @@
|
|||
from __future__ import absolute_import, division, print_function
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
|
||||
from functools import total_ordering
|
||||
|
||||
|
@ -8,7 +9,7 @@ from ._make import attrib, attrs
|
|||
|
||||
@total_ordering
|
||||
@attrs(eq=False, order=False, slots=True, frozen=True)
|
||||
class VersionInfo(object):
|
||||
class VersionInfo:
|
||||
"""
|
||||
A version object that can be compared to tuple of length 1--4:
|
||||
|
|
@ -1,10 +1,22 @@
|
|||
# SPDX-License-Identifier: MIT
|
||||
|
||||
"""
|
||||
Commonly useful converters.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
from ._make import NOTHING, Factory
|
||||
import typing
|
||||
|
||||
from ._compat import _AnnotationExtractor
|
||||
from ._make import NOTHING, Factory, pipe
|
||||
|
||||
|
||||
__all__ = [
|
||||
"default_if_none",
|
||||
"optional",
|
||||
"pipe",
|
||||
"to_bool",
|
||||
]
|
||||
|
||||
|
||||
def optional(converter):
|
||||
|
@ -12,6 +24,9 @@ def optional(converter):
|
|||
A converter that allows an attribute to be optional. An optional attribute
|
||||
is one which can be set to ``None``.
|
||||
|
||||
Type annotations will be inferred from the wrapped converter's, if it
|
||||
has any.
|
||||
|
||||
:param callable converter: the converter that is used for non-``None``
|
||||
values.
|
||||
|
||||
|
@ -23,6 +38,16 @@ def optional(converter):
|
|||
return None
|
||||
return converter(val)
|
||||
|
||||
xtr = _AnnotationExtractor(converter)
|
||||
|
||||
t = xtr.get_first_param_type()
|
||||
if t:
|
||||
optional_converter.__annotations__["val"] = typing.Optional[t]
|
||||
|
||||
rt = xtr.get_return_type()
|
||||
if rt:
|
||||
optional_converter.__annotations__["return"] = typing.Optional[rt]
|
||||
|
||||
return optional_converter
|
||||
|
||||
|
||||
|
@ -32,14 +57,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 `attr.Factory` is supported, however the ``takes_self`` option
|
||||
of `attrs.Factory` is supported, however the ``takes_self`` option
|
||||
is *not*.
|
||||
:param callable factory: A callable that takes not parameters whose result
|
||||
:param callable factory: A callable that takes no 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 `attr.Factory` is passed with
|
||||
:raises ValueError: If an instance of `attrs.Factory` is passed with
|
||||
``takes_self=True``.
|
||||
|
||||
.. versionadded:: 18.2.0
|
||||
|
@ -76,3 +101,44 @@ def default_if_none(default=NOTHING, factory=None):
|
|||
return default
|
||||
|
||||
return default_if_none_converter
|
||||
|
||||
|
||||
def to_bool(val):
|
||||
"""
|
||||
Convert "boolean" strings (e.g., from env. vars.) to real booleans.
|
||||
|
||||
Values mapping to :code:`True`:
|
||||
|
||||
- :code:`True`
|
||||
- :code:`"true"` / :code:`"t"`
|
||||
- :code:`"yes"` / :code:`"y"`
|
||||
- :code:`"on"`
|
||||
- :code:`"1"`
|
||||
- :code:`1`
|
||||
|
||||
Values mapping to :code:`False`:
|
||||
|
||||
- :code:`False`
|
||||
- :code:`"false"` / :code:`"f"`
|
||||
- :code:`"no"` / :code:`"n"`
|
||||
- :code:`"off"`
|
||||
- :code:`"0"`
|
||||
- :code:`0`
|
||||
|
||||
:raises ValueError: for any other value.
|
||||
|
||||
.. versionadded:: 21.3.0
|
||||
"""
|
||||
if isinstance(val, str):
|
||||
val = val.lower()
|
||||
truthy = {True, "true", "t", "yes", "y", "on", "1", 1}
|
||||
falsy = {False, "false", "f", "no", "n", "off", "0", 0}
|
||||
try:
|
||||
if val in truthy:
|
||||
return True
|
||||
if val in falsy:
|
||||
return False
|
||||
except TypeError:
|
||||
# Raised when "val" is not hashable (e.g., lists)
|
||||
pass
|
||||
raise ValueError(f"Cannot convert value to bool: {val}")
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
from typing import TypeVar, Optional, Callable, overload
|
||||
from typing import Callable, TypeVar, overload
|
||||
|
||||
from . import _ConverterType
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
def optional(
|
||||
converter: _ConverterType[_T]
|
||||
) -> _ConverterType[Optional[_T]]: ...
|
||||
def pipe(*validators: _ConverterType) -> _ConverterType: ...
|
||||
def optional(converter: _ConverterType) -> _ConverterType: ...
|
||||
@overload
|
||||
def default_if_none(default: _T) -> _ConverterType[_T]: ...
|
||||
def default_if_none(default: _T) -> _ConverterType: ...
|
||||
@overload
|
||||
def default_if_none(*, factory: Callable[[], _T]) -> _ConverterType[_T]: ...
|
||||
def default_if_none(*, factory: Callable[[], _T]) -> _ConverterType: ...
|
||||
def to_bool(val: str) -> bool: ...
|
||||
|
|
|
@ -1,23 +1,40 @@
|
|||
from __future__ import absolute_import, division, print_function
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
|
||||
class FrozenInstanceError(AttributeError):
|
||||
class FrozenError(AttributeError):
|
||||
"""
|
||||
A frozen/immutable instance has been attempted to be modified.
|
||||
A frozen/immutable instance or attribute have been attempted to be
|
||||
modified.
|
||||
|
||||
It mirrors the behavior of ``namedtuples`` by using the same error message
|
||||
and subclassing `AttributeError`.
|
||||
|
||||
.. versionadded:: 16.1.0
|
||||
.. versionadded:: 20.1.0
|
||||
"""
|
||||
|
||||
msg = "can't set attribute"
|
||||
args = [msg]
|
||||
|
||||
|
||||
class FrozenInstanceError(FrozenError):
|
||||
"""
|
||||
A frozen instance has been attempted to be modified.
|
||||
|
||||
.. versionadded:: 16.1.0
|
||||
"""
|
||||
|
||||
|
||||
class FrozenAttributeError(FrozenError):
|
||||
"""
|
||||
A frozen attribute has been attempted to be modified.
|
||||
|
||||
.. versionadded:: 20.1.0
|
||||
"""
|
||||
|
||||
|
||||
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
|
||||
"""
|
||||
|
@ -25,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
|
||||
"""
|
||||
|
@ -33,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
|
||||
|
@ -42,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
|
||||
"""
|
||||
|
@ -51,7 +67,8 @@ class UnannotatedAttributeError(RuntimeError):
|
|||
|
||||
class PythonTooOldError(RuntimeError):
|
||||
"""
|
||||
An ``attrs`` feature requiring a more recent python version has been used.
|
||||
It was attempted to use an *attrs* feature that requires a newer Python
|
||||
version.
|
||||
|
||||
.. versionadded:: 18.2.0
|
||||
"""
|
||||
|
@ -59,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
|
||||
"""
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
from typing import Any
|
||||
|
||||
class FrozenInstanceError(AttributeError):
|
||||
class FrozenError(AttributeError):
|
||||
msg: str = ...
|
||||
|
||||
class FrozenInstanceError(FrozenError): ...
|
||||
class FrozenAttributeError(FrozenError): ...
|
||||
class AttrsAttributeNotFoundError(ValueError): ...
|
||||
class NotAnAttrsClassError(ValueError): ...
|
||||
class DefaultAlreadySetError(RuntimeError): ...
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
# SPDX-License-Identifier: MIT
|
||||
|
||||
"""
|
||||
Commonly useful filters for `attr.asdict`.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
from ._compat import isclass
|
||||
from ._make import Attribute
|
||||
|
||||
|
||||
|
@ -13,40 +12,55 @@ def _split_what(what):
|
|||
Returns a tuple of `frozenset`s of classes and attributes.
|
||||
"""
|
||||
return (
|
||||
frozenset(cls for cls in what if isclass(cls)),
|
||||
frozenset(cls for cls in what if isinstance(cls, type)),
|
||||
frozenset(cls for cls in what if isinstance(cls, str)),
|
||||
frozenset(cls for cls in what if isinstance(cls, Attribute)),
|
||||
)
|
||||
|
||||
|
||||
def include(*what):
|
||||
"""
|
||||
Whitelist *what*.
|
||||
Include *what*.
|
||||
|
||||
:param what: What to whitelist.
|
||||
:type what: `list` of `type` or `attr.Attribute`\\ s
|
||||
:param what: What to include.
|
||||
:type what: `list` of classes `type`, field names `str` or
|
||||
`attrs.Attribute`\\ s
|
||||
|
||||
:rtype: `callable`
|
||||
|
||||
.. versionchanged:: 23.1.0 Accept strings with field names.
|
||||
"""
|
||||
cls, attrs = _split_what(what)
|
||||
cls, names, attrs = _split_what(what)
|
||||
|
||||
def include_(attribute, value):
|
||||
return value.__class__ in cls or attribute in attrs
|
||||
return (
|
||||
value.__class__ in cls
|
||||
or attribute.name in names
|
||||
or attribute in attrs
|
||||
)
|
||||
|
||||
return include_
|
||||
|
||||
|
||||
def exclude(*what):
|
||||
"""
|
||||
Blacklist *what*.
|
||||
Exclude *what*.
|
||||
|
||||
:param what: What to blacklist.
|
||||
:type what: `list` of classes or `attr.Attribute`\\ s.
|
||||
:param what: What to exclude.
|
||||
:type what: `list` of classes `type`, field names `str` or
|
||||
`attrs.Attribute`\\ s.
|
||||
|
||||
:rtype: `callable`
|
||||
|
||||
.. versionchanged:: 23.3.0 Accept field name string as input argument
|
||||
"""
|
||||
cls, attrs = _split_what(what)
|
||||
cls, names, attrs = _split_what(what)
|
||||
|
||||
def exclude_(attribute, value):
|
||||
return value.__class__ not in cls and attribute not in attrs
|
||||
return not (
|
||||
value.__class__ in cls
|
||||
or attribute.name in names
|
||||
or attribute in attrs
|
||||
)
|
||||
|
||||
return exclude_
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from typing import Union, Any
|
||||
from typing import Any, Union
|
||||
|
||||
from . import Attribute, _FilterType
|
||||
|
||||
def include(*what: Union[type, Attribute[Any]]) -> _FilterType[Any]: ...
|
||||
def exclude(*what: Union[type, Attribute[Any]]) -> _FilterType[Any]: ...
|
||||
def include(*what: Union[type, str, Attribute[Any]]) -> _FilterType[Any]: ...
|
||||
def exclude(*what: Union[type, str, Attribute[Any]]) -> _FilterType[Any]: ...
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
# SPDX-License-Identifier: MIT
|
||||
|
||||
"""
|
||||
Commonly used hooks for on_setattr.
|
||||
"""
|
||||
|
||||
|
||||
from . import _config
|
||||
from .exceptions import FrozenAttributeError
|
||||
|
||||
|
||||
def pipe(*setters):
|
||||
"""
|
||||
Run all *setters* and return the return value of the last one.
|
||||
|
||||
.. versionadded:: 20.1.0
|
||||
"""
|
||||
|
||||
def wrapped_pipe(instance, attrib, new_value):
|
||||
rv = new_value
|
||||
|
||||
for setter in setters:
|
||||
rv = setter(instance, attrib, rv)
|
||||
|
||||
return rv
|
||||
|
||||
return wrapped_pipe
|
||||
|
||||
|
||||
def frozen(_, __, ___):
|
||||
"""
|
||||
Prevent an attribute to be modified.
|
||||
|
||||
.. versionadded:: 20.1.0
|
||||
"""
|
||||
raise FrozenAttributeError()
|
||||
|
||||
|
||||
def validate(instance, attrib, new_value):
|
||||
"""
|
||||
Run *attrib*'s validator on *new_value* if it has one.
|
||||
|
||||
.. versionadded:: 20.1.0
|
||||
"""
|
||||
if _config._run_validators is False:
|
||||
return new_value
|
||||
|
||||
v = attrib.validator
|
||||
if not v:
|
||||
return new_value
|
||||
|
||||
v(instance, attrib, new_value)
|
||||
|
||||
return new_value
|
||||
|
||||
|
||||
def convert(instance, attrib, new_value):
|
||||
"""
|
||||
Run *attrib*'s converter -- if it has one -- on *new_value* and return the
|
||||
result.
|
||||
|
||||
.. versionadded:: 20.1.0
|
||||
"""
|
||||
c = attrib.converter
|
||||
if c:
|
||||
return c(new_value)
|
||||
|
||||
return new_value
|
||||
|
||||
|
||||
# Sentinel for disabling class-wide *on_setattr* hooks for certain attributes.
|
||||
# autodata stopped working, so the docstring is inlined in the API docs.
|
||||
NO_OP = object()
|
|
@ -0,0 +1,19 @@
|
|||
from typing import Any, NewType, NoReturn, TypeVar
|
||||
|
||||
from . import Attribute, _OnSetAttrType
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
def frozen(
|
||||
instance: Any, attribute: Attribute[Any], new_value: Any
|
||||
) -> NoReturn: ...
|
||||
def pipe(*setters: _OnSetAttrType) -> _OnSetAttrType: ...
|
||||
def validate(instance: Any, attribute: Attribute[_T], new_value: _T) -> _T: ...
|
||||
|
||||
# convert is allowed to return Any, because they can be chained using pipe.
|
||||
def convert(
|
||||
instance: Any, attribute: Attribute[Any], new_value: Any
|
||||
) -> Any: ...
|
||||
|
||||
_NoOpType = NewType("_NoOpType", object)
|
||||
NO_OP: _NoOpType
|
|
@ -1,12 +1,19 @@
|
|||
# SPDX-License-Identifier: MIT
|
||||
|
||||
"""
|
||||
Commonly useful validators.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
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
|
||||
from .converters import default_if_none
|
||||
from .exceptions import NotCallableError
|
||||
|
||||
|
||||
|
@ -14,17 +21,75 @@ __all__ = [
|
|||
"and_",
|
||||
"deep_iterable",
|
||||
"deep_mapping",
|
||||
"disabled",
|
||||
"ge",
|
||||
"get_disabled",
|
||||
"gt",
|
||||
"in_",
|
||||
"instance_of",
|
||||
"is_callable",
|
||||
"le",
|
||||
"lt",
|
||||
"matches_re",
|
||||
"max_len",
|
||||
"min_len",
|
||||
"not_",
|
||||
"optional",
|
||||
"provides",
|
||||
"set_disabled",
|
||||
]
|
||||
|
||||
|
||||
def set_disabled(disabled):
|
||||
"""
|
||||
Globally disable or enable running validators.
|
||||
|
||||
By default, they are run.
|
||||
|
||||
:param disabled: If ``True``, disable running all validators.
|
||||
:type disabled: bool
|
||||
|
||||
.. warning::
|
||||
|
||||
This function is not thread-safe!
|
||||
|
||||
.. versionadded:: 21.3.0
|
||||
"""
|
||||
set_run_validators(not disabled)
|
||||
|
||||
|
||||
def get_disabled():
|
||||
"""
|
||||
Return a bool indicating whether validators are currently disabled or not.
|
||||
|
||||
:return: ``True`` if validators are currently disabled.
|
||||
:rtype: bool
|
||||
|
||||
.. versionadded:: 21.3.0
|
||||
"""
|
||||
return not get_run_validators()
|
||||
|
||||
|
||||
@contextmanager
|
||||
def disabled():
|
||||
"""
|
||||
Context manager that disables running validators within its context.
|
||||
|
||||
.. warning::
|
||||
|
||||
This context manager is not thread-safe!
|
||||
|
||||
.. versionadded:: 21.3.0
|
||||
"""
|
||||
set_run_validators(False)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
set_run_validators(True)
|
||||
|
||||
|
||||
@attrs(repr=False, slots=True, hash=True)
|
||||
class _InstanceOfValidator(object):
|
||||
class _InstanceOfValidator:
|
||||
type = attrib()
|
||||
|
||||
def __call__(self, inst, attr, value):
|
||||
|
@ -58,19 +123,18 @@ def instance_of(type):
|
|||
`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
|
||||
:type type: type or tuple of type
|
||||
|
||||
:raises TypeError: With a human readable error message, the attribute
|
||||
(of type `attr.Attribute`), the expected type, and the value it
|
||||
(of type `attrs.Attribute`), the expected type, and the value it
|
||||
got.
|
||||
"""
|
||||
return _InstanceOfValidator(type)
|
||||
|
||||
|
||||
@attrs(repr=False, frozen=True)
|
||||
class _MatchesReValidator(object):
|
||||
regex = attrib()
|
||||
flags = attrib()
|
||||
@attrs(repr=False, frozen=True, slots=True)
|
||||
class _MatchesReValidator:
|
||||
pattern = attrib()
|
||||
match_func = attrib()
|
||||
|
||||
def __call__(self, inst, attr, value):
|
||||
|
@ -79,18 +143,18 @@ class _MatchesReValidator(object):
|
|||
"""
|
||||
if not self.match_func(value):
|
||||
raise ValueError(
|
||||
"'{name}' must match regex {regex!r}"
|
||||
"'{name}' must match regex {pattern!r}"
|
||||
" ({value!r} doesn't)".format(
|
||||
name=attr.name, regex=self.regex.pattern, value=value
|
||||
name=attr.name, pattern=self.pattern.pattern, value=value
|
||||
),
|
||||
attr,
|
||||
self.regex,
|
||||
self.pattern,
|
||||
value,
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return "<matches_re validator for pattern {regex!r}>".format(
|
||||
regex=self.regex
|
||||
return "<matches_re validator for pattern {pattern!r}>".format(
|
||||
pattern=self.pattern
|
||||
)
|
||||
|
||||
|
||||
|
@ -99,48 +163,51 @@ def matches_re(regex, flags=0, func=None):
|
|||
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 regex: a regex string or precompiled pattern 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.
|
||||
:param callable func: which underlying `re` function to call. Valid options
|
||||
are `re.fullmatch`, `re.search`, and `re.match`; the default ``None``
|
||||
means `re.fullmatch`. For performance reasons, the pattern is always
|
||||
precompiled using `re.compile`.
|
||||
|
||||
.. versionadded:: 19.2.0
|
||||
.. versionchanged:: 21.3.0 *regex* can be a pre-compiled pattern.
|
||||
"""
|
||||
fullmatch = getattr(re, "fullmatch", None)
|
||||
valid_funcs = (fullmatch, None, re.search, re.match)
|
||||
valid_funcs = (re.fullmatch, None, re.search, re.match)
|
||||
if func not in valid_funcs:
|
||||
raise ValueError(
|
||||
"'func' must be one of %s."
|
||||
% (
|
||||
"'func' must be one of {}.".format(
|
||||
", ".join(
|
||||
sorted(
|
||||
e and e.__name__ or "None" for e in set(valid_funcs)
|
||||
)
|
||||
),
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
pattern = re.compile(regex, flags)
|
||||
if isinstance(regex, Pattern):
|
||||
if flags:
|
||||
raise TypeError(
|
||||
"'flags' can only be used with a string pattern; "
|
||||
"pass flags to re.compile() instead"
|
||||
)
|
||||
pattern = regex
|
||||
else:
|
||||
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
|
||||
match_func = pattern.fullmatch
|
||||
|
||||
return _MatchesReValidator(pattern, flags, match_func)
|
||||
return _MatchesReValidator(pattern, match_func)
|
||||
|
||||
|
||||
@attrs(repr=False, slots=True, hash=True)
|
||||
class _ProvidesValidator(object):
|
||||
class _ProvidesValidator:
|
||||
interface = attrib()
|
||||
|
||||
def __call__(self, inst, attr, value):
|
||||
|
@ -171,17 +238,28 @@ def provides(interface):
|
|||
performed using ``interface.providedBy(value)`` (see `zope.interface
|
||||
<https://zopeinterface.readthedocs.io/en/latest/>`_).
|
||||
|
||||
:param zope.interface.Interface interface: The interface to check for.
|
||||
:param interface: The interface to check for.
|
||||
:type interface: ``zope.interface.Interface``
|
||||
|
||||
:raises TypeError: With a human readable error message, the attribute
|
||||
(of type `attr.Attribute`), the expected interface, and the
|
||||
(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)
|
||||
|
||||
|
||||
@attrs(repr=False, slots=True, hash=True)
|
||||
class _OptionalValidator(object):
|
||||
class _OptionalValidator:
|
||||
validator = attrib()
|
||||
|
||||
def __call__(self, inst, attr, value):
|
||||
|
@ -202,20 +280,21 @@ 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)
|
||||
|
||||
|
||||
@attrs(repr=False, slots=True, hash=True)
|
||||
class _InValidator(object):
|
||||
class _InValidator:
|
||||
options = attrib()
|
||||
|
||||
def __call__(self, inst, attr, value):
|
||||
|
@ -228,7 +307,10 @@ class _InValidator(object):
|
|||
raise ValueError(
|
||||
"'{name}' must be in {options!r} (got {value!r})".format(
|
||||
name=attr.name, options=self.options, value=value
|
||||
)
|
||||
),
|
||||
attr,
|
||||
self.options,
|
||||
value,
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
|
@ -247,16 +329,20 @@ def in_(options):
|
|||
:type options: list, tuple, `enum.Enum`, ...
|
||||
|
||||
:raises ValueError: With a human readable error message, the attribute (of
|
||||
type `attr.Attribute`), the expected options, and the value it
|
||||
type `attrs.Attribute`), the expected options, and the value it
|
||||
got.
|
||||
|
||||
.. versionadded:: 17.1.0
|
||||
.. versionchanged:: 22.1.0
|
||||
The ValueError was incomplete until now and only contained the human
|
||||
readable error message. Now it contains all the information that has
|
||||
been promised since 17.1.0.
|
||||
"""
|
||||
return _InValidator(options)
|
||||
|
||||
|
||||
@attrs(repr=False, slots=False, hash=True)
|
||||
class _IsCallableValidator(object):
|
||||
class _IsCallableValidator:
|
||||
def __call__(self, inst, attr, value):
|
||||
"""
|
||||
We use a callable class to be able to change the ``__repr__``.
|
||||
|
@ -279,21 +365,21 @@ class _IsCallableValidator(object):
|
|||
|
||||
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
|
||||
message containing the attribute (`attr.Attribute`) name,
|
||||
:raises attrs.exceptions.NotCallableError: With a human readable error
|
||||
message containing the attribute (`attrs.Attribute`) name,
|
||||
and the value it got.
|
||||
"""
|
||||
return _IsCallableValidator()
|
||||
|
||||
|
||||
@attrs(repr=False, slots=True, hash=True)
|
||||
class _DeepIterable(object):
|
||||
class _DeepIterable:
|
||||
member_validator = attrib(validator=is_callable())
|
||||
iterable_validator = attrib(
|
||||
default=None, validator=optional(is_callable())
|
||||
|
@ -313,7 +399,7 @@ class _DeepIterable(object):
|
|||
iterable_identifier = (
|
||||
""
|
||||
if self.iterable_validator is None
|
||||
else " {iterable!r}".format(iterable=self.iterable_validator)
|
||||
else f" {self.iterable_validator!r}"
|
||||
)
|
||||
return (
|
||||
"<deep_iterable validator for{iterable_identifier}"
|
||||
|
@ -328,7 +414,7 @@ def deep_iterable(member_validator, iterable_validator=None):
|
|||
"""
|
||||
A validator that performs deep validation of an iterable.
|
||||
|
||||
:param member_validator: Validator to apply to iterable members
|
||||
:param member_validator: Validator(s) to apply to iterable members
|
||||
:param iterable_validator: Validator to apply to iterable itself
|
||||
(optional)
|
||||
|
||||
|
@ -336,11 +422,13 @@ def deep_iterable(member_validator, iterable_validator=None):
|
|||
|
||||
:raises TypeError: if any sub-validators fail
|
||||
"""
|
||||
if isinstance(member_validator, (list, tuple)):
|
||||
member_validator = and_(*member_validator)
|
||||
return _DeepIterable(member_validator, iterable_validator)
|
||||
|
||||
|
||||
@attrs(repr=False, slots=True, hash=True)
|
||||
class _DeepMapping(object):
|
||||
class _DeepMapping:
|
||||
key_validator = attrib(validator=is_callable())
|
||||
value_validator = attrib(validator=is_callable())
|
||||
mapping_validator = attrib(default=None, validator=optional(is_callable()))
|
||||
|
@ -376,3 +464,257 @@ def deep_mapping(key_validator, value_validator, mapping_validator=None):
|
|||
:raises TypeError: if any sub-validators fail
|
||||
"""
|
||||
return _DeepMapping(key_validator, value_validator, mapping_validator)
|
||||
|
||||
|
||||
@attrs(repr=False, frozen=True, slots=True)
|
||||
class _NumberValidator:
|
||||
bound = attrib()
|
||||
compare_op = attrib()
|
||||
compare_func = attrib()
|
||||
|
||||
def __call__(self, inst, attr, value):
|
||||
"""
|
||||
We use a callable class to be able to change the ``__repr__``.
|
||||
"""
|
||||
if not self.compare_func(value, self.bound):
|
||||
raise ValueError(
|
||||
"'{name}' must be {op} {bound}: {value}".format(
|
||||
name=attr.name,
|
||||
op=self.compare_op,
|
||||
bound=self.bound,
|
||||
value=value,
|
||||
)
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return "<Validator for x {op} {bound}>".format(
|
||||
op=self.compare_op, bound=self.bound
|
||||
)
|
||||
|
||||
|
||||
def lt(val):
|
||||
"""
|
||||
A validator that raises `ValueError` if the initializer is called
|
||||
with a number larger or equal to *val*.
|
||||
|
||||
:param val: Exclusive upper bound for values
|
||||
|
||||
.. versionadded:: 21.3.0
|
||||
"""
|
||||
return _NumberValidator(val, "<", operator.lt)
|
||||
|
||||
|
||||
def le(val):
|
||||
"""
|
||||
A validator that raises `ValueError` if the initializer is called
|
||||
with a number greater than *val*.
|
||||
|
||||
:param val: Inclusive upper bound for values
|
||||
|
||||
.. versionadded:: 21.3.0
|
||||
"""
|
||||
return _NumberValidator(val, "<=", operator.le)
|
||||
|
||||
|
||||
def ge(val):
|
||||
"""
|
||||
A validator that raises `ValueError` if the initializer is called
|
||||
with a number smaller than *val*.
|
||||
|
||||
:param val: Inclusive lower bound for values
|
||||
|
||||
.. versionadded:: 21.3.0
|
||||
"""
|
||||
return _NumberValidator(val, ">=", operator.ge)
|
||||
|
||||
|
||||
def gt(val):
|
||||
"""
|
||||
A validator that raises `ValueError` if the initializer is called
|
||||
with a number smaller or equal to *val*.
|
||||
|
||||
:param val: Exclusive lower bound for values
|
||||
|
||||
.. versionadded:: 21.3.0
|
||||
"""
|
||||
return _NumberValidator(val, ">", operator.gt)
|
||||
|
||||
|
||||
@attrs(repr=False, frozen=True, slots=True)
|
||||
class _MaxLengthValidator:
|
||||
max_length = attrib()
|
||||
|
||||
def __call__(self, inst, attr, value):
|
||||
"""
|
||||
We use a callable class to be able to change the ``__repr__``.
|
||||
"""
|
||||
if len(value) > self.max_length:
|
||||
raise ValueError(
|
||||
"Length of '{name}' must be <= {max}: {len}".format(
|
||||
name=attr.name, max=self.max_length, len=len(value)
|
||||
)
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<max_len validator for {self.max_length}>"
|
||||
|
||||
|
||||
def max_len(length):
|
||||
"""
|
||||
A validator that raises `ValueError` if the initializer is called
|
||||
with a string or iterable that is longer than *length*.
|
||||
|
||||
:param int length: Maximum length of the string or iterable
|
||||
|
||||
.. versionadded:: 21.3.0
|
||||
"""
|
||||
return _MaxLengthValidator(length)
|
||||
|
||||
|
||||
@attrs(repr=False, frozen=True, slots=True)
|
||||
class _MinLengthValidator:
|
||||
min_length = attrib()
|
||||
|
||||
def __call__(self, inst, attr, value):
|
||||
"""
|
||||
We use a callable class to be able to change the ``__repr__``.
|
||||
"""
|
||||
if len(value) < self.min_length:
|
||||
raise ValueError(
|
||||
"Length of '{name}' must be => {min}: {len}".format(
|
||||
name=attr.name, min=self.min_length, len=len(value)
|
||||
)
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return f"<min_len validator for {self.min_length}>"
|
||||
|
||||
|
||||
def min_len(length):
|
||||
"""
|
||||
A validator that raises `ValueError` if the initializer is called
|
||||
with a string or iterable that is shorter than *length*.
|
||||
|
||||
:param int length: Minimum length of the string or iterable
|
||||
|
||||
.. versionadded:: 22.1.0
|
||||
"""
|
||||
return _MinLengthValidator(length)
|
||||
|
||||
|
||||
@attrs(repr=False, slots=True, hash=True)
|
||||
class _SubclassOfValidator:
|
||||
type = attrib()
|
||||
|
||||
def __call__(self, inst, attr, value):
|
||||
"""
|
||||
We use a callable class to be able to change the ``__repr__``.
|
||||
"""
|
||||
if not issubclass(value, self.type):
|
||||
raise TypeError(
|
||||
"'{name}' must be a subclass of {type!r} "
|
||||
"(got {value!r}).".format(
|
||||
name=attr.name,
|
||||
type=self.type,
|
||||
value=value,
|
||||
),
|
||||
attr,
|
||||
self.type,
|
||||
value,
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return "<subclass_of validator for type {type!r}>".format(
|
||||
type=self.type
|
||||
)
|
||||
|
||||
|
||||
def _subclass_of(type):
|
||||
"""
|
||||
A validator that raises a `TypeError` if the initializer is called
|
||||
with a wrong type for this particular attribute (checks are performed using
|
||||
`issubclass` 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 `attrs.Attribute`), the expected type, and the value it
|
||||
got.
|
||||
"""
|
||||
return _SubclassOfValidator(type)
|
||||
|
||||
|
||||
@attrs(repr=False, slots=True, hash=True)
|
||||
class _NotValidator:
|
||||
validator = attrib()
|
||||
msg = attrib(
|
||||
converter=default_if_none(
|
||||
"not_ validator child '{validator!r}' "
|
||||
"did not raise a captured error"
|
||||
)
|
||||
)
|
||||
exc_types = attrib(
|
||||
validator=deep_iterable(
|
||||
member_validator=_subclass_of(Exception),
|
||||
iterable_validator=instance_of(tuple),
|
||||
),
|
||||
)
|
||||
|
||||
def __call__(self, inst, attr, value):
|
||||
try:
|
||||
self.validator(inst, attr, value)
|
||||
except self.exc_types:
|
||||
pass # suppress error to invert validity
|
||||
else:
|
||||
raise ValueError(
|
||||
self.msg.format(
|
||||
validator=self.validator,
|
||||
exc_types=self.exc_types,
|
||||
),
|
||||
attr,
|
||||
self.validator,
|
||||
value,
|
||||
self.exc_types,
|
||||
)
|
||||
|
||||
def __repr__(self):
|
||||
return (
|
||||
"<not_ validator wrapping {what!r}, " "capturing {exc_types!r}>"
|
||||
).format(
|
||||
what=self.validator,
|
||||
exc_types=self.exc_types,
|
||||
)
|
||||
|
||||
|
||||
def not_(validator, *, msg=None, exc_types=(ValueError, TypeError)):
|
||||
"""
|
||||
A validator that wraps and logically 'inverts' the validator passed to it.
|
||||
It will raise a `ValueError` if the provided validator *doesn't* raise a
|
||||
`ValueError` or `TypeError` (by default), and will suppress the exception
|
||||
if the provided validator *does*.
|
||||
|
||||
Intended to be used with existing validators to compose logic without
|
||||
needing to create inverted variants, for example, ``not_(in_(...))``.
|
||||
|
||||
:param validator: A validator to be logically inverted.
|
||||
:param msg: Message to raise if validator fails.
|
||||
Formatted with keys ``exc_types`` and ``validator``.
|
||||
:type msg: str
|
||||
:param exc_types: Exception type(s) to capture.
|
||||
Other types raised by child validators will not be intercepted and
|
||||
pass through.
|
||||
|
||||
:raises ValueError: With a human readable error message,
|
||||
the attribute (of type `attrs.Attribute`),
|
||||
the validator that failed to raise an exception,
|
||||
the value it got,
|
||||
and the expected exception types.
|
||||
|
||||
.. versionadded:: 22.2.0
|
||||
"""
|
||||
try:
|
||||
exc_types = tuple(exc_types)
|
||||
except TypeError:
|
||||
exc_types = (exc_types,)
|
||||
return _NotValidator(validator, msg, exc_types)
|
||||
|
|
|
@ -1,20 +1,24 @@
|
|||
from typing import (
|
||||
Container,
|
||||
List,
|
||||
Union,
|
||||
TypeVar,
|
||||
Type,
|
||||
Any,
|
||||
Optional,
|
||||
Tuple,
|
||||
Iterable,
|
||||
Mapping,
|
||||
Callable,
|
||||
Match,
|
||||
AnyStr,
|
||||
Callable,
|
||||
Container,
|
||||
ContextManager,
|
||||
Iterable,
|
||||
List,
|
||||
Mapping,
|
||||
Match,
|
||||
Optional,
|
||||
Pattern,
|
||||
Tuple,
|
||||
Type,
|
||||
TypeVar,
|
||||
Union,
|
||||
overload,
|
||||
)
|
||||
|
||||
from . import _ValidatorType
|
||||
from . import _ValidatorArgType
|
||||
|
||||
_T = TypeVar("_T")
|
||||
_T1 = TypeVar("_T1")
|
||||
|
@ -25,6 +29,10 @@ _K = TypeVar("_K")
|
|||
_V = TypeVar("_V")
|
||||
_M = TypeVar("_M", bound=Mapping)
|
||||
|
||||
def set_disabled(run: bool) -> None: ...
|
||||
def get_disabled() -> bool: ...
|
||||
def disabled() -> ContextManager[None]: ...
|
||||
|
||||
# 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
|
||||
|
@ -43,19 +51,21 @@ 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]: ...
|
||||
def matches_re(
|
||||
regex: AnyStr,
|
||||
regex: Union[Pattern[AnyStr], AnyStr],
|
||||
flags: int = ...,
|
||||
func: Optional[
|
||||
Callable[[AnyStr, AnyStr, int], Optional[Match[AnyStr]]]
|
||||
] = ...,
|
||||
) -> _ValidatorType[AnyStr]: ...
|
||||
def deep_iterable(
|
||||
member_validator: _ValidatorType[_T],
|
||||
member_validator: _ValidatorArgType[_T],
|
||||
iterable_validator: Optional[_ValidatorType[_I]] = ...,
|
||||
) -> _ValidatorType[_I]: ...
|
||||
def deep_mapping(
|
||||
|
@ -64,3 +74,15 @@ def deep_mapping(
|
|||
mapping_validator: Optional[_ValidatorType[_M]] = ...,
|
||||
) -> _ValidatorType[_M]: ...
|
||||
def is_callable() -> _ValidatorType[_T]: ...
|
||||
def lt(val: _T) -> _ValidatorType[_T]: ...
|
||||
def le(val: _T) -> _ValidatorType[_T]: ...
|
||||
def ge(val: _T) -> _ValidatorType[_T]: ...
|
||||
def gt(val: _T) -> _ValidatorType[_T]: ...
|
||||
def max_len(length: int) -> _ValidatorType[_T]: ...
|
||||
def min_len(length: int) -> _ValidatorType[_T]: ...
|
||||
def not_(
|
||||
validator: _ValidatorType[_T],
|
||||
*,
|
||||
msg: Optional[str] = None,
|
||||
exc_types: Union[Type[Exception], Iterable[Type[Exception]]] = ...,
|
||||
) -> _ValidatorType[_T]: ...
|
||||
|
|
|
@ -1,284 +0,0 @@
|
|||
Metadata-Version: 2.1
|
||||
Name: attrs
|
||||
Version: 19.2.0
|
||||
Summary: Classes Without Boilerplate
|
||||
Home-page: https://www.attrs.org/
|
||||
Author: Hynek Schlawack
|
||||
Author-email: hs@ox.cx
|
||||
Maintainer: Hynek Schlawack
|
||||
Maintainer-email: hs@ox.cx
|
||||
License: MIT
|
||||
Project-URL: Documentation, https://www.attrs.org/
|
||||
Project-URL: Bug Tracker, https://github.com/python-attrs/attrs/issues
|
||||
Project-URL: Source Code, https://github.com/python-attrs/attrs
|
||||
Keywords: class,attribute,boilerplate
|
||||
Platform: UNKNOWN
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: Natural Language :: English
|
||||
Classifier: License :: OSI Approved :: MIT License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 2
|
||||
Classifier: Programming Language :: Python :: 2.7
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.4
|
||||
Classifier: Programming Language :: Python :: 3.5
|
||||
Classifier: Programming Language :: Python :: 3.6
|
||||
Classifier: Programming Language :: Python :: 3.7
|
||||
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 (>=4.3.0) ; extra == 'dev'
|
||||
Requires-Dist: six ; extra == 'dev'
|
||||
Requires-Dist: zope.interface ; extra == 'dev'
|
||||
Requires-Dist: sphinx ; extra == 'dev'
|
||||
Requires-Dist: pre-commit ; extra == 'dev'
|
||||
Provides-Extra: docs
|
||||
Requires-Dist: sphinx ; extra == 'docs'
|
||||
Requires-Dist: zope.interface ; extra == 'docs'
|
||||
Provides-Extra: tests
|
||||
Requires-Dist: coverage ; extra == 'tests'
|
||||
Requires-Dist: hypothesis ; extra == 'tests'
|
||||
Requires-Dist: pympler ; extra == 'tests'
|
||||
Requires-Dist: pytest (>=4.3.0) ; extra == 'tests'
|
||||
Requires-Dist: six ; extra == 'tests'
|
||||
Requires-Dist: zope.interface ; extra == 'tests'
|
||||
|
||||
.. image:: https://www.attrs.org/en/latest/_static/attrs_logo.png
|
||||
:alt: attrs Logo
|
||||
|
||||
======================================
|
||||
``attrs``: Classes Without Boilerplate
|
||||
======================================
|
||||
|
||||
.. image:: https://readthedocs.org/projects/attrs/badge/?version=stable
|
||||
:target: https://www.attrs.org/en/stable/?badge=stable
|
||||
:alt: Documentation Status
|
||||
|
||||
.. 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
|
||||
:target: https://codecov.io/github/python-attrs/attrs
|
||||
:alt: Test Coverage
|
||||
|
||||
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
|
||||
:target: https://github.com/psf/black
|
||||
:alt: Code style: black
|
||||
|
||||
.. teaser-begin
|
||||
|
||||
``attrs`` is the Python package that will bring back the **joy** of **writing classes** by relieving you from the drudgery of implementing object protocols (aka `dunder <https://nedbatchelder.com/blog/200605/dunder.html>`_ methods).
|
||||
|
||||
Its main goal is to help you to write **concise** and **correct** software without slowing down your code.
|
||||
|
||||
.. -spiel-end-
|
||||
|
||||
For that, it gives you a class decorator and a way to declaratively define the attributes on that class:
|
||||
|
||||
.. -code-begin-
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import attr
|
||||
|
||||
>>> @attr.s
|
||||
... class SomeClass(object):
|
||||
... a_number = attr.ib(default=42)
|
||||
... list_of_numbers = attr.ib(factory=list)
|
||||
...
|
||||
... def hard_math(self, another_number):
|
||||
... return self.a_number + sum(self.list_of_numbers) * another_number
|
||||
|
||||
|
||||
>>> sc = SomeClass(1, [1, 2, 3])
|
||||
>>> sc
|
||||
SomeClass(a_number=1, list_of_numbers=[1, 2, 3])
|
||||
|
||||
>>> sc.hard_math(3)
|
||||
19
|
||||
>>> sc == SomeClass(1, [1, 2, 3])
|
||||
True
|
||||
>>> sc != SomeClass(2, [3, 2, 1])
|
||||
True
|
||||
|
||||
>>> attr.asdict(sc)
|
||||
{'a_number': 1, 'list_of_numbers': [1, 2, 3]}
|
||||
|
||||
>>> SomeClass()
|
||||
SomeClass(a_number=42, list_of_numbers=[])
|
||||
|
||||
>>> C = attr.make_class("C", ["a", "b"])
|
||||
>>> C("foo", "bar")
|
||||
C(a='foo', b='bar')
|
||||
|
||||
|
||||
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 (equality and ordering),
|
||||
- an initializer,
|
||||
- and much more,
|
||||
|
||||
*without* writing dull boilerplate code again and again and *without* runtime performance penalties.
|
||||
|
||||
On Python 3.6 and later, you can often even drop the calls to ``attr.ib()`` by using `type annotations <https://www.attrs.org/en/latest/types.html>`_.
|
||||
|
||||
This gives you the power to use actual classes with actual types in your code instead of confusing ``tuple``\ s or `confusingly behaving <https://www.attrs.org/en/stable/why.html#namedtuples>`_ ``namedtuple``\ s.
|
||||
Which in turn encourages you to write *small classes* that do `one thing well <https://www.destroyallsoftware.com/talks/boundaries>`_.
|
||||
Never again violate the `single responsibility principle <https://en.wikipedia.org/wiki/Single_responsibility_principle>`_ just because implementing ``__init__`` et al is a painful drag.
|
||||
|
||||
|
||||
.. -testimonials-
|
||||
|
||||
Testimonials
|
||||
============
|
||||
|
||||
**Amber Hawkie Brown**, Twisted Release Manager and Computer Owl:
|
||||
|
||||
Writing a fully-functional class using attrs takes me less time than writing this testimonial.
|
||||
|
||||
|
||||
**Glyph Lefkowitz**, creator of `Twisted <https://twistedmatrix.com/>`_, `Automat <https://pypi.org/project/Automat/>`_, and other open source software, in `The One Python Library Everyone Needs <https://glyph.twistedmatrix.com/2016/08/attrs.html>`_:
|
||||
|
||||
I’m looking forward to is being able to program in Python-with-attrs everywhere.
|
||||
It exerts a subtle, but positive, design influence in all the codebases I’ve see it used in.
|
||||
|
||||
|
||||
**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**, 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!
|
||||
|
||||
|
||||
.. -end-
|
||||
|
||||
.. -project-information-
|
||||
|
||||
Getting Help
|
||||
============
|
||||
|
||||
Please use the ``python-attrs`` tag on `StackOverflow <https://stackoverflow.com/questions/tagged/python-attrs>`_ to get help.
|
||||
|
||||
Answering questions of your fellow developers is also great way to help the project!
|
||||
|
||||
|
||||
Project Information
|
||||
===================
|
||||
|
||||
``attrs`` is released under the `MIT <https://choosealicense.com/licenses/mit/>`_ license,
|
||||
its documentation lives at `Read the Docs <https://www.attrs.org/>`_,
|
||||
the code on `GitHub <https://github.com/python-attrs/attrs>`_,
|
||||
and the latest release on `PyPI <https://pypi.org/project/attrs/>`_.
|
||||
It’s rigorously tested on Python 2.7, 3.4+, and PyPy.
|
||||
|
||||
We collect information on **third-party extensions** in our `wiki <https://github.com/python-attrs/attrs/wiki/Extensions-to-attrs>`_.
|
||||
Feel free to browse and add your own!
|
||||
|
||||
If you'd like to contribute to ``attrs`` you're most welcome and we've written `a little guide <https://www.attrs.org/en/latest/contributing.html>`_ to get you started!
|
||||
|
||||
|
||||
Release Information
|
||||
===================
|
||||
|
||||
19.2.0 (2019-10-01)
|
||||
-------------------
|
||||
|
||||
Backward-incompatible Changes
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- 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
|
||||
^^^^^^^
|
||||
|
||||
- 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>`_
|
||||
|
||||
.. _`#425`: https://github.com/python-attrs/attrs/issues/425
|
||||
|
||||
`Full changelog <https://www.attrs.org/en/stable/changelog.html>`_.
|
||||
|
||||
Credits
|
||||
=======
|
||||
|
||||
``attrs`` is written and maintained by `Hynek Schlawack <https://hynek.me/>`_.
|
||||
|
||||
The development is kindly supported by `Variomedia AG <https://www.variomedia.de/>`_.
|
||||
|
||||
A full list of contributors can be found in `GitHub's overview <https://github.com/python-attrs/attrs/graphs/contributors>`_.
|
||||
|
||||
It’s the spiritual successor of `characteristic <https://characteristic.readthedocs.io/>`_ and aspires to fix some of it clunkiness and unfortunate decisions.
|
||||
Both were inspired by Twisted’s `FancyEqMixin <https://twistedmatrix.com/documents/current/api/twisted.python.util.FancyEqMixin.html>`_ but both are implemented using class decorators because `subclassing is bad for you <https://www.youtube.com/watch?v=3MNVP9-hglc>`_, m’kay?
|
||||
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
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 +0,0 @@
|
|||
attr
|
|
@ -0,0 +1,243 @@
|
|||
Metadata-Version: 2.1
|
||||
Name: attrs
|
||||
Version: 23.1.0
|
||||
Summary: Classes Without Boilerplate
|
||||
Project-URL: Documentation, https://www.attrs.org/
|
||||
Project-URL: Changelog, https://www.attrs.org/en/stable/changelog.html
|
||||
Project-URL: Bug Tracker, https://github.com/python-attrs/attrs/issues
|
||||
Project-URL: Source Code, https://github.com/python-attrs/attrs
|
||||
Project-URL: Funding, https://github.com/sponsors/hynek
|
||||
Project-URL: Tidelift, https://tidelift.com/subscription/pkg/pypi-attrs?utm_source=pypi-attrs&utm_medium=pypi
|
||||
Author-email: Hynek Schlawack <hs@ox.cx>
|
||||
License-Expression: MIT
|
||||
License-File: LICENSE
|
||||
Keywords: attribute,boilerplate,class
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: MIT License
|
||||
Classifier: Programming Language :: Python :: 3.7
|
||||
Classifier: Programming Language :: Python :: 3.8
|
||||
Classifier: Programming Language :: Python :: 3.9
|
||||
Classifier: Programming Language :: Python :: 3.10
|
||||
Classifier: Programming Language :: Python :: 3.11
|
||||
Classifier: Programming Language :: Python :: Implementation :: CPython
|
||||
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
||||
Classifier: Typing :: Typed
|
||||
Requires-Python: >=3.7
|
||||
Requires-Dist: importlib-metadata; python_version < '3.8'
|
||||
Provides-Extra: cov
|
||||
Requires-Dist: attrs[tests]; extra == 'cov'
|
||||
Requires-Dist: coverage[toml]>=5.3; extra == 'cov'
|
||||
Provides-Extra: dev
|
||||
Requires-Dist: attrs[docs,tests]; extra == 'dev'
|
||||
Requires-Dist: pre-commit; extra == 'dev'
|
||||
Provides-Extra: docs
|
||||
Requires-Dist: furo; extra == 'docs'
|
||||
Requires-Dist: myst-parser; extra == 'docs'
|
||||
Requires-Dist: sphinx; extra == 'docs'
|
||||
Requires-Dist: sphinx-notfound-page; extra == 'docs'
|
||||
Requires-Dist: sphinxcontrib-towncrier; extra == 'docs'
|
||||
Requires-Dist: towncrier; extra == 'docs'
|
||||
Requires-Dist: zope-interface; extra == 'docs'
|
||||
Provides-Extra: tests
|
||||
Requires-Dist: attrs[tests-no-zope]; extra == 'tests'
|
||||
Requires-Dist: zope-interface; extra == 'tests'
|
||||
Provides-Extra: tests-no-zope
|
||||
Requires-Dist: cloudpickle; platform_python_implementation == 'CPython' and extra == 'tests-no-zope'
|
||||
Requires-Dist: hypothesis; extra == 'tests-no-zope'
|
||||
Requires-Dist: mypy>=1.1.1; platform_python_implementation == 'CPython' and extra == 'tests-no-zope'
|
||||
Requires-Dist: pympler; extra == 'tests-no-zope'
|
||||
Requires-Dist: pytest-mypy-plugins; platform_python_implementation == 'CPython' and python_version < '3.11' and extra == 'tests-no-zope'
|
||||
Requires-Dist: pytest-xdist[psutil]; extra == 'tests-no-zope'
|
||||
Requires-Dist: pytest>=4.3.0; extra == 'tests-no-zope'
|
||||
Description-Content-Type: text/markdown
|
||||
|
||||
<p align="center">
|
||||
<a href="https://www.attrs.org/">
|
||||
<img src="https://raw.githubusercontent.com/python-attrs/attrs/main/docs/_static/attrs_logo.svg" width="35%" alt="attrs" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
*attrs* is the Python package that will bring back the **joy** of **writing classes** by relieving you from the drudgery of implementing object protocols (aka [dunder methods](https://www.attrs.org/en/latest/glossary.html#term-dunder-methods)).
|
||||
[Trusted by NASA](https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-github-profile/customizing-your-profile/personalizing-your-profile#list-of-qualifying-repositories-for-mars-2020-helicopter-contributor-achievement) for Mars missions since 2020!
|
||||
|
||||
Its main goal is to help you to write **concise** and **correct** software without slowing down your code.
|
||||
|
||||
|
||||
## Sponsors
|
||||
|
||||
*attrs* would not be possible without our [amazing sponsors](https://github.com/sponsors/hynek).
|
||||
Especially those generously supporting us at the *The Organization* tier and higher:
|
||||
|
||||
<p align="center">
|
||||
<a href="https://www.variomedia.de/">
|
||||
<img src="https://raw.githubusercontent.com/python-attrs/attrs/main/.github/sponsors/Variomedia.svg" width="200" height="60"></img>
|
||||
</a>
|
||||
|
||||
<a href="https://tidelift.com/subscription/pkg/pypi-attrs?utm_source=pypi-attrs&utm_medium=referral&utm_campaign=enterprise&utm_term=repo">
|
||||
<img src="https://raw.githubusercontent.com/python-attrs/attrs/main/.github/sponsors/Tidelift.svg" width="200" height="60"></img>
|
||||
</a>
|
||||
|
||||
<a href="https://sentry.io/">
|
||||
<img src="https://raw.githubusercontent.com/python-attrs/attrs/main/.github/sponsors/Sentry.svg" width="200" height="60"></img>
|
||||
</a>
|
||||
|
||||
<a href="https://filepreviews.io/">
|
||||
<img src="https://raw.githubusercontent.com/python-attrs/attrs/main/.github/sponsors/FilePreviews.svg" width="200" height="60"></img>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<strong>Please consider <a href="https://github.com/sponsors/hynek">joining them</a> to help make <em>attrs</em>’s maintenance more sustainable!</strong>
|
||||
</p>
|
||||
|
||||
<!-- teaser-end -->
|
||||
|
||||
## Example
|
||||
|
||||
*attrs* gives you a class decorator and a way to declaratively define the attributes on that class:
|
||||
|
||||
<!-- code-begin -->
|
||||
|
||||
```pycon
|
||||
>>> from attrs import asdict, define, make_class, Factory
|
||||
|
||||
>>> @define
|
||||
... class SomeClass:
|
||||
... a_number: int = 42
|
||||
... list_of_numbers: list[int] = Factory(list)
|
||||
...
|
||||
... def hard_math(self, another_number):
|
||||
... return self.a_number + sum(self.list_of_numbers) * another_number
|
||||
|
||||
|
||||
>>> sc = SomeClass(1, [1, 2, 3])
|
||||
>>> sc
|
||||
SomeClass(a_number=1, list_of_numbers=[1, 2, 3])
|
||||
|
||||
>>> sc.hard_math(3)
|
||||
19
|
||||
>>> sc == SomeClass(1, [1, 2, 3])
|
||||
True
|
||||
>>> sc != SomeClass(2, [3, 2, 1])
|
||||
True
|
||||
|
||||
>>> asdict(sc)
|
||||
{'a_number': 1, 'list_of_numbers': [1, 2, 3]}
|
||||
|
||||
>>> SomeClass()
|
||||
SomeClass(a_number=42, list_of_numbers=[])
|
||||
|
||||
>>> C = make_class("C", ["a", "b"])
|
||||
>>> C("foo", "bar")
|
||||
C(a='foo', b='bar')
|
||||
```
|
||||
|
||||
After *declaring* your attributes, *attrs* gives you:
|
||||
|
||||
- a concise and explicit overview of the class's attributes,
|
||||
- a nice human-readable `__repr__`,
|
||||
- equality-checking methods,
|
||||
- an initializer,
|
||||
- and much more,
|
||||
|
||||
*without* writing dull boilerplate code again and again and *without* runtime performance penalties.
|
||||
|
||||
**Hate type annotations**!?
|
||||
No problem!
|
||||
Types are entirely **optional** with *attrs*.
|
||||
Simply assign `attrs.field()` to the attributes instead of annotating them with types.
|
||||
|
||||
---
|
||||
|
||||
This example uses *attrs*'s modern APIs that have been introduced in version 20.1.0, and the *attrs* package import name that has been added in version 21.3.0.
|
||||
The classic APIs (`@attr.s`, `attr.ib`, plus their serious-business aliases) and the `attr` package import name will remain **indefinitely**.
|
||||
|
||||
Please check out [*On The Core API Names*](https://www.attrs.org/en/latest/names.html) for a more in-depth explanation.
|
||||
|
||||
|
||||
## Data Classes
|
||||
|
||||
On the tin, *attrs* might remind you of `dataclasses` (and indeed, `dataclasses` [are a descendant](https://hynek.me/articles/import-attrs/) of *attrs*).
|
||||
In practice it does a lot more and is more flexible.
|
||||
For instance it allows you to define [special handling of NumPy arrays for equality checks](https://www.attrs.org/en/stable/comparison.html#customization), or allows more ways to [plug into the initialization process](https://www.attrs.org/en/stable/init.html#hooking-yourself-into-initialization).
|
||||
|
||||
For more details, please refer to our [comparison page](https://www.attrs.org/en/stable/why.html#data-classes).
|
||||
|
||||
|
||||
## Project Information
|
||||
|
||||
- [**Changelog**](https://www.attrs.org/en/stable/changelog.html)
|
||||
- [**Documentation**](https://www.attrs.org/)
|
||||
- [**PyPI**](https://pypi.org/project/attrs/)
|
||||
- [**Source Code**](https://github.com/python-attrs/attrs)
|
||||
- [**Contributing**](https://github.com/python-attrs/attrs/blob/main/.github/CONTRIBUTING.md)
|
||||
- [**Third-party Extensions**](https://github.com/python-attrs/attrs/wiki/Extensions-to-attrs)
|
||||
- **License**: [MIT](https://www.attrs.org/en/latest/license.html)
|
||||
- **Get Help**: please use the `python-attrs` tag on [StackOverflow](https://stackoverflow.com/questions/tagged/python-attrs)
|
||||
- **Supported Python Versions**: 3.7 and later
|
||||
|
||||
|
||||
### *attrs* for Enterprise
|
||||
|
||||
Available as part of the Tidelift Subscription.
|
||||
|
||||
The maintainers of *attrs* and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications.
|
||||
Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use.
|
||||
[Learn more.](https://tidelift.com/subscription/pkg/pypi-attrs?utm_source=pypi-attrs&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)
|
||||
|
||||
## Release Information
|
||||
|
||||
### Backwards-incompatible Changes
|
||||
|
||||
- Python 3.6 has been dropped and packaging switched to static package data using [Hatch](https://hatch.pypa.io/latest/).
|
||||
[#993](https://github.com/python-attrs/attrs/issues/993)
|
||||
|
||||
|
||||
### Deprecations
|
||||
|
||||
- The support for *zope-interface* via the `attrs.validators.provides` validator is now deprecated and will be removed in, or after, April 2024.
|
||||
|
||||
The presence of a C-based package in our developement dependencies has caused headaches and we're not under the impression it's used a lot.
|
||||
|
||||
Let us know if you're using it and we might publish it as a separate package.
|
||||
[#1120](https://github.com/python-attrs/attrs/issues/1120)
|
||||
|
||||
|
||||
### Changes
|
||||
|
||||
- `attrs.filters.exclude()` and `attrs.filters.include()` now support the passing of attribute names as strings.
|
||||
[#1068](https://github.com/python-attrs/attrs/issues/1068)
|
||||
- `attrs.has()` and `attrs.fields()` now handle generic classes correctly.
|
||||
[#1079](https://github.com/python-attrs/attrs/issues/1079)
|
||||
- Fix frozen exception classes when raised within e.g. `contextlib.contextmanager`, which mutates their `__traceback__` attributes.
|
||||
[#1081](https://github.com/python-attrs/attrs/issues/1081)
|
||||
- `@frozen` now works with type checkers that implement [PEP-681](https://peps.python.org/pep-0681/) (ex. [pyright](https://github.com/microsoft/pyright/)).
|
||||
[#1084](https://github.com/python-attrs/attrs/issues/1084)
|
||||
- Restored ability to unpickle instances pickled before 22.2.0.
|
||||
[#1085](https://github.com/python-attrs/attrs/issues/1085)
|
||||
- `attrs.asdict()`'s and `attrs.astuple()`'s type stubs now accept the `attrs.AttrsInstance` protocol.
|
||||
[#1090](https://github.com/python-attrs/attrs/issues/1090)
|
||||
- Fix slots class cellvar updating closure in CPython 3.8+ even when `__code__` introspection is unavailable.
|
||||
[#1092](https://github.com/python-attrs/attrs/issues/1092)
|
||||
- `attrs.resolve_types()` can now pass `include_extras` to `typing.get_type_hints()` on Python 3.9+, and does so by default.
|
||||
[#1099](https://github.com/python-attrs/attrs/issues/1099)
|
||||
- Added instructions for pull request workflow to `CONTRIBUTING.md`.
|
||||
[#1105](https://github.com/python-attrs/attrs/issues/1105)
|
||||
- Added *type* parameter to `attrs.field()` function for use with `attrs.make_class()`.
|
||||
|
||||
Please note that type checkers ignore type metadata passed into `make_class()`, but it can be useful if you're wrapping _attrs_.
|
||||
[#1107](https://github.com/python-attrs/attrs/issues/1107)
|
||||
- It is now possible for `attrs.evolve()` (and `attr.evolve()`) to change fields named `inst` if the instance is passed as a positional argument.
|
||||
|
||||
Passing the instance using the `inst` keyword argument is now deprecated and will be removed in, or after, April 2024.
|
||||
[#1117](https://github.com/python-attrs/attrs/issues/1117)
|
||||
- `attrs.validators.optional()` now also accepts a tuple of validators (in addition to lists of validators).
|
||||
[#1122](https://github.com/python-attrs/attrs/issues/1122)
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
[Full changelog](https://www.attrs.org/en/stable/changelog.html)
|
|
@ -0,0 +1,35 @@
|
|||
attr/__init__.py,sha256=dSRUBxRVTh-dXMrMR_oQ3ZISu2QSfhSZlik03Mjbu30,3241
|
||||
attr/__init__.pyi,sha256=rIK-2IakIoehVtqXK5l5rs9_fJNCbnYtKTS3cOAVJD8,17609
|
||||
attr/_cmp.py,sha256=diMUQV-BIg7IjIb6-o1hswtnjrR4qdAUz_tE8gxS96w,4098
|
||||
attr/_cmp.pyi,sha256=sGQmOM0w3_K4-X8cTXR7g0Hqr290E8PTObA9JQxWQqc,399
|
||||
attr/_compat.py,sha256=d3cpIu60IbKrLywPni17RUEQY7MvkqqKifyzJ5H3zRU,5803
|
||||
attr/_config.py,sha256=5W8lgRePuIOWu1ZuqF1899e2CmXGc95-ipwTpF1cEU4,826
|
||||
attr/_funcs.py,sha256=YMtzHRSOnFvOVJ7at3E0K95A2lW26HDjby96TMTDbc0,16730
|
||||
attr/_make.py,sha256=JIyKV-HRh3IcHi-EvOj2dw6tRoqATlx2kBHFrrxZpk0,96979
|
||||
attr/_next_gen.py,sha256=8lB_S5SFgX2KsflksK8Zygk6XDXToQYtIlmgd37I9aY,6271
|
||||
attr/_typing_compat.pyi,sha256=XDP54TUn-ZKhD62TOQebmzrwFyomhUCoGRpclb6alRA,469
|
||||
attr/_version_info.py,sha256=exSqb3b5E-fMSsgZAlEw9XcLpEgobPORCZpcaEglAM4,2121
|
||||
attr/_version_info.pyi,sha256=x_M3L3WuB7r_ULXAWjx959udKQ4HLB8l-hsc1FDGNvk,209
|
||||
attr/converters.py,sha256=xfGVSPRgWGcym6N5FZM9fyfvCQePqFyApWeC5BXKvoM,3602
|
||||
attr/converters.pyi,sha256=jKlpHBEt6HVKJvgrMFJRrHq8p61GXg4-Nd5RZWKJX7M,406
|
||||
attr/exceptions.py,sha256=0ZTyH_mHmI9utwTTbBWrdS_ck5jps9R2M_fYJPXxH_U,1890
|
||||
attr/exceptions.pyi,sha256=zZq8bCUnKAy9mDtBEw42ZhPhAUIHoTKedDQInJD883M,539
|
||||
attr/filters.py,sha256=9pYvXqdg6mtLvKIIb56oALRMoHFnQTcGCO4EXTc1qyM,1470
|
||||
attr/filters.pyi,sha256=0mRCjLKxdcvAo0vD-Cr81HfRXXCp9j_cAXjOoAHtPGM,225
|
||||
attr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||
attr/setters.py,sha256=pbCZQ-pE6ZxjDqZfWWUhUFefXtpekIU4qS_YDMLPQ50,1400
|
||||
attr/setters.pyi,sha256=pyY8TVNBu8TWhOldv_RxHzmGvdgFQH981db70r0fn5I,567
|
||||
attr/validators.py,sha256=C2MQgX7ubL_cs5YzibWa8m0YxdMq5_3Ch3dVIzsLO-Y,20702
|
||||
attr/validators.pyi,sha256=167Dl9nt7NUhE9wht1I-buo039qyUT1nEUT_nKjSWr4,2580
|
||||
attrs/__init__.py,sha256=9_5waVbFs7rLqtXZ73tNDrxhezyZ8VZeX4BbvQ3EeJw,1039
|
||||
attrs/__init__.pyi,sha256=s_ajQ_U14DOsOz0JbmAKDOi46B3v2PcdO0UAV1MY6Ek,2168
|
||||
attrs/converters.py,sha256=fCBEdlYWcmI3sCnpUk2pz22GYtXzqTkp6NeOpdI64PY,70
|
||||
attrs/exceptions.py,sha256=SlDli6AY77f6ny-H7oy98OkQjsrw-D_supEuErIVYkE,70
|
||||
attrs/filters.py,sha256=dc_dNey29kH6KLU1mT2Dakq7tZ3kBfzEGwzOmDzw1F8,67
|
||||
attrs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||
attrs/setters.py,sha256=oKw51C72Hh45wTwYvDHJP9kbicxiMhMR4Y5GvdpKdHQ,67
|
||||
attrs/validators.py,sha256=4ag1SyVD2Hm3PYKiNG_NOtR_e7f81Hr6GiNl4YvXo4Q,70
|
||||
attrs-23.1.0.dist-info/METADATA,sha256=yglwUXko75Q-IJ6LmPVQ4Y99KJS3CPK0NW8ovXFYsDg,11348
|
||||
attrs-23.1.0.dist-info/WHEEL,sha256=EI2JsGydwUL5GP9t6kzZv7G3HDPi7FuZDDf9In6amRM,87
|
||||
attrs-23.1.0.dist-info/licenses/LICENSE,sha256=iCEVyV38KvHutnFPjsbVy8q_Znyv-HKfQkINpj9xTp8,1109
|
||||
attrs-23.1.0.dist-info/RECORD,,
|
|
@ -1,6 +1,4 @@
|
|||
Wheel-Version: 1.0
|
||||
Generator: bdist_wheel (0.33.6)
|
||||
Generator: hatchling 1.14.0
|
||||
Root-Is-Purelib: true
|
||||
Tag: py2-none-any
|
||||
Tag: py3-none-any
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Hynek Schlawack
|
||||
Copyright (c) 2015 Hynek Schlawack and the attrs contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
|
@ -0,0 +1,65 @@
|
|||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from attr import (
|
||||
NOTHING,
|
||||
Attribute,
|
||||
AttrsInstance,
|
||||
Factory,
|
||||
_make_getattr,
|
||||
assoc,
|
||||
cmp_using,
|
||||
define,
|
||||
evolve,
|
||||
field,
|
||||
fields,
|
||||
fields_dict,
|
||||
frozen,
|
||||
has,
|
||||
make_class,
|
||||
mutable,
|
||||
resolve_types,
|
||||
validate,
|
||||
)
|
||||
from attr._next_gen import asdict, astuple
|
||||
|
||||
from . import converters, exceptions, filters, setters, validators
|
||||
|
||||
|
||||
__all__ = [
|
||||
"__author__",
|
||||
"__copyright__",
|
||||
"__description__",
|
||||
"__doc__",
|
||||
"__email__",
|
||||
"__license__",
|
||||
"__title__",
|
||||
"__url__",
|
||||
"__version__",
|
||||
"__version_info__",
|
||||
"asdict",
|
||||
"assoc",
|
||||
"astuple",
|
||||
"Attribute",
|
||||
"AttrsInstance",
|
||||
"cmp_using",
|
||||
"converters",
|
||||
"define",
|
||||
"evolve",
|
||||
"exceptions",
|
||||
"Factory",
|
||||
"field",
|
||||
"fields_dict",
|
||||
"fields",
|
||||
"filters",
|
||||
"frozen",
|
||||
"has",
|
||||
"make_class",
|
||||
"mutable",
|
||||
"NOTHING",
|
||||
"resolve_types",
|
||||
"setters",
|
||||
"validate",
|
||||
"validators",
|
||||
]
|
||||
|
||||
__getattr__ = _make_getattr(__name__)
|
|
@ -0,0 +1,67 @@
|
|||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Dict,
|
||||
Mapping,
|
||||
Optional,
|
||||
Sequence,
|
||||
Tuple,
|
||||
Type,
|
||||
)
|
||||
|
||||
# Because we need to type our own stuff, we have to make everything from
|
||||
# attr explicitly public too.
|
||||
from attr import __author__ as __author__
|
||||
from attr import __copyright__ as __copyright__
|
||||
from attr import __description__ as __description__
|
||||
from attr import __email__ as __email__
|
||||
from attr import __license__ as __license__
|
||||
from attr import __title__ as __title__
|
||||
from attr import __url__ as __url__
|
||||
from attr import __version__ as __version__
|
||||
from attr import __version_info__ as __version_info__
|
||||
from attr import _FilterType
|
||||
from attr import assoc as assoc
|
||||
from attr import Attribute as Attribute
|
||||
from attr import AttrsInstance as AttrsInstance
|
||||
from attr import cmp_using as cmp_using
|
||||
from attr import converters as converters
|
||||
from attr import define as define
|
||||
from attr import evolve as evolve
|
||||
from attr import exceptions as exceptions
|
||||
from attr import Factory as Factory
|
||||
from attr import field as field
|
||||
from attr import fields as fields
|
||||
from attr import fields_dict as fields_dict
|
||||
from attr import filters as filters
|
||||
from attr import frozen as frozen
|
||||
from attr import has as has
|
||||
from attr import make_class as make_class
|
||||
from attr import mutable as mutable
|
||||
from attr import NOTHING as NOTHING
|
||||
from attr import resolve_types as resolve_types
|
||||
from attr import setters as setters
|
||||
from attr import validate as validate
|
||||
from attr import validators as validators
|
||||
|
||||
# TODO: see definition of attr.asdict/astuple
|
||||
def asdict(
|
||||
inst: AttrsInstance,
|
||||
recurse: bool = ...,
|
||||
filter: Optional[_FilterType[Any]] = ...,
|
||||
dict_factory: Type[Mapping[Any, Any]] = ...,
|
||||
retain_collection_types: bool = ...,
|
||||
value_serializer: Optional[
|
||||
Callable[[type, Attribute[Any], Any], Any]
|
||||
] = ...,
|
||||
tuple_keys: bool = ...,
|
||||
) -> Dict[str, Any]: ...
|
||||
|
||||
# TODO: add support for returning NamedTuple from the mypy plugin
|
||||
def astuple(
|
||||
inst: AttrsInstance,
|
||||
recurse: bool = ...,
|
||||
filter: Optional[_FilterType[Any]] = ...,
|
||||
tuple_factory: Type[Sequence[Any]] = ...,
|
||||
retain_collection_types: bool = ...,
|
||||
) -> Tuple[Any, ...]: ...
|
|
@ -0,0 +1,3 @@
|
|||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from attr.converters import * # noqa
|
|
@ -0,0 +1,3 @@
|
|||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from attr.exceptions import * # noqa
|
|
@ -0,0 +1,3 @@
|
|||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from attr.filters import * # noqa
|
|
@ -0,0 +1,3 @@
|
|||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from attr.setters import * # noqa
|
|
@ -0,0 +1,3 @@
|
|||
# SPDX-License-Identifier: MIT
|
||||
|
||||
from attr.validators import * # noqa
|
|
@ -1,4 +1,4 @@
|
|||
# This file is automatically @generated by Poetry 1.4.0 and should not be changed by hand.
|
||||
# This file is automatically @generated by Poetry 0.4.0 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "aiohttp"
|
||||
|
@ -96,21 +96,25 @@ files = [
|
|||
|
||||
[[package]]
|
||||
name = "attrs"
|
||||
version = "19.2.0"
|
||||
version = "23.1.0"
|
||||
description = "Classes Without Boilerplate"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "attrs-19.2.0-py2.py3-none-any.whl", hash = "sha256:ec20e7a4825331c1b5ebf261d111e16fa9612c1f7a5e1f884f12bd53a664dfd2"},
|
||||
{file = "attrs-19.2.0.tar.gz", hash = "sha256:f913492e1663d3c36f502e5e9ba6cd13cf19d7fab50aa13239e420fef95e1396"},
|
||||
{file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"},
|
||||
{file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
|
||||
|
||||
[package.extras]
|
||||
azure-pipelines = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-azurepipelines", "six", "zope.interface"]
|
||||
dev = ["coverage", "hypothesis", "pre-commit", "pympler", "pytest (>=4.3.0)", "six", "sphinx", "zope.interface"]
|
||||
docs = ["sphinx", "zope.interface"]
|
||||
tests = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"]
|
||||
cov = ["attrs[tests]", "coverage[toml] (>=5.3)"]
|
||||
dev = ["attrs[docs,tests]", "pre-commit"]
|
||||
docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"]
|
||||
tests = ["attrs[tests-no-zope]", "zope-interface"]
|
||||
tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"]
|
||||
|
||||
[[package]]
|
||||
name = "blessed"
|
||||
|
@ -350,6 +354,7 @@ Jinja2 = ">=2.10.1"
|
|||
jsonschema = ">=3.0.2"
|
||||
MarkupSafe = ">=1.1.1,<=2.0.1"
|
||||
PyYAML = ">=5.3.1"
|
||||
yamllint = ">=1.18.0"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Not direct dependency - only needed to fix a constraint problem
|
||||
MarkupSafe==2.0.1
|
||||
appdirs==1.4.4
|
||||
attrs==19.2.0
|
||||
attrs==23.1.0
|
||||
blessed==1.19.1
|
||||
cbor2==4.0.1
|
||||
certifi==2022.12.7
|
||||
|
|
|
@ -45,9 +45,9 @@ appdirs==1.4.4 ; python_version >= "3.7" and python_version < "4.0" \
|
|||
async-timeout==3.0.1 ; python_version >= "3.7" and python_version < "4.0" \
|
||||
--hash=sha256:0c3c816a028d47f659d6ff5c745cb2acf1f966da1fe5c19c77a70282b25f4c5f \
|
||||
--hash=sha256:4291ca197d287d274d0b6cb5d6f8f8f82d434ed288f962539ff18cc9012f9ea3
|
||||
attrs==19.2.0 ; python_version >= "3.7" and python_version < "4.0" \
|
||||
--hash=sha256:ec20e7a4825331c1b5ebf261d111e16fa9612c1f7a5e1f884f12bd53a664dfd2 \
|
||||
--hash=sha256:f913492e1663d3c36f502e5e9ba6cd13cf19d7fab50aa13239e420fef95e1396
|
||||
attrs==23.1.0 ; python_version >= "3.7" and python_version < "4.0" \
|
||||
--hash=sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04 \
|
||||
--hash=sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015
|
||||
blessed==1.19.1 ; python_version >= "3.7" and python_version < "4.0" \
|
||||
--hash=sha256:63b8554ae2e0e7f43749b6715c734cc8f3883010a809bf16790102563e6cf25b \
|
||||
--hash=sha256:9a0d099695bf621d4680dd6c73f6ad547f6a3442fbdbe80c4b1daa1edbc492fc
|
||||
|
|
|
@ -271,7 +271,7 @@ class TaskGraphGenerator:
|
|||
# Always add legacy target tasks method until we deprecate that API.
|
||||
if "target_tasks_method" not in filters:
|
||||
filters.insert(0, "target_tasks_method")
|
||||
filters = [filter_tasks.filter_task_functions[f] for f in filters if f]
|
||||
filters = [filter_tasks.filter_task_functions[f] for f in filters]
|
||||
|
||||
yield self.verify("parameters", parameters)
|
||||
|
||||
|
|
|
@ -80,76 +80,46 @@ def get_version(repo_path):
|
|||
def _get_defaults(repo_root=None):
|
||||
repo_path = repo_root or os.getcwd()
|
||||
repo = get_repository(repo_path)
|
||||
if repo:
|
||||
try:
|
||||
repo_url = repo.get_url()
|
||||
parsed_url = mozilla_repo_urls.parse(repo_url)
|
||||
project = parsed_url.repo_name
|
||||
except (
|
||||
CalledProcessError,
|
||||
mozilla_repo_urls.errors.InvalidRepoUrlError,
|
||||
mozilla_repo_urls.errors.UnsupportedPlatformError,
|
||||
):
|
||||
repo_url = ""
|
||||
project = ""
|
||||
try:
|
||||
repo_url = repo.get_url()
|
||||
parsed_url = mozilla_repo_urls.parse(repo_url)
|
||||
project = parsed_url.repo_name
|
||||
except (
|
||||
CalledProcessError,
|
||||
mozilla_repo_urls.errors.InvalidRepoUrlError,
|
||||
mozilla_repo_urls.errors.UnsupportedPlatformError,
|
||||
):
|
||||
repo_url = ""
|
||||
project = ""
|
||||
|
||||
return {
|
||||
"base_repository": repo_url,
|
||||
"base_ref": "",
|
||||
"base_rev": "",
|
||||
"build_date": int(time.time()),
|
||||
"build_number": 1,
|
||||
"do_not_optimize": [],
|
||||
"enable_always_target": True,
|
||||
"existing_tasks": {},
|
||||
"filters": ["target_tasks_method"],
|
||||
"head_ref": repo.branch or repo.head_rev,
|
||||
"head_repository": repo_url,
|
||||
"head_rev": repo.head_rev,
|
||||
"head_tag": "",
|
||||
"level": "3",
|
||||
"moz_build_date": datetime.now().strftime("%Y%m%d%H%M%S"),
|
||||
"next_version": None,
|
||||
"optimize_strategies": None,
|
||||
"optimize_target_tasks": True,
|
||||
"owner": "nobody@mozilla.com",
|
||||
"project": project,
|
||||
"pushdate": int(time.time()),
|
||||
"pushlog_id": "0",
|
||||
"repository_type": repo.tool,
|
||||
"target_tasks_method": "default",
|
||||
"tasks_for": "",
|
||||
"version": get_version(repo_path),
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"base_repository": "SOURCE",
|
||||
"base_ref": "",
|
||||
"base_rev": "",
|
||||
"build_date": int(time.time()),
|
||||
"build_number": 1,
|
||||
"do_not_optimize": [],
|
||||
"enable_always_target": True,
|
||||
"existing_tasks": {},
|
||||
"filters": ["target_tasks_method"],
|
||||
"head_ref": "",
|
||||
"head_repository": "",
|
||||
"head_rev": "",
|
||||
"head_tag": "",
|
||||
"level": "3",
|
||||
"moz_build_date": datetime.now().strftime("%Y%m%d%H%M%S"),
|
||||
"next_version": None,
|
||||
"optimize_strategies": None,
|
||||
"optimize_target_tasks": True,
|
||||
"owner": "nobody@mozilla.com",
|
||||
"project": "",
|
||||
"pushdate": int(time.time()),
|
||||
"pushlog_id": "0",
|
||||
"repository_type": "",
|
||||
"target_tasks_method": "default",
|
||||
"tasks_for": "",
|
||||
"version": "",
|
||||
}
|
||||
return {
|
||||
"base_repository": repo_url,
|
||||
"base_ref": "",
|
||||
"base_rev": "",
|
||||
"build_date": int(time.time()),
|
||||
"build_number": 1,
|
||||
"do_not_optimize": [],
|
||||
"enable_always_target": True,
|
||||
"existing_tasks": {},
|
||||
"filters": ["target_tasks_method"],
|
||||
"head_ref": repo.branch or repo.head_rev,
|
||||
"head_repository": repo_url,
|
||||
"head_rev": repo.head_rev,
|
||||
"head_tag": "",
|
||||
"level": "3",
|
||||
"moz_build_date": datetime.now().strftime("%Y%m%d%H%M%S"),
|
||||
"next_version": None,
|
||||
"optimize_strategies": None,
|
||||
"optimize_target_tasks": True,
|
||||
"owner": "nobody@mozilla.com",
|
||||
"project": project,
|
||||
"pushdate": int(time.time()),
|
||||
"pushlog_id": "0",
|
||||
"repository_type": repo.tool,
|
||||
"target_tasks_method": "default",
|
||||
"tasks_for": "",
|
||||
"version": get_version(repo_path),
|
||||
}
|
||||
|
||||
|
||||
defaults_functions = [_get_defaults]
|
||||
|
@ -225,14 +195,13 @@ class Parameters(ReadOnlyDict):
|
|||
@staticmethod
|
||||
def _fill_defaults(repo_root=None, **kwargs):
|
||||
defaults = {}
|
||||
if repo_root != "SOURCE":
|
||||
for fn in defaults_functions:
|
||||
defaults.update(fn(repo_root))
|
||||
for fn in defaults_functions:
|
||||
defaults.update(fn(repo_root))
|
||||
|
||||
for name, default in defaults.items():
|
||||
if name not in kwargs:
|
||||
kwargs[name] = default
|
||||
return kwargs
|
||||
for name, default in defaults.items():
|
||||
if name not in kwargs:
|
||||
kwargs[name] = default
|
||||
return kwargs
|
||||
|
||||
def check(self):
|
||||
schema = (
|
||||
|
|
|
@ -504,10 +504,8 @@ def get_repository(path):
|
|||
return HgRepository(path)
|
||||
elif os.path.exists(os.path.join(path, ".git")):
|
||||
return GitRepository(path)
|
||||
elif os.path.exists(os.path.join(path, "moz.configure")):
|
||||
return None
|
||||
|
||||
raise RuntimeError("Current directory is neither a git or hg repository, nor a release source")
|
||||
raise RuntimeError("Current directory is neither a git or hg repository")
|
||||
|
||||
|
||||
def find_hg_revision_push_info(repository, revision):
|
||||
|
|
Загрузка…
Ссылка в новой задаче