Coverage for moptipy / utils / text_format.py: 84%

44 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2025-11-24 08:49 +0000

1"""Infrastructure to created structured text, like Markdown and LaTeX.""" 

2 

3from io import TextIOBase 

4from typing import Final 

5 

6from pycommons.io.path import Path, directory_path 

7from pycommons.types import type_error 

8 

9from moptipy.utils.lang import Lang 

10 

11#: indicates a normal row or cell 

12MODE_NORMAL: Final[int] = 0 

13#: indicates a row or cell in the table header 

14MODE_TABLE_HEADER: Final[int] = 1 

15#: indicates a row or cell in the section header 

16MODE_SECTION_HEADER: Final[int] = 2 

17 

18 

19class TextFormatDriver: 

20 """ 

21 A base class for text format drivers. 

22 

23 Table drivers allow us to render the structured text. It used, for 

24 example, by instances of :class:`~moptipy.utils.table.Table` to write 

25 tabular data to a text format stream. 

26 """ 

27 

28 def begin_table_body(self, stream: TextIOBase, cols: str) -> None: 

29 """ 

30 Write the beginning of the table body. 

31 

32 :param stream: the stream to write to 

33 :param cols: the column definition 

34 """ 

35 

36 def end_table_body(self, stream: TextIOBase, cols: str) -> None: 

37 """ 

38 Write the ending of the table body. 

39 

40 :param stream: the stream to write to 

41 :param cols: the column definition 

42 """ 

43 

44 def begin_table_header(self, stream: TextIOBase, cols: str) -> None: 

45 """ 

46 Begin the header row of the table. 

47 

48 :param stream: the stream to write to 

49 :param cols: the column definition 

50 """ 

51 

52 def end_table_header(self, stream: TextIOBase, cols: str) -> None: 

53 """ 

54 End the header row of the table. 

55 

56 :param stream: the stream to write to 

57 :param cols: the column definition 

58 """ 

59 

60 def begin_table_section(self, stream: TextIOBase, cols: str, 

61 section_index: int) -> None: 

62 """ 

63 Begin a new section of the table. 

64 

65 :param stream: the stream to write to 

66 :param cols: the column definition 

67 :param section_index: the index of the section, `0` for the first 

68 section 

69 """ 

70 

71 def end_table_section(self, stream: TextIOBase, cols: str, 

72 section_index: int, 

73 n_rows: int) -> None: 

74 """ 

75 End a section of the table. 

76 

77 :param stream: the stream to write to 

78 :param cols: the column definition 

79 :param section_index: the index of the section, `0` for the first 

80 section 

81 :param n_rows: the number of rows that were written in the section 

82 """ 

83 

84 def begin_table_section_header(self, stream: TextIOBase, cols: str, 

85 section_index: int) -> None: 

86 """ 

87 Begin the header row of a section. 

88 

89 :param stream: the stream to write to 

90 :param cols: the column definition 

91 :param section_index: the index of the section, `0` for the first 

92 section 

93 """ 

94 

95 def end_table_section_header(self, stream: TextIOBase, cols: str, 

96 section_index: int) -> None: 

97 """ 

98 End the header row of a section. 

99 

100 :param stream: the stream to write to 

101 :param cols: the column definition 

102 :param section_index: the index of the section, `0` for the first 

103 section 

104 """ 

105 

106 def begin_table_row(self, stream: TextIOBase, cols: str, 

107 section_index: int, row_index: int, 

108 row_mode: int) -> None: 

109 """ 

110 Begin a table header row, section row, or normal row in a section. 

111 

112 :param stream: the stream to write to 

113 :param cols: the column definition 

114 :param section_index: the index of the current section, `-1` if we 

115 are in the table header 

116 :param row_index: the row index in the section or header 

117 :param row_mode: the mode of the row, will be one of `MODE_NORMAL`, 

118 `MODE_TABLE_HEADER`, or `MODE_SECTION_HEADER` 

119 """ 

120 

121 def end_table_row(self, stream: TextIOBase, cols: str, 

122 section_index: int, row_index: int) -> None: 

123 """ 

124 End a table header row, section row, or normal row in a section. 

125 

126 :param stream: the stream to write to 

127 :param cols: the column definition 

128 :param section_index: the index of the current section 

129 :param row_index: the row index in the section or header 

130 """ 

131 

132 def begin_table_cell(self, stream: TextIOBase, cols: str, 

133 section_index: int, row_index: int, 

134 col_index: int, cell_mode: int) -> None: 

135 """ 

136 Begin a header cell, section header cell, or normal cell. 

137 

138 :param stream: the stream to write to 

139 :param cols: the column definitions 

140 :param section_index: the index of the current section, `-1` if this 

141 is a table header cell 

142 :param row_index: the row index in the section or header 

143 :param col_index: the column index, `0` for the first column 

144 :param cell_mode: the mode of the cell, will be one of `MODE_NORMAL`, 

145 `MODE_TABLE_HEADER`, or `MODE_SECTION_HEADER` 

146 """ 

147 

148 def end_table_cell(self, stream: TextIOBase, cols: str, 

149 section_index: int, row_index: int, 

150 col_index: int, cell_mode: int) -> None: 

151 """ 

152 End a header cell, section header cell, or normal cell. 

153 

154 :param stream: the stream to write to 

155 :param cols: the column definitions 

156 :param section_index: the index of the current section, `-1` if this 

157 is a table header cell 

158 :param row_index: the row index in the section or header 

159 :param col_index: the column index, `0` for the first column 

160 :param cell_mode: the mode of the cell, will be one of `MODE_NORMAL`, 

161 `MODE_TABLE_HEADER`, or `MODE_SECTION_HEADER` 

162 """ 

163 

164 def text(self, stream: TextIOBase, text: str, bold: bool, italic: bool, 

165 code: bool, mode: int) -> None: 

166 """ 

167 Write a chunk of text. 

168 

169 :param stream: the stream to write to 

170 :param text: the text to write 

171 :param bold: is the text in bold face? 

172 :param italic: is the text in italic face? 

173 :param code: is the text in code face? 

174 :param mode: the mode of a formatted text piece, see the attribute 

175 :attr:`~moptipy.utils.formatted_string.FormattedStr.mode` of 

176 :class:`~moptipy.utils.formatted_string.FormattedStr` 

177 """ 

178 

179 def filename(self, 

180 file_name: str = "file", 

181 dir_name: str = ".", 

182 use_lang: bool = True) -> Path: 

183 """ 

184 Get the right filename for this text driver. 

185 

186 :param file_name: the base file name 

187 :param dir_name: the base directory 

188 :param use_lang: should we use the language to define the filename? 

189 :returns: the path to the file to generate 

190 """ 

191 if not isinstance(dir_name, str): 

192 raise type_error(dir_name, "dir_name", str) 

193 if len(dir_name) <= 0: 

194 raise ValueError(f"invalid dir_name: {dir_name!r}.") 

195 if not isinstance(file_name, str): 

196 raise type_error(file_name, "file_name", str) 

197 if len(file_name) <= 0: 

198 raise ValueError(f"invalid file_name: {file_name!r}.") 

199 if not isinstance(use_lang, bool): 

200 raise type_error(use_lang, "use_lang", bool) 

201 out_dir = directory_path(dir_name) 

202 suffix = str(self) 

203 if not isinstance(suffix, str): 

204 raise type_error(suffix, "result of str(table driver)", str) 

205 if len(suffix) <= 0: 

206 raise ValueError(f"invalid driver suffix: {suffix!r}") 

207 if use_lang: 

208 file_name = Lang.current().filename(file_name) 

209 file: Final[Path] = out_dir.resolve_inside(f"{file_name}.{suffix}") 

210 file.ensure_file_exists() 

211 return file