Coverage for pymend\output.py: 24%

44 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2024-04-20 19:09 +0200

1"""Nice output for pymend. 

2 

3The double calls are for patching purposes in tests. 

4""" 

5 

6import difflib 

7import tempfile 

8from typing import Any, Optional 

9 

10from click import echo, style 

11 

12 

13def out( 

14 message: Optional[str] = None, 

15 *, 

16 nl: bool = True, 

17 **styles: Any, # noqa: ANN401 

18) -> None: 

19 """Output normal message. 

20 

21 Parameters 

22 ---------- 

23 message : Optional[str] 

24 Message to output (Default value = None) 

25 nl : bool 

26 Whether to print a newline after the message. (Default value = True) 

27 **styles : Any 

28 Style options for click. 

29 """ 

30 if message is not None: 

31 if "bold" not in styles: 

32 styles["bold"] = True 

33 message = style(message, **styles) 

34 echo(message, nl=nl, err=True) 

35 

36 

37def err( 

38 message: Optional[str] = None, 

39 *, 

40 nl: bool = True, 

41 **styles: Any, # noqa: ANN401 

42) -> None: 

43 """Output error message. 

44 

45 Parameters 

46 ---------- 

47 message : Optional[str] 

48 Message to output (Default value = None) 

49 nl : bool 

50 Whether to print a newline after the message. (Default value = True) 

51 **styles : Any 

52 Style options for click. 

53 """ 

54 if message is not None: 

55 if "fg" not in styles: 

56 styles["fg"] = "red" 

57 message = style(message, **styles) 

58 echo(message, nl=nl, err=True) 

59 

60 

61def diff(a: list[str], b: list[str], a_name: str, b_name: str) -> list[str]: 

62 """Return a unified diff list between lists`a` and `b`. 

63 

64 Parameters 

65 ---------- 

66 a : list[str] 

67 Source for the diff 

68 b : list[str] 

69 Target for the diff. 

70 a_name : str 

71 Path to the source file of the diff. (Default value = '') 

72 b_name : str 

73 Path to the target file of the diff. (Default value = '') 

74 

75 Returns 

76 ------- 

77 list[str] 

78 The resulting diff 

79 """ 

80 diff_lines: list[str] = [] 

81 for line in difflib.unified_diff(a, b, fromfile=a_name, tofile=b_name): 

82 # Work around https://bugs.python.org/issue2142 

83 # See: 

84 # https://www.gnu.org/software/diffutils/manual/html_node/Incomplete-Lines.html 

85 if line[-1] == "\n": 85 ↛ 88line 85 didn't jump to line 88, because the condition on line 85 was never false

86 diff_lines.append(line) 

87 else: 

88 diff_lines.append(line + "\n") 

89 diff_lines.append("\\ No newline at end of file\n") 

90 return diff_lines 

91 

92 

93def color_diff(contents: str) -> str: 

94 """Inject the ANSI color codes to the diff. 

95 

96 Parameters 

97 ---------- 

98 contents : str 

99 Diff content to color. 

100 

101 Returns 

102 ------- 

103 str 

104 Colored diff. 

105 """ 

106 lines = contents.split("\n") 

107 for i, line in enumerate(lines): 

108 if line.startswith(("+++", "---")): 

109 line = "\033[1m" + line + "\033[0m" # bold, reset # noqa: PLW2901 

110 elif line.startswith("@@"): 

111 line = "\033[36m" + line + "\033[0m" # cyan, reset # noqa: PLW2901 

112 elif line.startswith("+"): 

113 line = "\033[32m" + line + "\033[0m" # green, reset # noqa: PLW2901 

114 elif line.startswith("-"): 

115 line = "\033[31m" + line + "\033[0m" # red, reset # noqa: PLW2901 

116 lines[i] = line 

117 return "\n".join(lines) 

118 

119 

120def dump_to_file(*output: str, ensure_final_newline: bool = True) -> str: 

121 """Dump `output` to a temporary file. Return path to the file. 

122 

123 Parameters 

124 ---------- 

125 *output : str 

126 List of strings to dump into the output. 

127 ensure_final_newline : bool 

128 Whether to make sure that every dumped string 

129 ends in a new line. (Default value = True) 

130 

131 Returns 

132 ------- 

133 str 

134 Path to the produced temp file. 

135 """ 

136 with tempfile.NamedTemporaryFile( 

137 mode="w", prefix="blk_", suffix=".log", delete=False, encoding="utf8" 

138 ) as f: 

139 for lines in output: 

140 f.write(lines) 

141 if ensure_final_newline and lines and lines[-1] != "\n": 

142 f.write("\n") 

143 return f.name