Coverage for little_loops / frontmatter.py: 17%

41 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2026-03-18 16:18 -0500

1"""Frontmatter parsing utilities for little-loops. 

2 

3Provides shared YAML-subset frontmatter parsing and stripping used by 

4issue_parser, sync, and issue_history modules. 

5""" 

6 

7from __future__ import annotations 

8 

9import logging 

10import re 

11from typing import Any 

12 

13logger = logging.getLogger(__name__) 

14 

15 

16def parse_frontmatter(content: str, *, coerce_types: bool = False) -> dict[str, Any]: 

17 """Extract YAML frontmatter from content. 

18 

19 Looks for content between opening and closing '---' markers. 

20 Parses a subset of YAML: simple ``key: value`` pairs only. Lists, 

21 block scalars, and nested structures are not supported and will emit 

22 a ``logging.WARNING``. Returns empty dict if no frontmatter found. 

23 

24 Args: 

25 content: File content to parse 

26 coerce_types: If True, coerce digit strings to int 

27 

28 Returns: 

29 Dictionary of frontmatter fields, or empty dict 

30 """ 

31 if not content or not content.startswith("---"): 

32 return {} 

33 

34 end_match = re.search(r"\n---\s*\n", content[3:]) 

35 if not end_match: 

36 return {} 

37 

38 frontmatter_text = content[4 : 3 + end_match.start()] 

39 

40 result: dict[str, Any] = {} 

41 for line in frontmatter_text.split("\n"): 

42 line = line.strip() 

43 if not line or line.startswith("#"): 

44 continue 

45 if line.startswith("- "): 

46 logger.warning("Unsupported YAML list syntax in frontmatter: %r", line) 

47 continue 

48 if ":" in line: 

49 key, value = line.split(":", 1) 

50 key = key.strip() 

51 value = value.strip() 

52 if value.startswith("|") or value.startswith(">"): 

53 logger.warning("Unsupported YAML block scalar in frontmatter: %r", line) 

54 result[key] = None 

55 continue 

56 if value.lower() in ("null", "~", ""): 

57 result[key] = None 

58 elif coerce_types and value.isdigit(): 

59 result[key] = int(value) 

60 else: 

61 result[key] = value 

62 return result 

63 

64 

65def strip_frontmatter(content: str) -> str: 

66 """Remove YAML frontmatter from content, returning the body. 

67 

68 Strips the ``---`` delimited frontmatter block (if present) and 

69 returns everything after the closing delimiter. 

70 

71 Args: 

72 content: File content possibly starting with frontmatter 

73 

74 Returns: 

75 Content with frontmatter removed. Returns original content 

76 unchanged if no valid frontmatter block is found. 

77 """ 

78 if not content or not content.startswith("---"): 

79 return content 

80 

81 end_match = re.search(r"\n---\s*\n", content[3:]) 

82 if not end_match: 

83 return content 

84 

85 return content[3 + end_match.end() :]