Coverage for src/debputy/plugin/api/doc_parsing.py: 96%

44 statements  

« prev     ^ index     » next       coverage.py v7.8.2, created at 2026-01-16 17:20 +0000

1import dataclasses 

2from typing import NotRequired, Any, Self, TypeVar 

3from collections.abc import Mapping, Iterable 

4 

5from debputy.manifest_parser.declarative_parser import ParserGenerator 

6from debputy.manifest_parser.tagging_types import DebputyParsedContent 

7from debputy.plugin.api.spec import ParserDocumentation 

8 

9_DEBPUTY_DOC_PARSER_GENERATOR = ParserGenerator() 

10 

11 

12class AttributeRefDoc(DebputyParsedContent): 

13 attr: NotRequired[str | list[str]] 

14 description: str 

15 

16 

17class ParserRefDocumentation(DebputyParsedContent): 

18 title: str 

19 description: NotRequired[str] 

20 attributes: NotRequired[list[AttributeRefDoc]] 

21 undocumented_attributes: NotRequired[list[str]] 

22 non_mapping_description: NotRequired[str] 

23 ref_doc_url: NotRequired[str] 

24 synopsis: NotRequired[str] 

25 

26 

27class PMRData(ParserRefDocumentation): 

28 rule_reference: str 

29 documentation: NotRequired[ParserRefDocumentation] 

30 

31 

32class DebputyDocReferenceData(DebputyParsedContent): 

33 pluggable_manifest_rules: NotRequired[list[PMRData]] 

34 

35 

36DEBPUTY_DOC_REFERENCE_DATA_PARSER = _DEBPUTY_DOC_PARSER_GENERATOR.generate_parser( 

37 DebputyDocReferenceData 

38) 

39 

40 

41D = TypeVar("D") 

42 

43 

44def _unique( 

45 topic: str, key_attr_name: str, kd: Iterable[tuple[str, D]] 

46) -> Mapping[str, D]: 

47 seen = set() 

48 r = {} 

49 for k, d in kd: 

50 if k in seen: 50 ↛ 51line 50 didn't jump to line 51 because the condition on line 50 was never true

51 raise ValueError( 

52 f"The multiple instances of the {key_attr_name} attribute in {topic}" 

53 f' had the value: "{k}". The value must be unique' 

54 ) 

55 seen.add(k) 

56 r[k] = d 

57 

58 return r 

59 

60 

61@dataclasses.dataclass(slots=True, frozen=True) 

62class DebputyParsedDoc: 

63 pluggable_manifest_rules: Mapping[str, ParserDocumentation] 

64 

65 @classmethod 

66 def from_ref_data(cls, docs: DebputyDocReferenceData) -> Self: 

67 return cls( 

68 pluggable_manifest_rules=_unique( 

69 "pluggable-manifest-rules", 

70 "rule-reference", 

71 ( 

72 (d["rule_reference"], ParserDocumentation.from_ref_doc(d)) 

73 for d in docs.get("pluggable_manifest_rules", []) 

74 ), 

75 ) 

76 ) 

77 

78 

79def parser_type_name(v: str | type[Any]) -> str: 

80 if isinstance(v, str): 

81 return v if v != "<ROOT>" else "" 

82 return v.__name__