Update attr 22.2.0 (683d056) → 23.1.0 (67e4ff2).

This commit is contained in:
JackDandy 2023-10-08 00:21:06 +01:00
parent 9962c1a112
commit 0b0c8827a2
14 changed files with 319 additions and 341 deletions

View file

@ -1,5 +1,6 @@
### 3.31.0 (2023-1x-xx xx:xx:00 UTC) ### 3.31.0 (2023-1x-xx xx:xx:00 UTC)
* Update attr 22.2.0 (683d056) to 23.1.0 (67e4ff2)
* Update Beautiful Soup 4.12.2 to 4.12.2 (30c58a1) * Update Beautiful Soup 4.12.2 to 4.12.2 (30c58a1)
* Update soupsieve 2.4.1 (2e66beb) to 2.5.0 (dc71495) * Update soupsieve 2.4.1 (2e66beb) to 2.5.0 (dc71495)
* Update hachoir 3.1.2 (f739b43) to 3.2.0 (38d759f) * Update hachoir 3.1.2 (f739b43) to 3.2.0 (38d759f)

View file

@ -9,6 +9,7 @@ from typing import Callable
from . import converters, exceptions, filters, setters, validators from . import converters, exceptions, filters, setters, validators
from ._cmp import cmp_using from ._cmp import cmp_using
from ._compat import Protocol
from ._config import get_run_validators, set_run_validators from ._config import get_run_validators, set_run_validators
from ._funcs import asdict, assoc, astuple, evolve, has, resolve_types from ._funcs import asdict, assoc, astuple, evolve, has, resolve_types
from ._make import ( from ._make import (
@ -31,7 +32,7 @@ ib = attr = attrib
dataclass = partial(attrs, auto_attribs=True) # happy Easter ;) dataclass = partial(attrs, auto_attribs=True) # happy Easter ;)
class AttrsInstance: class AttrsInstance(Protocol):
pass pass
@ -90,8 +91,9 @@ def _make_getattr(mod_name: str) -> Callable:
"__email__": "", "__email__": "",
"__license__": "license", "__license__": "license",
} }
if name not in dunder_to_metadata.keys(): if name not in dunder_to_metadata:
raise AttributeError(f"module {mod_name} has no attribute {name}") msg = f"module {mod_name} has no attribute {name}"
raise AttributeError(msg)
import sys import sys
import warnings import warnings
@ -101,7 +103,7 @@ def _make_getattr(mod_name: str) -> Callable:
else: else:
from importlib.metadata import metadata from importlib.metadata import metadata
if name != "__version_info__": if name not in ("__version__", "__version_info__"):
warnings.warn( warnings.warn(
f"Accessing {mod_name}.{name} is deprecated and will be " f"Accessing {mod_name}.{name} is deprecated and will be "
"removed in a future release. Use importlib.metadata directly " "removed in a future release. Use importlib.metadata directly "
@ -113,15 +115,15 @@ def _make_getattr(mod_name: str) -> Callable:
meta = metadata("attrs") meta = metadata("attrs")
if name == "__license__": if name == "__license__":
return "MIT" return "MIT"
elif name == "__copyright__": if name == "__copyright__":
return "Copyright (c) 2015 Hynek Schlawack" return "Copyright (c) 2015 Hynek Schlawack"
elif name in ("__uri__", "__url__"): if name in ("__uri__", "__url__"):
return meta["Project-URL"].split(" ", 1)[-1] return meta["Project-URL"].split(" ", 1)[-1]
elif name == "__version_info__": if name == "__version_info__":
return VersionInfo._from_version_string(meta["version"]) return VersionInfo._from_version_string(meta["version"])
elif name == "__author__": if name == "__author__":
return meta["Author-email"].rsplit(" ", 1)[0] return meta["Author-email"].rsplit(" ", 1)[0]
elif name == "__email__": if name == "__email__":
return meta["Author-email"].rsplit("<", 1)[1][:-1] return meta["Author-email"].rsplit("<", 1)[1][:-1]
return meta[dunder_to_metadata[name]] return meta[dunder_to_metadata[name]]

View file

@ -33,6 +33,11 @@ if sys.version_info >= (3, 10):
else: else:
from typing_extensions import TypeGuard from typing_extensions import TypeGuard
if sys.version_info >= (3, 11):
from typing import dataclass_transform
else:
from typing_extensions import dataclass_transform
__version__: str __version__: str
__version_info__: VersionInfo __version_info__: VersionInfo
__title__: str __title__: str
@ -69,8 +74,7 @@ _ValidatorArgType = Union[_ValidatorType[_T], Sequence[_ValidatorType[_T]]]
class AttrsInstance(AttrsInstance_, Protocol): class AttrsInstance(AttrsInstance_, Protocol):
pass pass
_A = TypeVar("_A", bound=AttrsInstance) _A = TypeVar("_A", bound=type[AttrsInstance])
# _make --
class _Nothing(enum.Enum): class _Nothing(enum.Enum):
NOTHING = enum.auto() NOTHING = enum.auto()
@ -104,23 +108,6 @@ else:
takes_self: bool = ..., takes_self: bool = ...,
) -> _T: ... ) -> _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]): class Attribute(Generic[_T]):
name: str name: str
default: Optional[_T] default: Optional[_T]
@ -323,7 +310,7 @@ def field(
type: Optional[type] = ..., type: Optional[type] = ...,
) -> Any: ... ) -> Any: ...
@overload @overload
@__dataclass_transform__(order_default=True, field_descriptors=(attrib, field)) @dataclass_transform(order_default=True, field_specifiers=(attrib, field))
def attrs( def attrs(
maybe_cls: _C, maybe_cls: _C,
these: Optional[Dict[str, Any]] = ..., these: Optional[Dict[str, Any]] = ...,
@ -351,7 +338,7 @@ def attrs(
unsafe_hash: Optional[bool] = ..., unsafe_hash: Optional[bool] = ...,
) -> _C: ... ) -> _C: ...
@overload @overload
@__dataclass_transform__(order_default=True, field_descriptors=(attrib, field)) @dataclass_transform(order_default=True, field_specifiers=(attrib, field))
def attrs( def attrs(
maybe_cls: None = ..., maybe_cls: None = ...,
these: Optional[Dict[str, Any]] = ..., these: Optional[Dict[str, Any]] = ...,
@ -379,7 +366,7 @@ def attrs(
unsafe_hash: Optional[bool] = ..., unsafe_hash: Optional[bool] = ...,
) -> Callable[[_C], _C]: ... ) -> Callable[[_C], _C]: ...
@overload @overload
@__dataclass_transform__(field_descriptors=(attrib, field)) @dataclass_transform(field_specifiers=(attrib, field))
def define( def define(
maybe_cls: _C, maybe_cls: _C,
*, *,
@ -405,7 +392,7 @@ def define(
match_args: bool = ..., match_args: bool = ...,
) -> _C: ... ) -> _C: ...
@overload @overload
@__dataclass_transform__(field_descriptors=(attrib, field)) @dataclass_transform(field_specifiers=(attrib, field))
def define( def define(
maybe_cls: None = ..., maybe_cls: None = ...,
*, *,
@ -434,9 +421,7 @@ def define(
mutable = define mutable = define
@overload @overload
@__dataclass_transform__( @dataclass_transform(frozen_default=True, field_specifiers=(attrib, field))
frozen_default=True, field_descriptors=(attrib, field)
)
def frozen( def frozen(
maybe_cls: _C, maybe_cls: _C,
*, *,
@ -462,9 +447,7 @@ def frozen(
match_args: bool = ..., match_args: bool = ...,
) -> _C: ... ) -> _C: ...
@overload @overload
@__dataclass_transform__( @dataclass_transform(frozen_default=True, field_specifiers=(attrib, field))
frozen_default=True, field_descriptors=(attrib, field)
)
def frozen( def frozen(
maybe_cls: None = ..., maybe_cls: None = ...,
*, *,

View file

@ -92,10 +92,8 @@ def cmp_using(
if not has_eq_function: if not has_eq_function:
# functools.total_ordering requires __eq__ to be defined, # functools.total_ordering requires __eq__ to be defined,
# so raise early error here to keep a nice stack. # so raise early error here to keep a nice stack.
raise ValueError( msg = "eq must be define is order to complete ordering from lt, le, gt, ge."
"eq must be define is order to complete ordering from " raise ValueError(msg)
"lt, le, gt, ge."
)
type_ = functools.total_ordering(type_) type_ = functools.total_ordering(type_)
return type_ return type_
@ -142,10 +140,7 @@ def _is_comparable_to(self, other):
""" """
Check whether `other` is comparable to `self`. Check whether `other` is comparable to `self`.
""" """
for func in self._requirements: return all(func(self, other) for func in self._requirements)
if not func(self, other):
return False
return True
def _check_same_type(self, other): def _check_same_type(self, other):

View file

@ -1,6 +1,5 @@
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
import inspect import inspect
import platform import platform
import sys import sys
@ -8,7 +7,7 @@ import threading
import types import types
import warnings import warnings
from collections.abc import Mapping, Sequence # noqa from collections.abc import Mapping, Sequence # noqa: F401
from typing import _GenericAlias from typing import _GenericAlias
@ -18,6 +17,15 @@ PY310 = sys.version_info[:2] >= (3, 10)
PY_3_12_PLUS = sys.version_info[:2] >= (3, 12) PY_3_12_PLUS = sys.version_info[:2] >= (3, 12)
if sys.version_info < (3, 8):
try:
from typing_extensions import Protocol
except ImportError: # pragma: no cover
Protocol = object
else:
from typing import Protocol # noqa: F401
def just_warn(*args, **kw): def just_warn(*args, **kw):
warnings.warn( warnings.warn(
"Running interpreter doesn't sufficiently support code object " "Running interpreter doesn't sufficiently support code object "
@ -155,7 +163,7 @@ def make_set_closure_cell():
if cell.cell_contents != 100: if cell.cell_contents != 100:
raise AssertionError # pragma: no cover raise AssertionError # pragma: no cover
except Exception: except Exception: # noqa: BLE001
return just_warn return just_warn
else: else:
return set_closure_cell return set_closure_cell

View file

@ -1,6 +1,5 @@
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
__all__ = ["set_run_validators", "get_run_validators"] __all__ = ["set_run_validators", "get_run_validators"]
_run_validators = True _run_validators = True
@ -15,7 +14,8 @@ def set_run_validators(run):
instead. instead.
""" """
if not isinstance(run, bool): if not isinstance(run, bool):
raise TypeError("'run' must be bool.") msg = "'run' must be bool."
raise TypeError(msg)
global _run_validators global _run_validators
_run_validators = run _run_validators = run

View file

@ -72,8 +72,7 @@ def asdict(
) )
elif isinstance(v, (tuple, list, set, frozenset)): elif isinstance(v, (tuple, list, set, frozenset)):
cf = v.__class__ if retain_collection_types is True else list cf = v.__class__ if retain_collection_types is True else list
rv[a.name] = cf( items = [
[
_asdict_anything( _asdict_anything(
i, i,
is_key=False, is_key=False,
@ -84,7 +83,14 @@ def asdict(
) )
for i in v for i in v
] ]
) try:
rv[a.name] = cf(items)
except TypeError:
if not issubclass(cf, tuple):
raise
# Workaround for TypeError: cf.__new__() missing 1 required
# positional argument (which appears, for a namedturle)
rv[a.name] = cf(*items)
elif isinstance(v, dict): elif isinstance(v, dict):
df = dict_factory df = dict_factory
rv[a.name] = df( rv[a.name] = df(
@ -241,9 +247,7 @@ def astuple(
) )
elif isinstance(v, (tuple, list, set, frozenset)): elif isinstance(v, (tuple, list, set, frozenset)):
cf = v.__class__ if retain is True else list cf = v.__class__ if retain is True else list
rv.append( items = [
cf(
[
astuple( astuple(
j, j,
recurse=True, recurse=True,
@ -255,8 +259,14 @@ def astuple(
else j else j
for j in v for j in v
] ]
) try:
) rv.append(cf(items))
except TypeError:
if not issubclass(cf, tuple):
raise
# Workaround for TypeError: cf.__new__() missing 1 required
# positional argument (which appears, for a namedturle)
rv.append(cf(*items))
elif isinstance(v, dict): elif isinstance(v, dict):
df = v.__class__ if retain is True else dict df = v.__class__ if retain is True else dict
rv.append( rv.append(
@ -344,9 +354,8 @@ def assoc(inst, **changes):
for k, v in changes.items(): for k, v in changes.items():
a = getattr(attrs, k, NOTHING) a = getattr(attrs, k, NOTHING)
if a is NOTHING: if a is NOTHING:
raise AttrsAttributeNotFoundError( msg = f"{k} is not an attrs attribute on {new.__class__}."
f"{k} is not an attrs attribute on {new.__class__}." raise AttrsAttributeNotFoundError(msg)
)
_obj_setattr(new, k, v) _obj_setattr(new, k, v)
return new return new
@ -379,17 +388,14 @@ def evolve(*args, **changes):
try: try:
(inst,) = args (inst,) = args
except ValueError: except ValueError:
raise TypeError( msg = f"evolve() takes 1 positional argument, but {len(args)} were given"
f"evolve() takes 1 positional argument, but {len(args)} " raise TypeError(msg) from None
"were given"
) from None
else: else:
try: try:
inst = changes.pop("inst") inst = changes.pop("inst")
except KeyError: except KeyError:
raise TypeError( msg = "evolve() missing 1 required positional argument: 'inst'"
"evolve() missing 1 required positional argument: 'inst'" raise TypeError(msg) from None
) from None
import warnings import warnings

View file

@ -1,7 +1,9 @@
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
import contextlib
import copy import copy
import enum import enum
import inspect
import linecache import linecache
import sys import sys
import types import types
@ -87,7 +89,7 @@ class _CacheHashWrapper(int):
See GH #613 for more details. See GH #613 for more details.
""" """
def __reduce__(self, _none_constructor=type(None), _args=()): def __reduce__(self, _none_constructor=type(None), _args=()): # noqa: B008
return _none_constructor, _args return _none_constructor, _args
@ -248,18 +250,18 @@ def attrib(
) )
if hash is not None and hash is not True and hash is not False: if hash is not None and hash is not True and hash is not False:
raise TypeError( msg = "Invalid value for hash. Must be True, False, or None."
"Invalid value for hash. Must be True, False, or None." raise TypeError(msg)
)
if factory is not None: if factory is not None:
if default is not NOTHING: if default is not NOTHING:
raise ValueError( msg = (
"The `default` and `factory` arguments are mutually " "The `default` and `factory` arguments are mutually exclusive."
"exclusive."
) )
raise ValueError(msg)
if not callable(factory): if not callable(factory):
raise ValueError("The `factory` argument must be a callable.") msg = "The `factory` argument must be a callable."
raise ValueError(msg)
default = Factory(factory) default = Factory(factory)
if metadata is None: if metadata is None:
@ -323,7 +325,7 @@ def _make_method(name, script, filename, globs):
old_val = linecache.cache.setdefault(filename, linecache_tuple) old_val = linecache.cache.setdefault(filename, linecache_tuple)
if old_val == linecache_tuple: if old_val == linecache_tuple:
break break
else:
filename = f"{base_filename[:-1]}-{count}>" filename = f"{base_filename[:-1]}-{count}>"
count += 1 count += 1
@ -430,7 +432,7 @@ def _collect_base_attrs(cls, taken_attr_names):
if a.inherited or a.name in taken_attr_names: if a.inherited or a.name in taken_attr_names:
continue continue
a = a.evolve(inherited=True) a = a.evolve(inherited=True) # noqa: PLW2901
base_attrs.append(a) base_attrs.append(a)
base_attr_map[a.name] = base_cls base_attr_map[a.name] = base_cls
@ -468,7 +470,7 @@ def _collect_base_attrs_broken(cls, taken_attr_names):
if a.name in taken_attr_names: if a.name in taken_attr_names:
continue continue
a = a.evolve(inherited=True) a = a.evolve(inherited=True) # noqa: PLW2901
taken_attr_names.add(a.name) taken_attr_names.add(a.name)
base_attrs.append(a) base_attrs.append(a)
base_attr_map[a.name] = base_cls base_attr_map[a.name] = base_cls
@ -493,7 +495,7 @@ def _transform_attrs(
anns = _get_annotations(cls) anns = _get_annotations(cls)
if these is not None: if these is not None:
ca_list = [(name, ca) for name, ca in these.items()] ca_list = list(these.items())
elif auto_attribs is True: elif auto_attribs is True:
ca_names = { ca_names = {
name name
@ -509,10 +511,7 @@ def _transform_attrs(
a = cd.get(attr_name, NOTHING) a = cd.get(attr_name, NOTHING)
if not isinstance(a, _CountingAttr): if not isinstance(a, _CountingAttr):
if a is NOTHING: a = attrib() if a is NOTHING else attrib(default=a)
a = attrib()
else:
a = attrib(default=a)
ca_list.append((attr_name, a)) ca_list.append((attr_name, a))
unannotated = ca_names - annot_names unannotated = ca_names - annot_names
@ -563,10 +562,8 @@ def _transform_attrs(
had_default = False had_default = False
for a in (a for a in attrs if a.init is not False and a.kw_only is False): for a in (a for a in attrs if a.init is not False and a.kw_only is False):
if had_default is True and a.default is NOTHING: if had_default is True and a.default is NOTHING:
raise ValueError( msg = f"No mandatory attributes allowed after an attribute with a default value or factory. Attribute in question: {a!r}"
"No mandatory attributes allowed after an attribute with a " raise ValueError(msg)
f"default value or factory. Attribute in question: {a!r}"
)
if had_default is False and a.default is not NOTHING: if had_default is False and a.default is not NOTHING:
had_default = True had_default = True
@ -628,6 +625,7 @@ class _ClassBuilder:
"_delete_attribs", "_delete_attribs",
"_frozen", "_frozen",
"_has_pre_init", "_has_pre_init",
"_pre_init_has_args",
"_has_post_init", "_has_post_init",
"_is_exc", "_is_exc",
"_on_setattr", "_on_setattr",
@ -674,6 +672,13 @@ class _ClassBuilder:
self._weakref_slot = weakref_slot self._weakref_slot = weakref_slot
self._cache_hash = cache_hash self._cache_hash = cache_hash
self._has_pre_init = bool(getattr(cls, "__attrs_pre_init__", False)) self._has_pre_init = bool(getattr(cls, "__attrs_pre_init__", False))
self._pre_init_has_args = False
if self._has_pre_init:
# Check if the pre init method has more arguments than just `self`
# We want to pass arguments if pre init expects arguments
pre_init_func = cls.__attrs_pre_init__
pre_init_signature = inspect.signature(pre_init_func)
self._pre_init_has_args = len(pre_init_signature.parameters) > 1
self._has_post_init = bool(getattr(cls, "__attrs_post_init__", False)) self._has_post_init = bool(getattr(cls, "__attrs_post_init__", False))
self._delete_attribs = not bool(these) self._delete_attribs = not bool(these)
self._is_exc = is_exc self._is_exc = is_exc
@ -768,13 +773,11 @@ class _ClassBuilder:
name not in base_names name not in base_names
and getattr(cls, name, _sentinel) is not _sentinel and getattr(cls, name, _sentinel) is not _sentinel
): ):
try: # An AttributeError can happen if a base class defines a
delattr(cls, name) # class variable and we want to set an attribute with the
except AttributeError:
# This can happen if a base class defines a class
# variable and we want to set an attribute with the
# same name by using only a type annotation. # same name by using only a type annotation.
pass with contextlib.suppress(AttributeError):
delattr(cls, name)
# Attach our dunder methods. # Attach our dunder methods.
for name, value in self._cls_dict.items(): for name, value in self._cls_dict.items():
@ -799,7 +802,7 @@ class _ClassBuilder:
cd = { cd = {
k: v k: v
for k, v in self._cls_dict.items() for k, v in self._cls_dict.items()
if k not in tuple(self._attr_names) + ("__dict__", "__weakref__") if k not in (*tuple(self._attr_names), "__dict__", "__weakref__")
} }
# If our class doesn't have its own implementation of __setattr__ # If our class doesn't have its own implementation of __setattr__
@ -821,7 +824,7 @@ class _ClassBuilder:
# Traverse the MRO to collect existing slots # Traverse the MRO to collect existing slots
# and check for an existing __weakref__. # and check for an existing __weakref__.
existing_slots = dict() existing_slots = {}
weakref_inherited = False weakref_inherited = False
for base_cls in self._cls.__mro__[1:-1]: for base_cls in self._cls.__mro__[1:-1]:
if base_cls.__dict__.get("__weakref__", None) is not None: if base_cls.__dict__.get("__weakref__", None) is not None:
@ -890,7 +893,8 @@ class _ClassBuilder:
for cell in closure_cells: for cell in closure_cells:
try: try:
match = cell.cell_contents is self._cls match = cell.cell_contents is self._cls
except ValueError: # ValueError: Cell is empty except ValueError: # noqa: PERF203
# ValueError: Cell is empty
pass pass
else: else:
if match: if match:
@ -907,9 +911,8 @@ class _ClassBuilder:
def add_str(self): def add_str(self):
repr = self._cls_dict.get("__repr__") repr = self._cls_dict.get("__repr__")
if repr is None: if repr is None:
raise ValueError( msg = "__str__ can only be generated if a __repr__ exists."
"__str__ can only be generated if a __repr__ exists." raise ValueError(msg)
)
def __str__(self): def __str__(self):
return self.__repr__() return self.__repr__()
@ -980,6 +983,7 @@ class _ClassBuilder:
self._cls, self._cls,
self._attrs, self._attrs,
self._has_pre_init, self._has_pre_init,
self._pre_init_has_args,
self._has_post_init, self._has_post_init,
self._frozen, self._frozen,
self._slots, self._slots,
@ -1006,6 +1010,7 @@ class _ClassBuilder:
self._cls, self._cls,
self._attrs, self._attrs,
self._has_pre_init, self._has_pre_init,
self._pre_init_has_args,
self._has_post_init, self._has_post_init,
self._frozen, self._frozen,
self._slots, self._slots,
@ -1054,9 +1059,8 @@ class _ClassBuilder:
if self._has_custom_setattr: if self._has_custom_setattr:
# We need to write a __setattr__ but there already is one! # We need to write a __setattr__ but there already is one!
raise ValueError( msg = "Can't combine custom __setattr__ with on_setattr hooks."
"Can't combine custom __setattr__ with on_setattr hooks." raise ValueError(msg)
)
# docstring comes from _add_method_dunders # docstring comes from _add_method_dunders
def __setattr__(self, name, val): def __setattr__(self, name, val):
@ -1079,25 +1083,17 @@ class _ClassBuilder:
""" """
Add __module__ and __qualname__ to a *method* if possible. Add __module__ and __qualname__ to a *method* if possible.
""" """
try: with contextlib.suppress(AttributeError):
method.__module__ = self._cls.__module__ method.__module__ = self._cls.__module__
except AttributeError:
pass
try: with contextlib.suppress(AttributeError):
method.__qualname__ = ".".join( method.__qualname__ = f"{self._cls.__qualname__}.{method.__name__}"
(self._cls.__qualname__, method.__name__)
)
except AttributeError:
pass
try: with contextlib.suppress(AttributeError):
method.__doc__ = ( method.__doc__ = (
"Method generated by attrs for class " "Method generated by attrs for class "
f"{self._cls.__qualname__}." f"{self._cls.__qualname__}."
) )
except AttributeError:
pass
return method return method
@ -1108,7 +1104,8 @@ def _determine_attrs_eq_order(cmp, eq, order, default_eq):
values of eq and order. If *eq* is None, set it to *default_eq*. values of eq and order. If *eq* is None, set it to *default_eq*.
""" """
if cmp is not None and any((eq is not None, order is not None)): if cmp is not None and any((eq is not None, order is not None)):
raise ValueError("Don't mix `cmp` with `eq' and `order`.") msg = "Don't mix `cmp` with `eq' and `order`."
raise ValueError(msg)
# cmp takes precedence due to bw-compatibility. # cmp takes precedence due to bw-compatibility.
if cmp is not None: if cmp is not None:
@ -1123,7 +1120,8 @@ def _determine_attrs_eq_order(cmp, eq, order, default_eq):
order = eq order = eq
if eq is False and order is True: if eq is False and order is True:
raise ValueError("`order` can only be True if `eq` is True too.") msg = "`order` can only be True if `eq` is True too."
raise ValueError(msg)
return eq, order return eq, order
@ -1134,7 +1132,8 @@ def _determine_attrib_eq_order(cmp, eq, order, default_eq):
values of eq and order. If *eq* is None, set it to *default_eq*. values of eq and order. If *eq* is None, set it to *default_eq*.
""" """
if cmp is not None and any((eq is not None, order is not None)): if cmp is not None and any((eq is not None, order is not None)):
raise ValueError("Don't mix `cmp` with `eq' and `order`.") msg = "Don't mix `cmp` with `eq' and `order`."
raise ValueError(msg)
def decide_callable_or_boolean(value): def decide_callable_or_boolean(value):
""" """
@ -1164,7 +1163,8 @@ def _determine_attrib_eq_order(cmp, eq, order, default_eq):
order, order_key = decide_callable_or_boolean(order) order, order_key = decide_callable_or_boolean(order)
if eq is False and order is True: if eq is False and order is True:
raise ValueError("`order` can only be True if `eq` is True too.") msg = "`order` can only be True if `eq` is True too."
raise ValueError(msg)
return eq, eq_key, order, order_key return eq, eq_key, order, order_key
@ -1494,7 +1494,8 @@ def attrs(
) )
if has_own_setattr and is_frozen: if has_own_setattr and is_frozen:
raise ValueError("Can't freeze a class with a custom __setattr__.") msg = "Can't freeze a class with a custom __setattr__."
raise ValueError(msg)
builder = _ClassBuilder( builder = _ClassBuilder(
cls, cls,
@ -1547,18 +1548,15 @@ def attrs(
if hash is not True and hash is not False and hash is not None: if hash is not True and hash is not False and hash is not None:
# Can't use `hash in` because 1 == True for example. # Can't use `hash in` because 1 == True for example.
raise TypeError( msg = "Invalid value for hash. Must be True, False, or None."
"Invalid value for hash. Must be True, False, or None." raise TypeError(msg)
)
elif hash is False or (hash is None and eq is False) or is_exc: if hash is False or (hash is None and eq is False) or is_exc:
# Don't do anything. Should fall back to __object__'s __hash__ # Don't do anything. Should fall back to __object__'s __hash__
# which is by id. # which is by id.
if cache_hash: if cache_hash:
raise TypeError( msg = "Invalid value for cache_hash. To use hash caching, hashing must be either explicitly or implicitly enabled."
"Invalid value for cache_hash. To use hash caching," raise TypeError(msg)
" hashing must be either explicitly or implicitly "
"enabled."
)
elif hash is True or ( elif hash is True or (
hash is None and eq is True and is_frozen is True hash is None and eq is True and is_frozen is True
): ):
@ -1567,11 +1565,8 @@ def attrs(
else: else:
# Raise TypeError on attempts to hash. # Raise TypeError on attempts to hash.
if cache_hash: if cache_hash:
raise TypeError( msg = "Invalid value for cache_hash. To use hash caching, hashing must be either explicitly or implicitly enabled."
"Invalid value for cache_hash. To use hash caching," raise TypeError(msg)
" hashing must be either explicitly or implicitly "
"enabled."
)
builder.make_unhashable() builder.make_unhashable()
if _determine_whether_to_implement( if _determine_whether_to_implement(
@ -1581,10 +1576,8 @@ def attrs(
else: else:
builder.add_attrs_init() builder.add_attrs_init()
if cache_hash: if cache_hash:
raise TypeError( msg = "Invalid value for cache_hash. To use hash caching, init must be True."
"Invalid value for cache_hash. To use hash caching," raise TypeError(msg)
" init must be True."
)
if ( if (
PY310 PY310
@ -1599,7 +1592,7 @@ def attrs(
# if it's used as `@attrs` but ``None`` if used as `@attrs()`. # if it's used as `@attrs` but ``None`` if used as `@attrs()`.
if maybe_cls is None: if maybe_cls is None:
return wrap return wrap
else:
return wrap(maybe_cls) return wrap(maybe_cls)
@ -1648,10 +1641,7 @@ def _make_hash(cls, attrs, frozen, cache_hash):
else: else:
hash_def += ", *" hash_def += ", *"
hash_def += ( hash_def += ", _cache_wrapper=__import__('attr._make')._make._CacheHashWrapper):"
", _cache_wrapper="
+ "__import__('attr._make')._make._CacheHashWrapper):"
)
hash_func = "_cache_wrapper(" + hash_func hash_func = "_cache_wrapper(" + hash_func
closing_braces += ")" closing_braces += ")"
@ -1760,7 +1750,7 @@ def _make_eq(cls, attrs):
lines.append(f" self.{a.name},") lines.append(f" self.{a.name},")
others.append(f" other.{a.name},") others.append(f" other.{a.name},")
lines += others + [" )"] lines += [*others, " )"]
else: else:
lines.append(" return True") lines.append(" return True")
@ -1928,7 +1918,8 @@ def fields(cls):
generic_base = get_generic_base(cls) generic_base = get_generic_base(cls)
if generic_base is None and not isinstance(cls, type): if generic_base is None and not isinstance(cls, type):
raise TypeError("Passed object must be a class.") msg = "Passed object must be a class."
raise TypeError(msg)
attrs = getattr(cls, "__attrs_attrs__", None) attrs = getattr(cls, "__attrs_attrs__", None)
@ -1941,7 +1932,8 @@ def fields(cls):
# efficient. # efficient.
cls.__attrs_attrs__ = attrs cls.__attrs_attrs__ = attrs
return attrs return attrs
raise NotAnAttrsClassError(f"{cls!r} is not an attrs-decorated class.") msg = f"{cls!r} is not an attrs-decorated class."
raise NotAnAttrsClassError(msg)
return attrs return attrs
@ -1962,10 +1954,12 @@ def fields_dict(cls):
.. versionadded:: 18.1.0 .. versionadded:: 18.1.0
""" """
if not isinstance(cls, type): if not isinstance(cls, type):
raise TypeError("Passed object must be a class.") msg = "Passed object must be a class."
raise TypeError(msg)
attrs = getattr(cls, "__attrs_attrs__", None) attrs = getattr(cls, "__attrs_attrs__", None)
if attrs is None: if attrs is None:
raise NotAnAttrsClassError(f"{cls!r} is not an attrs-decorated class.") msg = f"{cls!r} is not an attrs-decorated class."
raise NotAnAttrsClassError(msg)
return {a.name: a for a in attrs} return {a.name: a for a in attrs}
@ -2001,6 +1995,7 @@ def _make_init(
cls, cls,
attrs, attrs,
pre_init, pre_init,
pre_init_has_args,
post_init, post_init,
frozen, frozen,
slots, slots,
@ -2015,7 +2010,8 @@ def _make_init(
) )
if frozen and has_cls_on_setattr: if frozen and has_cls_on_setattr:
raise ValueError("Frozen classes can't use on_setattr.") msg = "Frozen classes can't use on_setattr."
raise ValueError(msg)
needs_cached_setattr = cache_hash or frozen needs_cached_setattr = cache_hash or frozen
filtered_attrs = [] filtered_attrs = []
@ -2029,7 +2025,8 @@ def _make_init(
if a.on_setattr is not None: if a.on_setattr is not None:
if frozen is True: if frozen is True:
raise ValueError("Frozen classes can't use on_setattr.") msg = "Frozen classes can't use on_setattr."
raise ValueError(msg)
needs_cached_setattr = True needs_cached_setattr = True
elif has_cls_on_setattr and a.on_setattr is not setters.NO_OP: elif has_cls_on_setattr and a.on_setattr is not setters.NO_OP:
@ -2042,6 +2039,7 @@ def _make_init(
frozen, frozen,
slots, slots,
pre_init, pre_init,
pre_init_has_args,
post_init, post_init,
cache_hash, cache_hash,
base_attr_map, base_attr_map,
@ -2122,6 +2120,7 @@ def _attrs_to_init_script(
frozen, frozen,
slots, slots,
pre_init, pre_init,
pre_init_has_args,
post_init, post_init,
cache_hash, cache_hash,
base_attr_map, base_attr_map,
@ -2208,10 +2207,7 @@ def _attrs_to_init_script(
arg_name = a.alias arg_name = a.alias
has_factory = isinstance(a.default, Factory) has_factory = isinstance(a.default, Factory)
if has_factory and a.default.takes_self: maybe_self = "self" if has_factory and a.default.takes_self else ""
maybe_self = "self"
else:
maybe_self = ""
if a.init is False: if a.init is False:
if has_factory: if has_factory:
@ -2235,8 +2231,7 @@ def _attrs_to_init_script(
) )
) )
names_for_globals[init_factory_name] = a.default.factory names_for_globals[init_factory_name] = a.default.factory
else: elif a.converter is not None:
if a.converter is not None:
lines.append( lines.append(
fmt_setter_with_converter( fmt_setter_with_converter(
attr_name, attr_name,
@ -2362,7 +2357,7 @@ def _attrs_to_init_script(
# hash code would result in silent bugs. # hash code would result in silent bugs.
if cache_hash: if cache_hash:
if frozen: if frozen:
if slots: if slots: # noqa: SIM108
# if frozen and slots, then _setattr defined above # if frozen and slots, then _setattr defined above
init_hash_cache = "_setattr('%s', %s)" init_hash_cache = "_setattr('%s', %s)"
else: else:
@ -2380,11 +2375,23 @@ def _attrs_to_init_script(
lines.append(f"BaseException.__init__(self, {vals})") lines.append(f"BaseException.__init__(self, {vals})")
args = ", ".join(args) args = ", ".join(args)
pre_init_args = args
if kw_only_args: if kw_only_args:
args += "%s*, %s" % ( args += "%s*, %s" % (
", " if args else "", # leading comma ", " if args else "", # leading comma
", ".join(kw_only_args), # kw_only args ", ".join(kw_only_args), # kw_only args
) )
pre_init_kw_only_args = ", ".join(
["%s=%s" % (kw_arg, kw_arg) for kw_arg in kw_only_args]
)
pre_init_args += (
", " if pre_init_args else ""
) # handle only kwargs and no regular args
pre_init_args += pre_init_kw_only_args
if pre_init and pre_init_has_args:
# If pre init method has arguments, pass same arguments as `__init__`
lines[0] = "self.__attrs_pre_init__(%s)" % pre_init_args
return ( return (
"def %s(self, %s):\n %s\n" "def %s(self, %s):\n %s\n"
@ -2537,9 +2544,8 @@ class Attribute:
if type is None: if type is None:
type = ca.type type = ca.type
elif ca.type is not None: elif ca.type is not None:
raise ValueError( msg = "Type annotation and type argument cannot both be present"
"Type annotation and type argument cannot both be present" raise ValueError(msg)
)
inst_dict = { inst_dict = {
k: getattr(ca, k) k: getattr(ca, k)
for k in Attribute.__slots__ for k in Attribute.__slots__
@ -2663,7 +2669,8 @@ class _CountingAttr:
"on_setattr", "on_setattr",
"alias", "alias",
) )
__attrs_attrs__ = tuple( __attrs_attrs__ = (
*tuple(
Attribute( Attribute(
name=name, name=name,
alias=_default_init_alias_for(name), alias=_default_init_alias_for(name),
@ -2692,7 +2699,7 @@ class _CountingAttr:
"on_setattr", "on_setattr",
"alias", "alias",
) )
) + ( ),
Attribute( Attribute(
name="metadata", name="metadata",
alias="metadata", alias="metadata",
@ -2868,7 +2875,8 @@ def make_class(name, attrs, bases=(object,), **attributes_arguments):
elif isinstance(attrs, (list, tuple)): elif isinstance(attrs, (list, tuple)):
cls_dict = {a: attrib() for a in attrs} cls_dict = {a: attrib() for a in attrs}
else: else:
raise TypeError("attrs argument must be a dict or a list.") msg = "attrs argument must be a dict or a list."
raise TypeError(msg)
pre_init = cls_dict.pop("__attrs_pre_init__", None) pre_init = cls_dict.pop("__attrs_pre_init__", None)
post_init = cls_dict.pop("__attrs_post_init__", None) post_init = cls_dict.pop("__attrs_post_init__", None)
@ -2888,12 +2896,10 @@ def make_class(name, attrs, bases=(object,), **attributes_arguments):
# frame where the class is created. Bypass this step in environments where # frame where the class is created. Bypass this step in environments where
# sys._getframe is not defined (Jython for example) or sys._getframe is not # sys._getframe is not defined (Jython for example) or sys._getframe is not
# defined for arguments greater than 0 (IronPython). # defined for arguments greater than 0 (IronPython).
try: with contextlib.suppress(AttributeError, ValueError):
type_.__module__ = sys._getframe(1).f_globals.get( type_.__module__ = sys._getframe(1).f_globals.get(
"__name__", "__main__" "__name__", "__main__"
) )
except (AttributeError, ValueError):
pass
# We do it here for proper warnings with meaningful stacklevel. # We do it here for proper warnings with meaningful stacklevel.
cmp = attributes_arguments.pop("cmp", None) cmp = attributes_arguments.pop("cmp", None)

View file

@ -59,7 +59,7 @@ def define(
.. caution:: .. caution::
Usually this has only upsides and few visible effects in everyday Usually this has only upsides and few visible effects in everyday
programming. But it *can* lead to some suprising behaviors, so please programming. But it *can* lead to some surprising behaviors, so please
make sure to read :term:`slotted classes`. make sure to read :term:`slotted classes`.
- *auto_exc=True* - *auto_exc=True*
- *auto_detect=True* - *auto_detect=True*
@ -131,10 +131,8 @@ def define(
for base_cls in cls.__bases__: for base_cls in cls.__bases__:
if base_cls.__setattr__ is _frozen_setattrs: if base_cls.__setattr__ is _frozen_setattrs:
if had_on_setattr: if had_on_setattr:
raise ValueError( msg = "Frozen classes can't use on_setattr (frozen-ness was inherited)."
"Frozen classes can't use on_setattr " raise ValueError(msg)
"(frozen-ness was inherited)."
)
on_setattr = setters.NO_OP on_setattr = setters.NO_OP
break break
@ -151,7 +149,7 @@ def define(
# if it's used as `@attrs` but ``None`` if used as `@attrs()`. # if it's used as `@attrs` but ``None`` if used as `@attrs()`.
if maybe_cls is None: if maybe_cls is None:
return wrap return wrap
else:
return wrap(maybe_cls) return wrap(maybe_cls)
@ -180,10 +178,9 @@ def field(
Identical to `attr.ib`, except keyword-only and with some arguments Identical to `attr.ib`, except keyword-only and with some arguments
removed. removed.
.. versionadded:: 22.3.0 .. versionadded:: 23.1.0
The *type* parameter has been re-added; mostly for The *type* parameter has been re-added; mostly for `attrs.make_class`.
{func}`attrs.make_class`. Please note that type checkers ignore this Please note that type checkers ignore this metadata.
metadata.
.. versionadded:: 20.1.0 .. versionadded:: 20.1.0
""" """
return attrib( return attrib(

View file

@ -70,21 +70,20 @@ def default_if_none(default=NOTHING, factory=None):
.. versionadded:: 18.2.0 .. versionadded:: 18.2.0
""" """
if default is NOTHING and factory is None: if default is NOTHING and factory is None:
raise TypeError("Must pass either `default` or `factory`.") msg = "Must pass either `default` or `factory`."
raise TypeError(msg)
if default is not NOTHING and factory is not None: if default is not NOTHING and factory is not None:
raise TypeError( msg = "Must pass either `default` or `factory` but not both."
"Must pass either `default` or `factory` but not both." raise TypeError(msg)
)
if factory is not None: if factory is not None:
default = Factory(factory) default = Factory(factory)
if isinstance(default, Factory): if isinstance(default, Factory):
if default.takes_self: if default.takes_self:
raise ValueError( msg = "`takes_self` is not supported by default_if_none."
"`takes_self` is not supported by default_if_none." raise ValueError(msg)
)
def default_if_none_converter(val): def default_if_none_converter(val):
if val is not None: if val is not None:
@ -141,4 +140,5 @@ def to_bool(val):
except TypeError: except TypeError:
# Raised when "val" is not hashable (e.g., lists) # Raised when "val" is not hashable (e.g., lists)
pass pass
raise ValueError(f"Cannot convert value to bool: {val}") msg = f"Cannot convert value to bool: {val}"
raise ValueError(msg)

View file

@ -1,5 +1,9 @@
# SPDX-License-Identifier: MIT # SPDX-License-Identifier: MIT
from __future__ import annotations
from typing import ClassVar
class FrozenError(AttributeError): class FrozenError(AttributeError):
""" """
@ -13,7 +17,7 @@ class FrozenError(AttributeError):
""" """
msg = "can't set attribute" msg = "can't set attribute"
args = [msg] args: ClassVar[tuple[str]] = [msg]
class FrozenInstanceError(FrozenError): class FrozenInstanceError(FrozenError):

View file

@ -13,6 +13,7 @@ def _split_what(what):
""" """
return ( return (
frozenset(cls for cls in what if isinstance(cls, type)), 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)), frozenset(cls for cls in what if isinstance(cls, Attribute)),
) )
@ -22,14 +23,21 @@ def include(*what):
Include *what*. Include *what*.
:param what: What to include. :param what: What to include.
:type what: `list` of `type` or `attrs.Attribute`\\ s :type what: `list` of classes `type`, field names `str` or
`attrs.Attribute`\\ s
:rtype: `callable` :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): 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_ return include_
@ -39,13 +47,20 @@ def exclude(*what):
Exclude *what*. Exclude *what*.
:param what: What to exclude. :param what: What to exclude.
:type what: `list` of classes or `attrs.Attribute`\\ s. :type what: `list` of classes `type`, field names `str` or
`attrs.Attribute`\\ s.
:rtype: `callable` :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): 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_ return exclude_

View file

@ -2,5 +2,5 @@ from typing import Any, Union
from . import Attribute, _FilterType from . import Attribute, _FilterType
def include(*what: Union[type, Attribute[Any]]) -> _FilterType[Any]: ... def include(*what: Union[type, str, Attribute[Any]]) -> _FilterType[Any]: ...
def exclude(*what: Union[type, Attribute[Any]]) -> _FilterType[Any]: ... def exclude(*what: Union[type, str, Attribute[Any]]) -> _FilterType[Any]: ...

View file

@ -97,23 +97,21 @@ class _InstanceOfValidator:
We use a callable class to be able to change the ``__repr__``. We use a callable class to be able to change the ``__repr__``.
""" """
if not isinstance(value, self.type): if not isinstance(value, self.type):
raise TypeError( msg = "'{name}' must be {type!r} (got {value!r} that is a {actual!r}).".format(
"'{name}' must be {type!r} (got {value!r} that is a "
"{actual!r}).".format(
name=attr.name, name=attr.name,
type=self.type, type=self.type,
actual=value.__class__, actual=value.__class__,
value=value, value=value,
), )
raise TypeError(
msg,
attr, attr,
self.type, self.type,
value, value,
) )
def __repr__(self): def __repr__(self):
return "<instance_of validator for type {type!r}>".format( return f"<instance_of validator for type {self.type!r}>"
type=self.type
)
def instance_of(type): def instance_of(type):
@ -142,20 +140,18 @@ class _MatchesReValidator:
We use a callable class to be able to change the ``__repr__``. We use a callable class to be able to change the ``__repr__``.
""" """
if not self.match_func(value): if not self.match_func(value):
raise ValueError( msg = "'{name}' must match regex {pattern!r} ({value!r} doesn't)".format(
"'{name}' must match regex {pattern!r}"
" ({value!r} doesn't)".format(
name=attr.name, pattern=self.pattern.pattern, value=value name=attr.name, pattern=self.pattern.pattern, value=value
), )
raise ValueError(
msg,
attr, attr,
self.pattern, self.pattern,
value, value,
) )
def __repr__(self): def __repr__(self):
return "<matches_re validator for pattern {pattern!r}>".format( return f"<matches_re validator for pattern {self.pattern!r}>"
pattern=self.pattern
)
def matches_re(regex, flags=0, func=None): def matches_re(regex, flags=0, func=None):
@ -176,22 +172,17 @@ def matches_re(regex, flags=0, func=None):
""" """
valid_funcs = (re.fullmatch, None, re.search, re.match) valid_funcs = (re.fullmatch, None, re.search, re.match)
if func not in valid_funcs: if func not in valid_funcs:
raise ValueError( msg = "'func' must be one of {}.".format(
"'func' must be one of {}.".format(
", ".join( ", ".join(
sorted( sorted(e and e.__name__ or "None" for e in set(valid_funcs))
e and e.__name__ or "None" for e in set(valid_funcs)
)
)
) )
) )
raise ValueError(msg)
if isinstance(regex, Pattern): if isinstance(regex, Pattern):
if flags: if flags:
raise TypeError( msg = "'flags' can only be used with a string pattern; pass flags to re.compile() instead"
"'flags' can only be used with a string pattern; " raise TypeError(msg)
"pass flags to re.compile() instead"
)
pattern = regex pattern = regex
else: else:
pattern = re.compile(regex, flags) pattern = re.compile(regex, flags)
@ -215,20 +206,18 @@ class _ProvidesValidator:
We use a callable class to be able to change the ``__repr__``. We use a callable class to be able to change the ``__repr__``.
""" """
if not self.interface.providedBy(value): if not self.interface.providedBy(value):
raise TypeError( msg = "'{name}' must provide {interface!r} which {value!r} doesn't.".format(
"'{name}' must provide {interface!r} which {value!r} "
"doesn't.".format(
name=attr.name, interface=self.interface, value=value name=attr.name, interface=self.interface, value=value
), )
raise TypeError(
msg,
attr, attr,
self.interface, self.interface,
value, value,
) )
def __repr__(self): def __repr__(self):
return "<provides validator for interface {interface!r}>".format( return f"<provides validator for interface {self.interface!r}>"
interface=self.interface
)
def provides(interface): def provides(interface):
@ -269,9 +258,7 @@ class _OptionalValidator:
self.validator(inst, attr, value) self.validator(inst, attr, value)
def __repr__(self): def __repr__(self):
return "<optional validator for {what} or None>".format( return f"<optional validator for {self.validator!r} or None>"
what=repr(self.validator)
)
def optional(validator): def optional(validator):
@ -304,19 +291,16 @@ class _InValidator:
in_options = False in_options = False
if not in_options: if not in_options:
msg = f"'{attr.name}' must be in {self.options!r} (got {value!r})"
raise ValueError( raise ValueError(
"'{name}' must be in {options!r} (got {value!r})".format( msg,
name=attr.name, options=self.options, value=value
),
attr, attr,
self.options, self.options,
value, value,
) )
def __repr__(self): def __repr__(self):
return "<in_ validator with options {options!r}>".format( return f"<in_ validator with options {self.options!r}>"
options=self.options
)
def in_(options): def in_(options):
@ -402,11 +386,8 @@ class _DeepIterable:
else f" {self.iterable_validator!r}" else f" {self.iterable_validator!r}"
) )
return ( return (
"<deep_iterable validator for{iterable_identifier}" f"<deep_iterable validator for{iterable_identifier}"
" iterables of {member!r}>" f" iterables of {self.member_validator!r}>"
).format(
iterable_identifier=iterable_identifier,
member=self.member_validator,
) )
@ -477,19 +458,11 @@ class _NumberValidator:
We use a callable class to be able to change the ``__repr__``. We use a callable class to be able to change the ``__repr__``.
""" """
if not self.compare_func(value, self.bound): if not self.compare_func(value, self.bound):
raise ValueError( msg = f"'{attr.name}' must be {self.compare_op} {self.bound}: {value}"
"'{name}' must be {op} {bound}: {value}".format( raise ValueError(msg)
name=attr.name,
op=self.compare_op,
bound=self.bound,
value=value,
)
)
def __repr__(self): def __repr__(self):
return "<Validator for x {op} {bound}>".format( return f"<Validator for x {self.compare_op} {self.bound}>"
op=self.compare_op, bound=self.bound
)
def lt(val): def lt(val):
@ -549,11 +522,8 @@ class _MaxLengthValidator:
We use a callable class to be able to change the ``__repr__``. We use a callable class to be able to change the ``__repr__``.
""" """
if len(value) > self.max_length: if len(value) > self.max_length:
raise ValueError( msg = f"Length of '{attr.name}' must be <= {self.max_length}: {len(value)}"
"Length of '{name}' must be <= {max}: {len}".format( raise ValueError(msg)
name=attr.name, max=self.max_length, len=len(value)
)
)
def __repr__(self): def __repr__(self):
return f"<max_len validator for {self.max_length}>" return f"<max_len validator for {self.max_length}>"
@ -580,11 +550,8 @@ class _MinLengthValidator:
We use a callable class to be able to change the ``__repr__``. We use a callable class to be able to change the ``__repr__``.
""" """
if len(value) < self.min_length: if len(value) < self.min_length:
raise ValueError( msg = f"Length of '{attr.name}' must be => {self.min_length}: {len(value)}"
"Length of '{name}' must be => {min}: {len}".format( raise ValueError(msg)
name=attr.name, min=self.min_length, len=len(value)
)
)
def __repr__(self): def __repr__(self):
return f"<min_len validator for {self.min_length}>" return f"<min_len validator for {self.min_length}>"
@ -611,22 +578,16 @@ class _SubclassOfValidator:
We use a callable class to be able to change the ``__repr__``. We use a callable class to be able to change the ``__repr__``.
""" """
if not issubclass(value, self.type): if not issubclass(value, self.type):
msg = f"'{attr.name}' must be a subclass of {self.type!r} (got {value!r})."
raise TypeError( raise TypeError(
"'{name}' must be a subclass of {type!r} " msg,
"(got {value!r}).".format(
name=attr.name,
type=self.type,
value=value,
),
attr, attr,
self.type, self.type,
value, value,
) )
def __repr__(self): def __repr__(self):
return "<subclass_of validator for type {type!r}>".format( return f"<subclass_of validator for type {self.type!r}>"
type=self.type
)
def _subclass_of(type): def _subclass_of(type):
@ -680,7 +641,7 @@ class _NotValidator:
def __repr__(self): def __repr__(self):
return ( return (
"<not_ validator wrapping {what!r}, " "capturing {exc_types!r}>" "<not_ validator wrapping {what!r}, capturing {exc_types!r}>"
).format( ).format(
what=self.validator, what=self.validator,
exc_types=self.exc_types, exc_types=self.exc_types,