Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/cattrs/_compat.py: 54%
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 = _signature
213if sys.version_info >= (3, 10):
214 signature = partial(_signature, eval_str=True)
217try:
218 # Not present on 3.9.0, so we try carefully.
219 from typing import _LiteralGenericAlias
221 def is_literal(type: Any) -> bool:
222 """Is this a literal?"""
223 return type in LITERALS or (
224 isinstance(
225 type, (_GenericAlias, _LiteralGenericAlias, _SpecialGenericAlias)
226 )
227 and type.__origin__ in LITERALS
228 )
230except ImportError: # pragma: no cover
232 def is_literal(_) -> bool:
233 return False
236Set = AbcSet
237MutableSet = AbcMutableSet
238Sequence = AbcSequence
239MutableSequence = AbcMutableSequence
240MutableMapping = AbcMutableMapping
241Mapping = AbcMapping
242FrozenSetSubscriptable = frozenset
243TupleSubscriptable = tuple
246def is_annotated(type) -> bool:
247 return getattr(type, "__class__", None) is _AnnotatedAlias
250def is_tuple(type):
251 return (
252 type in (Tuple, tuple)
253 or (type.__class__ is _GenericAlias and is_subclass(type.__origin__, Tuple))
254 or (getattr(type, "__origin__", None) is tuple)
255 )
258if sys.version_info >= (3, 14):
260 def is_union_type(obj):
261 from types import UnionType # noqa: PLC0415
263 return obj is Union or isinstance(obj, UnionType)
265 def get_newtype_base(typ: Any) -> Optional[type]:
266 if typ is NewType or isinstance(typ, NewType):
267 return typ.__supertype__
268 return None
270 from typing import NotRequired, Required
272elif sys.version_info >= (3, 10):
273 from typing import _UnionGenericAlias
275 def is_union_type(obj):
276 from types import UnionType # noqa: PLC0415
278 return (
279 obj is Union
280 or (isinstance(obj, _UnionGenericAlias) and obj.__origin__ is Union)
281 or isinstance(obj, UnionType)
282 )
284 def get_newtype_base(typ: Any) -> Optional[type]:
285 if typ is NewType or isinstance(typ, NewType):
286 return typ.__supertype__
287 return None
289 if sys.version_info >= (3, 11):
290 from typing import NotRequired, Required
291 else:
292 from typing_extensions import NotRequired, Required
294else:
295 # 3.9
296 from typing import _UnionGenericAlias
298 from typing_extensions import NotRequired, Required
300 def is_union_type(obj):
301 return obj is Union or (
302 isinstance(obj, _UnionGenericAlias) and obj.__origin__ is Union
303 )
305 def get_newtype_base(typ: Any) -> Optional[type]:
306 supertype = getattr(typ, "__supertype__", None)
307 if (
308 supertype is not None
309 and getattr(typ, "__qualname__", "") == "NewType.<locals>.new_type"
310 and typ.__module__ in ("typing", "typing_extensions")
311 ):
312 return supertype
313 return None
316def get_notrequired_base(type) -> Union[Any, NothingType]:
317 if is_annotated(type):
318 # Handle `Annotated[NotRequired[int]]`
319 type = get_args(type)[0]
320 if get_origin(type) in (NotRequired, Required):
321 return get_args(type)[0]
322 return NOTHING
325def is_mutable_sequence(type: Any) -> bool:
326 """A predicate function for mutable sequences.
328 Matches lists, mutable sequences, and deques.
329 """
330 origin = getattr(type, "__origin__", None)
331 return (
332 type in (List, list, TypingMutableSequence, AbcMutableSequence, deque, Deque)
333 or (
334 type.__class__ is _GenericAlias
335 and (
336 ((origin is not tuple) and is_subclass(origin, TypingMutableSequence))
337 or (origin is tuple and type.__args__[1] is ...)
338 )
339 )
340 or (origin in (list, deque, AbcMutableSequence))
341 )
344def is_sequence(type: Any) -> bool:
345 """A predicate function for sequences.
347 Matches lists, sequences, mutable sequences, deques and homogenous
348 tuples.
349 """
350 origin = getattr(type, "__origin__", None)
351 return is_mutable_sequence(type) or (
352 type in (TypingSequence, tuple, Tuple)
353 or (
354 type.__class__ is _GenericAlias
355 and (
356 ((origin is not tuple) and is_subclass(origin, TypingSequence))
357 or (origin is tuple and type.__args__[1] is ...)
358 )
359 )
360 or (origin is AbcSequence)
361 or (origin is tuple and type.__args__[1] is ...)
362 )
365def is_deque(type):
366 return (
367 type in (deque, Deque)
368 or (type.__class__ is _GenericAlias and is_subclass(type.__origin__, deque))
369 or (getattr(type, "__origin__", None) is deque)
370 )
373def is_mutable_set(type: Any) -> bool:
374 """A predicate function for (mutable) sets.
376 Matches built-in sets and sets from the typing module.
377 """
378 return (
379 type in (TypingSet, TypingMutableSet, set)
380 or (
381 type.__class__ is _GenericAlias
382 and is_subclass(type.__origin__, TypingMutableSet)
383 )
384 or (getattr(type, "__origin__", None) in (set, AbcMutableSet, AbcSet))
385 )
388def is_frozenset(type: Any) -> bool:
389 """A predicate function for frozensets.
391 Matches built-in frozensets and frozensets from the typing module.
392 """
393 return (
394 type in (FrozenSet, frozenset)
395 or (type.__class__ is _GenericAlias and is_subclass(type.__origin__, FrozenSet))
396 or (getattr(type, "__origin__", None) is frozenset)
397 )
400def is_bare(type):
401 return isinstance(type, _SpecialGenericAlias) or (
402 not hasattr(type, "__origin__") and not hasattr(type, "__args__")
403 )
406def is_mapping(type: Any) -> bool:
407 """A predicate function for mappings."""
408 return (
409 type in (dict, Dict, TypingMapping, TypingMutableMapping, AbcMutableMapping)
410 or (
411 type.__class__ is _GenericAlias
412 and is_subclass(type.__origin__, TypingMapping)
413 )
414 or is_subclass(
415 getattr(type, "__origin__", type), (dict, AbcMutableMapping, AbcMapping)
416 )
417 )
420def is_counter(type):
421 return (
422 type in (Counter, TypingCounter) or getattr(type, "__origin__", None) is Counter
423 )
426def is_generic(type) -> bool:
427 """Whether `type` is a generic type."""
428 # Inheriting from protocol will inject `Generic` into the MRO
429 # without `__orig_bases__`.
430 return (
431 isinstance(type, (_GenericAlias, GenericAlias))
432 or (is_subclass(type, Generic) and hasattr(type, "__orig_bases__"))
433 or type.__class__ is Union # On 3.14, unions are no longer typing._GenericAlias
434 )
437def copy_with(type, args):
438 """Replace a generic type's arguments."""
439 if is_annotated(type):
440 # typing.Annotated requires a special case.
441 return Annotated[args]
442 if isinstance(args, tuple) and len(args) == 1:
443 # Some annotations can't handle 1-tuples.
444 args = args[0]
445 return type.__origin__[args]
448def get_full_type_hints(obj, globalns=None, localns=None):
449 return get_type_hints(obj, globalns, localns, include_extras=True)
452def is_generic_attrs(type) -> bool:
453 """Return True for both specialized (A[int]) and unspecialized (A) generics."""
454 return is_generic(type) and has(type.__origin__)