Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/cattrs/gen/_generics.py: 15%

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

34 statements  

1from __future__ import annotations 

2 

3from typing import TypeVar 

4 

5from .._compat import get_args, get_origin, is_generic 

6 

7 

8def _tvar_has_default(tvar) -> bool: 

9 """Does `tvar` have a default? 

10 

11 In CPython 3.13+ and typing_extensions>=4.12.0: 

12 - TypeVars have a `no_default()` method for detecting 

13 if a TypeVar has a default 

14 - TypeVars with `default=None` have `__default__` set to `None` 

15 - TypeVars with no `default` parameter passed 

16 have `__default__` set to `typing(_extensions).NoDefault 

17 

18 On typing_exensions<4.12.0: 

19 - TypeVars do not have a `no_default()` method for detecting 

20 if a TypeVar has a default 

21 - TypeVars with `default=None` have `__default__` set to `NoneType` 

22 - TypeVars with no `default` parameter passed 

23 have `__default__` set to `typing(_extensions).NoDefault 

24 """ 

25 try: 

26 return tvar.has_default() 

27 except AttributeError: 

28 # compatibility for typing_extensions<4.12.0 

29 return getattr(tvar, "__default__", None) is not None 

30 

31 

32def generate_mapping(cl: type, old_mapping: dict[str, type] = {}) -> dict[str, type]: 

33 """Generate a mapping of typevars to actual types for a generic class.""" 

34 mapping = dict(old_mapping) 

35 

36 origin = get_origin(cl) 

37 

38 if origin is not None: 

39 # To handle the cases where classes in the typing module are using 

40 # the GenericAlias structure but aren't a Generic and hence 

41 # end up in this function but do not have an `__parameters__` 

42 # attribute. These classes are interface types, for example 

43 # `typing.Hashable`. 

44 parameters = getattr(get_origin(cl), "__parameters__", None) 

45 if parameters is None: 

46 return dict(old_mapping) 

47 

48 for p, t in zip(parameters, get_args(cl)): 

49 if isinstance(t, TypeVar): 

50 continue 

51 mapping[p.__name__] = t 

52 

53 elif is_generic(cl): 

54 # Origin is None, so this may be a subclass of a generic class. 

55 orig_bases = cl.__orig_bases__ 

56 for base in orig_bases: 

57 if not hasattr(base, "__args__"): 

58 continue 

59 base_args = base.__args__ 

60 if hasattr(base.__origin__, "__parameters__"): 

61 base_params = base.__origin__.__parameters__ 

62 elif any(_tvar_has_default(base_arg) for base_arg in base_args): 

63 # TypeVar with a default e.g. PEP 696 

64 # https://www.python.org/dev/peps/pep-0696/ 

65 # Extract the defaults for the TypeVars and insert 

66 # them into the mapping 

67 mapping_params = [ 

68 (base_arg, base_arg.__default__) 

69 for base_arg in base_args 

70 if _tvar_has_default(base_arg) 

71 ] 

72 base_params, base_args = zip(*mapping_params) 

73 else: 

74 continue 

75 

76 for param, arg in zip(base_params, base_args): 

77 mapping[param.__name__] = arg 

78 

79 return mapping