Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
_funcs.py498 linesDownload Raw Back to attr
1# SPDX-License-Identifier: MIT2 3 4import copy5 6from ._compat import get_generic_base7from ._make import _OBJ_SETATTR, NOTHING, fields8from .exceptions import AttrsAttributeNotFoundError9 10 11_ATOMIC_TYPES = frozenset(12    {13        type(None),14        bool,15        int,16        float,17        str,18        complex,19        bytes,20        type(...),21        type,22        range,23        property,24    }25)26 27 28def asdict(29    inst,30    recurse=True,31    filter=None,32    dict_factory=dict,33    retain_collection_types=False,34    value_serializer=None,35):36    """37    Return the *attrs* attribute values of *inst* as a dict.38 39    Optionally recurse into other *attrs*-decorated classes.40 41    Args:42        inst: Instance of an *attrs*-decorated class.43 44        recurse (bool): Recurse into classes that are also *attrs*-decorated.45 46        filter (~typing.Callable):47            A callable whose return code determines whether an attribute or48            element is included (`True`) or dropped (`False`).  Is called with49            the `attrs.Attribute` as the first argument and the value as the50            second argument.51 52        dict_factory (~typing.Callable):53            A callable to produce dictionaries from.  For example, to produce54            ordered dictionaries instead of normal Python dictionaries, pass in55            ``collections.OrderedDict``.56 57        retain_collection_types (bool):58            Do not convert to `list` when encountering an attribute whose type59            is `tuple` or `set`.  Only meaningful if *recurse* is `True`.60 61        value_serializer (typing.Callable | None):62            A hook that is called for every attribute or dict key/value.  It63            receives the current instance, field and value and must return the64            (updated) value.  The hook is run *after* the optional *filter* has65            been applied.66 67    Returns:68        Return type of *dict_factory*.69 70    Raises:71        attrs.exceptions.NotAnAttrsClassError:72            If *cls* is not an *attrs* class.73 74    ..  versionadded:: 16.0.0 *dict_factory*75    ..  versionadded:: 16.1.0 *retain_collection_types*76    ..  versionadded:: 20.3.0 *value_serializer*77    ..  versionadded:: 21.3.078        If a dict has a collection for a key, it is serialized as a tuple.79    """80    attrs = fields(inst.__class__)81    rv = dict_factory()82    for a in attrs:83        v = getattr(inst, a.name)84        if filter is not None and not filter(a, v):85            continue86 87        if value_serializer is not None:88            v = value_serializer(inst, a, v)89 90        if recurse is True:91            value_type = type(v)92            if value_type in _ATOMIC_TYPES:93                rv[a.name] = v94            elif has(value_type):95                rv[a.name] = asdict(96                    v,97                    recurse=True,98                    filter=filter,99                    dict_factory=dict_factory,100                    retain_collection_types=retain_collection_types,101                    value_serializer=value_serializer,102                )103            elif issubclass(value_type, (tuple, list, set, frozenset)):104                cf = value_type if retain_collection_types is True else list105                items = [106                    _asdict_anything(107                        i,108                        is_key=False,109                        filter=filter,110                        dict_factory=dict_factory,111                        retain_collection_types=retain_collection_types,112                        value_serializer=value_serializer,113                    )114                    for i in v115                ]116                try:117                    rv[a.name] = cf(items)118                except TypeError:119                    if not issubclass(cf, tuple):120                        raise121                    # Workaround for TypeError: cf.__new__() missing 1 required122                    # positional argument (which appears, for a namedturle)123                    rv[a.name] = cf(*items)124            elif issubclass(value_type, dict):125                df = dict_factory126                rv[a.name] = df(127                    (128                        _asdict_anything(129                            kk,130                            is_key=True,131                            filter=filter,132                            dict_factory=df,133                            retain_collection_types=retain_collection_types,134                            value_serializer=value_serializer,135                        ),136                        _asdict_anything(137                            vv,138                            is_key=False,139                            filter=filter,140                            dict_factory=df,141                            retain_collection_types=retain_collection_types,142                            value_serializer=value_serializer,143                        ),144                    )145                    for kk, vv in v.items()146                )147            else:148                rv[a.name] = v149        else:150            rv[a.name] = v151    return rv152 153 154def _asdict_anything(155    val,156    is_key,157    filter,158    dict_factory,159    retain_collection_types,160    value_serializer,161):162    """163    ``asdict`` only works on attrs instances, this works on anything.164    """165    val_type = type(val)166    if val_type in _ATOMIC_TYPES:167        rv = val168        if value_serializer is not None:169            rv = value_serializer(None, None, rv)170    elif getattr(val_type, "__attrs_attrs__", None) is not None:171        # Attrs class.172        rv = asdict(173            val,174            recurse=True,175            filter=filter,176            dict_factory=dict_factory,177            retain_collection_types=retain_collection_types,178            value_serializer=value_serializer,179        )180    elif issubclass(val_type, (tuple, list, set, frozenset)):181        if retain_collection_types is True:182            cf = val.__class__183        elif is_key:184            cf = tuple185        else:186            cf = list187 188        rv = cf(189            [190                _asdict_anything(191                    i,192                    is_key=False,193                    filter=filter,194                    dict_factory=dict_factory,195                    retain_collection_types=retain_collection_types,196                    value_serializer=value_serializer,197                )198                for i in val199            ]200        )201    elif issubclass(val_type, dict):202        df = dict_factory203        rv = df(204            (205                _asdict_anything(206                    kk,207                    is_key=True,208                    filter=filter,209                    dict_factory=df,210                    retain_collection_types=retain_collection_types,211                    value_serializer=value_serializer,212                ),213                _asdict_anything(214                    vv,215                    is_key=False,216                    filter=filter,217                    dict_factory=df,218                    retain_collection_types=retain_collection_types,219                    value_serializer=value_serializer,220                ),221            )222            for kk, vv in val.items()223        )224    else:225        rv = val226        if value_serializer is not None:227            rv = value_serializer(None, None, rv)228 229    return rv230 231 232def astuple(233    inst,234    recurse=True,235    filter=None,236    tuple_factory=tuple,237    retain_collection_types=False,238):239    """240    Return the *attrs* attribute values of *inst* as a tuple.241 242    Optionally recurse into other *attrs*-decorated classes.243 244    Args:245        inst: Instance of an *attrs*-decorated class.246 247        recurse (bool):248            Recurse into classes that are also *attrs*-decorated.249 250        filter (~typing.Callable):251            A callable whose return code determines whether an attribute or252            element is included (`True`) or dropped (`False`).  Is called with253            the `attrs.Attribute` as the first argument and the value as the254            second argument.255 256        tuple_factory (~typing.Callable):257            A callable to produce tuples from. For example, to produce lists258            instead of tuples.259 260        retain_collection_types (bool):261            Do not convert to `list` or `dict` when encountering an attribute262            which type is `tuple`, `dict` or `set`. Only meaningful if263            *recurse* is `True`.264 265    Returns:266        Return type of *tuple_factory*267 268    Raises:269        attrs.exceptions.NotAnAttrsClassError:270            If *cls* is not an *attrs* class.271 272    ..  versionadded:: 16.2.0273    """274    attrs = fields(inst.__class__)275    rv = []276    retain = retain_collection_types  # Very long. :/277    for a in attrs:278        v = getattr(inst, a.name)279        if filter is not None and not filter(a, v):280            continue281        value_type = type(v)282        if recurse is True:283            if value_type in _ATOMIC_TYPES:284                rv.append(v)285            elif has(value_type):286                rv.append(287                    astuple(288                        v,289                        recurse=True,290                        filter=filter,291                        tuple_factory=tuple_factory,292                        retain_collection_types=retain,293                    )294                )295            elif issubclass(value_type, (tuple, list, set, frozenset)):296                cf = v.__class__ if retain is True else list297                items = [298                    (299                        astuple(300                            j,301                            recurse=True,302                            filter=filter,303                            tuple_factory=tuple_factory,304                            retain_collection_types=retain,305                        )306                        if has(j.__class__)307                        else j308                    )309                    for j in v310                ]311                try:312                    rv.append(cf(items))313                except TypeError:314                    if not issubclass(cf, tuple):315                        raise316                    # Workaround for TypeError: cf.__new__() missing 1 required317                    # positional argument (which appears, for a namedturle)318                    rv.append(cf(*items))319            elif issubclass(value_type, dict):320                df = value_type if retain is True else dict321                rv.append(322                    df(323                        (324                            (325                                astuple(326                                    kk,327                                    tuple_factory=tuple_factory,328                                    retain_collection_types=retain,329                                )330                                if has(kk.__class__)331                                else kk332                            ),333                            (334                                astuple(335                                    vv,336                                    tuple_factory=tuple_factory,337                                    retain_collection_types=retain,338                                )339                                if has(vv.__class__)340                                else vv341                            ),342                        )343                        for kk, vv in v.items()344                    )345                )346            else:347                rv.append(v)348        else:349            rv.append(v)350 351    return rv if tuple_factory is list else tuple_factory(rv)352 353 354def has(cls):355    """356    Check whether *cls* is a class with *attrs* attributes.357 358    Args:359        cls (type): Class to introspect.360 361    Raises:362        TypeError: If *cls* is not a class.363 364    Returns:365        bool:366    """367    attrs = getattr(cls, "__attrs_attrs__", None)368    if attrs is not None:369        return True370 371    # No attrs, maybe it's a specialized generic (A[str])?372    generic_base = get_generic_base(cls)373    if generic_base is not None:374        generic_attrs = getattr(generic_base, "__attrs_attrs__", None)375        if generic_attrs is not None:376            # Stick it on here for speed next time.377            cls.__attrs_attrs__ = generic_attrs378        return generic_attrs is not None379    return False380 381 382def assoc(inst, **changes):383    """384    Copy *inst* and apply *changes*.385 386    This is different from `evolve` that applies the changes to the arguments387    that create the new instance.388 389    `evolve`'s behavior is preferable, but there are `edge cases`_ where it390    doesn't work. Therefore `assoc` is deprecated, but will not be removed.391 392    .. _`edge cases`: https://github.com/python-attrs/attrs/issues/251393 394    Args:395        inst: Instance of a class with *attrs* attributes.396 397        changes: Keyword changes in the new copy.398 399    Returns:400        A copy of inst with *changes* incorporated.401 402    Raises:403        attrs.exceptions.AttrsAttributeNotFoundError:404            If *attr_name* couldn't be found on *cls*.405 406        attrs.exceptions.NotAnAttrsClassError:407            If *cls* is not an *attrs* class.408 409    ..  deprecated:: 17.1.0410        Use `attrs.evolve` instead if you can. This function will not be411        removed du to the slightly different approach compared to412        `attrs.evolve`, though.413    """414    new = copy.copy(inst)415    attrs = fields(inst.__class__)416    for k, v in changes.items():417        a = getattr(attrs, k, NOTHING)418        if a is NOTHING:419            msg = f"{k} is not an attrs attribute on {new.__class__}."420            raise AttrsAttributeNotFoundError(msg)421        _OBJ_SETATTR(new, k, v)422    return new423 424 425def resolve_types(426    cls, globalns=None, localns=None, attribs=None, include_extras=True427):428    """429    Resolve any strings and forward annotations in type annotations.430 431    This is only required if you need concrete types in :class:`Attribute`'s432    *type* field. In other words, you don't need to resolve your types if you433    only use them for static type checking.434 435    With no arguments, names will be looked up in the module in which the class436    was created. If this is not what you want, for example, if the name only437    exists inside a method, you may pass *globalns* or *localns* to specify438    other dictionaries in which to look up these names. See the docs of439    `typing.get_type_hints` for more details.440 441    Args:442        cls (type): Class to resolve.443 444        globalns (dict | None): Dictionary containing global variables.445 446        localns (dict | None): Dictionary containing local variables.447 448        attribs (list | None):449            List of attribs for the given class. This is necessary when calling450            from inside a ``field_transformer`` since *cls* is not an *attrs*451            class yet.452 453        include_extras (bool):454            Resolve more accurately, if possible. Pass ``include_extras`` to455            ``typing.get_hints``, if supported by the typing module. On456            supported Python versions (3.9+), this resolves the types more457            accurately.458 459    Raises:460        TypeError: If *cls* is not a class.461 462        attrs.exceptions.NotAnAttrsClassError:463            If *cls* is not an *attrs* class and you didn't pass any attribs.464 465        NameError: If types cannot be resolved because of missing variables.466 467    Returns:468        *cls* so you can use this function also as a class decorator. Please469        note that you have to apply it **after** `attrs.define`. That means the470        decorator has to come in the line **before** `attrs.define`.471 472    ..  versionadded:: 20.1.0473    ..  versionadded:: 21.1.0 *attribs*474    ..  versionadded:: 23.1.0 *include_extras*475    """476    # Since calling get_type_hints is expensive we cache whether we've477    # done it already.478    if getattr(cls, "__attrs_types_resolved__", None) != cls:479        import typing480 481        kwargs = {482            "globalns": globalns,483            "localns": localns,484            "include_extras": include_extras,485        }486 487        hints = typing.get_type_hints(cls, **kwargs)488        for field in fields(cls) if attribs is None else attribs:489            if field.name in hints:490                # Since fields have been frozen we must work around it.491                _OBJ_SETATTR(field, "type", hints[field.name])492        # We store the class we resolved so that subclasses know they haven't493        # been resolved.494        cls.__attrs_types_resolved__ = cls495 496    # Return the class so you can use it as a decorator too.497    return cls498 
codekingpro/portable-devtools · Team Ai