Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/cattrs/_compat.py: 56%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1import sys
2from collections import Counter, deque
3from collections.abc import Mapping as AbcMapping
4from collections.abc import MutableMapping as AbcMutableMapping
5from collections.abc import MutableSequence as AbcMutableSequence
6from collections.abc import MutableSet as AbcMutableSet
7from collections.abc import Sequence as AbcSequence
8from collections.abc import Set as AbcSet
9from dataclasses import MISSING, Field, is_dataclass
10from dataclasses import fields as dataclass_fields
11from functools import partial
12from inspect import signature as _signature
13from types import GenericAlias
14from typing import (
15 Annotated,
16 Any,
17 Deque,
18 Dict,
19 Final,
20 FrozenSet,
21 Generic,
22 List,
23 Literal,
24 NewType,
25 Optional,
26 Protocol,
27 Tuple,
28 Union,
29 _AnnotatedAlias,
30 _GenericAlias,
31 _SpecialGenericAlias,
32 get_args,
33 get_origin,
34 get_type_hints,
35)
36from typing import Counter as TypingCounter
37from typing import Mapping as TypingMapping
38from typing import MutableMapping as TypingMutableMapping
39from typing import MutableSequence as TypingMutableSequence
40from typing import MutableSet as TypingMutableSet
41from typing import Sequence as TypingSequence
42from typing import Set as TypingSet
44from attrs import NOTHING, Attribute, Factory, NothingType, resolve_types
45from attrs import fields as attrs_fields
46from attrs import fields_dict as attrs_fields_dict
48__all__ = [
49 "ANIES",
50 "ExceptionGroup",
51 "ExtensionsTypedDict",
52 "TypeAlias",
53 "adapted_fields",
54 "fields_dict",
55 "has",
56 "is_typeddict",
57]
59try:
60 from typing_extensions import TypedDict as ExtensionsTypedDict
61except ImportError: # pragma: no cover
62 ExtensionsTypedDict = None
64if sys.version_info >= (3, 11):
65 from builtins import ExceptionGroup
66else:
67 from exceptiongroup import ExceptionGroup
69try:
70 from typing_extensions import is_typeddict as _is_typeddict
71except ImportError: # pragma: no cover
72 assert sys.version_info >= (3, 10)
73 from typing import is_typeddict as _is_typeddict
75try:
76 from typing_extensions import TypeAlias
77except ImportError: # pragma: no cover
78 assert sys.version_info >= (3, 11)
79 from typing import TypeAlias
81LITERALS = {Literal}
82try:
83 from typing_extensions import Literal as teLiteral
85 LITERALS.add(teLiteral)
86except ImportError: # pragma: no cover
87 pass
89# On some Python versions, `typing_extensions.Any` is different than
90# `typing.Any`.
91try:
92 from typing_extensions import Any as teAny
94 ANIES = frozenset([Any, teAny])
95except ImportError: # pragma: no cover
96 ANIES = frozenset([Any])
98NoneType = type(None)
101def is_optional(typ: Any) -> bool:
102 return is_union_type(typ) and NoneType in typ.__args__ and len(typ.__args__) == 2
105def is_typeddict(cls: Any):
106 """Thin wrapper around typing(_extensions).is_typeddict"""
107 return _is_typeddict(getattr(cls, "__origin__", cls))
110def has(cls):
111 return hasattr(cls, "__attrs_attrs__") or hasattr(cls, "__dataclass_fields__")
114def has_with_generic(cls):
115 """Test whether the class if a normal or generic attrs or dataclass."""
116 return has(cls) or has(get_origin(cls))
119def fields(type):
120 try:
121 return type.__attrs_attrs__
122 except AttributeError:
123 return dataclass_fields(type)
126def fields_dict(type) -> dict[str, Union[Attribute, Field]]:
127 """Return the fields_dict for attrs and dataclasses."""
128 if is_dataclass(type):
129 return {f.name: f for f in dataclass_fields(type)}
130 return attrs_fields_dict(type)
133def adapted_fields(cl: type) -> list[Attribute]:
134 """Return the attrs format of `fields()` for attrs and dataclasses.
136 Resolves `attrs` stringified annotations, if present.
137 """
138 if is_dataclass(cl):
139 attrs = dataclass_fields(cl)
140 if any(isinstance(a.type, str) for a in attrs):
141 # Do this conditionally in case `get_type_hints` fails, so
142 # users can resolve on their own first.
143 type_hints = get_type_hints(cl)
144 else:
145 type_hints = {}
146 return [
147 Attribute(
148 attr.name,
149 (
150 attr.default
151 if attr.default is not MISSING
152 else (
153 Factory(attr.default_factory)
154 if attr.default_factory is not MISSING
155 else NOTHING
156 )
157 ),
158 None,
159 True,
160 None,
161 True,
162 attr.init,
163 True,
164 type=type_hints.get(attr.name, attr.type),
165 alias=attr.name,
166 kw_only=getattr(attr, "kw_only", False),
167 )
168 for attr in attrs
169 ]
170 attribs = attrs_fields(cl)
171 if any(isinstance(a.type, str) for a in attribs):
172 # PEP 563 annotations - need to be resolved.
173 resolve_types(cl)
174 attribs = attrs_fields(cl)
175 return attribs
178def is_subclass(obj: type, bases) -> bool:
179 """A safe version of issubclass (won't raise)."""
180 try:
181 return issubclass(obj, bases)
182 except TypeError:
183 return False
186def is_hetero_tuple(type: Any) -> bool:
187 origin = getattr(type, "__origin__", None)
188 return origin is tuple and ... not in type.__args__
191def is_protocol(type: Any) -> bool:
192 return is_subclass(type, Protocol) and getattr(type, "_is_protocol", False)
195def is_bare_final(type) -> bool:
196 return type is Final
199def get_final_base(type) -> Optional[type]:
200 """Return the base of the Final annotation, if it is Final."""
201 if type is Final:
202 return Any
203 if type.__class__ is _GenericAlias and type.__origin__ is Final:
204 return type.__args__[0]
205 return None
208OriginAbstractSet = AbcSet
209OriginMutableSet = AbcMutableSet
211signature = partial(_signature, eval_str=True)
214try:
215 # Not present on 3.9.0, so we try carefully.
216 from typing import _LiteralGenericAlias
218 def is_literal(type: Any) -> bool:
219 """Is this a literal?"""
220 return type in LITERALS or (
221 isinstance(
222 type, (_GenericAlias, _LiteralGenericAlias, _SpecialGenericAlias)
223 )
224 and type.__origin__ in LITERALS
225 )
227except ImportError: # pragma: no cover
229 def is_literal(_) -> bool:
230 return False
233Set = AbcSet
234MutableSet = AbcMutableSet
235Sequence = AbcSequence
236MutableSequence = AbcMutableSequence
237MutableMapping = AbcMutableMapping
238Mapping = AbcMapping
239FrozenSetSubscriptable = frozenset
240TupleSubscriptable = tuple
243def is_annotated(type) -> bool:
244 return getattr(type, "__class__", None) is _AnnotatedAlias
247def is_tuple(type):
248 return (
249 type in (Tuple, tuple)
250 or (type.__class__ is _GenericAlias and is_subclass(type.__origin__, Tuple))
251 or (getattr(type, "__origin__", None) is tuple)
252 )
255if sys.version_info >= (3, 14):
257 def is_union_type(obj):
258 from types import UnionType # noqa: PLC0415
260 return obj is Union or isinstance(obj, UnionType)
262 def get_newtype_base(typ: Any) -> Optional[type]:
263 if typ is NewType or isinstance(typ, NewType):
264 return typ.__supertype__
265 return None
267 from typing import NotRequired, Required
269else:
270 from typing import _UnionGenericAlias
272 def is_union_type(obj):
273 from types import UnionType # noqa: PLC0415
275 return (
276 obj is Union
277 or (isinstance(obj, _UnionGenericAlias) and obj.__origin__ is Union)
278 or isinstance(obj, UnionType)
279 )
281 def get_newtype_base(typ: Any) -> Optional[type]:
282 if typ is NewType or isinstance(typ, NewType):
283 return typ.__supertype__
284 return None
286 if sys.version_info >= (3, 11):
287 from typing import NotRequired, Required
288 else:
289 from typing_extensions import NotRequired, Required
292def get_notrequired_base(type) -> Union[Any, NothingType]:
293 if is_annotated(type):
294 # Handle `Annotated[NotRequired[int]]`
295 type = get_args(type)[0]
296 if get_origin(type) in (NotRequired, Required):
297 return get_args(type)[0]
298 return NOTHING
301def is_mutable_sequence(type: Any) -> bool:
302 """A predicate function for mutable sequences.
304 Matches lists, mutable sequences, and deques.
305 """
306 origin = getattr(type, "__origin__", None)
307 return (
308 type in (List, list, TypingMutableSequence, AbcMutableSequence, deque, Deque)
309 or (
310 type.__class__ is _GenericAlias
311 and (
312 ((origin is not tuple) and is_subclass(origin, TypingMutableSequence))
313 or (origin is tuple and type.__args__[1] is ...)
314 )
315 )
316 or (origin in (list, deque, AbcMutableSequence))
317 )
320def is_sequence(type: Any) -> bool:
321 """A predicate function for sequences.
323 Matches lists, sequences, mutable sequences, deques and homogenous
324 tuples.
325 """
326 origin = getattr(type, "__origin__", None)
327 return is_mutable_sequence(type) or (
328 type in (TypingSequence, tuple, Tuple)
329 or (
330 type.__class__ is _GenericAlias
331 and (
332 ((origin is not tuple) and is_subclass(origin, TypingSequence))
333 or (origin is tuple and type.__args__[1] is ...)
334 )
335 )
336 or (origin is AbcSequence)
337 or (origin is tuple and type.__args__[1] is ...)
338 )
341def is_deque(type):
342 return (
343 type in (deque, Deque)
344 or (type.__class__ is _GenericAlias and is_subclass(type.__origin__, deque))
345 or (getattr(type, "__origin__", None) is deque)
346 )
349def is_mutable_set(type: Any) -> bool:
350 """A predicate function for (mutable) sets.
352 Matches built-in sets and sets from the typing module.
353 """
354 return (
355 type in (TypingSet, TypingMutableSet, set)
356 or (
357 type.__class__ is _GenericAlias
358 and is_subclass(type.__origin__, TypingMutableSet)
359 )
360 or (getattr(type, "__origin__", None) in (set, AbcMutableSet, AbcSet))
361 )
364def is_frozenset(type: Any) -> bool:
365 """A predicate function for frozensets.
367 Matches built-in frozensets and frozensets from the typing module.
368 """
369 return (
370 type in (FrozenSet, frozenset)
371 or (type.__class__ is _GenericAlias and is_subclass(type.__origin__, FrozenSet))
372 or (getattr(type, "__origin__", None) is frozenset)
373 )
376def is_bare(type):
377 return isinstance(type, _SpecialGenericAlias) or (
378 not hasattr(type, "__origin__") and not hasattr(type, "__args__")
379 )
382def is_mapping(type: Any) -> bool:
383 """A predicate function for mappings."""
384 return (
385 type in (dict, Dict, TypingMapping, TypingMutableMapping, AbcMutableMapping)
386 or (
387 type.__class__ is _GenericAlias
388 and is_subclass(type.__origin__, TypingMapping)
389 )
390 or is_subclass(
391 getattr(type, "__origin__", type), (dict, AbcMutableMapping, AbcMapping)
392 )
393 )
396def is_counter(type):
397 return (
398 type in (Counter, TypingCounter) or getattr(type, "__origin__", None) is Counter
399 )
402def is_generic(type) -> bool:
403 """Whether `type` is a generic type."""
404 # Inheriting from protocol will inject `Generic` into the MRO
405 # without `__orig_bases__`.
406 return (
407 isinstance(type, (_GenericAlias, GenericAlias))
408 or (is_subclass(type, Generic) and hasattr(type, "__orig_bases__"))
409 or type.__class__ is Union # On 3.14, unions are no longer typing._GenericAlias
410 )
413def copy_with(type, args):
414 """Replace a generic type's arguments."""
415 if is_annotated(type):
416 # typing.Annotated requires a special case.
417 return Annotated[args]
418 if isinstance(args, tuple) and len(args) == 1:
419 # Some annotations can't handle 1-tuples.
420 args = args[0]
421 return type.__origin__[args]
424def get_full_type_hints(obj, globalns=None, localns=None):
425 return get_type_hints(obj, globalns, localns, include_extras=True)
428def is_generic_attrs(type) -> bool:
429 """Return True for both specialized (A[int]) and unspecialized (A) generics."""
430 return is_generic(type) and has(type.__origin__)