Skip to content

Commit aec154c

Browse files
authored
Simplify cattrs._compat.is_typeddict (#384)
* Simplify `cattrs._compat.is_typeddict` * Add `ExceptionGroup` to `__all__` as well * Fix the failing generic-typeddict test * More unnecessary imports of `_TypedDictMeta` * Changelog * Brackets
1 parent 05175ef commit aec154c

File tree

2 files changed

+17
-38
lines changed

2 files changed

+17
-38
lines changed

HISTORY.md

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
- Optimize and improve unstructuring of `Optional` (unions of one type and `None`).
1010
([#380](https://github.com/python-attrs/cattrs/issues/380) [#381](https://github.com/python-attrs/cattrs/pull/381))
1111
- Fix `format_exception` and `transform_error` type annotations.
12+
- Improve the implementation of `cattrs._compat.is_typeddict`. The implementation is now simpler, and relies on fewer private implementation details from `typing` and typing_extensions. ([#384](https://github.com/python-attrs/cattrs/pull/384))
1213

1314
## 23.1.2 (2023-06-02)
1415

src/cattrs/_compat.py

+16-38
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,13 @@
2020
from attr import fields as attrs_fields
2121
from attr import resolve_types
2222

23+
__all__ = ["ExceptionGroup", "ExtensionsTypedDict", "TypedDict", "is_typeddict"]
24+
2325
try:
2426
from typing_extensions import TypedDict as ExtensionsTypedDict
2527
except ImportError:
2628
ExtensionsTypedDict = None
2729

28-
try:
29-
from typing_extensions import _TypedDictMeta as ExtensionsTypedDictMeta
30-
except ImportError:
31-
ExtensionsTypedDictMeta = None
32-
3330
if sys.version_info >= (3, 8):
3431
from typing import Final, Protocol, get_args, get_origin
3532

@@ -44,9 +41,20 @@ def get_origin(cl):
4441
from typing_extensions import Final, Protocol
4542

4643
if sys.version_info >= (3, 11):
47-
ExceptionGroup = ExceptionGroup
44+
from builtins import ExceptionGroup
4845
else:
49-
from exceptiongroup import ExceptionGroup as ExceptionGroup # noqa: PLC0414
46+
from exceptiongroup import ExceptionGroup
47+
48+
try:
49+
from typing_extensions import is_typeddict as _is_typeddict
50+
except ImportError:
51+
assert sys.version_info >= (3, 10)
52+
from typing import is_typeddict as _is_typeddict
53+
54+
55+
def is_typeddict(cls):
56+
"""Thin wrapper around typing(_extensions).is_typeddict"""
57+
return _is_typeddict(getattr(cls, "__origin__", cls))
5058

5159

5260
def has(cls):
@@ -157,7 +165,6 @@ def get_final_base(type) -> Optional[type]:
157165
_AnnotatedAlias,
158166
_GenericAlias,
159167
_SpecialGenericAlias,
160-
_TypedDictMeta,
161168
_UnionGenericAlias,
162169
)
163170

@@ -234,20 +241,6 @@ def get_newtype_base(typ: Any) -> Optional[type]:
234241
return supertype
235242
return None
236243

237-
def is_typeddict(cls) -> bool:
238-
return (
239-
cls.__class__ is _TypedDictMeta
240-
or (is_generic(cls) and (cls.__origin__.__class__ is _TypedDictMeta))
241-
or (
242-
ExtensionsTypedDictMeta is not None
243-
and cls.__class__ is ExtensionsTypedDictMeta
244-
or (
245-
is_generic(cls)
246-
and (cls.__origin__.__class__ is ExtensionsTypedDictMeta)
247-
)
248-
)
249-
)
250-
251244
def get_notrequired_base(type) -> "Union[Any, Literal[NOTHING]]":
252245
if get_origin(type) in (NotRequired, Required):
253246
return get_args(type)[0]
@@ -364,9 +357,8 @@ def copy_with(type, args):
364357
from typing_extensions import get_origin as te_get_origin
365358

366359
if sys.version_info >= (3, 8):
367-
from typing import TypedDict, _TypedDictMeta
360+
from typing import TypedDict
368361
else:
369-
_TypedDictMeta = None
370362
TypedDict = ExtensionsTypedDict
371363

372364
def is_annotated(type) -> bool:
@@ -462,20 +454,6 @@ def copy_with(type, args):
462454
"""Replace a generic type's arguments."""
463455
return type.copy_with(args)
464456

465-
def is_typeddict(cls) -> bool:
466-
return (
467-
cls.__class__ is _TypedDictMeta
468-
or (is_generic(cls) and (cls.__origin__.__class__ is _TypedDictMeta))
469-
or (
470-
ExtensionsTypedDictMeta is not None
471-
and cls.__class__ is ExtensionsTypedDictMeta
472-
or (
473-
is_generic(cls)
474-
and (cls.__origin__.__class__ is ExtensionsTypedDictMeta)
475-
)
476-
)
477-
)
478-
479457
def get_notrequired_base(type) -> "Union[Any, Literal[NOTHING]]":
480458
if get_origin(type) in (NotRequired, Required):
481459
return get_args(type)[0]

0 commit comments

Comments
 (0)