Coverage for src/griffe_pydantic/_internal/dynamic.py: 65.62%

40 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-09-05 17:55 +0200

1from __future__ import annotations 

2 

3from typing import Any, Callable 

4 

5from griffe import ( 

6 Attribute, 

7 Class, 

8 Docstring, 

9 Function, 

10 Kind, 

11 get_logger, 

12) 

13 

14from griffe_pydantic._internal import common 

15 

16_logger = get_logger("griffe_pydantic") 

17 

18 

19def _process_attribute(obj: Any, attr: Attribute, cls: Class, *, processed: set[str]) -> None: 

20 """Handle Pydantic fields.""" 

21 from pydantic.fields import FieldInfo # noqa: PLC0415 

22 

23 if attr.canonical_path in processed: 

24 return 

25 processed.add(attr.canonical_path) 

26 if attr.name == "model_config": 

27 cls.extra[common._self_namespace]["config"] = obj 

28 return 

29 

30 if not isinstance(obj, FieldInfo): 30 ↛ 33line 30 didn't jump to line 33 because the condition on line 30 was always true

31 return 

32 

33 attr.labels = {"pydantic-field"} 

34 attr.value = obj.default 

35 constraints = {} 

36 for constraint in common._field_constraints: 

37 if (value := getattr(obj, constraint, None)) is not None: 

38 constraints[constraint] = value 

39 attr.extra[common._self_namespace]["constraints"] = constraints 

40 

41 # Populate docstring from the field's `description` argument. 

42 if not attr.docstring and (docstring := obj.description): 

43 attr.docstring = Docstring(docstring, parent=attr) 

44 

45 

46def _process_function(obj: Callable, func: Function, cls: Class, *, processed: set[str]) -> None: 

47 """Handle Pydantic field validators.""" 

48 if func.canonical_path in processed: 48 ↛ 49line 48 didn't jump to line 49 because the condition on line 48 was never true

49 return 

50 processed.add(func.canonical_path) 

51 if dec_info := getattr(obj, "decorator_info", None): 51 ↛ 52line 51 didn't jump to line 52 because the condition on line 51 was never true

52 common._process_function(func, cls, dec_info.fields) 

53 

54 

55def _process_class(obj: type, cls: Class, *, processed: set[str], schema: bool = False) -> None: 

56 """Detect and prepare Pydantic models.""" 

57 common._process_class(cls) 

58 if schema: 58 ↛ 60line 58 didn't jump to line 60 because the condition on line 58 was always true

59 cls.extra[common._self_namespace]["schema"] = common._json_schema(obj) 

60 for member in cls.all_members.values(): 

61 kind = member.kind 

62 if kind is Kind.ATTRIBUTE: 

63 _process_attribute(getattr(obj, member.name), member, cls, processed=processed) # type: ignore[arg-type] 

64 elif kind is Kind.FUNCTION: 64 ↛ 60line 64 didn't jump to line 60 because the condition on line 64 was always true

65 _process_function(getattr(obj, member.name), member, cls, processed=processed) # type: ignore[arg-type]