Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/cattrs/_compat.py: 57%
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 _UnionGenericAlias,
33 get_args,
34 get_origin,
35 get_type_hints,
36)
37from typing import Counter as TypingCounter
38from typing import Mapping as TypingMapping
39from typing import MutableMapping as TypingMutableMapping
40from typing import MutableSequence as TypingMutableSequence
41from typing import MutableSet as TypingMutableSet
42from typing import Sequence as TypingSequence
43from typing import Set as TypingSet
45from attrs import NOTHING, Attribute, Factory, NothingType, resolve_types
46from attrs import fields as attrs_fields
47from attrs import fields_dict as attrs_fields_dict
49__all__ = [
50 "ANIES",
51 "ExceptionGroup",
52 "ExtensionsTypedDict",
53 "TypeAlias",
54 "adapted_fields",
55 "fields_dict",
56 "has",
57 "is_typeddict",
58]
60try:
61 from typing_extensions import TypedDict as ExtensionsTypedDict
62except ImportError: # pragma: no cover
63 ExtensionsTypedDict = None
65if sys.version_info >= (3, 11):
66 from builtins import ExceptionGroup
67else:
68 from exceptiongroup import ExceptionGroup
70try:
71 from typing_extensions import is_typeddict as _is_typeddict
72except ImportError: # pragma: no cover
73 assert sys.version_info >= (3, 10)
74 from typing import is_typeddict as _is_typeddict
76try:
77 from typing_extensions import TypeAlias
78except ImportError: # pragma: no cover
79 assert sys.version_info >= (3, 11)
80 from typing import TypeAlias
82LITERALS = {Literal}
83try:
84 from typing_extensions import Literal as teLiteral
86 LITERALS.add(teLiteral)
87except ImportError: # pragma: no cover
88 pass
90# On some Python versions, `typing_extensions.Any` is different than
91# `typing.Any`.
92try:
93 from typing_extensions import Any as teAny
95 ANIES = frozenset([Any, teAny])
96except ImportError: # pragma: no cover
97 ANIES = frozenset([Any])
99NoneType = type(None)
102def is_optional(typ: Any) -> bool:
103 return is_union_type(typ) and NoneType in typ.__args__ and len(typ.__args__) == 2
106def is_typeddict(cls: Any):
107 """Thin wrapper around typing(_extensions).is_typeddict"""
108 return _is_typeddict(getattr(cls, "__origin__", cls))
111def has(cls):
112 return hasattr(cls, "__attrs_attrs__") or hasattr(cls, "__dataclass_fields__")
115def has_with_generic(cls):
116 """Test whether the class if a normal or generic attrs or dataclass."""
117 return has(cls) or has(get_origin(cls))
120def fields(type):
121 try:
122 return type.__attrs_attrs__
123 except AttributeError:
124 return dataclass_fields(type)
127def fields_dict(type) -> dict[str, Union[Attribute, Field]]:
128 """Return the fields_dict for attrs and dataclasses."""
129 if is_dataclass(type):
130 return {f.name: f for f in dataclass_fields(type)}
131 return attrs_fields_dict(type)
134def adapted_fields(cl: type) -> list[Attribute]:
135 """Return the attrs format of `fields()` for attrs and dataclasses.
137 Resolves `attrs` stringified annotations, if present.
138 """
139 if is_dataclass(cl):
140 attrs = dataclass_fields(cl)
141 if any(isinstance(a.type, str) for a in attrs):
142 # Do this conditionally in case `get_type_hints` fails, so
143 # users can resolve on their own first.
144 type_hints = get_type_hints(cl)
145 else:
146 type_hints = {}
147 return [
148 Attribute(
149 attr.name,
150 (
151 attr.default
152 if attr.default is not MISSING
153 else (
154 Factory(attr.default_factory)
155 if attr.default_factory is not MISSING
156 else NOTHING
157 )
158 ),
159 None,
160 True,
161 None,
162 True,
163 attr.init,
164 True,
165 type=type_hints.get(attr.name, attr.type),
166 alias=attr.name,
167 kw_only=getattr(attr, "kw_only", False),
168 )
169 for attr in attrs
170 ]
171 attribs = attrs_fields(cl)
172 if any(isinstance(a.type, str) for a in attribs):
173 # PEP 563 annotations - need to be resolved.
174 resolve_types(cl)
175 attribs = attrs_fields(cl)
176 return attribs
179def is_subclass(obj: type, bases) -> bool:
180 """A safe version of issubclass (won't raise)."""
181 try:
182 return issubclass(obj, bases)
183 except TypeError:
184 return False
187def is_hetero_tuple(type: Any) -> bool:
188 origin = getattr(type, "__origin__", None)
189 return origin is tuple and ... not in type.__args__
192def is_protocol(type: Any) -> bool:
193 return is_subclass(type, Protocol) and getattr(type, "_is_protocol", False)
196def is_bare_final(type) -> bool:
197 return type is Final
200def get_final_base(type) -> Optional[type]:
201 """Return the base of the Final annotation, if it is Final."""
202 if type is Final:
203 return Any
204 if type.__class__ is _GenericAlias and type.__origin__ is Final:
205 return type.__args__[0]
206 return None
209OriginAbstractSet = AbcSet
210OriginMutableSet = AbcMutableSet
212signature = _signature
214if sys.version_info >= (3, 10):
215 signature = partial(_signature, eval_str=True)
218try:
219 # Not present on 3.9.0, so we try carefully.
220 from typing import _LiteralGenericAlias
222 def is_literal(type: Any) -> bool:
223 """Is this a literal?"""
224 return type in LITERALS or (
225 isinstance(
226 type, (_GenericAlias, _LiteralGenericAlias, _SpecialGenericAlias)
227 )
228 and type.__origin__ in LITERALS
229 )
231except ImportError: # pragma: no cover
233 def is_literal(_) -> bool:
234 return False
237Set = AbcSet
238MutableSet = AbcMutableSet
239Sequence = AbcSequence
240MutableSequence = AbcMutableSequence
241MutableMapping = AbcMutableMapping
242Mapping = AbcMapping
243FrozenSetSubscriptable = frozenset
244TupleSubscriptable = tuple
247def is_annotated(type) -> bool:
248 return getattr(type, "__class__", None) is _AnnotatedAlias
251def is_tuple(type):
252 return (
253 type in (Tuple, tuple)
254 or (type.__class__ is _GenericAlias and is_subclass(type.__origin__, Tuple))
255 or (getattr(type, "__origin__", None) is tuple)
256 )
259if sys.version_info >= (3, 10):
261 def is_union_type(obj):
262 from types import UnionType
264 return (
265 obj is Union
266 or (isinstance(obj, _UnionGenericAlias) and obj.__origin__ is Union)
267 or isinstance(obj, UnionType)
268 )
270 def get_newtype_base(typ: Any) -> Optional[type]:
271 if typ is NewType or isinstance(typ, NewType):
272 return typ.__supertype__
273 return None
275 if sys.version_info >= (3, 11):
276 from typing import NotRequired, Required
277 else:
278 from typing_extensions import NotRequired, Required
280else:
281 # 3.9
282 from typing_extensions import NotRequired, Required
284 def is_union_type(obj):
285 return obj is Union or (
286 isinstance(obj, _UnionGenericAlias) and obj.__origin__ is Union
287 )
289 def get_newtype_base(typ: Any) -> Optional[type]:
290 supertype = getattr(typ, "__supertype__", None)
291 if (
292 supertype is not None
293 and getattr(typ, "__qualname__", "") == "NewType.<locals>.new_type"
294 and typ.__module__ in ("typing", "typing_extensions")
295 ):
296 return supertype
297 return None
300def get_notrequired_base(type) -> Union[Any, NothingType]:
301 if is_annotated(type):
302 # Handle `Annotated[NotRequired[int]]`
303 type = get_args(type)[0]
304 if get_origin(type) in (NotRequired, Required):
305 return get_args(type)[0]
306 return NOTHING
309def is_sequence(type: Any) -> bool:
310 """A predicate function for sequences.
312 Matches lists, sequences, mutable sequences, deques and homogenous
313 tuples.
314 """
315 origin = getattr(type, "__origin__", None)
316 return (
317 type
318 in (
319 List,
320 list,
321 TypingSequence,
322 TypingMutableSequence,
323 AbcMutableSequence,
324 tuple,
325 Tuple,
326 deque,
327 Deque,
328 )
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 in (list, deque, AbcMutableSequence, 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 isinstance(type, (_GenericAlias, GenericAlias)) or (
407 is_subclass(type, Generic) and hasattr(type, "__orig_bases__")
408 )
411def copy_with(type, args):
412 """Replace a generic type's arguments."""
413 if is_annotated(type):
414 # typing.Annotated requires a special case.
415 return Annotated[args]
416 if isinstance(args, tuple) and len(args) == 1:
417 # Some annotations can't handle 1-tuples.
418 args = args[0]
419 return type.__origin__[args]
422def get_full_type_hints(obj, globalns=None, localns=None):
423 return get_type_hints(obj, globalns, localns, include_extras=True)
426def is_generic_attrs(type) -> bool:
427 """Return True for both specialized (A[int]) and unspecialized (A) generics."""
428 return is_generic(type) and has(type.__origin__)