Coverage for src/griffe_pydantic/dynamic.py: 23.08%

27 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-02-18 01:11 +0100

1"""Griffe extension for Pydantic.""" 

2 

3from __future__ import annotations 

4 

5from typing import TYPE_CHECKING 

6 

7from griffe import ( 

8 Attribute, 

9 Class, 

10 Docstring, 

11 Function, 

12 get_logger, 

13) 

14from pydantic.fields import FieldInfo 

15 

16from griffe_pydantic import common 

17 

18if TYPE_CHECKING: 

19 from griffe import ObjectNode 

20 

21logger = get_logger(__name__) 

22 

23 

24def process_attribute(node: ObjectNode, attr: Attribute, cls: Class) -> None: 

25 """Handle Pydantic fields.""" 

26 if attr.name == "model_config": 

27 cls.extra[common.self_namespace]["config"] = node.obj 

28 return 

29 

30 if not isinstance(node.obj, FieldInfo): 

31 return 

32 

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

34 attr.value = node.obj.default 

35 constraints = {} 

36 for constraint in common.field_constraints: 

37 if (value := getattr(node.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 := node.obj.description): 

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

44 

45 

46def process_function(node: ObjectNode, func: Function, cls: Class) -> None: 

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

48 if dec_info := getattr(node.obj, "decorator_info", None): 

49 common.process_function(func, cls, dec_info.fields) 

50 

51 

52def process_class(node: ObjectNode, cls: Class) -> None: 

53 """Detect and prepare Pydantic models.""" 

54 common.process_class(cls) 

55 cls.extra[common.self_namespace]["schema"] = common.json_schema(node.obj)