about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/attr/validators.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/attr/validators.py')
-rw-r--r--.venv/lib/python3.12/site-packages/attr/validators.py710
1 files changed, 710 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/attr/validators.py b/.venv/lib/python3.12/site-packages/attr/validators.py
new file mode 100644
index 00000000..e7b75525
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/attr/validators.py
@@ -0,0 +1,710 @@
+# SPDX-License-Identifier: MIT
+
+"""
+Commonly useful validators.
+"""
+
+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
+
+
+__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",
+    "or_",
+    "set_disabled",
+]
+
+
+def set_disabled(disabled):
+    """
+    Globally disable or enable running validators.
+
+    By default, they are run.
+
+    Args:
+        disabled (bool): If `True`, disable running all validators.
+
+    .. 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.
+
+    Returns:
+        bool:`True` if validators are currently disabled.
+
+    .. 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, unsafe_hash=True)
+class _InstanceOfValidator:
+    type = attrib()
+
+    def __call__(self, inst, attr, value):
+        """
+        We use a callable class to be able to change the ``__repr__``.
+        """
+        if not isinstance(value, self.type):
+            msg = f"'{attr.name}' must be {self.type!r} (got {value!r} that is a {value.__class__!r})."
+            raise TypeError(
+                msg,
+                attr,
+                self.type,
+                value,
+            )
+
+    def __repr__(self):
+        return f"<instance_of validator for type {self.type!r}>"
+
+
+def instance_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
+    `isinstance` therefore it's also valid to pass a tuple of types).
+
+    Args:
+        type (type | tuple[type]): The type to check for.
+
+    Raises:
+        TypeError:
+            With a human readable error message, the attribute (of type
+            `attrs.Attribute`), the expected type, and the value it got.
+    """
+    return _InstanceOfValidator(type)
+
+
+@attrs(repr=False, frozen=True, slots=True)
+class _MatchesReValidator:
+    pattern = attrib()
+    match_func = attrib()
+
+    def __call__(self, inst, attr, value):
+        """
+        We use a callable class to be able to change the ``__repr__``.
+        """
+        if not self.match_func(value):
+            msg = f"'{attr.name}' must match regex {self.pattern.pattern!r} ({value!r} doesn't)"
+            raise ValueError(
+                msg,
+                attr,
+                self.pattern,
+                value,
+            )
+
+    def __repr__(self):
+        return f"<matches_re validator for pattern {self.pattern!r}>"
+
+
+def matches_re(regex, flags=0, func=None):
+    r"""
+    A validator that raises `ValueError` if the initializer is called with a
+    string that doesn't match *regex*.
+
+    Args:
+        regex (str, re.Pattern):
+            A regex string or precompiled pattern to match against
+
+        flags (int):
+            Flags that will be passed to the underlying re function (default 0)
+
+        func (typing.Callable):
+            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.
+    """
+    valid_funcs = (re.fullmatch, None, re.search, re.match)
+    if func not in valid_funcs:
+        msg = "'func' must be one of {}.".format(
+            ", ".join(
+                sorted((e and e.__name__) or "None" for e in set(valid_funcs))
+            )
+        )
+        raise ValueError(msg)
+
+    if isinstance(regex, Pattern):
+        if flags:
+            msg = "'flags' can only be used with a string pattern; pass flags to re.compile() instead"
+            raise TypeError(msg)
+        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:
+        match_func = pattern.fullmatch
+
+    return _MatchesReValidator(pattern, match_func)
+
+
+@attrs(repr=False, slots=True, unsafe_hash=True)
+class _OptionalValidator:
+    validator = attrib()
+
+    def __call__(self, inst, attr, value):
+        if value is None:
+            return
+
+        self.validator(inst, attr, value)
+
+    def __repr__(self):
+        return f"<optional validator for {self.validator!r} or None>"
+
+
+def optional(validator):
+    """
+    A validator that makes an attribute optional.  An optional attribute is one
+    which can be set to `None` in addition to satisfying the requirements of
+    the sub-validator.
+
+    Args:
+        validator
+            (typing.Callable | tuple[typing.Callable] | list[typing.Callable]):
+            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, tuple)):
+        return _OptionalValidator(_AndValidator(validator))
+
+    return _OptionalValidator(validator)
+
+
+@attrs(repr=False, slots=True, unsafe_hash=True)
+class _InValidator:
+    options = attrib()
+    _original_options = attrib(hash=False)
+
+    def __call__(self, inst, attr, value):
+        try:
+            in_options = value in self.options
+        except TypeError:  # e.g. `1 in "abc"`
+            in_options = False
+
+        if not in_options:
+            msg = f"'{attr.name}' must be in {self._original_options!r} (got {value!r})"
+            raise ValueError(
+                msg,
+                attr,
+                self._original_options,
+                value,
+            )
+
+    def __repr__(self):
+        return f"<in_ validator with options {self._original_options!r}>"
+
+
+def in_(options):
+    """
+    A validator that raises a `ValueError` if the initializer is called with a
+    value that does not belong in the *options* provided.
+
+    The check is performed using ``value in options``, so *options* has to
+    support that operation.
+
+    To keep the validator hashable, dicts, lists, and sets are transparently
+    transformed into a `tuple`.
+
+    Args:
+        options: Allowed options.
+
+    Raises:
+        ValueError:
+            With a human readable error message, the attribute (of 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.
+    .. versionchanged:: 24.1.0
+       *options* that are a list, dict, or a set are now transformed into a
+       tuple to keep the validator hashable.
+    """
+    repr_options = options
+    if isinstance(options, (list, dict, set)):
+        options = tuple(options)
+
+    return _InValidator(options, repr_options)
+
+
+@attrs(repr=False, slots=False, unsafe_hash=True)
+class _IsCallableValidator:
+    def __call__(self, inst, attr, value):
+        """
+        We use a callable class to be able to change the ``__repr__``.
+        """
+        if not callable(value):
+            message = (
+                "'{name}' must be callable "
+                "(got {value!r} that is a {actual!r})."
+            )
+            raise NotCallableError(
+                msg=message.format(
+                    name=attr.name, value=value, actual=value.__class__
+                ),
+                value=value,
+            )
+
+    def __repr__(self):
+        return "<is_callable validator>"
+
+
+def is_callable():
+    """
+    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:
+        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, unsafe_hash=True)
+class _DeepIterable:
+    member_validator = attrib(validator=is_callable())
+    iterable_validator = attrib(
+        default=None, validator=optional(is_callable())
+    )
+
+    def __call__(self, inst, attr, value):
+        """
+        We use a callable class to be able to change the ``__repr__``.
+        """
+        if self.iterable_validator is not None:
+            self.iterable_validator(inst, attr, value)
+
+        for member in value:
+            self.member_validator(inst, attr, member)
+
+    def __repr__(self):
+        iterable_identifier = (
+            ""
+            if self.iterable_validator is None
+            else f" {self.iterable_validator!r}"
+        )
+        return (
+            f"<deep_iterable validator for{iterable_identifier}"
+            f" iterables of {self.member_validator!r}>"
+        )
+
+
+def deep_iterable(member_validator, iterable_validator=None):
+    """
+    A validator that performs deep validation of an iterable.
+
+    Args:
+        member_validator: Validator to apply to iterable members.
+
+        iterable_validator:
+            Validator to apply to iterable itself (optional).
+
+    Raises
+        TypeError: if any sub-validators fail
+
+    .. versionadded:: 19.1.0
+    """
+    if isinstance(member_validator, (list, tuple)):
+        member_validator = and_(*member_validator)
+    return _DeepIterable(member_validator, iterable_validator)
+
+
+@attrs(repr=False, slots=True, unsafe_hash=True)
+class _DeepMapping:
+    key_validator = attrib(validator=is_callable())
+    value_validator = attrib(validator=is_callable())
+    mapping_validator = attrib(default=None, validator=optional(is_callable()))
+
+    def __call__(self, inst, attr, value):
+        """
+        We use a callable class to be able to change the ``__repr__``.
+        """
+        if self.mapping_validator is not None:
+            self.mapping_validator(inst, attr, value)
+
+        for key in value:
+            self.key_validator(inst, attr, key)
+            self.value_validator(inst, attr, value[key])
+
+    def __repr__(self):
+        return f"<deep_mapping validator for objects mapping {self.key_validator!r} to {self.value_validator!r}>"
+
+
+def deep_mapping(key_validator, value_validator, mapping_validator=None):
+    """
+    A validator that performs deep validation of a dictionary.
+
+    Args:
+        key_validator: Validator to apply to dictionary keys.
+
+        value_validator: Validator to apply to dictionary values.
+
+        mapping_validator:
+            Validator to apply to top-level mapping attribute (optional).
+
+    .. versionadded:: 19.1.0
+
+    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):
+            msg = f"'{attr.name}' must be {self.compare_op} {self.bound}: {value}"
+            raise ValueError(msg)
+
+    def __repr__(self):
+        return f"<Validator for x {self.compare_op} {self.bound}>"
+
+
+def lt(val):
+    """
+    A validator that raises `ValueError` if the initializer is called with a
+    number larger or equal to *val*.
+
+    The validator uses `operator.lt` to compare the values.
+
+    Args:
+        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*.
+
+    The validator uses `operator.le` to compare the values.
+
+    Args:
+        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*.
+
+    The validator uses `operator.ge` to compare the values.
+
+    Args:
+        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*.
+
+    The validator uses `operator.ge` to compare the values.
+
+    Args:
+       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:
+            msg = f"Length of '{attr.name}' must be <= {self.max_length}: {len(value)}"
+            raise ValueError(msg)
+
+    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*.
+
+    Args:
+        length (int): 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:
+            msg = f"Length of '{attr.name}' must be >= {self.min_length}: {len(value)}"
+            raise ValueError(msg)
+
+    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*.
+
+    Args:
+        length (int): Minimum length of the string or iterable
+
+    .. versionadded:: 22.1.0
+    """
+    return _MinLengthValidator(length)
+
+
+@attrs(repr=False, slots=True, unsafe_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):
+            msg = f"'{attr.name}' must be a subclass of {self.type!r} (got {value!r})."
+            raise TypeError(
+                msg,
+                attr,
+                self.type,
+                value,
+            )
+
+    def __repr__(self):
+        return f"<subclass_of validator for type {self.type!r}>"
+
+
+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).
+
+    Args:
+        type (type | tuple[type, ...]): The type(s) to check for.
+
+    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, unsafe_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 f"<not_ validator wrapping {self.validator!r}, capturing {self.exc_types!r}>"
+
+
+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_(...))``.
+
+    Args:
+        validator: A validator to be logically inverted.
+
+        msg (str):
+            Message to raise if validator fails. Formatted with keys
+            ``exc_types`` and ``validator``.
+
+        exc_types (tuple[type, ...]):
+            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)
+
+
+@attrs(repr=False, slots=True, unsafe_hash=True)
+class _OrValidator:
+    validators = attrib()
+
+    def __call__(self, inst, attr, value):
+        for v in self.validators:
+            try:
+                v(inst, attr, value)
+            except Exception:  # noqa: BLE001, PERF203, S112
+                continue
+            else:
+                return
+
+        msg = f"None of {self.validators!r} satisfied for value {value!r}"
+        raise ValueError(msg)
+
+    def __repr__(self):
+        return f"<or validator wrapping {self.validators!r}>"
+
+
+def or_(*validators):
+    """
+    A validator that composes multiple validators into one.
+
+    When called on a value, it runs all wrapped validators until one of them is
+    satisfied.
+
+    Args:
+        validators (~collections.abc.Iterable[typing.Callable]):
+            Arbitrary number of validators.
+
+    Raises:
+        ValueError:
+            If no validator is satisfied. Raised with a human-readable error
+            message listing all the wrapped validators and the value that
+            failed all of them.
+
+    .. versionadded:: 24.1.0
+    """
+    vals = []
+    for v in validators:
+        vals.extend(v.validators if isinstance(v, _OrValidator) else [v])
+
+    return _OrValidator(tuple(vals))