attrs/tests/typing_example.py

460 lines
8.4 KiB
Python
Raw Normal View History

2021-12-27 08:29:09 +00:00
# SPDX-License-Identifier: MIT
2019-09-09 09:11:50 +00:00
import re
from typing import Any, Dict, List, Tuple, Union
Add PEP484 stubs (#238) * Add PEP484 stubs * Deploy .pyi stubs alongside .py files. This is the recommended approach for 3rd party stubs. See: https://github.com/python/typing/issues/84#issuecomment-317217346 * Add support for the new type argument. * Add tests for stubs and address a few issues. * Improve declaration of private vs public objects in stubs * More stub tests * Separate the stub tests into their own tox env it does not make sense to test the stubs in multiple python *runtime* environments (e.g. python 3.5, 3.6, pypy3) because the results of static analysis wrt attrs is not dependent on the runtime. Moreover, mypy is not installing correctly in pypy3 which has nothing to do with attrs. * Update the manifest with stub files * Remove mypy from the dev requirements * Allow _CountingAttr to be instantiated, but not Attribute. * Incorporate defaults into attr.ib typing * Fix a bug with validators.and_ * Add more tests * Remove _CountingAttr from public interface It is crucial to ensure that make_class() works with attr.ib(), as a result we no longer have any functions that care about _CountingAttr. * Lie about return type of Factory this allows for an abbreviated idiom: `x: List[int] = Factory(list)` * Add tox stubs env to travis * used the wrong comment character in mypy tests * Improve overloads using PyCharm order-based approach overloads are pretty broken in mypy. the best we can do for now is target PyCharm, which is much more forgiving. * Remove features not yet working in mypy. Document remaining issues. * Test stubs against euresti fork of mypy with attrs plugin Copied the pytest plugin from mypy for testing annotations: It is not an officially supported API and using the plugin from mypy could break after any update. * Add some types and TypeVars to some types. Make tests pass * Suppress warnings about named attribute access from fields() e.g. fields(C).x Eventually it would be good to add support for returning NamedTuple from the mypy plugin * Add WIP mypy-doctest plugin * Deal with a few remaining type issues in the docs * sphinx doctest: don't turn warnings into errors. doing so makes the tests abort after the first failed group. * Update "type: ignore" comments to reflect issues fixed in mypy plugin * doctest2: improve output formatting * Update manifest * static tests: use inline error declarations * More tests * Tests passing (with notes about remaining issues) * Attempt to get latest plugin from euresti working * Issues fixed. Had to place calls to attr.ib under a class definition. * Deal with a PyCharm bug * Minor test improvements * Make tests prettier * Use 2 decorators instead of 3 * doctest2: add support for skipping mypy tests * Add tests for inheritance, eq, and cmp * Add fixmes and todos * Rename convert to converter * Attribute.validator is always a single validator * Conform stubs to typeshed coding style And add auto_attrib kw * backport style fixes from typeshed * Add test cases to cover forward references and Any * Add fixes for forward references and Any * Address typeshed review notes * Use Sequence instead of List/Tuple for validator arg list and tuple are invariant and so prevent passing subtypes of _ValidatorType * backports changes from typeshed #1914 * backport changes from typeshed #1933 * Prevent mypy tests from getting picked up Evidently the discovery rules changed recently for pytest. * make our doctest extension compatible with latest sphinx * Adjustments to the tests * Fix flake and manifest tests (hopefully) * Fix tests on pypy3 (hopefully) * Update stubs from typeshed Also update tests. * Make PEP 561-compliant * minor cleanup * Consolidate stub support files into stub directory In preparation for removing them from the pyi_stubs branch. * Get tests passing This is a final test of the current stubs before moving the stub tests to a new branch. * Revert stub test additions Replace with a simple mypy pass/fail test * get pre-commit passing * Address review feedback * Move typing test up in tox envlist
2018-07-12 10:19:24 +00:00
import attr
2021-12-25 14:15:10 +00:00
import attrs
Add PEP484 stubs (#238) * Add PEP484 stubs * Deploy .pyi stubs alongside .py files. This is the recommended approach for 3rd party stubs. See: https://github.com/python/typing/issues/84#issuecomment-317217346 * Add support for the new type argument. * Add tests for stubs and address a few issues. * Improve declaration of private vs public objects in stubs * More stub tests * Separate the stub tests into their own tox env it does not make sense to test the stubs in multiple python *runtime* environments (e.g. python 3.5, 3.6, pypy3) because the results of static analysis wrt attrs is not dependent on the runtime. Moreover, mypy is not installing correctly in pypy3 which has nothing to do with attrs. * Update the manifest with stub files * Remove mypy from the dev requirements * Allow _CountingAttr to be instantiated, but not Attribute. * Incorporate defaults into attr.ib typing * Fix a bug with validators.and_ * Add more tests * Remove _CountingAttr from public interface It is crucial to ensure that make_class() works with attr.ib(), as a result we no longer have any functions that care about _CountingAttr. * Lie about return type of Factory this allows for an abbreviated idiom: `x: List[int] = Factory(list)` * Add tox stubs env to travis * used the wrong comment character in mypy tests * Improve overloads using PyCharm order-based approach overloads are pretty broken in mypy. the best we can do for now is target PyCharm, which is much more forgiving. * Remove features not yet working in mypy. Document remaining issues. * Test stubs against euresti fork of mypy with attrs plugin Copied the pytest plugin from mypy for testing annotations: It is not an officially supported API and using the plugin from mypy could break after any update. * Add some types and TypeVars to some types. Make tests pass * Suppress warnings about named attribute access from fields() e.g. fields(C).x Eventually it would be good to add support for returning NamedTuple from the mypy plugin * Add WIP mypy-doctest plugin * Deal with a few remaining type issues in the docs * sphinx doctest: don't turn warnings into errors. doing so makes the tests abort after the first failed group. * Update "type: ignore" comments to reflect issues fixed in mypy plugin * doctest2: improve output formatting * Update manifest * static tests: use inline error declarations * More tests * Tests passing (with notes about remaining issues) * Attempt to get latest plugin from euresti working * Issues fixed. Had to place calls to attr.ib under a class definition. * Deal with a PyCharm bug * Minor test improvements * Make tests prettier * Use 2 decorators instead of 3 * doctest2: add support for skipping mypy tests * Add tests for inheritance, eq, and cmp * Add fixmes and todos * Rename convert to converter * Attribute.validator is always a single validator * Conform stubs to typeshed coding style And add auto_attrib kw * backport style fixes from typeshed * Add test cases to cover forward references and Any * Add fixes for forward references and Any * Address typeshed review notes * Use Sequence instead of List/Tuple for validator arg list and tuple are invariant and so prevent passing subtypes of _ValidatorType * backports changes from typeshed #1914 * backport changes from typeshed #1933 * Prevent mypy tests from getting picked up Evidently the discovery rules changed recently for pytest. * make our doctest extension compatible with latest sphinx * Adjustments to the tests * Fix flake and manifest tests (hopefully) * Fix tests on pypy3 (hopefully) * Update stubs from typeshed Also update tests. * Make PEP 561-compliant * minor cleanup * Consolidate stub support files into stub directory In preparation for removing them from the pyi_stubs branch. * Get tests passing This is a final test of the current stubs before moving the stub tests to a new branch. * Revert stub test additions Replace with a simple mypy pass/fail test * get pre-commit passing * Address review feedback * Move typing test up in tox envlist
2018-07-12 10:19:24 +00:00
# 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()
2021-12-25 14:15:10 +00:00
@attrs.define
class FFF:
z: int
FFF(1)
Add PEP484 stubs (#238) * Add PEP484 stubs * Deploy .pyi stubs alongside .py files. This is the recommended approach for 3rd party stubs. See: https://github.com/python/typing/issues/84#issuecomment-317217346 * Add support for the new type argument. * Add tests for stubs and address a few issues. * Improve declaration of private vs public objects in stubs * More stub tests * Separate the stub tests into their own tox env it does not make sense to test the stubs in multiple python *runtime* environments (e.g. python 3.5, 3.6, pypy3) because the results of static analysis wrt attrs is not dependent on the runtime. Moreover, mypy is not installing correctly in pypy3 which has nothing to do with attrs. * Update the manifest with stub files * Remove mypy from the dev requirements * Allow _CountingAttr to be instantiated, but not Attribute. * Incorporate defaults into attr.ib typing * Fix a bug with validators.and_ * Add more tests * Remove _CountingAttr from public interface It is crucial to ensure that make_class() works with attr.ib(), as a result we no longer have any functions that care about _CountingAttr. * Lie about return type of Factory this allows for an abbreviated idiom: `x: List[int] = Factory(list)` * Add tox stubs env to travis * used the wrong comment character in mypy tests * Improve overloads using PyCharm order-based approach overloads are pretty broken in mypy. the best we can do for now is target PyCharm, which is much more forgiving. * Remove features not yet working in mypy. Document remaining issues. * Test stubs against euresti fork of mypy with attrs plugin Copied the pytest plugin from mypy for testing annotations: It is not an officially supported API and using the plugin from mypy could break after any update. * Add some types and TypeVars to some types. Make tests pass * Suppress warnings about named attribute access from fields() e.g. fields(C).x Eventually it would be good to add support for returning NamedTuple from the mypy plugin * Add WIP mypy-doctest plugin * Deal with a few remaining type issues in the docs * sphinx doctest: don't turn warnings into errors. doing so makes the tests abort after the first failed group. * Update "type: ignore" comments to reflect issues fixed in mypy plugin * doctest2: improve output formatting * Update manifest * static tests: use inline error declarations * More tests * Tests passing (with notes about remaining issues) * Attempt to get latest plugin from euresti working * Issues fixed. Had to place calls to attr.ib under a class definition. * Deal with a PyCharm bug * Minor test improvements * Make tests prettier * Use 2 decorators instead of 3 * doctest2: add support for skipping mypy tests * Add tests for inheritance, eq, and cmp * Add fixmes and todos * Rename convert to converter * Attribute.validator is always a single validator * Conform stubs to typeshed coding style And add auto_attrib kw * backport style fixes from typeshed * Add test cases to cover forward references and Any * Add fixes for forward references and Any * Address typeshed review notes * Use Sequence instead of List/Tuple for validator arg list and tuple are invariant and so prevent passing subtypes of _ValidatorType * backports changes from typeshed #1914 * backport changes from typeshed #1933 * Prevent mypy tests from getting picked up Evidently the discovery rules changed recently for pytest. * make our doctest extension compatible with latest sphinx * Adjustments to the tests * Fix flake and manifest tests (hopefully) * Fix tests on pypy3 (hopefully) * Update stubs from typeshed Also update tests. * Make PEP 561-compliant * minor cleanup * Consolidate stub support files into stub directory In preparation for removing them from the pyi_stubs branch. * Get tests passing This is a final test of the current stubs before moving the stub tests to a new branch. * Revert stub test additions Replace with a simple mypy pass/fail test * get pre-commit passing * Address review feedback * Move typing test up in tox envlist
2018-07-12 10:19:24 +00:00
# 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)
2021-12-25 14:15:10 +00:00
@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
2021-12-25 14:15:10 +00:00
# Converters
# XXX: Currently converters can only be functions so none of this works
# although the stubs should be correct.
# @attr.s
# class ConvCOptional:
# x: Optional[int] = attr.ib(converter=attr.converters.optional(int))
# ConvCOptional(1)
# ConvCOptional(None)
# @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)
),
)
a = 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)
),
)
e: str = attr.ib(validator=attr.validators.matches_re(re.compile(r"foo")))
f: str = attr.ib(
2019-09-09 09:11:50 +00:00
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: Union[int, str] = attr.ib(
validator=attr.validators.instance_of((int, str))
)
k: Union[int, str, C] = attr.ib(
2021-12-25 14:15:10 +00:00
validator=attrs.validators.instance_of((int, C, str))
)
2022-09-04 11:55:02 +00:00
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)
)
@attr.define
class Validated2:
num: int = attr.field(validator=attr.validators.ge(0))
2021-12-25 14:15:10 +00:00
@attrs.define
class Validated3:
num: int = attr.field(validator=attr.validators.ge(0))
with attr.validators.disabled():
Validated2(num=-1)
2021-12-25 14:15:10 +00:00
with attrs.validators.disabled():
Validated3(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)
2021-12-25 14:15:10 +00:00
@attrs.define
class WithCustomRepr2:
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
@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
)
)
2021-12-25 14:15:10 +00:00
@attrs.define(on_setattr=attr.setters.validate)
class ValidatedSetter2:
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(
on_setattr=[attrs.setters.convert, attrs.setters.validate]
)
e: bool = attrs.field(
on_setattr=attrs.setters.pipe(
attrs.setters.convert, attrs.setters.validate
)
)
# field_transformer
def ft_hook(cls: type, attribs: List[attr.Attribute]) -> List[attr.Attribute]:
return attribs
2021-12-25 14:15:10 +00:00
# field_transformer
def ft_hook2(
cls: type, attribs: List[attrs.Attribute]
) -> List[attrs.Attribute]:
return attribs
@attr.s(field_transformer=ft_hook)
class TransformedAttrs:
x: int
2021-12-25 14:15:10 +00:00
@attrs.define(field_transformer=ft_hook2)
class TransformedAttrs2:
x: int
# Auto-detect
@attr.s(auto_detect=True)
class AutoDetect:
x: int
def __init__(self, x: int):
self.x = x
# Provisional APIs
@attr.define(order=True)
class NGClass:
x: int = attr.field(default=42)
ngc = NGClass(1)
@attr.mutable(slots=False)
class NGClass2:
x: int
ngc2 = NGClass2(1)
@attr.frozen(str=True)
class NGFrozen:
x: int
ngf = NGFrozen(1)
attr.fields(NGFrozen).x.evolve(eq=False)
a = attr.fields(NGFrozen).x
a.evolve(repr=False)
2021-12-25 14:15:10 +00:00
attrs.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))
2021-12-25 14:15:10 +00:00
@attrs.define
class FactoryTest2:
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(FactoryTest2())
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)
2021-12-25 14:15:10 +00:00
# Check match_args stub
@attrs.define(match_args=False)
class MatchArgs2:
a: int
b: int
# NG versions of asdict/astuple
attrs.asdict(MatchArgs2(1, 2))
attrs.astuple(MatchArgs2(1, 2))
def accessing_from_attr() -> None:
2021-12-25 14:15:10 +00:00
"""
Use a function to keep the ns clean.
"""
attr.converters.optional
attr.exceptions.FrozenError
attr.filters.include
attr.setters.frozen
attr.validators.and_
attr.cmp_using
2021-12-25 14:15:10 +00:00
def accessing_from_attrs() -> None:
2021-12-25 14:15:10 +00:00
"""
Use a function to keep the ns clean.
"""
attrs.converters.optional
attrs.exceptions.FrozenError
attrs.filters.include
attrs.setters.frozen
attrs.validators.and_
attrs.cmp_using
2022-08-16 05:26:50 +00:00
foo = object
if attrs.has(foo) or attr.has(foo):
foo.__attrs_attrs__
@attrs.define(unsafe_hash=True)
class Hashable:
pass