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
« 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."""
3from io import TextIOBase
4from typing import Final
6from pycommons.io.path import Path, directory_path
7from pycommons.types import type_error
9from moptipy.utils.lang import Lang
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
19class TextFormatDriver:
20 """
21 A base class for text format drivers.
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 """
28 def begin_table_body(self, stream: TextIOBase, cols: str) -> None:
29 """
30 Write the beginning of the table body.
32 :param stream: the stream to write to
33 :param cols: the column definition
34 """
36 def end_table_body(self, stream: TextIOBase, cols: str) -> None:
37 """
38 Write the ending of the table body.
40 :param stream: the stream to write to
41 :param cols: the column definition
42 """
44 def begin_table_header(self, stream: TextIOBase, cols: str) -> None:
45 """
46 Begin the header row of the table.
48 :param stream: the stream to write to
49 :param cols: the column definition
50 """
52 def end_table_header(self, stream: TextIOBase, cols: str) -> None:
53 """
54 End the header row of the table.
56 :param stream: the stream to write to
57 :param cols: the column definition
58 """
60 def begin_table_section(self, stream: TextIOBase, cols: str,
61 section_index: int) -> None:
62 """
63 Begin a new section of the table.
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 """
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.
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 """
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.
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 """
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.
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 """
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.
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 """
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.
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 """
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.
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 """
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.
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 """
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.
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 """
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.
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