Coverage for pydantic/annotated_types.py: 100.00%

22 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-08-15 13:26 +0000

1import sys 1JKabcdefvwxyLMghijklzABCDmnoPQRSTUENOpqrstuFGHI

2from typing import TYPE_CHECKING, Any, Dict, FrozenSet, NamedTuple, Type 1JKabcdefvwxyLMghijklzABCDmnoPQRSTUENOpqrstuFGHI

3 

4from pydantic.fields import Required 1JKabcdefvwxyLMghijklzABCDmnoPQRSTUENOpqrstuFGHI

5from pydantic.main import BaseModel, create_model 1JKabcdefvwxyLMghijklzABCDmnoPQRSTUENOpqrstuFGHI

6from pydantic.typing import is_typeddict, is_typeddict_special 1JKabcdefvwxyLMghijklzABCDmnoPQRSTUENOpqrstuFGHI

7 

8if TYPE_CHECKING: 1JKabcdefvwxyLMghijklzABCDmnoPQRSTUENOpqrstuFGHI

9 from typing_extensions import TypedDict 

10 

11if sys.version_info < (3, 11): 1JKabcdefvwxyLMghijklzABCDmnoPQRSTUENOpqrstuFGHI

12 

13 def is_legacy_typeddict(typeddict_cls: Type['TypedDict']) -> bool: # type: ignore[valid-type] 1JKabcdefLMghijklDmnoPQRSTUNOpqrstu

14 return is_typeddict(typeddict_cls) and type(typeddict_cls).__module__ == 'typing' 1JKabcdefLMghijklDmnoPQRSTUNOpqrstu

15 

16else: 

17 

18 def is_legacy_typeddict(_: Any) -> Any: 1vwxyzABCEFGHI

19 return False 1vwxyzABCEFGHI

20 

21 

22def create_model_from_typeddict( 1abcdefvwxyghijklzABCDmnoPQRSTUEpqrstuFGHI

23 # Mypy bug: `Type[TypedDict]` is resolved as `Any` https://github.com/python/mypy/issues/11030 

24 typeddict_cls: Type['TypedDict'], # type: ignore[valid-type] 

25 **kwargs: Any, 

26) -> Type['BaseModel']: 

27 """ 

28 Create a `BaseModel` based on the fields of a `TypedDict`. 

29 Since `typing.TypedDict` in Python 3.8 does not store runtime information about optional keys, 

30 we raise an error if this happens (see https://bugs.python.org/issue38834). 

31 """ 

32 field_definitions: Dict[str, Any] 

33 

34 # Best case scenario: with python 3.9+ or when `TypedDict` is imported from `typing_extensions` 

35 if not hasattr(typeddict_cls, '__required_keys__'): 1JKabcdefvwxyLMghijklzABCDmnoPQRSTUENOpqrstuFGHI

36 raise TypeError( 1abghmpq

37 'You should use `typing_extensions.TypedDict` instead of `typing.TypedDict` with Python < 3.9.2. ' 

38 'Without it, there is no way to differentiate required and optional fields when subclassed.' 

39 ) 

40 

41 if is_legacy_typeddict(typeddict_cls) and any( 1JKabcdefvwxyLMghijklzABCDmnoPQRSTUENOpqrstuFGHI

42 is_typeddict_special(t) for t in typeddict_cls.__annotations__.values() 

43 ): 

44 raise TypeError( 1cdefijklnorstu

45 'You should use `typing_extensions.TypedDict` instead of `typing.TypedDict` with Python < 3.11. ' 

46 'Without it, there is no way to reflect Required/NotRequired keys.' 

47 ) 

48 

49 required_keys: FrozenSet[str] = typeddict_cls.__required_keys__ # type: ignore[attr-defined] 1JKabcdefvwxyLMghijklzABCDmnoPQRSTUENOpqrstuFGHI

50 field_definitions = { 1JKabcdefvwxyLMghijklzABCDmnoPQRSTUENOpqrstuFGHI

51 field_name: (field_type, Required if field_name in required_keys else None) 

52 for field_name, field_type in typeddict_cls.__annotations__.items() 

53 } 

54 

55 return create_model(typeddict_cls.__name__, **kwargs, **field_definitions) 1JKabcdefvwxyLMghijklzABCDmnoPQRSTUENOpqrstuFGHI

56 

57 

58def create_model_from_namedtuple(namedtuple_cls: Type['NamedTuple'], **kwargs: Any) -> Type['BaseModel']: 1JKabcdefvwxyLMghijklzABCDmnoPQRSTUENOpqrstuFGHI

59 """ 

60 Create a `BaseModel` based on the fields of a named tuple. 

61 A named tuple can be created with `typing.NamedTuple` and declared annotations 

62 but also with `collections.namedtuple`, in this case we consider all fields 

63 to have type `Any`. 

64 """ 

65 # With python 3.10+, `__annotations__` always exists but can be empty hence the `getattr... or...` logic 

66 namedtuple_annotations: Dict[str, Type[Any]] = getattr(namedtuple_cls, '__annotations__', None) or { 1JKabcdefvwxyLMghijklzABCDmnoENOpqrstuFGHI

67 k: Any for k in namedtuple_cls._fields 

68 } 

69 field_definitions: Dict[str, Any] = { 1JKabcdefvwxyLMghijklzABCDmnoENOpqrstuFGHI

70 field_name: (field_type, Required) for field_name, field_type in namedtuple_annotations.items() 

71 } 

72 return create_model(namedtuple_cls.__name__, **kwargs, **field_definitions) 1JKabcdefvwxyLMghijklzABCDmnoENOpqrstuFGHI