Coverage for phml\helpers.py: 100%

41 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-04-05 15:06 -0500

1import sys 

2from pathlib import Path 

3from traceback import print_tb 

4from typing import Any, Iterator 

5 

6from phml.nodes import AST, Element, Node, Parent 

7 

8 

9def build_recursive_context(node: Node, context: dict[str, Any]) -> dict[str, Any]: 

10 """Build recursive context for the current node.""" 

11 parent = node.parent 

12 parents = [] 

13 result = {**context} 

14 

15 while parent is not None and not isinstance(parent, AST): 

16 parents.append(parent) 

17 parent = parent.parent 

18 

19 for parent in parents: 

20 result.update(parent.context) 

21 

22 if isinstance(node, Element): 

23 result.update(node.context) 

24 return result 

25 

26 

27def iterate_nodes(node: Parent) -> Iterator[Node]: 

28 """Recursively iterate over nodes and their children.""" 

29 yield node 

30 for child in node: 

31 if isinstance(child, Parent): 

32 yield from iterate_nodes(child) 

33 

34 

35def calc_offset(content: str) -> int: 

36 """Get the leading offset of the first line of the string.""" 

37 return len(content) - len(content.lstrip()) 

38 

39 

40def strip_blank_lines(data: str) -> str: 

41 """Strip the blank lines at the start and end of a list.""" 

42 data = data.rstrip().replace("\r\n", "\n") 

43 data_lines = data.split("\n") 

44 

45 # remove leading blank lines 

46 for idx in range(0, len(data_lines)): # pylint: disable=consider-using-enumerate 

47 if data_lines[idx].strip() != "": 

48 return "\n".join(data_lines[idx:]) 

49 

50 return "" 

51 

52 

53def normalize_indent(content: str, indent: int = 0) -> str: 

54 """Normalize the indent between all lines. 

55 

56 Args: 

57 content (str): The content to normalize the indent for 

58 indent (bool): The amount of offset to add to each line after normalization. 

59 

60 Returns: 

61 str: The normalized string 

62 """ 

63 

64 lines = strip_blank_lines(content).split("\n") 

65 offset = calc_offset(lines[0]) 

66 

67 result = [] 

68 for line in lines: 

69 if len(line) > 0: 

70 result.append( 

71 " " * indent + line[min(calc_offset(line), offset) :], 

72 ) 

73 else: 

74 result.append(line) 

75 return "\n".join(result) 

76 

77 

78class PHMLTryCatch: 

79 """Context manager around core PHML actions. When an exception is raised 

80 it is caught here and the current file that is being handled is prepended 

81 to the exception message. 

82 """ 

83 

84 def __init__(self, path: str | Path | None = None, fallback: str = "") -> None: 

85 if path is None or str(path) == "": 

86 path = fallback 

87 self._path = str(path or fallback) 

88 

89 def __enter__(self): 

90 pass 

91 

92 # (self, exc_type, exc_val, exc_tb) 

93 def __exit__(self, _, exc_val, exc_tb): 

94 if exc_val is not None and not isinstance(exc_val, SystemExit): 

95 print_tb(exc_tb) 

96 if self._path != "": 

97 sys.stderr.write(f"[{self._path}]: {exc_val}") 

98 else: 

99 sys.stderr.write(str(exc_val)) 

100 sys.stderr.flush() 

101 exit()