| # SPDX-License-Identifier: MIT |
| |
| """ |
| Typing examples that rely on Pyrefly's advanced attrs integration. |
| """ |
| |
| from __future__ import annotations |
| |
| import re |
| |
| from typing import Any |
| |
| import attrs |
| |
| |
| @attrs.define |
| class C: |
| a: int = attrs.field() |
| |
| |
| cc = C(1) |
| C(a=1) |
| |
| |
| @attrs.define |
| class D: |
| x: list[int] = attrs.field() |
| |
| |
| li: list[int] = D([1]).x |
| |
| |
| @attrs.define |
| class E: |
| y: "list[int]" = attrs.field() |
| |
| |
| li = E([1]).y |
| |
| |
| @attrs.define |
| class F: |
| z: Any = attrs.field() |
| |
| |
| # Inheritance -- |
| |
| |
| @attrs.define |
| class GG(D): |
| y: str = attrs.field() |
| |
| |
| GG(x=[1], y="foo") |
| |
| |
| @attrs.define |
| class HH(D, E): |
| z: float = attrs.field() |
| |
| |
| HH(x=[1], y=[], z=1.1) |
| |
| |
| # Exceptions |
| @attrs.define |
| class Error(Exception): |
| x: int = attrs.field() |
| |
| |
| try: |
| raise Error(1) |
| except Error as e: |
| e.x |
| e.args |
| str(e) |
| |
| |
| @attrs.define(auto_exc=False) |
| 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 = attrs.field(alias="_with_alias") |
| |
| |
| attrs.fields(AliasExample).without_alias.alias |
| attrs.fields(AliasExample)._with_alias.alias |
| |
| |
| # Converters |
| |
| |
| @attrs.define |
| class ConvCOptional: |
| x: int | None = attrs.field(converter=attrs.converters.optional(int)) |
| |
| |
| ConvCOptional(1) |
| ConvCOptional(None) |
| |
| |
| @attrs.define |
| class ConvCPipe: |
| x: str = attrs.field(converter=attrs.converters.pipe(int, str)) |
| |
| |
| ConvCPipe(3.4) |
| ConvCPipe("09") |
| ConvCPipe({}) # XXX pipe makes field Any |
| |
| |
| @attrs.define |
| class ConvCDefaultIfNone: |
| x: int = attrs.field(converter=attrs.converters.default_if_none(42)) |
| |
| |
| ConvCDefaultIfNone(1) |
| ConvCDefaultIfNone(None) |
| ConvCDefaultIfNone({}) # XXX: default_if_none makes field any |
| |
| |
| @attrs.define |
| class ConvCToBool: |
| x: int = attrs.field(converter=attrs.converters.to_bool) |
| |
| |
| ConvCToBool(1) |
| ConvCToBool(True) |
| ConvCToBool("on") |
| ConvCToBool("yes") |
| ConvCToBool(0) |
| ConvCToBool(False) |
| ConvCToBool("n") |
| |
| |
| @attrs.define |
| class DecoratorConverter: |
| x: int = attrs.field() |
| |
| @x.converter |
| def _to_int(self, val: str | float) -> int: |
| return int(val) |
| |
| |
| # XXX: fails |
| # DecoratorConverter("foo") |
| |
| |
| # Validators |
| @attrs.define |
| class Validated: |
| a: list[C] = attrs.field( |
| validator=attrs.validators.deep_iterable( |
| attrs.validators.instance_of(C), attrs.validators.instance_of(list) |
| ), |
| ) |
| a2: tuple[C] = attrs.field( |
| validator=attrs.validators.deep_iterable( |
| attrs.validators.instance_of(C), |
| attrs.validators.instance_of(tuple), |
| ), |
| ) |
| a3: tuple[C] = attrs.field( |
| validator=attrs.validators.deep_iterable( |
| [attrs.validators.instance_of(C)], |
| [attrs.validators.instance_of(tuple)], |
| ), |
| ) |
| b: list[C] = attrs.field( |
| validator=attrs.validators.deep_iterable( |
| attrs.validators.instance_of(C) |
| ), |
| ) |
| c: dict[C, D] = attrs.field( |
| validator=attrs.validators.deep_mapping( |
| attrs.validators.instance_of(C), |
| attrs.validators.instance_of(D), |
| attrs.validators.instance_of(dict), |
| ), |
| ) |
| d: dict[C, D] = attrs.field( |
| validator=attrs.validators.deep_mapping( |
| attrs.validators.instance_of(C), attrs.validators.instance_of(D) |
| ), |
| ) |
| d2: dict[C, D] = attrs.field( |
| validator=attrs.validators.deep_mapping( |
| attrs.validators.instance_of(C) |
| ), |
| ) |
| d3: dict[C, D] = attrs.field( |
| validator=attrs.validators.deep_mapping( |
| value_validator=attrs.validators.instance_of(C) |
| ), |
| ) |
| d4: dict[C, D] = attrs.field( |
| validator=attrs.validators.deep_mapping( |
| key_validator=[attrs.validators.instance_of(C)], |
| value_validator=[attrs.validators.instance_of(C)], |
| mapping_validator=[attrs.validators.instance_of(dict)], |
| ), |
| ) |
| e: str = attrs.field( |
| validator=attrs.validators.matches_re(re.compile(r"foo")) |
| ) |
| f: str = attrs.field( |
| validator=attrs.validators.matches_re(r"foo", flags=42, func=re.search) |
| ) |
| |
| # Test different forms of instance_of |
| g: int = attrs.field(validator=attrs.validators.instance_of(int)) |
| h: int = attrs.field(validator=attrs.validators.instance_of((int,))) |
| j: int | str = attrs.field( |
| validator=attrs.validators.instance_of((int, str)) |
| ) |
| k: int | str | C = attrs.field( |
| validator=attrs.validators.instance_of((int, C, str)) |
| ) |
| kk: int | str | C = attrs.field( |
| validator=attrs.validators.instance_of(int | C | str) |
| ) |
| |
| l: Any = attrs.field( |
| validator=attrs.validators.not_(attrs.validators.in_("abc")) |
| ) |
| m: Any = attrs.field( |
| validator=attrs.validators.not_( |
| attrs.validators.in_("abc"), exc_types=ValueError |
| ) |
| ) |
| n: Any = attrs.field( |
| validator=attrs.validators.not_( |
| attrs.validators.in_("abc"), exc_types=(ValueError,) |
| ) |
| ) |
| o: Any = attrs.field( |
| validator=attrs.validators.not_( |
| attrs.validators.in_("abc"), msg="spam" |
| ) |
| ) |
| p: Any = attrs.field( |
| validator=attrs.validators.not_(attrs.validators.in_("abc"), msg=None) |
| ) |
| q: Any = attrs.field( |
| validator=attrs.validators.optional(attrs.validators.instance_of(C)) |
| ) |
| r: Any = attrs.field( |
| validator=attrs.validators.optional([attrs.validators.instance_of(C)]) |
| ) |
| s: Any = attrs.field( |
| validator=attrs.validators.optional((attrs.validators.instance_of(C),)) |
| ) |
| |
| |
| @attrs.define |
| class Validated2: |
| num: int = attrs.field(validator=attrs.validators.ge(0)) |
| |
| |
| with attrs.validators.disabled(): |
| Validated2(num=-1) |
| |
| |
| try: |
| attrs.validators.set_disabled(True) |
| Validated2(num=-1) |
| finally: |
| attrs.validators.set_disabled(False) |
| |
| |
| # Custom repr() |
| @attrs.define |
| class WithCustomRepr: |
| a: int = attrs.field(repr=True) |
| b: str = attrs.field(repr=False) |
| c: str = attrs.field(repr=lambda value: "c is for cookie") |
| d: bool = attrs.field(repr=str) |
| |
| |
| # Check some of our own types |
| @attrs.define(eq=True, order=False) |
| class OrderFlags: |
| a: int = attrs.field(eq=False, order=False) |
| b: int = attrs.field(eq=True, order=True) |
| |
| |
| # on_setattr hooks |
| @attrs.define(on_setattr=attrs.setters.validate) |
| class ValidatedSetter: |
| a: int |
| b: str = attrs.field(on_setattr=attrs.setters.NO_OP) |
| c: bool = attrs.field(on_setattr=attrs.setters.frozen) |
| d: int = attrs.field( |
| converter=int, |
| on_setattr=[attrs.setters.convert, attrs.setters.validate], |
| ) |
| e: bool = attrs.field( |
| converter=attrs.converters.to_bool, |
| on_setattr=attrs.setters.pipe( |
| attrs.setters.convert, attrs.setters.validate |
| ), |
| ) |
| |
| |
| vs = ValidatedSetter(1, "2", True, 4, False) |
| vs.d = "2" |
| vs.e = "yes" |
| vs.e = "foo" # XXX: should only allow the literals we know |
| |
| |
| # field_transformer |
| def ft_hook( |
| cls: type, attribs: list[attrs.Attribute] |
| ) -> list[attrs.Attribute]: |
| return attribs |
| |
| |
| @attrs.define(field_transformer=ft_hook) |
| class TransformedAttrs: |
| x: int |
| |
| |
| # Auto-detect |
| @attrs.define(auto_detect=True) |
| class AutoDetect: |
| x: int |
| |
| def __init__(self, x: int): |
| self.x = x |
| |
| |
| @attrs.define(order=True) |
| class NGClass: |
| x: int = attrs.field(default=42) |
| |
| |
| ngc = NGClass(1) |
| |
| |
| @attrs.frozen(str=True) |
| class NGFrozen: |
| x: int |
| |
| |
| attrs.fields(NGFrozen).x.evolve(eq=False) |
| a = attrs.fields(NGFrozen).x |
| a.evolve(repr=False) |
| |
| |
| @attrs.define |
| class FactoryTest: |
| a: list[int] = attrs.field(default=attrs.Factory(list)) |
| b: list[Any] = attrs.field(default=attrs.Factory(list, False)) |
| c: list[int] = attrs.field(default=attrs.Factory((lambda s: s.a), True)) |
| |
| |
| attrs.asdict(FactoryTest(), tuple_keys=True) |
| |
| |
| # Check match_args stub |
| @attrs.define(match_args=False) |
| class MatchArgs: |
| a: int = attrs.field() |
| b: int = attrs.field() |
| |
| |
| attrs.asdict(FactoryTest()) |
| attrs.asdict(FactoryTest(), retain_collection_types=False) |
| |
| |
| foo = object |
| if attrs.has(foo) or attrs.has(foo): |
| foo.__attrs_attrs__ |
| |
| |
| @attrs.define(unsafe_hash=True) |
| class Hashable: |
| pass |
| |
| |
| def test(cls: type) -> None: |
| if attrs.has(cls): |
| attrs.resolve_types(cls) |