Completed
Pull Request — master (#45)
by Oleg
02:04
created

SDoc.run_sdoc()   A

Complexity

Conditions 3

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 3

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 20
ccs 11
cts 11
cp 1
rs 9.4285
cc 3
crap 3
1
"""
2
SDoc
3
4
Copyright 2016 Set Based IT Consultancy
5
6
Licence MIT
7
"""
8
# ----------------------------------------------------------------------------------------------------------------------
9 1
import configparser
10 1
import os
11
12 1
from sdoc import sdoc2
13 1
from sdoc.error import SDocError
14 1
from sdoc.sdoc1.SDoc1Interpreter import SDoc1Interpreter
15 1
from sdoc.sdoc2.NodeStore import NodeStore
16 1
from sdoc.sdoc2.SDoc2Interpreter import SDoc2Interpreter
17
18
19 1
class SDoc:
20
    """
21
    The SDoc program.
22
    """
23
24
    # ------------------------------------------------------------------------------------------------------------------
25 1
    def __init__(self):
26
        """
27
        Object contructor.
28
        """
29 1
        self._io = None
30
        """
31
        The IO object.
32
33
        :type: None|sdoc.style.SdocStyle.SdocStyle
34
        """
35
36 1
        self._format = None
37
        """
38
        The class for generation the document in the target format.
39
40
        :type: sdoc.format.Format.Format
41
        """
42
43 1
        self._target_dir = '.'
44
        """
45
        The directory where the document in the target format must be created.
46
47
        :type: str
48
        """
49
50 1
        self._temp_dir = '.'
51
        """
52
        The directory where temporary files are stored.
53
54
        :type: str
55
        """
56
57 1
        self._config_path = ''
58
        """
59
        The path of the config file.
60
61
        :type: str
62
        """
63
64 1
        self._nodes_paths = []
65
        """
66
        A list with path names from with node modules must be imported.
67
68
        :type: [str]
69
        """
70
71 1
        self._formatter_paths = []
72
        """
73
        A list with path names from with node modules must be imported.
74
75
        :type: [str]
76
        """
77
78 1
        self._errors = 0
79 1
        """
80
        The total number of errors encountered at SDoc level 1 and level 2.
81
82
        :type: int
83
        """
84
85
    # ------------------------------------------------------------------------------------------------------------------
86 1
    @property
87
    def io(self):
0 ignored issues
show
Coding Style Naming introduced by
The name io does not conform to the attribute naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
88
        """
89
        Getter for io.
90
91
        :rtype: cleo.styles.output_style.OutputStyle
92
        """
93
        return self._io
94
95
    # ------------------------------------------------------------------------------------------------------------------
96 1
    @io.setter
97
    def io(self, io):
0 ignored issues
show
Coding Style Naming introduced by
The name io does not conform to the attribute naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style Naming introduced by
The name io does not conform to the argument naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
98
        """
99
        Setter for io.
100
101
        :param cleo.styles.output_style.OutputStyle io: The IO object.
102
        """
103 1
        self._io = io
104
105
    # ------------------------------------------------------------------------------------------------------------------
106 1
    @property
107
    def config_path(self):
108
        """
109
        Getter for config_path.
110
111
        :rtype: str
112
        """
113
        return self._config_path
114
115
    # ------------------------------------------------------------------------------------------------------------------
116 1
    @config_path.setter
117
    def config_path(self, config_path):
118
        """
119
        Setter for config_path.
120
121
        :param cleo.styles.output_style.OutputStyle config_path: The path of the config file.
122
        """
123 1
        self._config_path = config_path
124
125
    # ------------------------------------------------------------------------------------------------------------------
126 1
    @property
127
    def target_dir(self):
128
        """
129
        Getter for target_dir.
130
131
        :rtype: str
132
        """
133
        return self.target_dir
134
135
    # ------------------------------------------------------------------------------------------------------------------
136 1
    @property
137
    def temp_dir(self):
138
        """
139
        Getter for temp_dir.
140
141
        :rtype: str
142
        """
143
        return self.temp_dir
144
145
    # ------------------------------------------------------------------------------------------------------------------
146 1
    def _config_create_formatter(self, config):
147
        """
148
        Creates the formatter for generating the document in the target format.
149
150
        :param configparser.ConfigParser config: The config parser.
151
        """
152 1
        available_formats = ['html']
153
154
        # Read the target format of the document.
155 1
        target_format = config.get('sdoc', 'format', fallback=None)
156 1
        if target_format not in available_formats:
157
            raise SDocError("The format '{0!s}' is not available in SDoc. Set another in config file '{1!s}'"
158
                            .format(target_format, self._config_path))
159
160 1
        if not target_format:
161
            raise SDocError("Option 'format' in section 'sdoc' not set in config file '{0!s}'"
162
                            .format(self._config_path))
163
164
        # Read the class name for formatting the SDoc2 nodes into the target format.
165 1
        section = 'format_' + target_format
166 1
        class_name = config.get(section, 'class', fallback=None)
167 1
        if not class_name:
168
            raise SDocError("Option 'class' in section '{0!s}' not set in config file '{1!s}'".
169
                            format(section, self._config_path))
170
171
        # Import the class.
172 1
        try:
173 1
            parts = class_name.split('.')
174 1
            module = ".".join(parts[:-1])
175 1
            __import__(module)
176 1
            m = __import__(module)
177 1
            for comp in parts[1:]:
178 1
                m = getattr(m, comp)
179
        except AttributeError:
180
            raise SDocError("There is no module named '{0!s}'! Set name correctly in config file '{1!s}'"
181
                            .format(class_name, self._config_path))
182
183
        # Create the formatter.
184 1
        self._format = m(self._io, config[section])
185
186
    # ------------------------------------------------------------------------------------------------------------------
187 1
    def _config_set_temp_dir(self, config):
188
        """
189
        Reads the directory for storing temporary files.
190
191
        :param configparser.ConfigParser config: The config parser.
192
        """
193 1
        self._temp_dir = config.get('sdoc', 'temp_dir', fallback=self._temp_dir)
194
195 1
        if not self._temp_dir:
196
            raise SDocError("Option 'temp_dir' in section 'sdoc' not set correctly in config file '{0!s}'".
197
                            format(self._config_path))
198
199 1
        if not os.access(self._temp_dir, os.W_OK):
200
            raise SDocError("Directory '{0!s}' is not writable".format(self._temp_dir))
201
202
    # ------------------------------------------------------------------------------------------------------------------
203 1
    def _config_set_target_dir(self, config):
204
        """
205
        Reads the directory where the document in the target format must be created.
206
207
        :param configparser.ConfigParser config: The config parser.
208
        """
209 1
        self._target_dir = config.get('sdoc', 'target_dir', fallback=self._target_dir)
210
211 1
        if not self._target_dir:
212
            raise SDocError("Option 'target_dir' in section 'sdoc' not set correctly in config file '{0!s}'".
213
                            format(self._config_path))
214
215 1
        if not os.access(self._target_dir, os.W_OK):
216
            raise SDocError("Directory '{0!s}' is not writable".format(self._target_dir))
217
218
    # ------------------------------------------------------------------------------------------------------------------
219 1
    def _read_config_file(self):
220
        """
221
        Reads the configuration file.
222
        """
223 1
        config = configparser.ConfigParser()
224 1
        config.read(self._config_path)
225
226
        # Get the temp and target directory.
227 1
        self._config_set_temp_dir(config)
228 1
        self._config_set_target_dir(config)
229
230
        # Create the formatter for generating the document in the target format.
231 1
        self._config_create_formatter(config)
232
233 1
        self._formatter_paths.append(os.path.dirname(__file__) + '/sdoc2/formatter')
234 1
        self._nodes_paths.append(os.path.dirname(__file__) + '/sdoc2/node')
235
236
    # ------------------------------------------------------------------------------------------------------------------
237 1
    def _create_node_store(self):
238
        """
239
        Creates the node store (for storing nodes).
240
        """
241 1
        sdoc2.node_store = NodeStore(self._io)
242
243
    # ------------------------------------------------------------------------------------------------------------------
244 1
    @staticmethod
245
    def importing(path):
246
        """
247
        Imports modules from specific path.
248
249
        :param str path: The specific path.
250
        """
251 1
        modules = os.listdir(os.path.dirname(__file__) + path)
252
253 1
        path = path.replace('/', '.')
254
255 1
        for module in modules:
256 1
            if module != '__init__.py' and module[-3:] == '.py':
257 1
                __import__('sdoc' + path + module[:-3], locals(), globals())
258
259
    # ------------------------------------------------------------------------------------------------------------------
260 1
    def _import_nodes(self):
261
        """
262
        Imports nodes from path which is declared below.
263
        """
264
        # @todo improve
265 1
        self.importing('/sdoc2/node/')
266
267
    # ------------------------------------------------------------------------------------------------------------------
268 1
    def _import_formatters(self):
269
        """
270
        Imports formatters from path which is declared below.
271
        """
272
        # @todo improve
273 1
        self.importing('/sdoc2/formatter/html/')
274
275
    # ------------------------------------------------------------------------------------------------------------------
276 1
    def init(self):
0 ignored issues
show
Coding Style introduced by
This method should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
277 1
        self._read_config_file()
278 1
        self._create_node_store()
279 1
        self._import_nodes()
280 1
        self._import_formatters()
281
282
    # ------------------------------------------------------------------------------------------------------------------
283 1
    def run_sdoc1(self, sdoc1_path, sdoc2_path, log_errors=True):
284
        """
285
        Run the SDoc1 parser.
286
287
        :param str sdoc1_path: The path of the SDoc1 document.
288
        :param str sdoc2_path: The path were the the SDoc2 document mut be stored.
289
        :param bool log_errors: If true the number of errors will be logged.
290
        """
291 1
        self._io.title('SDoc1')
292
293 1
        interpreter1 = SDoc1Interpreter(self._io)
294 1
        self._errors += interpreter1.process(sdoc1_path, sdoc2_path)
295
296 1
        if log_errors and self._errors:
297
            self._io.writeln(" ")
298
            self._io.title('Errors')
299
            self._io.error('There were {0} errors in total'.format(self._errors))
300
301 1
        return self._errors
302
303
    # ------------------------------------------------------------------------------------------------------------------
304 1
    def run_sdoc2(self, sdoc2_path, log_errors=True):
305
        """
306
        Run the SDoc2 parser.
307
308
        :param str sdoc2_path: The path of the SDoc2 document.
309
        :param bool log_errors: If true the number of errors will be logged.
310
        """
311 1
        self._io.writeln('')
312 1
        self._io.title('SDoc2')
313
314 1
        interpreter2 = SDoc2Interpreter(self._io)
315 1
        self._errors += interpreter2.process(sdoc2_path)
316
317 1
        if log_errors and self._errors:
318
            self._io.writeln(" ")
319
            self._io.title('Errors')
320
            self._io.error('There were {0} errors in total'.format(self._errors))
321
322 1
        return self._errors
323
324
    # ------------------------------------------------------------------------------------------------------------------
325 1
    def run_format(self, log_errors=True):
326
        """
327
        Generates the target document in the specific format.
328
329
        :param bool log_errors: If true the number of errors will be logged.
330
        """
331 1
        self._io.writeln('')
332 1
        self._io.title('Format')
333
334 1
        self._errors += sdoc2.node_store.generate(self._format)
335
336 1
        if log_errors and self._errors:
337
            self._io.writeln(" ")
338
            self._io.title('Errors')
339
            self._io.error('There were {0} errors in total'.format(self._errors))
340
341 1
        return self._errors
342
343
    # ------------------------------------------------------------------------------------------------------------------
344 1
    def run_sdoc(self, main_filename, log_errors=True):
345
        """
346
        Runs the SDoc1 and SDoc2 parser.
347
348
        :param str main_filename: The path of the SDoc1 document.
349
        :param bool log_errors: If true the number of errors will be logged.
350
        """
351 1
        self.init()
352
353 1
        temp_filename = self._temp_dir + '/' + os.path.basename(main_filename) + '.sdoc2'
354 1
        self.run_sdoc1(main_filename, temp_filename, False)
355 1
        self.run_sdoc2(temp_filename, False)
356 1
        self.run_format(False)
357
358 1
        if log_errors and self._errors:
359 1
            self._io.writeln(" ")
360 1
            self._io.title('Errors')
361 1
            self._io.error('There were {0} errors in total'.format(self._errors))
362
363 1
        return self._errors
364
365
# ----------------------------------------------------------------------------------------------------------------------
366