view mercurial/thirdparty/attr/_next_gen.py @ 51486:0ddc34330d41

branchcache: do not accept "empty update" This currently does not happens and it will be simpler that is remains that way. If all update do something, we will be able to simply declare, in a later changesets, that all update to result in a dirty branchcache.
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Fri, 08 Mar 2024 15:06:54 +0100
parents e1c586b9a43c
children
line wrap: on
line source

# SPDX-License-Identifier: MIT

"""
These are Python 3.6+-only and 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,
    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``.
    """

    def do_it(cls, auto_attribs):
        return attrs(
            maybe_cls=cls,
            these=these,
            repr=repr,
            hash=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,
    converter=None,
    factory=None,
    kw_only=False,
    eq=None,
    order=None,
    on_setattr=None,
):
    """
    Identical to `attr.ib`, except keyword-only and with some arguments
    removed.

    .. versionadded:: 20.1.0
    """
    return attrib(
        default=default,
        validator=validator,
        repr=repr,
        hash=hash,
        init=init,
        metadata=metadata,
        converter=converter,
        factory=factory,
        kw_only=kw_only,
        eq=eq,
        order=order,
        on_setattr=on_setattr,
    )


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
    )