diff --git a/docs/recipes.md b/docs/recipes.md index dbf753a9..5d356e46 100644 --- a/docs/recipes.md +++ b/docs/recipes.md @@ -11,29 +11,28 @@ In certain situations, you might want to deviate from this behavior and use alte For example, consider the following `Point` class describing points in 2D space, which offers two `classmethod`s for alternative creation: ```{doctest} -from __future__ import annotations - -import math - -from attrs import define - - -@define -class Point: - """A point in 2D space.""" - - x: float - y: float - - @classmethod - def from_tuple(cls, coordinates: tuple[float, float]) -> Point: - """Create a point from a tuple of Cartesian coordinates.""" - return Point(*coordinates) - - @classmethod - def from_polar(cls, radius: float, angle: float) -> Point: - """Create a point from its polar coordinates.""" - return Point(radius * math.cos(angle), radius * math.sin(angle)) +>>> from __future__ import annotations + +>>> import math + +>>> from attrs import define + + +>>> @define +... class Point: +... """A point in 2D space.""" +... x: float +... y: float +... +... @classmethod +... def from_tuple(cls, coordinates: tuple[float, float]) -> Point: +... """Create a point from a tuple of Cartesian coordinates.""" +... return Point(*coordinates) +... +... @classmethod +... def from_polar(cls, radius: float, angle: float) -> Point: +... """Create a point from its polar coordinates.""" +... return Point(radius * math.cos(angle), radius * math.sin(angle)) ``` @@ -42,33 +41,33 @@ class Point: A simple way to _statically_ set one of the `classmethod`s as initializer is to register a structuring hook that holds a reference to the respective callable: ```{doctest} -from inspect import signature -from typing import Callable, TypedDict - -from cattrs import Converter -from cattrs.dispatch import StructureHook - -def signature_to_typed_dict(fn: Callable) -> type[TypedDict]: - """Create a TypedDict reflecting a callable's signature.""" - params = {p: t.annotation for p, t in signature(fn).parameters.items()} - return TypedDict(f"{fn.__name__}_args", params) - -def make_initializer_from(fn: Callable, conv: Converter) -> StructureHook: - """Return a structuring hook from a given callable.""" - td = signature_to_typed_dict(fn) - td_hook = conv.get_structure_hook(td) - return lambda v, _: fn(**td_hook(v, td)) +>>> from inspect import signature +>>> from typing import Callable, TypedDict + +>>> from cattrs import Converter +>>> from cattrs.dispatch import StructureHook + +>>> def signature_to_typed_dict(fn: Callable) -> type[TypedDict]: +... """Create a TypedDict reflecting a callable's signature.""" +... params = {p: t.annotation for p, t in signature(fn).parameters.items()} +... return TypedDict(f"{fn.__name__}_args", params) + +>>> def make_initializer_from(fn: Callable, conv: Converter) -> StructureHook: +... """Return a structuring hook from a given callable.""" +... td = signature_to_typed_dict(fn) +... td_hook = conv.get_structure_hook(td) +... return lambda v, _: fn(**td_hook(v, td)) ``` Now, you can easily structure `Point`s from the specified alternative representation: ```{doctest} -c = Converter() -c.register_structure_hook(Point, make_initializer_from(Point.from_polar, c)) +>>> c = Converter() +>>> c.register_structure_hook(Point, make_initializer_from(Point.from_polar, c)) -p0 = Point(1.0, 0.0) -p1 = c.structure({"radius": 1.0, "angle": 0.0}, Point) -assert p0 == p1 +>>> p0 = Point(1.0, 0.0) +>>> p1 = c.structure({"radius": 1.0, "angle": 0.0}, Point) +>>> assert p0 == p1 ``` @@ -80,49 +79,49 @@ A typical scenario would be when object structuring happens behind an API and yo In such situations, the following hook factory can help you achieve your goal: ```{doctest} -from inspect import signature -from typing import Callable, TypedDict - -from cattrs import Converter -from cattrs.dispatch import StructureHook - -def signature_to_typed_dict(fn: Callable) -> type[TypedDict]: - """Create a TypedDict reflecting a callable's signature.""" - params = {p: t.annotation for p, t in signature(fn).parameters.items()} - return TypedDict(f"{fn.__name__}_args", params) - -def make_initializer_selection_hook( - initializer_key: str, - converter: Converter, -) -> StructureHook: - """Return a structuring hook that dynamically switches between initializers.""" - - def select_initializer_hook(specs: dict, cls: type[T]) -> T: - """Deserialization with dynamic initializer selection.""" - - # If no initializer keyword is specified, use regular __init__ - if initializer_key not in specs: - return converter.structure_attrs_fromdict(specs, cls) - - # Otherwise, call the specified initializer with deserialized arguments - specs = specs.copy() - initializer_name = specs.pop(initializer_key) - initializer = getattr(cls, initializer_name) - td = signature_to_typed_dict(initializer) - td_hook = converter.get_structure_hook(td) - return initializer(**td_hook(specs, td)) - - return select_initializer_hook +>>> from inspect import signature +>>> from typing import Callable, TypedDict + +>>> from cattrs import Converter +>>> from cattrs.dispatch import StructureHook + +>>> def signature_to_typed_dict(fn: Callable) -> type[TypedDict]: +... """Create a TypedDict reflecting a callable's signature.""" +... params = {p: t.annotation for p, t in signature(fn).parameters.items()} +... return TypedDict(f"{fn.__name__}_args", params) + +>>> def make_initializer_selection_hook( +... initializer_key: str, +... converter: Converter, +... ) -> StructureHook: +... """Return a structuring hook that dynamically switches between initializers.""" +... +... def select_initializer_hook(specs: dict, cls: type[T]) -> T: +... """Deserialization with dynamic initializer selection.""" +... +... # If no initializer keyword is specified, use regular __init__ +... if initializer_key not in specs: +... return converter.structure_attrs_fromdict(specs, cls) +... +... # Otherwise, call the specified initializer with deserialized arguments +... specs = specs.copy() +... initializer_name = specs.pop(initializer_key) +... initializer = getattr(cls, initializer_name) +... td = signature_to_typed_dict(initializer) +... td_hook = converter.get_structure_hook(td) +... return initializer(**td_hook(specs, td)) +... +... return select_initializer_hook ``` Specifying the key that determines the initializer to be used now lets you dynamically select the `classmethod` as part of the object specification itself: ```{doctest} -c = Converter() -c.register_structure_hook(Point, make_initializer_selection_hook("initializer", c)) +>>> c = Converter() +>>> c.register_structure_hook(Point, make_initializer_selection_hook("initializer", c)) -p0 = Point(1.0, 0.0) -p1 = c.structure({"initializer": "from_polar", "radius": 1.0, "angle": 0.0}, Point) -p2 = c.structure({"initializer": "from_tuple", "coordinates": (1.0, 0.0)}, Point) -assert p0 == p1 == p2 +>>> p0 = Point(1.0, 0.0) +>>> p1 = c.structure({"initializer": "from_polar", "radius": 1.0, "angle": 0.0}, Point) +>>> p2 = c.structure({"initializer": "from_tuple", "coordinates": (1.0, 0.0)}, Point) +>>> assert p0 == p1 == p2 ``` diff --git a/pdm.lock b/pdm.lock index 086fc6ef..53b69d49 100644 --- a/pdm.lock +++ b/pdm.lock @@ -2,10 +2,10 @@ # It is not intended for manual editing. [metadata] -groups = ["default", "bench", "bson", "cbor2", "docs", "lint", "msgpack", "orjson", "pyyaml", "test", "tomlkit", "ujson", "msgspec"] +groups = ["default", "bench", "bson", "cbor2", "docs", "lint", "msgpack", "msgspec", "orjson", "pyyaml", "test", "tomlkit", "ujson"] strategy = ["cross_platform"] lock_version = "4.4.1" -content_hash = "sha256:7f0761ff761a474620f436f9a8f8ef5b00a94cdd2d0669d3d6f241706ab27b95" +content_hash = "sha256:80497e8d5b756fc000f8a8b58b2ae6e6501168628e264daf7de6049fa45b096e" [[package]] name = "alabaster" @@ -69,7 +69,7 @@ files = [ [[package]] name = "black" -version = "23.11.0" +version = "24.2.0" requires_python = ">=3.8" summary = "The uncompromising code formatter." dependencies = [ @@ -82,24 +82,28 @@ dependencies = [ "typing-extensions>=4.0.1; python_version < \"3.11\"", ] files = [ - {file = "black-23.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dbea0bb8575c6b6303cc65017b46351dc5953eea5c0a59d7b7e3a2d2f433a911"}, - {file = "black-23.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:412f56bab20ac85927f3a959230331de5614aecda1ede14b373083f62ec24e6f"}, - {file = "black-23.11.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d136ef5b418c81660ad847efe0e55c58c8208b77a57a28a503a5f345ccf01394"}, - {file = "black-23.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:6c1cac07e64433f646a9a838cdc00c9768b3c362805afc3fce341af0e6a9ae9f"}, - {file = "black-23.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cf57719e581cfd48c4efe28543fea3d139c6b6f1238b3f0102a9c73992cbb479"}, - {file = "black-23.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:698c1e0d5c43354ec5d6f4d914d0d553a9ada56c85415700b81dc90125aac244"}, - {file = "black-23.11.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:760415ccc20f9e8747084169110ef75d545f3b0932ee21368f63ac0fee86b221"}, - {file = "black-23.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:58e5f4d08a205b11800332920e285bd25e1a75c54953e05502052738fe16b3b5"}, - {file = "black-23.11.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:45aa1d4675964946e53ab81aeec7a37613c1cb71647b5394779e6efb79d6d187"}, - {file = "black-23.11.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4c44b7211a3a0570cc097e81135faa5f261264f4dfaa22bd5ee2875a4e773bd6"}, - {file = "black-23.11.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a9acad1451632021ee0d146c8765782a0c3846e0e0ea46659d7c4f89d9b212b"}, - {file = "black-23.11.0-cp38-cp38-win_amd64.whl", hash = "sha256:fc7f6a44d52747e65a02558e1d807c82df1d66ffa80a601862040a43ec2e3142"}, - {file = "black-23.11.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7f622b6822f02bfaf2a5cd31fdb7cd86fcf33dab6ced5185c35f5db98260b055"}, - {file = "black-23.11.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:250d7e60f323fcfc8ea6c800d5eba12f7967400eb6c2d21ae85ad31c204fb1f4"}, - {file = "black-23.11.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5133f5507007ba08d8b7b263c7aa0f931af5ba88a29beacc4b2dc23fcefe9c06"}, - {file = "black-23.11.0-cp39-cp39-win_amd64.whl", hash = "sha256:421f3e44aa67138ab1b9bfbc22ee3780b22fa5b291e4db8ab7eee95200726b07"}, - {file = "black-23.11.0-py3-none-any.whl", hash = "sha256:54caaa703227c6e0c87b76326d0862184729a69b73d3b7305b6288e1d830067e"}, - {file = "black-23.11.0.tar.gz", hash = "sha256:4c68855825ff432d197229846f971bc4d6666ce90492e5b02013bcaca4d9ab05"}, + {file = "black-24.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6981eae48b3b33399c8757036c7f5d48a535b962a7c2310d19361edeef64ce29"}, + {file = "black-24.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d533d5e3259720fdbc1b37444491b024003e012c5173f7d06825a77508085430"}, + {file = "black-24.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61a0391772490ddfb8a693c067df1ef5227257e72b0e4108482b8d41b5aee13f"}, + {file = "black-24.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:992e451b04667116680cb88f63449267c13e1ad134f30087dec8527242e9862a"}, + {file = "black-24.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:163baf4ef40e6897a2a9b83890e59141cc8c2a98f2dda5080dc15c00ee1e62cd"}, + {file = "black-24.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e37c99f89929af50ffaf912454b3e3b47fd64109659026b678c091a4cd450fb2"}, + {file = "black-24.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9de21bafcba9683853f6c96c2d515e364aee631b178eaa5145fc1c61a3cc92"}, + {file = "black-24.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:9db528bccb9e8e20c08e716b3b09c6bdd64da0dd129b11e160bf082d4642ac23"}, + {file = "black-24.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d84f29eb3ee44859052073b7636533ec995bd0f64e2fb43aeceefc70090e752b"}, + {file = "black-24.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e08fb9a15c914b81dd734ddd7fb10513016e5ce7e6704bdd5e1251ceee51ac9"}, + {file = "black-24.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:810d445ae6069ce64030c78ff6127cd9cd178a9ac3361435708b907d8a04c693"}, + {file = "black-24.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:ba15742a13de85e9b8f3239c8f807723991fbfae24bad92d34a2b12e81904982"}, + {file = "black-24.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7e53a8c630f71db01b28cd9602a1ada68c937cbf2c333e6ed041390d6968faf4"}, + {file = "black-24.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:93601c2deb321b4bad8f95df408e3fb3943d85012dddb6121336b8e24a0d1218"}, + {file = "black-24.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0057f800de6acc4407fe75bb147b0c2b5cbb7c3ed110d3e5999cd01184d53b0"}, + {file = "black-24.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:faf2ee02e6612577ba0181f4347bcbcf591eb122f7841ae5ba233d12c39dcb4d"}, + {file = "black-24.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:057c3dc602eaa6fdc451069bd027a1b2635028b575a6c3acfd63193ced20d9c8"}, + {file = "black-24.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:08654d0797e65f2423f850fc8e16a0ce50925f9337fb4a4a176a7aa4026e63f8"}, + {file = "black-24.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca610d29415ee1a30a3f30fab7a8f4144e9d34c89a235d81292a1edb2b55f540"}, + {file = "black-24.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:4dd76e9468d5536abd40ffbc7a247f83b2324f0c050556d9c371c2b9a9a95e31"}, + {file = "black-24.2.0-py3-none-any.whl", hash = "sha256:e8a6ae970537e67830776488bca52000eaa37fa63b9988e8c487458d9cd5ace6"}, + {file = "black-24.2.0.tar.gz", hash = "sha256:bce4f25c27c3435e4dace4815bcb2008b87e167e3bf4ee47ccdc5ce906eb4894"}, ] [[package]] @@ -345,7 +349,7 @@ files = [ [[package]] name = "furo" -version = "2023.9.10" +version = "2024.1.29" requires_python = ">=3.8" summary = "A clean customisable Sphinx documentation theme." dependencies = [ @@ -355,8 +359,8 @@ dependencies = [ "sphinx<8.0,>=6.0", ] files = [ - {file = "furo-2023.9.10-py3-none-any.whl", hash = "sha256:513092538537dc5c596691da06e3c370714ec99bc438680edc1debffb73e5bfc"}, - {file = "furo-2023.9.10.tar.gz", hash = "sha256:5707530a476d2a63b8cad83b4f961f3739a69f4b058bcf38a03a39fa537195b2"}, + {file = "furo-2024.1.29-py3-none-any.whl", hash = "sha256:3548be2cef45a32f8cdc0272d415fcb3e5fa6a0eb4ddfe21df3ecf1fe45a13cf"}, + {file = "furo-2024.1.29.tar.gz", hash = "sha256:4d6b2fe3f10a6e36eb9cc24c1e7beb38d7a23fc7b3c382867503b7fcac8a1e02"}, ] [[package]] diff --git a/pyproject.toml b/pyproject.toml index deb127e3..a5e8d140 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ skip-magic-trailing-comma = true [tool.pdm.dev-dependencies] lint = [ - "black>=23.3.0", + "black>=24.2.0", "ruff>=0.0.277", ] test = [ @@ -17,7 +17,7 @@ test = [ ] docs = [ "sphinx>=5.3.0", - "furo>=2023.3.27", + "furo>=2024.1.29", "sphinx-copybutton>=0.5.2", "myst-parser>=1.0.0", "pendulum>=2.1.2", diff --git a/src/cattr/preconf/bson.py b/src/cattr/preconf/bson.py index 1ebe448e..4ac97437 100644 --- a/src/cattr/preconf/bson.py +++ b/src/cattr/preconf/bson.py @@ -1,4 +1,5 @@ """Preconfigured converters for bson.""" + from cattrs.preconf.bson import BsonConverter, configure_converter, make_converter __all__ = ["BsonConverter", "configure_converter", "make_converter"] diff --git a/src/cattr/preconf/json.py b/src/cattr/preconf/json.py index 42cd0a40..d590bd6d 100644 --- a/src/cattr/preconf/json.py +++ b/src/cattr/preconf/json.py @@ -1,4 +1,5 @@ """Preconfigured converters for the stdlib json.""" + from cattrs.preconf.json import JsonConverter, configure_converter, make_converter __all__ = ["configure_converter", "JsonConverter", "make_converter"] diff --git a/src/cattr/preconf/msgpack.py b/src/cattr/preconf/msgpack.py index 270764e3..1a579d63 100644 --- a/src/cattr/preconf/msgpack.py +++ b/src/cattr/preconf/msgpack.py @@ -1,4 +1,5 @@ """Preconfigured converters for msgpack.""" + from cattrs.preconf.msgpack import MsgpackConverter, configure_converter, make_converter __all__ = ["configure_converter", "make_converter", "MsgpackConverter"] diff --git a/src/cattr/preconf/orjson.py b/src/cattr/preconf/orjson.py index 1540fce7..44509901 100644 --- a/src/cattr/preconf/orjson.py +++ b/src/cattr/preconf/orjson.py @@ -1,4 +1,5 @@ """Preconfigured converters for orjson.""" + from cattrs.preconf.orjson import OrjsonConverter, configure_converter, make_converter __all__ = ["configure_converter", "make_converter", "OrjsonConverter"] diff --git a/src/cattr/preconf/pyyaml.py b/src/cattr/preconf/pyyaml.py index d22cdb9b..63d39f18 100644 --- a/src/cattr/preconf/pyyaml.py +++ b/src/cattr/preconf/pyyaml.py @@ -1,4 +1,5 @@ """Preconfigured converters for pyyaml.""" + from cattrs.preconf.pyyaml import PyyamlConverter, configure_converter, make_converter __all__ = ["configure_converter", "make_converter", "PyyamlConverter"] diff --git a/src/cattr/preconf/tomlkit.py b/src/cattr/preconf/tomlkit.py index 432881df..6add7319 100644 --- a/src/cattr/preconf/tomlkit.py +++ b/src/cattr/preconf/tomlkit.py @@ -1,4 +1,5 @@ """Preconfigured converters for tomlkit.""" + from cattrs.preconf.tomlkit import TomlkitConverter, configure_converter, make_converter __all__ = ["configure_converter", "make_converter", "TomlkitConverter"] diff --git a/src/cattr/preconf/ujson.py b/src/cattr/preconf/ujson.py index e85c2ff5..ef85c475 100644 --- a/src/cattr/preconf/ujson.py +++ b/src/cattr/preconf/ujson.py @@ -1,4 +1,5 @@ """Preconfigured converters for ujson.""" + from cattrs.preconf.ujson import UjsonConverter, configure_converter, make_converter __all__ = ["configure_converter", "make_converter", "UjsonConverter"] diff --git a/src/cattrs/_compat.py b/src/cattrs/_compat.py index 465b08e0..bad9d037 100644 --- a/src/cattrs/_compat.py +++ b/src/cattrs/_compat.py @@ -151,12 +151,14 @@ def adapted_fields(cl) -> List[Attribute]: return [ Attribute( attr.name, - attr.default - if attr.default is not MISSING - else ( - Factory(attr.default_factory) - if attr.default_factory is not MISSING - else NOTHING + ( + attr.default + if attr.default is not MISSING + else ( + Factory(attr.default_factory) + if attr.default_factory is not MISSING + else NOTHING + ) ), None, True, diff --git a/src/cattrs/_generics.py b/src/cattrs/_generics.py index b69e0580..c473f433 100644 --- a/src/cattrs/_generics.py +++ b/src/cattrs/_generics.py @@ -12,9 +12,11 @@ def deep_copy_with(t, mapping: Mapping[str, Any]): args = (args[0],) new_args = ( tuple( - mapping[a.__name__] - if hasattr(a, "__name__") and a.__name__ in mapping - else (deep_copy_with(a, mapping) if is_generic(a) else a) + ( + mapping[a.__name__] + if hasattr(a, "__name__") and a.__name__ in mapping + else (deep_copy_with(a, mapping) if is_generic(a) else a) + ) for a in args ) + rest diff --git a/src/cattrs/converters.py b/src/cattrs/converters.py index 441b8c2c..4bed9991 100644 --- a/src/cattrs/converters.py +++ b/src/cattrs/converters.py @@ -286,12 +286,10 @@ def unstruct_strat(self) -> UnstructureStrategy: ) @overload - def register_unstructure_hook(self) -> Callable[[UnstructureHook], None]: - ... + def register_unstructure_hook(self) -> Callable[[UnstructureHook], None]: ... @overload - def register_unstructure_hook(self, cls: Any, func: UnstructureHook) -> None: - ... + def register_unstructure_hook(self, cls: Any, func: UnstructureHook) -> None: ... def register_unstructure_hook( self, cls: Any = None, func: UnstructureHook | None = None @@ -339,26 +337,22 @@ def register_unstructure_hook_func( @overload def register_unstructure_hook_factory( self, predicate: Predicate - ) -> Callable[[UnstructureHookFactory], UnstructureHookFactory]: - ... + ) -> Callable[[UnstructureHookFactory], UnstructureHookFactory]: ... @overload def register_unstructure_hook_factory( self, predicate: Predicate - ) -> Callable[[ExtendedUnstructureHookFactory], ExtendedUnstructureHookFactory]: - ... + ) -> Callable[[ExtendedUnstructureHookFactory], ExtendedUnstructureHookFactory]: ... @overload def register_unstructure_hook_factory( self, predicate: Predicate, factory: UnstructureHookFactory - ) -> UnstructureHookFactory: - ... + ) -> UnstructureHookFactory: ... @overload def register_unstructure_hook_factory( self, predicate: Predicate, factory: ExtendedUnstructureHookFactory - ) -> ExtendedUnstructureHookFactory: - ... + ) -> ExtendedUnstructureHookFactory: ... def register_unstructure_hook_factory(self, predicate, factory=None): """ @@ -427,12 +421,10 @@ def get_unstructure_hook( ) @overload - def register_structure_hook(self) -> Callable[[StructureHook], None]: - ... + def register_structure_hook(self) -> Callable[[StructureHook], None]: ... @overload - def register_structure_hook(self, cl: Any, func: StructuredValue) -> None: - ... + def register_structure_hook(self, cl: Any, func: StructuredValue) -> None: ... def register_structure_hook( self, cl: Any, func: StructureHook | None = None @@ -482,26 +474,22 @@ def register_structure_hook_func( @overload def register_structure_hook_factory( self, predicate: Predicate - ) -> Callable[[StructureHookFactory, StructureHookFactory]]: - ... + ) -> Callable[[StructureHookFactory, StructureHookFactory]]: ... @overload def register_structure_hook_factory( self, predicate: Predicate - ) -> Callable[[ExtendedStructureHookFactory, ExtendedStructureHookFactory]]: - ... + ) -> Callable[[ExtendedStructureHookFactory, ExtendedStructureHookFactory]]: ... @overload def register_structure_hook_factory( self, predicate: Predicate, factory: StructureHookFactory - ) -> StructureHookFactory: - ... + ) -> StructureHookFactory: ... @overload def register_structure_hook_factory( self, predicate: Predicate, factory: ExtendedStructureHookFactory - ) -> ExtendedStructureHookFactory: - ... + ) -> ExtendedStructureHookFactory: ... def register_structure_hook_factory(self, predicate, factory=None): """ @@ -1021,19 +1009,25 @@ def copy( """ res = self.__class__( dict_factory if dict_factory is not None else self._dict_factory, - unstruct_strat - if unstruct_strat is not None - else ( - UnstructureStrategy.AS_DICT - if self._unstructure_attrs == self.unstructure_attrs_asdict - else UnstructureStrategy.AS_TUPLE + ( + unstruct_strat + if unstruct_strat is not None + else ( + UnstructureStrategy.AS_DICT + if self._unstructure_attrs == self.unstructure_attrs_asdict + else UnstructureStrategy.AS_TUPLE + ) + ), + ( + prefer_attrib_converters + if prefer_attrib_converters is not None + else self._prefer_attrib_converters + ), + ( + detailed_validation + if detailed_validation is not None + else self.detailed_validation ), - prefer_attrib_converters - if prefer_attrib_converters is not None - else self._prefer_attrib_converters, - detailed_validation - if detailed_validation is not None - else self.detailed_validation, ) self._unstructure_func.copy_to(res._unstructure_func, self._unstruct_copy_skip) @@ -1347,27 +1341,37 @@ def copy( """ res = self.__class__( dict_factory if dict_factory is not None else self._dict_factory, - unstruct_strat - if unstruct_strat is not None - else ( - UnstructureStrategy.AS_DICT - if self._unstructure_attrs == self.unstructure_attrs_asdict - else UnstructureStrategy.AS_TUPLE + ( + unstruct_strat + if unstruct_strat is not None + else ( + UnstructureStrategy.AS_DICT + if self._unstructure_attrs == self.unstructure_attrs_asdict + else UnstructureStrategy.AS_TUPLE + ) ), omit_if_default if omit_if_default is not None else self.omit_if_default, - forbid_extra_keys - if forbid_extra_keys is not None - else self.forbid_extra_keys, + ( + forbid_extra_keys + if forbid_extra_keys is not None + else self.forbid_extra_keys + ), type_overrides if type_overrides is not None else self.type_overrides, - unstruct_collection_overrides - if unstruct_collection_overrides is not None - else self._unstruct_collection_overrides, - prefer_attrib_converters - if prefer_attrib_converters is not None - else self._prefer_attrib_converters, - detailed_validation - if detailed_validation is not None - else self.detailed_validation, + ( + unstruct_collection_overrides + if unstruct_collection_overrides is not None + else self._unstruct_collection_overrides + ), + ( + prefer_attrib_converters + if prefer_attrib_converters is not None + else self._prefer_attrib_converters + ), + ( + detailed_validation + if detailed_validation is not None + else self.detailed_validation + ), ) self._unstructure_func.copy_to( diff --git a/src/cattrs/disambiguators.py b/src/cattrs/disambiguators.py index 3a1e4391..ad36ae3b 100644 --- a/src/cattrs/disambiguators.py +++ b/src/cattrs/disambiguators.py @@ -1,4 +1,5 @@ """Utilities for union (sum type) disambiguation.""" + from __future__ import annotations from collections import defaultdict @@ -38,8 +39,9 @@ def create_default_dis_func( converter: BaseConverter, *classes: type[AttrsInstance], use_literals: bool = True, - overrides: dict[str, AttributeOverride] - | Literal["from_converter"] = "from_converter", + overrides: ( + dict[str, AttributeOverride] | Literal["from_converter"] + ) = "from_converter", ) -> Callable[[Mapping[Any, Any]], type[Any] | None]: """Given attrs classes or dataclasses, generate a disambiguation function. diff --git a/src/cattrs/dispatch.py b/src/cattrs/dispatch.py index f82ae878..3d746dbc 100644 --- a/src/cattrs/dispatch.py +++ b/src/cattrs/dispatch.py @@ -44,9 +44,9 @@ class FunctionDispatch: """ _converter: BaseConverter - _handler_pairs: list[ - tuple[Predicate, Callable[[Any, Any], Any], bool, bool] - ] = Factory(list) + _handler_pairs: list[tuple[Predicate, Callable[[Any, Any], Any], bool, bool]] = ( + Factory(list) + ) def register( self, diff --git a/src/cattrs/fns.py b/src/cattrs/fns.py index 7d3db677..748cfb3d 100644 --- a/src/cattrs/fns.py +++ b/src/cattrs/fns.py @@ -1,4 +1,5 @@ """Useful internal functions.""" + from typing import Any, Callable, NoReturn, Type, TypeVar from ._compat import TypeAlias diff --git a/src/cattrs/gen/_lc.py b/src/cattrs/gen/_lc.py index f6b147e9..e598a393 100644 --- a/src/cattrs/gen/_lc.py +++ b/src/cattrs/gen/_lc.py @@ -1,4 +1,5 @@ """Line-cache functionality.""" + import linecache from typing import Any, List diff --git a/src/cattrs/preconf/bson.py b/src/cattrs/preconf/bson.py index cab125be..e73d1316 100644 --- a/src/cattrs/preconf/bson.py +++ b/src/cattrs/preconf/bson.py @@ -1,4 +1,5 @@ """Preconfigured converters for bson.""" + from base64 import b85decode, b85encode from datetime import date, datetime from typing import Any, Type, TypeVar, Union diff --git a/src/cattrs/preconf/cbor2.py b/src/cattrs/preconf/cbor2.py index 414d19ce..73a9a972 100644 --- a/src/cattrs/preconf/cbor2.py +++ b/src/cattrs/preconf/cbor2.py @@ -1,4 +1,5 @@ """Preconfigured converters for cbor2.""" + from datetime import date, datetime, timezone from typing import Any, Type, TypeVar, Union diff --git a/src/cattrs/preconf/json.py b/src/cattrs/preconf/json.py index f4f5057a..acc82ae9 100644 --- a/src/cattrs/preconf/json.py +++ b/src/cattrs/preconf/json.py @@ -1,4 +1,5 @@ """Preconfigured converters for the stdlib json.""" + from base64 import b85decode, b85encode from datetime import date, datetime from json import dumps, loads diff --git a/src/cattrs/preconf/msgpack.py b/src/cattrs/preconf/msgpack.py index 2a63ccd8..dd7c3696 100644 --- a/src/cattrs/preconf/msgpack.py +++ b/src/cattrs/preconf/msgpack.py @@ -1,4 +1,5 @@ """Preconfigured converters for msgpack.""" + from datetime import date, datetime, time, timezone from typing import Any, Type, TypeVar, Union diff --git a/src/cattrs/preconf/msgspec.py b/src/cattrs/preconf/msgspec.py index b087047f..e58cbec8 100644 --- a/src/cattrs/preconf/msgspec.py +++ b/src/cattrs/preconf/msgspec.py @@ -1,4 +1,5 @@ """Preconfigured converters for msgspec.""" + from __future__ import annotations from base64 import b64decode diff --git a/src/cattrs/preconf/orjson.py b/src/cattrs/preconf/orjson.py index bcad43bf..60e46287 100644 --- a/src/cattrs/preconf/orjson.py +++ b/src/cattrs/preconf/orjson.py @@ -1,4 +1,5 @@ """Preconfigured converters for orjson.""" + from base64 import b85decode, b85encode from datetime import date, datetime from enum import Enum diff --git a/src/cattrs/preconf/pyyaml.py b/src/cattrs/preconf/pyyaml.py index 9b479113..45bc828a 100644 --- a/src/cattrs/preconf/pyyaml.py +++ b/src/cattrs/preconf/pyyaml.py @@ -1,4 +1,5 @@ """Preconfigured converters for pyyaml.""" + from datetime import date, datetime from functools import partial from typing import Any, Type, TypeVar, Union diff --git a/src/cattrs/preconf/tomlkit.py b/src/cattrs/preconf/tomlkit.py index 10daf49d..0d0180bf 100644 --- a/src/cattrs/preconf/tomlkit.py +++ b/src/cattrs/preconf/tomlkit.py @@ -1,4 +1,5 @@ """Preconfigured converters for tomlkit.""" + from base64 import b85decode, b85encode from datetime import date, datetime from enum import Enum diff --git a/src/cattrs/preconf/ujson.py b/src/cattrs/preconf/ujson.py index 0644186b..7256d52a 100644 --- a/src/cattrs/preconf/ujson.py +++ b/src/cattrs/preconf/ujson.py @@ -1,4 +1,5 @@ """Preconfigured converters for ujson.""" + from base64 import b85decode, b85encode from datetime import date, datetime from typing import Any, AnyStr, Type, TypeVar, Union diff --git a/src/cattrs/strategies/__init__.py b/src/cattrs/strategies/__init__.py index c2fe4fb7..9caf0732 100644 --- a/src/cattrs/strategies/__init__.py +++ b/src/cattrs/strategies/__init__.py @@ -1,4 +1,5 @@ """High level strategies for converters.""" + from ._class_methods import use_class_methods from ._subclasses import include_subclasses from ._unions import configure_tagged_union, configure_union_passthrough diff --git a/src/cattrs/strategies/_class_methods.py b/src/cattrs/strategies/_class_methods.py index 4eb03ec5..c2b63253 100644 --- a/src/cattrs/strategies/_class_methods.py +++ b/src/cattrs/strategies/_class_methods.py @@ -1,4 +1,5 @@ """Strategy for using class-specific (un)structuring methods.""" + from inspect import signature from typing import Any, Callable, Optional, Type, TypeVar diff --git a/src/cattrs/strategies/_subclasses.py b/src/cattrs/strategies/_subclasses.py index a026b8a7..06a92afa 100644 --- a/src/cattrs/strategies/_subclasses.py +++ b/src/cattrs/strategies/_subclasses.py @@ -1,4 +1,5 @@ """Strategies for customizing subclass behaviors.""" + from __future__ import annotations from gc import collect diff --git a/src/cattrs/v.py b/src/cattrs/v.py index 795a8aa2..c3ab18cc 100644 --- a/src/cattrs/v.py +++ b/src/cattrs/v.py @@ -1,4 +1,5 @@ """Cattrs validation.""" + from typing import Callable, List, Union from .errors import ( diff --git a/tests/preconf/test_msgspec_cpython.py b/tests/preconf/test_msgspec_cpython.py index 231d91b5..3e6c4362 100644 --- a/tests/preconf/test_msgspec_cpython.py +++ b/tests/preconf/test_msgspec_cpython.py @@ -1,4 +1,5 @@ """Tests for msgspec functionality.""" + from __future__ import annotations from enum import Enum diff --git a/tests/preconf/test_pyyaml.py b/tests/preconf/test_pyyaml.py index 6b8e73b8..ebf0cfb3 100644 --- a/tests/preconf/test_pyyaml.py +++ b/tests/preconf/test_pyyaml.py @@ -1,4 +1,5 @@ """Pyyaml-specific tests.""" + from datetime import date, datetime, timezone from attrs import define diff --git a/tests/strategies/test_class_methods.py b/tests/strategies/test_class_methods.py index c2d429fa..a99b2a78 100644 --- a/tests/strategies/test_class_methods.py +++ b/tests/strategies/test_class_methods.py @@ -52,17 +52,21 @@ def test_not_nested(get_converter, structure_method, unstructure_method, cls) -> assert converter.structure( { - "b" - if structure_method == "_structure" and hasattr(cls, "_structure") - else "a": 42 + ( + "b" + if structure_method == "_structure" and hasattr(cls, "_structure") + else "a" + ): 42 }, cls, ) == cls(42) assert converter.unstructure(cls(42)) == { - "c" - if unstructure_method == "_unstructure" and hasattr(cls, "_unstructure") - else "a": 42 + ( + "c" + if unstructure_method == "_unstructure" and hasattr(cls, "_unstructure") + else "a" + ): 42 } diff --git a/tests/strategies/test_native_unions.py b/tests/strategies/test_native_unions.py index cbe0b7e8..837831be 100644 --- a/tests/strategies/test_native_unions.py +++ b/tests/strategies/test_native_unions.py @@ -3,6 +3,7 @@ Note that a significant amount of test coverage for this is in the preconf tests. """ + from typing import List, Optional, Union import pytest diff --git a/tests/test_any.py b/tests/test_any.py index 291125d5..9b9cbced 100644 --- a/tests/test_any.py +++ b/tests/test_any.py @@ -1,4 +1,5 @@ """Tests for handling `typing.Any`.""" + from typing import Any, Dict, Optional from attrs import define diff --git a/tests/test_baseconverter.py b/tests/test_baseconverter.py index 63057015..558e9013 100644 --- a/tests/test_baseconverter.py +++ b/tests/test_baseconverter.py @@ -1,4 +1,5 @@ """Test both structuring and unstructuring.""" + from typing import Optional, Union import pytest diff --git a/tests/test_converter.py b/tests/test_converter.py index 13ebdb01..b401860c 100644 --- a/tests/test_converter.py +++ b/tests/test_converter.py @@ -1,4 +1,5 @@ """Test both structuring and unstructuring.""" + from collections import deque from typing import ( Any, @@ -580,9 +581,9 @@ def test_seq_of_simple_classes_unstructure(cls_and_vals, seq_type_and_annotation inputs = seq_type(cl(*vals, **kwargs) for cl, vals, kwargs in cls_and_vals) outputs = converter.unstructure( inputs, - unstructure_as=annotation[cl] - if annotation not in (Tuple, tuple) - else annotation[cl, ...], + unstructure_as=( + annotation[cl] if annotation not in (Tuple, tuple) else annotation[cl, ...] + ), ) assert all(e == test_val for e in outputs) @@ -628,9 +629,11 @@ class C: inputs = [{"a": cl(*vals), "b": cl(*vals)} for _ in range(5)] outputs = converter.structure( inputs, - cl=annotation[C] - if annotation not in (Tuple, tuple) - else annotation[C, ...], + cl=( + annotation[C] + if annotation not in (Tuple, tuple) + else annotation[C, ...] + ), ) expected = seq_type(C(a=cl(*vals), b=cl(*vals)) for _ in range(5)) diff --git a/tests/test_disambiguators.py b/tests/test_disambiguators.py index d9fc8d72..a9db5c91 100644 --- a/tests/test_disambiguators.py +++ b/tests/test_disambiguators.py @@ -1,4 +1,5 @@ """Tests for auto-disambiguators.""" + from dataclasses import dataclass from functools import partial from typing import Literal, Union diff --git a/tests/test_enums.py b/tests/test_enums.py index 35040843..59ebb6b6 100644 --- a/tests/test_enums.py +++ b/tests/test_enums.py @@ -1,4 +1,5 @@ """Tests for enums.""" + from hypothesis import given from hypothesis.strategies import data, sampled_from from pytest import raises diff --git a/tests/test_factory_hooks.py b/tests/test_factory_hooks.py index e0877d48..ed95f319 100644 --- a/tests/test_factory_hooks.py +++ b/tests/test_factory_hooks.py @@ -1,4 +1,5 @@ """Tests for the factory hooks documentation.""" + from attr import define, fields, has from cattrs.gen import make_dict_structure_fn, make_dict_unstructure_fn, override diff --git a/tests/test_gen.py b/tests/test_gen.py index dab20f10..929a7923 100644 --- a/tests/test_gen.py +++ b/tests/test_gen.py @@ -1,4 +1,5 @@ """Tests for functionality from the gen module.""" + import linecache from traceback import format_exc diff --git a/tests/test_gen_collections.py b/tests/test_gen_collections.py index 12d05a43..93437891 100644 --- a/tests/test_gen_collections.py +++ b/tests/test_gen_collections.py @@ -1,4 +1,5 @@ """Tests for collections in `cattrs.gen`.""" + from typing import Generic, Mapping, NewType, Tuple, TypeVar from cattrs import Converter diff --git a/tests/test_gen_dict.py b/tests/test_gen_dict.py index 0d90813d..5e5f5720 100644 --- a/tests/test_gen_dict.py +++ b/tests/test_gen_dict.py @@ -1,4 +1,5 @@ """Tests for generated dict functions.""" + from typing import Dict, Type import pytest diff --git a/tests/test_gen_dict_563.py b/tests/test_gen_dict_563.py index 3ab4fd8c..105ea25e 100644 --- a/tests/test_gen_dict_563.py +++ b/tests/test_gen_dict_563.py @@ -1,4 +1,5 @@ """`gen` tests under PEP 563.""" + from __future__ import annotations from dataclasses import dataclass diff --git a/tests/test_generics.py b/tests/test_generics.py index f5b6d813..d0898a5e 100644 --- a/tests/test_generics.py +++ b/tests/test_generics.py @@ -323,17 +323,14 @@ class C(Generic[T, U]): def test_nongeneric_protocols(converter): """Non-generic protocols work.""" - class NongenericProtocol(Protocol): - ... + class NongenericProtocol(Protocol): ... @define - class Entity(NongenericProtocol): - ... + class Entity(NongenericProtocol): ... assert generate_mapping(Entity) == {} - class GenericProtocol(Protocol[T]): - ... + class GenericProtocol(Protocol[T]): ... @define class GenericEntity(GenericProtocol[int]): diff --git a/tests/test_generics_604.py b/tests/test_generics_604.py index a224f1af..1bdbc77c 100644 --- a/tests/test_generics_604.py +++ b/tests/test_generics_604.py @@ -1,4 +1,5 @@ """Tests for generics under PEP 604 (unions as pipes).""" + from typing import Generic, TypeVar from attrs import define diff --git a/tests/test_generics_695.py b/tests/test_generics_695.py index 401be0e0..380d8e25 100644 --- a/tests/test_generics_695.py +++ b/tests/test_generics_695.py @@ -1,4 +1,5 @@ """Tests for PEP 695 (Type Parameter Syntax).""" + from dataclasses import dataclass import pytest diff --git a/tests/test_generics_696.py b/tests/test_generics_696.py index 2e0680eb..c56c894f 100644 --- a/tests/test_generics_696.py +++ b/tests/test_generics_696.py @@ -1,4 +1,5 @@ """Tests for generics under PEP 696 (type defaults).""" + from typing import Generic, List import pytest diff --git a/tests/test_newtypes.py b/tests/test_newtypes.py index c2eed500..75b2bdc5 100644 --- a/tests/test_newtypes.py +++ b/tests/test_newtypes.py @@ -1,4 +1,5 @@ """Tests for NewTypes.""" + from typing import NewType import pytest diff --git a/tests/test_preconf.py b/tests/test_preconf.py index 4ea59ce4..dba47fe0 100644 --- a/tests/test_preconf.py +++ b/tests/test_preconf.py @@ -124,9 +124,9 @@ def everythings( ) strings = text( characters( - blacklist_categories=("Cs",) - if allow_control_characters_in_values - else ("Cs", "Cc") + blacklist_categories=( + ("Cs",) if allow_control_characters_in_values else ("Cs", "Cc") + ) ) ) dts = datetimes( diff --git a/tests/test_recursive.py b/tests/test_recursive.py index e40b4c74..857cf435 100644 --- a/tests/test_recursive.py +++ b/tests/test_recursive.py @@ -1,4 +1,5 @@ """Test un/structuring recursive class graphs.""" + from __future__ import annotations from typing import List diff --git a/tests/test_structure.py b/tests/test_structure.py index 6090fe5d..4b3e61d8 100644 --- a/tests/test_structure.py +++ b/tests/test_structure.py @@ -1,4 +1,5 @@ """Test structuring of collections and primitives.""" + from typing import Any, Dict, FrozenSet, List, MutableSet, Optional, Set, Tuple, Union from attrs import define diff --git a/tests/test_structure_attrs.py b/tests/test_structure_attrs.py index d9365444..272ae696 100644 --- a/tests/test_structure_attrs.py +++ b/tests/test_structure_attrs.py @@ -1,4 +1,5 @@ """Loading of attrs classes.""" + from enum import Enum from ipaddress import IPv4Address, IPv6Address, ip_address from typing import Literal, Union diff --git a/tests/test_tuples.py b/tests/test_tuples.py index bf0ace5e..91c35a50 100644 --- a/tests/test_tuples.py +++ b/tests/test_tuples.py @@ -1,4 +1,5 @@ """Tests for tuples of all kinds.""" + from typing import NamedTuple, Tuple from cattrs.converters import Converter diff --git a/tests/test_typeddicts.py b/tests/test_typeddicts.py index 82f1a3c4..bf435fba 100644 --- a/tests/test_typeddicts.py +++ b/tests/test_typeddicts.py @@ -1,4 +1,5 @@ """Tests for TypedDict un/structuring.""" + from datetime import datetime, timezone from typing import Dict, Generic, NewType, Set, Tuple, TypedDict, TypeVar diff --git a/tests/test_unstructure.py b/tests/test_unstructure.py index 2cce4713..66da2c5e 100644 --- a/tests/test_unstructure.py +++ b/tests/test_unstructure.py @@ -1,4 +1,5 @@ """Tests for dumping.""" + from typing import Type from attr import asdict, astuple diff --git a/tests/test_v.py b/tests/test_v.py index f75f68ab..4aa97164 100644 --- a/tests/test_v.py +++ b/tests/test_v.py @@ -1,4 +1,5 @@ """Tests for the cattrs.v framework.""" + from typing import ( Dict, List, diff --git a/tests/test_validation.py b/tests/test_validation.py index 89a715bc..53027e31 100644 --- a/tests/test_validation.py +++ b/tests/test_validation.py @@ -1,4 +1,5 @@ """Tests for the extended validation mode.""" + import pickle from typing import Deque, Dict, FrozenSet, List, Set, Tuple diff --git a/tests/typed.py b/tests/typed.py index e3c79f7a..7589c9a6 100644 --- a/tests/typed.py +++ b/tests/typed.py @@ -1,4 +1,5 @@ """Strategies for attributes with types and classes using them.""" + import sys from collections import OrderedDict from collections.abc import MutableSequence as AbcMutableSequence @@ -294,12 +295,18 @@ def key(t): make_dataclass( "HypDataclass", [ - (n, a.type) - if a._default is NOTHING - else ( - (n, a.type, dc_field(default=a._default)) - if not isinstance(a._default, Factory) - else (n, a.type, dc_field(default_factory=a._default.factory)) + ( + (n, a.type) + if a._default is NOTHING + else ( + (n, a.type, dc_field(default=a._default)) + if not isinstance(a._default, Factory) + else ( + n, + a.type, + dc_field(default_factory=a._default.factory), + ) + ) ) for n, a in zip(gen_attr_names(), attrs) ], @@ -659,9 +666,11 @@ def mutable_seq_typed_attrs( return ( field( - type=AbcMutableSequence[float] - if not legacy_types_only - else MutableSequence[float], + type=( + AbcMutableSequence[float] + if not legacy_types_only + else MutableSequence[float] + ), default=default, kw_only=draw(booleans()) if kw_only is None else kw_only, ), diff --git a/tests/typeddicts.py b/tests/typeddicts.py index 53b71676..ce40762a 100644 --- a/tests/typeddicts.py +++ b/tests/typeddicts.py @@ -1,4 +1,5 @@ """Strategies for typed dicts.""" + from datetime import datetime, timezone from string import ascii_lowercase from typing import Any, Dict, Generic, List, Optional, Set, Tuple, Type, TypeVar @@ -70,9 +71,11 @@ def annotated_int_attributes( if total: if not_required and draw(booleans()): return ( - NotRequired[Annotated[int, "test"]] - if draw(booleans()) - else Annotated[NotRequired[int], "test"], + ( + NotRequired[Annotated[int, "test"]] + if draw(booleans()) + else Annotated[NotRequired[int], "test"] + ), integers() | just(NOTHING), text(ascii_lowercase), ) @@ -80,9 +83,11 @@ def annotated_int_attributes( if not_required and draw(booleans()): return ( - Required[Annotated[int, "test"]] - if draw(booleans()) - else Annotated[Required[int], "test"], + ( + Required[Annotated[int, "test"]] + if draw(booleans()) + else Annotated[Required[int], "test"] + ), integers(), text(ascii_lowercase), ) diff --git a/tests/untyped.py b/tests/untyped.py index eb78f148..9dc815b0 100644 --- a/tests/untyped.py +++ b/tests/untyped.py @@ -1,4 +1,5 @@ """Strategies for attributes without types and accompanying classes.""" + import keyword import string from collections import OrderedDict