1 | 1 | import configparser |
|
2 | 1 | import os |
|
3 | 1 | from typing import List, Optional |
|
4 | |||
5 | 1 | from sdoc import sdoc2 |
|
6 | 1 | from sdoc.error import SDocError |
|
7 | 1 | from sdoc.format.Format import Format |
|
8 | 1 | from sdoc.io.SDocIO import SDocIO |
|
9 | 1 | from sdoc.sdoc1.SDoc1Interpreter import SDoc1Interpreter |
|
10 | 1 | from sdoc.sdoc2.NodeStore import NodeStore |
|
11 | 1 | from sdoc.sdoc2.SDoc2Interpreter import SDoc2Interpreter |
|
12 | |||
13 | |||
14 | 1 | View Code Duplication | class SDoc: |
0 ignored issues
–
show
Duplication
introduced
by
![]() |
|||
15 | """ |
||
16 | The SDoc program. |
||
17 | """ |
||
18 | |||
19 | # ------------------------------------------------------------------------------------------------------------------ |
||
20 | 1 | def __init__(self): |
|
21 | """ |
||
22 | Object contructor. |
||
23 | """ |
||
24 | 1 | self._io: Optional[SDocIO] = None |
|
25 | """ |
||
26 | The IO object. |
||
27 | """ |
||
28 | |||
29 | 1 | self._format: Optional[Format] = None |
|
30 | """ |
||
31 | The class for generation the document in the target format. |
||
32 | """ |
||
33 | |||
34 | 1 | self._target_dir: str = '.' |
|
35 | """ |
||
36 | The directory where the document in the target format must be created. |
||
37 | """ |
||
38 | |||
39 | 1 | self._temp_dir: str = '.' |
|
40 | """ |
||
41 | The directory where temporary files are stored. |
||
42 | """ |
||
43 | |||
44 | 1 | self._config_path: str = '' |
|
45 | """ |
||
46 | The path of the config file. |
||
47 | """ |
||
48 | |||
49 | 1 | self._nodes_paths: List[str] = [] |
|
50 | """ |
||
51 | A list with path names from with node modules must be imported. |
||
52 | """ |
||
53 | |||
54 | 1 | self._formatter_paths: List[str] = [] |
|
55 | """ |
||
56 | A list with path names from with node modules must be imported. |
||
57 | """ |
||
58 | |||
59 | 1 | self._errors: int = 0 |
|
60 | 1 | """ |
|
61 | The total number of errors encountered at SDoc level 1 and level 2. |
||
62 | """ |
||
63 | |||
64 | # ------------------------------------------------------------------------------------------------------------------ |
||
65 | 1 | @property |
|
66 | 1 | def io(self) -> SDocIO: |
|
67 | """ |
||
68 | Getter for io. |
||
69 | """ |
||
70 | return self._io |
||
71 | |||
72 | # ------------------------------------------------------------------------------------------------------------------ |
||
73 | 1 | @io.setter |
|
74 | 1 | def io(self, io: SDocIO) -> None: |
|
75 | """ |
||
76 | Setter for io. |
||
77 | |||
78 | :param OutputStyle io: The IO object. |
||
79 | """ |
||
80 | 1 | self._io = io |
|
81 | |||
82 | # ------------------------------------------------------------------------------------------------------------------ |
||
83 | 1 | @property |
|
84 | 1 | def config_path(self) -> str: |
|
85 | """ |
||
86 | Getter for config_path. |
||
87 | """ |
||
88 | return self._config_path |
||
89 | |||
90 | # ------------------------------------------------------------------------------------------------------------------ |
||
91 | 1 | @config_path.setter |
|
92 | 1 | def config_path(self, config_path: str) -> None: |
|
93 | """ |
||
94 | Setter for config_path. |
||
95 | |||
96 | :param str config_path: The path of the config file. |
||
97 | """ |
||
98 | 1 | self._config_path = config_path |
|
99 | |||
100 | # ------------------------------------------------------------------------------------------------------------------ |
||
101 | 1 | @property |
|
102 | 1 | def target_dir(self) -> str: |
|
103 | """ |
||
104 | Getter for target_dir. |
||
105 | """ |
||
106 | return self.target_dir |
||
107 | |||
108 | # ------------------------------------------------------------------------------------------------------------------ |
||
109 | 1 | @property |
|
110 | 1 | def temp_dir(self) -> str: |
|
111 | """ |
||
112 | Getter for temp_dir. |
||
113 | """ |
||
114 | return self.temp_dir |
||
115 | |||
116 | # ------------------------------------------------------------------------------------------------------------------ |
||
117 | 1 | def _config_create_formatter(self, config: configparser.ConfigParser) -> None: |
|
118 | """ |
||
119 | Creates the formatter for generating the document in the target format. |
||
120 | |||
121 | :param configparser.ConfigParser config: The config parser. |
||
122 | """ |
||
123 | 1 | available_formats = ['html'] |
|
124 | |||
125 | # Read the target format of the document. |
||
126 | 1 | target_format = config.get('sdoc', 'format', fallback=None) |
|
127 | 1 | if target_format not in available_formats: |
|
128 | raise SDocError("The format '{}' is not available in SDoc. Set another in config file '{}'" |
||
129 | .format(target_format, self._config_path)) |
||
130 | |||
131 | 1 | if not target_format: |
|
132 | raise SDocError("Option 'format' in section 'sdoc' not set in config file '{0!s}'" |
||
133 | .format(self._config_path)) |
||
134 | |||
135 | # Read the class name for formatting the SDoc2 nodes into the target format. |
||
136 | 1 | section = 'format_' + target_format |
|
137 | 1 | class_name = config.get(section, 'class', fallback=None) |
|
138 | 1 | if not class_name: |
|
139 | raise SDocError("Option 'class' in section '{0!s}' not set in config file '{1!s}'". |
||
140 | format(section, self._config_path)) |
||
141 | |||
142 | # Import the class. |
||
143 | 1 | try: |
|
144 | 1 | parts = class_name.split('.') |
|
145 | 1 | module = ".".join(parts[:-1]) |
|
146 | 1 | __import__(module) |
|
147 | 1 | m = __import__(module) |
|
148 | 1 | for comp in parts[1:]: |
|
149 | 1 | m = getattr(m, comp) |
|
150 | except AttributeError: |
||
151 | raise SDocError("There is no module named '{0!s}'! Set name correctly in config file '{1!s}'" |
||
152 | .format(class_name, self._config_path)) |
||
153 | |||
154 | # Create the formatter. |
||
155 | 1 | self._format = m(self._io, target_format, config) |
|
156 | |||
157 | # ------------------------------------------------------------------------------------------------------------------ |
||
158 | 1 | def _config_set_temp_dir(self, config: configparser.ConfigParser) -> None: |
|
159 | """ |
||
160 | Reads the directory for storing temporary files. |
||
161 | |||
162 | :param configparser.ConfigParser config: The config parser. |
||
163 | """ |
||
164 | 1 | self._temp_dir = config.get('sdoc', 'temp_dir', fallback=self._temp_dir) |
|
165 | |||
166 | 1 | if not self._temp_dir: |
|
167 | raise SDocError("Option 'temp_dir' in section 'sdoc' not set correctly in config file '{0!s}'". |
||
168 | format(self._config_path)) |
||
169 | |||
170 | 1 | if not os.access(self._temp_dir, os.W_OK): |
|
171 | raise SDocError("Directory '{0!s}' is not writable".format(self._temp_dir)) |
||
172 | |||
173 | # ------------------------------------------------------------------------------------------------------------------ |
||
174 | 1 | def _config_set_target_dir(self, config: configparser.ConfigParser) -> None: |
|
175 | """ |
||
176 | Reads the directory where the document in the target format must be created. |
||
177 | |||
178 | :param configparser.ConfigParser config: The config parser. |
||
179 | """ |
||
180 | 1 | self._target_dir = config.get('sdoc', 'target_dir', fallback=self._target_dir) |
|
181 | |||
182 | 1 | if not self._target_dir: |
|
183 | raise SDocError("Option 'target_dir' in section 'sdoc' not set correctly in config file '{0!s}'". |
||
184 | format(self._config_path)) |
||
185 | |||
186 | 1 | if not os.access(self._target_dir, os.W_OK): |
|
187 | raise SDocError("Directory '{0!s}' is not writable".format(self._target_dir)) |
||
188 | |||
189 | # ------------------------------------------------------------------------------------------------------------------ |
||
190 | 1 | def _read_config_file(self) -> None: |
|
191 | """ |
||
192 | Reads the configuration file. |
||
193 | """ |
||
194 | 1 | config = configparser.ConfigParser() |
|
195 | 1 | config.read(self._config_path) |
|
196 | |||
197 | # Get the temp and target directory. |
||
198 | 1 | self._config_set_temp_dir(config) |
|
199 | 1 | self._config_set_target_dir(config) |
|
200 | |||
201 | # Create the formatter for generating the document in the target format. |
||
202 | 1 | self._config_create_formatter(config) |
|
203 | |||
204 | 1 | self._formatter_paths.append(os.path.dirname(__file__) + '/sdoc2/formatter') |
|
205 | 1 | self._nodes_paths.append(os.path.dirname(__file__) + '/sdoc2/node') |
|
206 | |||
207 | # ------------------------------------------------------------------------------------------------------------------ |
||
208 | 1 | def _create_node_store(self) -> None: |
|
209 | """ |
||
210 | Creates the node store (for storing nodes). |
||
211 | """ |
||
212 | 1 | sdoc2.node_store = NodeStore(self._io) |
|
213 | |||
214 | # ------------------------------------------------------------------------------------------------------------------ |
||
215 | 1 | @staticmethod |
|
216 | 1 | def importing(path: str) -> None: |
|
217 | """ |
||
218 | Imports modules from specific path. |
||
219 | |||
220 | :param str path: The specific path. |
||
221 | """ |
||
222 | 1 | modules = os.listdir(os.path.dirname(__file__) + path) |
|
223 | |||
224 | 1 | path = path.replace('/', '.') |
|
225 | |||
226 | 1 | for module in modules: |
|
227 | 1 | if module != '__init__.py' and module[-3:] == '.py': |
|
228 | 1 | __import__('sdoc' + path + module[:-3], locals(), globals()) |
|
229 | |||
230 | # ------------------------------------------------------------------------------------------------------------------ |
||
231 | 1 | def _import_nodes(self) -> None: |
|
232 | """ |
||
233 | Imports nodes from path which is declared below. |
||
234 | """ |
||
235 | # @todo improve |
||
236 | 1 | self.importing('/sdoc2/node/') |
|
237 | |||
238 | # ------------------------------------------------------------------------------------------------------------------ |
||
239 | 1 | def _import_formatters(self) -> None: |
|
240 | """ |
||
241 | Imports formatters from path which is declared below. |
||
242 | """ |
||
243 | # @todo improve |
||
244 | 1 | self.importing('/sdoc2/formatter/html/') |
|
245 | |||
246 | # ------------------------------------------------------------------------------------------------------------------ |
||
247 | 1 | def init(self) -> None: |
|
248 | """ |
||
249 | Executes initiations required before running SDoc. |
||
250 | """ |
||
251 | 1 | self._read_config_file() |
|
252 | 1 | self._create_node_store() |
|
253 | 1 | self._import_nodes() |
|
254 | 1 | self._import_formatters() |
|
255 | |||
256 | # ------------------------------------------------------------------------------------------------------------------ |
||
257 | 1 | def run_sdoc1(self, sdoc1_path: str, sdoc2_path: str, log_errors: bool = True) -> int: |
|
258 | """ |
||
259 | Run the SDoc1 parser and returns the error count. |
||
260 | |||
261 | :param str sdoc1_path: The path of the SDoc1 document. |
||
262 | :param str sdoc2_path: The path were the SDoc2 document mut be stored. |
||
263 | :param bool log_errors: If true the number of errors will be logged. |
||
264 | """ |
||
265 | 1 | self._io.title('SDoc1') |
|
266 | |||
267 | 1 | interpreter1 = SDoc1Interpreter(self._io) |
|
268 | 1 | self._errors += interpreter1.process(sdoc1_path, sdoc2_path) |
|
269 | |||
270 | 1 | if log_errors and self._errors: |
|
271 | self._io.write_line(" ") |
||
272 | self._io.title('Errors') |
||
273 | self._io.write_error('There were {0} errors in total'.format(self._errors)) |
||
274 | |||
275 | 1 | return self._errors |
|
276 | |||
277 | # ------------------------------------------------------------------------------------------------------------------ |
||
278 | 1 | def run_sdoc2(self, sdoc2_path: str, log_errors: bool = True) -> int: |
|
279 | """ |
||
280 | Run the SDoc2 parser and returns the error count. |
||
281 | |||
282 | :param str sdoc2_path: The path of the SDoc2 document. |
||
283 | :param bool log_errors: If true the number of errors will be logged. |
||
284 | """ |
||
285 | 1 | self._io.write_line('') |
|
286 | 1 | self._io.title('SDoc2') |
|
287 | |||
288 | 1 | interpreter2 = SDoc2Interpreter(self._io) |
|
289 | 1 | self._errors += interpreter2.process(sdoc2_path) |
|
290 | |||
291 | 1 | if log_errors and self._errors: |
|
292 | self._io.write_line(" ") |
||
293 | self._io.title('Errors') |
||
294 | self._io.write_error('There were {0} errors in total'.format(self._errors)) |
||
295 | |||
296 | 1 | return self._errors |
|
297 | |||
298 | # ------------------------------------------------------------------------------------------------------------------ |
||
299 | 1 | def run_format(self, log_errors: bool = True) -> int: |
|
300 | """ |
||
301 | Generates the target document in the specific format and returns the error count. |
||
302 | |||
303 | :param bool log_errors: If true the number of errors will be logged. |
||
304 | """ |
||
305 | 1 | self._io.write_line('') |
|
306 | 1 | self._io.title('Format') |
|
307 | |||
308 | 1 | self._errors += sdoc2.node_store.generate(self._format) |
|
309 | |||
310 | 1 | if log_errors and self._errors: |
|
311 | self._io.write_line(" ") |
||
312 | self._io.title('Errors') |
||
313 | self._io.write_error('There were {0} errors in total'.format(self._errors)) |
||
314 | |||
315 | 1 | return self._errors |
|
316 | |||
317 | # ------------------------------------------------------------------------------------------------------------------ |
||
318 | 1 | def run_sdoc(self, main_filename: str, log_errors: bool = True) -> int: |
|
319 | """ |
||
320 | Runs the SDoc1 and SDoc2 parser and returns the error count. |
||
321 | |||
322 | :param str main_filename: The path of the SDoc1 document. |
||
323 | :param bool log_errors: If true the number of errors will be logged. |
||
324 | """ |
||
325 | 1 | self.init() |
|
326 | |||
327 | 1 | temp_filename = self._temp_dir + '/' + os.path.basename(main_filename) + '.sdoc2' |
|
328 | 1 | self.run_sdoc1(main_filename, temp_filename, False) |
|
329 | 1 | self.run_sdoc2(temp_filename, False) |
|
330 | 1 | self.run_format(False) |
|
331 | |||
332 | 1 | if log_errors and self._errors: |
|
333 | 1 | self._io.write_line(" ") |
|
334 | 1 | self._io.title('Errors') |
|
335 | 1 | self._io.write_error('There were {0} errors in total'.format(self._errors)) |
|
336 | |||
337 | 1 | return self._errors |
|
338 | |||
339 | # ---------------------------------------------------------------------------------------------------------------------- |
||
340 |