Completed
Push — master ( 5a409f...256abe )
by P.R.
02:05
created

SDoc.run_sdoc()   A

Complexity

Conditions 2

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

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