| # SPDX-License-Identifier: MIT |
| |
| """ |
| Typing examples that rely on Mypy-specific features (mostly the attrs plugin). |
| """ |
| |
| from __future__ import annotations |
| |
| import re |
| |
| from typing import Any, Dict, List, Tuple |
| |
| import attr |
| import attrs |
| |
| |
| # Typing via "type" Argument --- |
| |
| |
| @attr.s |
| class C: |
| a = attr.ib(type=int) |
| |
| |
| c = C(1) |
| C(a=1) |
| |
| |
| @attr.s |
| class D: |
| x = attr.ib(type=List[int]) |
| |
| |
| @attr.s |
| class E: |
| y = attr.ib(type="List[int]") |
| |
| |
| @attr.s |
| class F: |
| z = attr.ib(type=Any) |
| |
| |
| # Typing via Annotations --- |
| |
| |
| @attr.s |
| class CC: |
| a: int = attr.ib() |
| |
| |
| cc = CC(1) |
| CC(a=1) |
| |
| |
| @attr.s |
| class DD: |
| x: list[int] = attr.ib() |
| |
| |
| @attr.s |
| class EE: |
| y: "list[int]" = attr.ib() |
| |
| |
| @attr.s |
| class FF: |
| z: Any = attr.ib() |
| |
| |
| @attrs.define |
| class FFF: |
| z: int |
| |
| |
| FFF(1) |
| |
| |
| # Inheritance -- |
| |
| |
| @attr.s |
| class GG(DD): |
| y: str = attr.ib() |
| |
| |
| GG(x=[1], y="foo") |
| |
| |
| @attr.s |
| class HH(DD, EE): |
| z: float = attr.ib() |
| |
| |
| HH(x=[1], y=[], z=1.1) |
| |
| |
| # same class |
| c == cc |
| |
| |
| # Exceptions |
| @attr.s(auto_exc=True) |
| class Error(Exception): |
| x: int = attr.ib() |
| |
| |
| try: |
| raise Error(1) |
| except Error as e: |
| e.x |
| e.args |
| str(e) |
| |
| |
| @attrs.define |
| class Error2(Exception): |
| x: int |
| |
| |
| try: |
| raise Error2(1) |
| except Error as e: |
| e.x |
| e.args |
| str(e) |
| |
| # Field aliases |
| |
| |
| @attrs.define |
| class AliasExample: |
| without_alias: int |
| _with_alias: int = attr.ib(alias="_with_alias") |
| |
| |
| attr.fields(AliasExample).without_alias.alias |
| attr.fields(AliasExample)._with_alias.alias |
| |
| |
| # Converters |
| |
| |
| @attr.s |
| class ConvCOptional: |
| x: int | None = attr.ib(converter=attr.converters.optional(int)) |
| |
| |
| ConvCOptional(1) |
| ConvCOptional(None) |
| |
| |
| # XXX: Fails with E: Unsupported converter, only named functions, types and lambdas are currently supported [misc] |
| # See https://github.com/python/mypy/issues/15736 |
| # |
| # @attr.s |
| # class ConvCPipe: |
| # x: str = attr.ib(converter=attr.converters.pipe(int, str)) |
| # |
| # |
| # ConvCPipe(3.4) |
| # ConvCPipe("09") |
| # |
| # |
| # @attr.s |
| # class ConvCDefaultIfNone: |
| # x: int = attr.ib(converter=attr.converters.default_if_none(42)) |
| # |
| # |
| # ConvCDefaultIfNone(1) |
| # ConvCDefaultIfNone(None) |
| |
| |
| @attr.s |
| class ConvCToBool: |
| x: int = attr.ib(converter=attr.converters.to_bool) |
| |
| |
| ConvCToBool(1) |
| ConvCToBool(True) |
| ConvCToBool("on") |
| ConvCToBool("yes") |
| ConvCToBool(0) |
| ConvCToBool(False) |
| ConvCToBool("n") |
| |
| |
| # Validators |
| @attr.s |
| class Validated: |
| a = attr.ib( |
| type=List[C], |
| validator=attr.validators.deep_iterable( |
| attr.validators.instance_of(C), attr.validators.instance_of(list) |
| ), |
| ) |
| a2 = attr.ib( |
| type=Tuple[C], |
| validator=attr.validators.deep_iterable( |
| attr.validators.instance_of(C), attr.validators.instance_of(tuple) |
| ), |
| ) |
| a3 = attr.ib( |
| type=Tuple[C], |
| validator=attr.validators.deep_iterable( |
| [attr.validators.instance_of(C)], |
| [attr.validators.instance_of(tuple)], |
| ), |
| ) |
| b = attr.ib( |
| type=List[C], |
| validator=attr.validators.deep_iterable( |
| attr.validators.instance_of(C) |
| ), |
| ) |
| c = attr.ib( |
| type=Dict[C, D], |
| validator=attr.validators.deep_mapping( |
| attr.validators.instance_of(C), |
| attr.validators.instance_of(D), |
| attr.validators.instance_of(dict), |
| ), |
| ) |
| d = attr.ib( |
| type=Dict[C, D], |
| validator=attr.validators.deep_mapping( |
| attr.validators.instance_of(C), attr.validators.instance_of(D) |
| ), |
| ) |
| d2 = attr.ib( |
| type=Dict[C, D], |
| validator=attr.validators.deep_mapping(attr.validators.instance_of(C)), |
| ) |
| d3 = attr.ib( |
| type=Dict[C, D], |
| validator=attr.validators.deep_mapping( |
| value_validator=attr.validators.instance_of(C) |
| ), |
| ) |
| d4 = attr.ib( |
| type=Dict[C, D], |
| validator=attr.validators.deep_mapping( |
| key_validator=[attr.validators.instance_of(C)], |
| value_validator=[attr.validators.instance_of(C)], |
| mapping_validator=[attr.validators.instance_of(dict)], |
| ), |
| ) |
| e: str = attr.ib(validator=attr.validators.matches_re(re.compile(r"foo"))) |
| f: str = attr.ib( |
| validator=attr.validators.matches_re(r"foo", flags=42, func=re.search) |
| ) |
| |
| # Test different forms of instance_of |
| g: int = attr.ib(validator=attr.validators.instance_of(int)) |
| h: int = attr.ib(validator=attr.validators.instance_of((int,))) |
| j: int | str = attr.ib(validator=attr.validators.instance_of((int, str))) |
| k: int | str | C = attr.ib( |
| validator=attrs.validators.instance_of((int, C, str)) |
| ) |
| kk: int | str | C = attr.ib( |
| validator=attrs.validators.instance_of(int | C | str) |
| ) |
| |
| l: Any = attr.ib( |
| validator=attr.validators.not_(attr.validators.in_("abc")) |
| ) |
| m: Any = attr.ib( |
| validator=attr.validators.not_( |
| attr.validators.in_("abc"), exc_types=ValueError |
| ) |
| ) |
| n: Any = attr.ib( |
| validator=attr.validators.not_( |
| attr.validators.in_("abc"), exc_types=(ValueError,) |
| ) |
| ) |
| o: Any = attr.ib( |
| validator=attr.validators.not_(attr.validators.in_("abc"), msg="spam") |
| ) |
| p: Any = attr.ib( |
| validator=attr.validators.not_(attr.validators.in_("abc"), msg=None) |
| ) |
| q: Any = attr.ib( |
| validator=attrs.validators.optional(attrs.validators.instance_of(C)) |
| ) |
| r: Any = attr.ib( |
| validator=attrs.validators.optional([attrs.validators.instance_of(C)]) |
| ) |
| s: Any = attr.ib( |
| validator=attrs.validators.optional((attrs.validators.instance_of(C),)) |
| ) |
| |
| |
| @attr.define |
| class Validated2: |
| num: int = attr.field(validator=attr.validators.ge(0)) |
| |
| |
| with attr.validators.disabled(): |
| Validated2(num=-1) |
| |
| |
| try: |
| attr.validators.set_disabled(True) |
| Validated2(num=-1) |
| finally: |
| attr.validators.set_disabled(False) |
| |
| |
| # Custom repr() |
| @attr.s |
| class WithCustomRepr: |
| a: int = attr.ib(repr=True) |
| b: str = attr.ib(repr=False) |
| c: str = attr.ib(repr=lambda value: "c is for cookie") |
| d: bool = attr.ib(repr=str) |
| |
| |
| # Check some of our own types |
| @attr.s(eq=True, order=False) |
| class OrderFlags: |
| a: int = attr.ib(eq=False, order=False) |
| b: int = attr.ib(eq=True, order=True) |
| |
| |
| # on_setattr hooks |
| @attr.s(on_setattr=attr.setters.validate) |
| class ValidatedSetter: |
| a: int |
| b: str = attr.ib(on_setattr=attr.setters.NO_OP) |
| c: bool = attr.ib(on_setattr=attr.setters.frozen) |
| d: int = attr.ib(on_setattr=[attr.setters.convert, attr.setters.validate]) |
| e: bool = attr.ib( |
| on_setattr=attr.setters.pipe( |
| attr.setters.convert, attr.setters.validate |
| ) |
| ) |
| |
| |
| # field_transformer |
| def ft_hook(cls: type, attribs: list[attr.Attribute]) -> list[attr.Attribute]: |
| return attribs |
| |
| |
| @attr.s(field_transformer=ft_hook) |
| class TransformedAttrs: |
| x: int |
| |
| |
| # Auto-detect |
| @attr.s(auto_detect=True) |
| class AutoDetect: |
| x: int |
| |
| def __init__(self, x: int): |
| self.x = x |
| |
| |
| @attr.define(order=True) |
| class NGClass: |
| x: int = attr.field(default=42) |
| |
| |
| ngc = NGClass(1) |
| |
| |
| @attr.frozen(str=True) |
| class NGFrozen: |
| x: int |
| |
| |
| attr.fields(NGFrozen).x.evolve(eq=False) |
| a = attrs.fields(NGFrozen).x |
| a.evolve(repr=False) |
| |
| |
| @attr.s(collect_by_mro=True) |
| class MRO: |
| pass |
| |
| |
| @attr.s |
| class FactoryTest: |
| a: list[int] = attr.ib(default=attr.Factory(list)) |
| b: list[Any] = attr.ib(default=attr.Factory(list, False)) |
| c: list[int] = attr.ib(default=attr.Factory((lambda s: s.a), True)) |
| |
| |
| attr.asdict(FactoryTest(), tuple_keys=True) |
| |
| |
| # Check match_args stub |
| @attr.s(match_args=False) |
| class MatchArgs: |
| a: int = attr.ib() |
| b: int = attr.ib() |
| |
| |
| attr.asdict(FactoryTest()) |
| attr.asdict(FactoryTest(), retain_collection_types=False) |
| |
| |
| def accessing_from_attr() -> None: |
| """ |
| Use a function to keep the ns clean. |
| """ |
| attr.converters.optional |
| attr.exceptions.FrozenError |
| attr.filters.include |
| attr.filters.exclude |
| attr.setters.frozen |
| attr.validators.and_ |
| attr.cmp_using |
| |
| |
| foo = object |
| if attrs.has(foo) or attr.has(foo): |
| foo.__attrs_attrs__ |
| |
| |
| @attrs.define(unsafe_hash=True) |
| class Hashable: |
| pass |
| |
| |
| def test(cls: type) -> None: |
| if attr.has(cls): |
| attr.resolve_types(cls) |