Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug] GenConverter: Wrong resolving / missing __parameters__ attribute when inheriting something from typing.* #217

Closed
raabf opened this issue Feb 2, 2022 · 7 comments

Comments

@raabf
Copy link
Contributor

raabf commented Feb 2, 2022

  • cattrs version: 1.10.0
  • Python version: 3.7
  • Operating System: Linux 5.16.1-1 OpenSUSE Thumbleweed

There is an exception AttributeError: type object 'CLASS' has no attribute '__parameters__' when you try to structure an attrs Class which inherts typing.CLASS

Reproduce

Have a look at this example where CLASS is Hashable:

import attr
import typing
import cattrs
import collections

converter = cattrs.GenConverter()

@attr.define
class A(collections.abc.Hashable):  # typing.Hashable should be preferred over collections.abc.Hashable
    attr1: str = ''
    attr2: int = 0

    def __hash__(self) -> int:
        return hash(self.attr2)

@attr.define
class B(typing.Hashable):
    attr1: str = ''
    attr2: int = 0

    def __hash__(self) -> int:
        return hash(self.attr2)


def main() -> None:
    converter.structure(dict(attr1='hello', attr2=42), A)  # OK
    converter.structure(dict(attr1='hello', attr2=42), B)  # AttributeError: type object 'Hashable' has no attribute '__parameters__'

if __name__ == '__main__':
    main()

Output

Traceback (most recent call last):
  File "/home/raabf/sl/demo.py", line 40, in <module>
    main()
  File "/home/raabf/sl/demo.py", line 37, in main
    converter.structure(dict(attr1='hello', attr2=42), B)  # AttributeError: type object 'Hashable' has no attribute '__parameters__'
  File "/home/raabf/.pyenv/versions/sl37/lib/python3.7/site-packages/cattr/converters.py", line 300, in structure
    return self._structure_func.dispatch(cl)(obj, cl)
  File "/home/raabf/.pyenv/versions/sl37/lib/python3.7/site-packages/cattr/dispatch.py", line 49, in _dispatch
    return self._function_dispatch.dispatch(cl)
  File "/home/raabf/.pyenv/versions/sl37/lib/python3.7/site-packages/cattr/dispatch.py", line 126, in dispatch
    return handler(typ)
  File "/home/raabf/.pyenv/versions/sl37/lib/python3.7/site-packages/cattr/converters.py", line 745, in gen_structure_attrs_fromdict
    **attrib_overrides,
  File "/home/raabf/.pyenv/versions/sl37/lib/python3.7/site-packages/cattr/gen.py", line 224, in make_dict_structure_fn
    mapping = _generate_mapping(base, mapping)
  File "/home/raabf/.pyenv/versions/sl37/lib/python3.7/site-packages/cattr/gen.py", line 195, in _generate_mapping
    for p, t in zip(get_origin(cl).__parameters__, get_args(cl)):
AttributeError: type object 'Hashable' has no attribute '__parameters__'

Note that in the above code snippet I use typing.Hashable just as an example, the same exception occurs if you inherit something other from typing.*, such as typing.Reversable or typing.Iterable.

Offending Part

The offending part in cattrs is:

https://github.com/python-attrs/cattrs/blob/v1.10.0/src/cattr/gen.py#L206-L225

def make_dict_structure_fn(
    cl: Type[T],
    converter: "Converter",
    _cattrs_forbid_extra_keys: bool = False,
    _cattrs_use_linecache: bool = True,
    _cattrs_prefer_attrib_converters: bool = False,
    **kwargs,
) -> Callable[[Mapping[str, Any]], T]:
    """Generate a specialized dict structuring function for an attrs class."""

    mapping = {}
    if is_generic(cl):
        base = get_origin(cl)
        mapping = _generate_mapping(cl, mapping)
        cl = base

    for base in getattr(cl, "__orig_bases__", ()):
        if is_generic(base) and not str(base).startswith("typing.Generic"):
            mapping = _generate_mapping(base, mapping)
            break

which again calls

def _generate_mapping(
    cl: Type, old_mapping: Dict[str, type]
) -> Dict[str, type]:
    mapping = {}
    for p, t in zip(get_origin(cl).__parameters__, get_args(cl)):
        if isinstance(t, TypeVar):
            continue
        mapping[p.__name__] = t

    if not mapping:
        return old_mapping

    return mapping

where __parameters__ is accessed. During the exception cl is typing.Hashable, which resolves to collections.abc.Hashable by get_origin(cl).

Reason

The use of typing.Hashable is correct. As far as I understand, during static analyisis from tools such as mypy it acts as type checking, during runtime it is resolved to collections.abc.Hashable where the class is implemented.
I personally rely on inheriting from typing.Hashable instead of collections.abc.Hashable, or else mypy would do wrong type checking.

I do not understand what cattrs is doing, so I just can make some guesses:

  • The resolving from typing.Hashable to collections.abc.Hashable at runtime (i.e. typing.Hashable has collections.abc.Hashable as its origin) confuses cattrs.
  • Some check before fails and _generate_mapping should have been never called with typing.Hashable
  • In general you cannot assume that all origins have the __parameters__ attribute, i.e. use getattr(get_origin(cl), '__parameters__', ()) instead.

Reference for getting parameters

The library typing_inspect has an get_parameters function to get __parameters__. The function is a bit longer and handles some corner cases , I do not know if this helps with problem reported in this issue here or can be utilized here.

@Tinche
Copy link
Member

Tinche commented Feb 2, 2022

Interesting.

Before I dive deeper into this, since I had a Python 3.11 virtual env activated I tried your code in that and it worked. So this might've gotten fixed in Python at some point? 3.8 seems to also error, 3.9 works.

@raabf
Copy link
Contributor Author

raabf commented Feb 2, 2022

Can confirm, did not expect a different bahaviour on different python versions, since I thought this is something by design.

  • 3.7.12 ↦ Error occurs
  • 3.8.12 ↦ Error occurs
  • 3.9.10 ↦ No Error

However, I am not sure what exactly got ‘fixed‘ in python 3.9. collections.abc.Hashable still does not have a __parameters__ attribute, but the error does not occur any more because _generate_mapping is not called any more. That again is caused because is_generic(base) in if is_generic(base) and not str(base).startswith("typing.Generic"): of the function make_dict_structure_fn returns False since python 3.9.

@Tinche
Copy link
Member

Tinche commented Feb 2, 2022

Yeah, this is development using Python runtime types in a nutshell ;) If you look around on the issue tracker, you'll find an issue introduced between 3.9.9 and 3.9.10.

get_args from typing also has its idiosyncrasies between versions. If we can fix this issue with getattr(get_origin(cl), '__parameters__', ()) I'm very open to merging that in since it's a super minimal change.

raabf added a commit to raabf/cattrs that referenced this issue Feb 3, 2022
There are Generic types in the typing modules
from which you can inherit in your own classes
which do not have an __parameters__ attribute,
such classes are now ignored making
gen._generate_mapping effectively a no-op
in case the class do not have an __parameters__
attribute.

As https://github.com/ilevkivskyi/typing_inspect/blob/8f6aa2075ba448ab322def454137e7c59b9b302d/typing_inspect.py#L405
is showing there are also cases where __parameters__
could be None, so I test for both cases, that it
is None or that it does not exist.

See Also:
python-attrs#217
@raabf
Copy link
Contributor Author

raabf commented Feb 3, 2022

Using getattr(get_origin(cl), '__parameters__', ()) as it makes _generate_mapping a no-op if the __parameters__ do not exist. And It seems to fix my current problem. Hence I made #221 .

is_generic

Still as a follow up question I must ask if cattrs._compat.is_generic() is a correct implementation?

I compared that with typing_inspection.is_generic_type Which always returns True:

Python 3.7.12 (default, Nov 12 2021, 17:41:59)
[GCC 11.2.1 20210816 [revision 056e324ce46a7924b5cf10f61010cf9dd2ca10e9]] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from typing_inspect import is_generic_type
>>> from typing import Hashable 
>>> is_generic_type(Hashable)
True
>>> from cattr._compat import is_generic
>>> is_generic(Hashable)
True
Python 3.9.10 (main, Feb  2 2022, 19:30:01) 
[GCC 11.2.1 20220103 [revision d4a1d3c4b377f1d4acb34fe1b55b5088a3f293f6]] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from typing_inspect import is_generic_type
>>> from typing import Hashable
>>> is_generic_type(Hashable)
True
>>> from cattr._compat import is_generic
>>> is_generic(Hashable)
False

So typing_inspect.is_generic_type() is always True, although there is no special handling betwenn the python versions, but cattrs._compat.is_generic() behaves differently.

@raabf
Copy link
Contributor Author

raabf commented Feb 3, 2022

Beside that I thought about it, and I think #221 is correct behaviour, because if a class is generic it must not neccessary have __parameters__.
For example

>>> import typing
>>> class A(typing.Generic[T]):                                                                                      
...         ...                                                                                                            
...
>>> class B(A[str]):
...         ...                                                                                                            
...
>>> A.__parameters__
(~T,)
>>> B.__parameters__
()

With B.__parameters__ there is no AttributeError like on accessing typing.Hashable.__parameters__ but the list is still empty and hence _generate_mapping would be in effect a no-op.

raabf added a commit to raabf/cattrs that referenced this issue Feb 5, 2022
There are Generic types in the typing modules
from which you can inherit in your own classes
which do not have an __parameters__ attribute,
such classes are now ignored making
gen._generate_mapping effectively a no-op
in case the class do not have an __parameters__
attribute.

As https://github.com/ilevkivskyi/typing_inspect/blob/8f6aa2075ba448ab322def454137e7c59b9b302d/typing_inspect.py#L405
is showing there are also cases where __parameters__
could be None, so I test for both cases, that it
is None or that it does not exist.

See Also:
python-attrs#217
raabf added a commit to raabf/cattrs that referenced this issue Feb 5, 2022
There are Generic types in the typing modules
from which you can inherit in your own classes
which do not have an __parameters__ attribute,
such classes are now ignored making
gen._generate_mapping effectively a no-op
in case the class do not have an __parameters__
attribute.

As https://github.com/ilevkivskyi/typing_inspect/blob/8f6aa2075ba448ab322def454137e7c59b9b302d/typing_inspect.py#L405
is showing there are also cases where __parameters__
could be None, so I test for both cases, that it
is None or that it does not exist.

See Also:
python-attrs#217
Tinche pushed a commit that referenced this issue Feb 7, 2022
There are Generic types in the typing modules
from which you can inherit in your own classes
which do not have an __parameters__ attribute,
such classes are now ignored making
gen._generate_mapping effectively a no-op
in case the class do not have an __parameters__
attribute.

As https://github.com/ilevkivskyi/typing_inspect/blob/8f6aa2075ba448ab322def454137e7c59b9b302d/typing_inspect.py#L405
is showing there are also cases where __parameters__
could be None, so I test for both cases, that it
is None or that it does not exist.

See Also:
#217
@Tinche
Copy link
Member

Tinche commented Feb 8, 2022

Hm can this be closed?

@raabf
Copy link
Contributor Author

raabf commented Feb 8, 2022

Yea thanks, as far as I see now, the MR #221 should handle all cases where a Generic is not complete, i.e. does not have an __parameters__.

@raabf raabf closed this as completed Feb 8, 2022
mergify bot pushed a commit to aws/jsii that referenced this issue Apr 4, 2022
…2 in /packages/@jsii/python-runtime (#3470)

Updates the requirements on [cattrs](https://github.com/python-attrs/cattrs) to permit the latest version.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/python-attrs/cattrs/blob/main/HISTORY.rst">cattrs's changelog</a>.</em></p>
<blockquote>
<h2>22.1.0 (2022-04-03)</h2>
<ul>
<li>cattrs now uses the CalVer versioning convention.</li>
<li>cattrs now has a detailed validation mode, which is enabled by default. Learn more <code>here &lt;https://cattrs.readthedocs.io/en/latest/validation.html&gt;</code>_.
The old behavior can be restored by creating the converter with <code>detailed_validation=False</code>.</li>
<li><code>attrs</code> and dataclass structuring is now ~25% faster.</li>
<li>Fix an issue structuring bare <code>typing.List</code> s on Pythons lower than 3.9.
(<code>[#209](python-attrs/cattrs#209) &lt;https://github.com/python-attrs/cattrs/issues/209&gt;</code>_)</li>
<li>Fix structuring of non-parametrized containers like <code>list/dict/...</code> on Pythons lower than 3.9.
(<code>[#218](python-attrs/cattrs#218) &lt;https://github.com/python-attrs/cattrs/issues/218&gt;</code>_)</li>
<li>Fix structuring bare <code>typing.Tuple</code> on Pythons lower than 3.9.
(<code>[#218](python-attrs/cattrs#218) &lt;https://github.com/python-attrs/cattrs/issues/218&gt;</code>_)</li>
<li>Fix a wrong <code>AttributeError</code> of an missing <code>__parameters__</code> attribute. This could happen
when inheriting certain generic classes – for example <code>typing.*</code> classes are affected.
(<code>[#217](python-attrs/cattrs#217) &lt;https://github.com/python-attrs/cattrs/issues/217&gt;</code>_)</li>
<li>Fix structuring of <code>enum.Enum</code> instances in <code>typing.Literal</code> types.
(<code>[#231](python-attrs/cattrs#231) &lt;https://github.com/python-attrs/cattrs/pull/231&gt;</code>_)</li>
<li>Fix unstructuring all tuples - unannotated, variable-length, homogenous and heterogenous - to <code>list</code>.
(<code>[#226](python-attrs/cattrs#226) &lt;https://github.com/python-attrs/cattrs/issues/226&gt;</code>_)</li>
<li>For <code>forbid_extra_keys</code> raise custom <code>ForbiddenExtraKeyError</code> instead of generic <code>Exception</code>.
(<code>[#225](python-attrs/cattrs#225) &lt;https://github.com/python-attrs/cattrs/pull/225&gt;</code>_)</li>
<li>All preconf converters now support <code>loads</code> and <code>dumps</code> directly. See an example <code>here &lt;https://cattrs.readthedocs.io/en/latest/preconf.html&gt;</code>_.</li>
<li>Fix mappings with byte keys for the orjson, bson and tomlkit converters.
(<code>[#241](python-attrs/cattrs#241) &lt;https://github.com/python-attrs/cattrs/issues/241&gt;</code>_)</li>
</ul>
<h2>1.10.0 (2022-01-04)</h2>
<ul>
<li>Add PEP 563 (string annotations) support for dataclasses.
(<code>[#195](python-attrs/cattrs#195) &lt;https://github.com/python-attrs/cattrs/issues/195&gt;</code>_)</li>
<li>Fix handling of dictionaries with string Enum keys for bson, orjson, and tomlkit.</li>
<li>Rename the <code>cattr.gen.make_dict_unstructure_fn.omit_if_default</code> parameter to <code>_cattrs_omit_if_default</code>, for consistency. The <code>omit_if_default</code> parameters to <code>GenConverter</code> and <code>override</code> are unchanged.</li>
<li>Following the changes in <code>attrs</code> 21.3.0, add a <code>cattrs</code> package mirroring the existing <code>cattr</code> package. Both package names may be used as desired, and the <code>cattr</code> package isn't going away.</li>
</ul>
<h2>1.9.0 (2021-12-06)</h2>
<ul>
<li>Python 3.10 support, including support for the new union syntax (<code>A | B</code> vs <code>Union[A, B]</code>).</li>
<li>The <code>GenConverter</code> can now properly structure generic classes with generic collection fields.
(<code>[#149](python-attrs/cattrs#149) &lt;https://github.com/python-attrs/cattrs/issues/149&gt;</code>_)</li>
<li><code>omit=True</code> now also affects generated structuring functions.
(<code>[#166](python-attrs/cattrs#166) &lt;https://github.com/python-attrs/cattrs/issues/166&gt;</code>_)</li>
<li><code>cattr.gen.{make_dict_structure_fn, make_dict_unstructure_fn}</code> now resolve type annotations automatically when PEP 563 is used.
(<code>[#169](python-attrs/cattrs#169) &lt;https://github.com/python-attrs/cattrs/issues/169&gt;</code>_)</li>
<li>Protocols are now unstructured as their runtime types.
(<code>[#177](python-attrs/cattrs#177) &lt;https://github.com/python-attrs/cattrs/pull/177&gt;</code>_)</li>
<li>Fix an issue generating structuring functions with renaming and <code>_cattrs_forbid_extra_keys=True</code>.
(<code>[#190](python-attrs/cattrs#190) &lt;https://github.com/python-attrs/cattrs/issues/190&gt;</code>_)</li>
</ul>
<h2>1.8.0 (2021-08-13)</h2>
<ul>
<li>Fix <code>GenConverter</code> mapping structuring for unannotated dicts on Python 3.8.</li>
</ul>

</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a href="https://github.com/python-attrs/cattrs/commits">compare view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>
mergify bot pushed a commit to aws/jsii that referenced this issue Oct 3, 2022
…3 in /packages/@jsii/python-runtime (#3785)

Updates the requirements on [cattrs](https://github.com/python-attrs/cattrs) to permit the latest version.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/python-attrs/cattrs/blob/main/HISTORY.rst">cattrs's changelog</a>.</em></p>
<blockquote>
<h2>22.2.0 (2022-10-03)</h2>
<ul>
<li><em>Potentially breaking</em>: <code>cattrs.Converter</code> has been renamed to <code>cattrs.BaseConverter</code>, and <code>cattrs.GenConverter</code> to <code>cattrs.Converter</code>.
The <code>GenConverter</code> name is still available for backwards compatibility, but is deprecated.
If you were depending on functionality specific to the old <code>Converter</code>, change your import to <code>from cattrs import BaseConverter</code>.</li>
<li><code>NewTypes &lt;https://docs.python.org/3/library/typing.html#newtype&gt;</code>_ are now supported by the <code>cattrs.Converter</code>.
(<code>[#255](python-attrs/cattrs#255) &lt;https://github.com/python-attrs/cattrs/pull/255&gt;</code><em>, <code>[#94](python-attrs/cattrs#94) &lt;https://github.com/python-attrs/cattrs/issues/94&gt;</code></em>, <code>[#297](python-attrs/cattrs#297) &lt;https://github.com/python-attrs/cattrs/issues/297&gt;</code>_)</li>
<li><code>cattrs.Converter</code> and <code>cattrs.BaseConverter</code> can now copy themselves using the <code>copy</code> method.
(<code>[#284](python-attrs/cattrs#284) &lt;https://github.com/python-attrs/cattrs/pull/284&gt;</code>_)</li>
<li>Python 3.11 support.</li>
<li>cattrs now supports un/structuring <code>kw_only</code> fields on attrs classes into/from dictionaries.
(<code>[#247](python-attrs/cattrs#247) &lt;https://github.com/python-attrs/cattrs/pull/247&gt;</code>_)</li>
<li>PyPy support (and tests, using a minimal Hypothesis profile) restored.
(<code>[#253](python-attrs/cattrs#253) &lt;https://github.com/python-attrs/cattrs/issues/253&gt;</code>_)</li>
<li>Fix propagating the <code>detailed_validation</code> flag to mapping and counter structuring generators.</li>
<li>Fix <code>typing.Set</code> applying too broadly when used with the <code>GenConverter.unstruct_collection_overrides</code> parameter on Python versions below 3.9. Switch to <code>typing.AbstractSet</code> on those versions to restore the old behavior.
(<code>[#264](python-attrs/cattrs#264) &lt;https://github.com/python-attrs/cattrs/issues/264&gt;</code>_)</li>
<li>Uncap the required Python version, to avoid problems detailed in <a href="https://iscinumpy.dev/post/bound-version-constraints/#pinning-the-python-version-is-special">https://iscinumpy.dev/post/bound-version-constraints/#pinning-the-python-version-is-special</a>
(<code>[#275](python-attrs/cattrs#275) &lt;https://github.com/python-attrs/cattrs/issues/275&gt;</code>_)</li>
<li>Fix <code>Converter.register_structure_hook_factory</code> and <code>cattrs.gen.make_dict_unstructure_fn</code> type annotations.
(<code>[#281](python-attrs/cattrs#281) &lt;https://github.com/python-attrs/cattrs/issues/281&gt;</code>_)</li>
<li>Expose all error classes in the <code>cattr.errors</code> namespace. Note that it is deprecated, just use <code>cattrs.errors</code>.
(<code>[#252](python-attrs/cattrs#252) &lt;https://github.com/python-attrs/cattrs/issues/252&gt;</code>_)</li>
<li>Fix generating structuring functions for types with quotes in the name.
(<code>[#291](python-attrs/cattrs#291) &lt;https://github.com/python-attrs/cattrs/issues/291&gt;</code>_ <code>[#277](python-attrs/cattrs#277) &lt;https://github.com/python-attrs/cattrs/issues/277&gt;</code>_)</li>
<li>Fix usage of notes for the final version of <code>PEP 678 &lt;https://peps.python.org/pep-0678/&gt;</code><em>, supported since <code>exceptiongroup&gt;=1.0.0rc4</code>.
(<code>[#303](python-attrs/cattrs#303) &lt;303 &lt;https://github.com/python-attrs/cattrs/pull/303&gt;</code></em>)</li>
</ul>
<h2>22.1.0 (2022-04-03)</h2>
<ul>
<li>cattrs now uses the CalVer versioning convention.</li>
<li>cattrs now has a detailed validation mode, which is enabled by default. Learn more <code>here &lt;https://cattrs.readthedocs.io/en/latest/validation.html&gt;</code>_.
The old behavior can be restored by creating the converter with <code>detailed_validation=False</code>.</li>
<li><code>attrs</code> and dataclass structuring is now ~25% faster.</li>
<li>Fix an issue structuring bare <code>typing.List</code> s on Pythons lower than 3.9.
(<code>[#209](python-attrs/cattrs#209) &lt;https://github.com/python-attrs/cattrs/issues/209&gt;</code>_)</li>
<li>Fix structuring of non-parametrized containers like <code>list/dict/...</code> on Pythons lower than 3.9.
(<code>[#218](python-attrs/cattrs#218) &lt;https://github.com/python-attrs/cattrs/issues/218&gt;</code>_)</li>
<li>Fix structuring bare <code>typing.Tuple</code> on Pythons lower than 3.9.
(<code>[#218](python-attrs/cattrs#218) &lt;https://github.com/python-attrs/cattrs/issues/218&gt;</code>_)</li>
<li>Fix a wrong <code>AttributeError</code> of an missing <code>__parameters__</code> attribute. This could happen
when inheriting certain generic classes – for example <code>typing.*</code> classes are affected.
(<code>[#217](python-attrs/cattrs#217) &lt;https://github.com/python-attrs/cattrs/issues/217&gt;</code>_)</li>
<li>Fix structuring of <code>enum.Enum</code> instances in <code>typing.Literal</code> types.
(<code>[#231](python-attrs/cattrs#231) &lt;https://github.com/python-attrs/cattrs/pull/231&gt;</code>_)</li>
<li>Fix unstructuring all tuples - unannotated, variable-length, homogenous and heterogenous - to <code>list</code>.
(<code>[#226](python-attrs/cattrs#226) &lt;https://github.com/python-attrs/cattrs/issues/226&gt;</code>_)</li>
<li>For <code>forbid_extra_keys</code> raise custom <code>ForbiddenExtraKeyError</code> instead of generic <code>Exception</code>.
(<code>[#225](python-attrs/cattrs#225) &lt;https://github.com/python-attrs/cattrs/pull/225&gt;</code>_)</li>
<li>All preconf converters now support <code>loads</code> and <code>dumps</code> directly. See an example <code>here &lt;https://cattrs.readthedocs.io/en/latest/preconf.html&gt;</code>_.</li>
</ul>

</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/python-attrs/cattrs/commit/405f0291b958ae9eb45ee38febeb91fb65dd644f"><code>405f029</code></a> v22.2.0</li>
<li><a href="https://github.com/python-attrs/cattrs/commit/89de04f57aa774d6abfb0ae62517dc8a8064e3c2"><code>89de04f</code></a> Fix some mor</li>
<li><a href="https://github.com/python-attrs/cattrs/commit/0abbf271461babca203862f3581c20743f6118e0"><code>0abbf27</code></a> Fix tests</li>
<li><a href="https://github.com/python-attrs/cattrs/commit/3b750439aec826a8fd20976ca113f30a27e75408"><code>3b75043</code></a> <strong>notes</strong> is list[str]</li>
<li><a href="https://github.com/python-attrs/cattrs/commit/906b95cfef4903e2d6247abf0fdd72f7da21617a"><code>906b95c</code></a> Reorder HISTORY</li>
<li><a href="https://github.com/python-attrs/cattrs/commit/ed7f86a0ccd9adeab895b9afac2dea69fa02bcff"><code>ed7f86a</code></a> Improve NewTypes (<a href="https://github-redirect.dependabot.com/python-attrs/cattrs/issues/310">#310</a>)</li>
<li><a href="https://github.com/python-attrs/cattrs/commit/e7926599ad44e07d8325ae4072626e1a24705542"><code>e792659</code></a> Fix missing imports of preconf converters (<a href="https://github-redirect.dependabot.com/python-attrs/cattrs/issues/309">#309</a>)</li>
<li><a href="https://github.com/python-attrs/cattrs/commit/e425d6378aa8dfc74bbdd9e152365e1b962fd8cf"><code>e425d63</code></a> Reorder HISTORY</li>
<li><a href="https://github.com/python-attrs/cattrs/commit/cc56b2b873852a4e91b16706a39da00755c87759"><code>cc56b2b</code></a> Remove spurious type comment</li>
<li><a href="https://github.com/python-attrs/cattrs/commit/cbd6f29d2c0ebc40805b3ca0d81accfc330eb10c"><code>cbd6f29</code></a> Reformat</li>
<li>Additional commits viewable in <a href="https://github.com/python-attrs/cattrs/compare/v1.8.0...v22.2.0">compare view</a></li>
</ul>
</details>
<br />


Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants