Completed
Push — master ( 57e7bf...8a74a7 )
by P.R.
01:36
created

sdoc.SDoc   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 356
Duplicated Lines 0 %
Metric Value
dl 0
loc 356
rs 9.2
wmc 34

18 Methods

Rating   Name   Duplication   Size   Complexity  
A SDoc._config_set_temp_dir() 0 14 3
A SDoc.run_sdoc1() 0 10 1
A SDoc._read_config_file() 0 16 1
B SDoc._config_create_formatter() 0 39 6
A SDoc._import_formatters() 0 3 1
B SDoc.test_sdoc2() 32 32 2
A SDoc.temp_dir() 0 8 1
A SDoc.importing() 0 14 4
B SDoc.test_sdoc1() 27 27 2
B SDoc.__init__() 0 45 1
A SDoc._create_node_store() 0 6 1
A SDoc._config_set_target_dir() 0 14 3
B SDoc._parse_arguments() 0 26 1
A SDoc.main() 0 22 2
A SDoc._run_sdoc() 0 17 2
A SDoc.run_sdoc2() 0 12 1
A SDoc.target_dir() 0 8 1
A SDoc._import_nodes() 0 3 1
1
"""
2
SDoc
3
4
Copyright 2016 Set Based IT Consultancy
5
6
Licence MIT
7
"""
8
# ----------------------------------------------------------------------------------------------------------------------
9
import argparse
10
import configparser
11
import os
12
13
import sys
14
from io import StringIO
15
16
import sdoc
17
from sdoc.error import SDocError
18
from sdoc.sdoc1.SDoc1Interpreter import SDoc1Interpreter
19
from sdoc.sdoc2.NodeStore import NodeStore
20
from sdoc.sdoc2.SDoc2Interpreter import SDoc2Interpreter
21
22
23
class SDoc:
24
    """
25
    The SDoc program.
26
    """
27
28
    # ------------------------------------------------------------------------------------------------------------------
29
    def __init__(self):
30
        """
31
        Object contructor.
32
        """
33
        self._args = None
34
        """
35
        The parsed arguments of this program.
36
37
        :type: Namespace
38
        """
39
40
        self._formatter = None
41
        """
42
        The class for generation the document in the target format.
43
44
        :type: sdoc.format.Format.Format
45
        """
46
47
        self._target_dir = '.'
48
        """
49
        The directory where the document in the target format must be created.
50
51
        :type: str
52
        """
53
54
        self._temp_dir = '.'
55
        """
56
        The directory where temporary files are stored.
57
58
        :type: str
59
        """
60
61
        self._nodes_paths = []
62
        """
63
        A list with path names from with node modules must be imported.
64
65
        :type: [str]
66
        """
67
68
        self._formatter_paths = []
69
        """
70
        A list with path names from with node modules must be imported.
71
72
        :type: [str]
73
        """
74
75
    # ------------------------------------------------------------------------------------------------------------------
76
    @property
77
    def target_dir(self):
78
        """
79
        Getter for target_dir.
80
81
        :rtype: str
82
        """
83
        return self.target_dir
84
85
    # ------------------------------------------------------------------------------------------------------------------
86
    @property
87
    def temp_dir(self):
88
        """
89
        Getter for temp_dir.
90
91
        :rtype: str
92
        """
93
        return self.temp_dir
94
95
    # ------------------------------------------------------------------------------------------------------------------
96
    def _parse_arguments(self):
97
        """
98
        Parses the arguments for SDoc program.
99
        """
100
        parser = argparse.ArgumentParser(description='Description')
101
102
        parser.add_argument(metavar='[main.sdoc]',
103
                            nargs=1,
104
                            dest='main',
105
                            help='path to main SDoc document')
106
107
        parser.add_argument('-c',
108
                            '--config',
109
                            metavar='<config.cfg>',
110
                            required=True,
111
                            dest='config_filename',
112
                            help='path to configuration file')
113
114
        parser.add_argument('-1',
115
                            '--sdoc1-only',
116
                            action='store_true',
117
                            dest='sdoc1-only',
118
                            default=False,
119
                            help='runs only the SDoc1 parser')
120
121
        self._args = parser.parse_args()
122
123
    # ------------------------------------------------------------------------------------------------------------------
124
    def _config_create_formatter(self, config):
125
        """
126
        Creates the formatter for generating the document in the target format.
127
128
        :param configparser.ConfigParser config: The config parser.
129
        """
130
        available_formats = ['html']
131
132
        # Read the target format of the document.
133
        target_format = config.get('sdoc', 'format', fallback=None)
134
        if target_format not in available_formats:
135
            raise SDocError("The format '{0!s}' is not available in SDoc. Set another in config file '{1!s}'".format(
136
                    target_format, self._args.config_filename))
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation.
target_format, self._args.config_filename))
| ^
Loading history...
137
138
        if not target_format:
139
            raise SDocError("Option 'format' in section 'sdoc' not set in config file '{0!s}'"
140
                            .format(self._args.config_filename))
141
142
        # Read the class name for formatting the SDoc2 nodes into the target format.
143
        section = 'format_' + target_format
144
        class_name = config.get(section, 'class', fallback=None)
145
        if not class_name:
146
            raise SDocError("Option 'class' in section '{0!s}' not set in config file '{1!s}'".
147
                            format(section, self._args.config_filename))
148
149
        # Import the class.
150
        try:
151
            parts = class_name.split('.')
152
            module = ".".join(parts[:-1])
153
            __import__(module)
154
            m = __import__(module)
155
            for comp in parts[1:]:
156
                m = getattr(m, comp)
157
        except AttributeError:
158
            raise SDocError("There is no module named '{0!s}'! Set name correctly in config file '{1!s}'"
159
                            .format(class_name, self._args.config_filename))
160
161
        # Create the formatter.
162
        self._formatter = m(config[section])
163
164
    # ------------------------------------------------------------------------------------------------------------------
165
    def _config_set_temp_dir(self, config):
166
        """
167
        Reads the directory for storing temporary files.
168
169
        :param configparser.ConfigParser config: The config parser.
170
        """
171
        self._temp_dir = config.get('sdoc', 'temp_dir', fallback=self._temp_dir)
172
173
        if not self._temp_dir:
174
            raise SDocError("Option 'temp_dir' in section 'sdoc' not set correctly in config file '{0!s}'".
175
                            format(self._args.config_filename))
176
177
        if not os.access(self._temp_dir, os.W_OK):
178
            raise SDocError("Directory '{0!s}' is not writable".format(self._temp_dir))
179
180
    # ------------------------------------------------------------------------------------------------------------------
181
    def _config_set_target_dir(self, config):
182
        """
183
        Reads the directory where the document in the target format must be created.
184
185
        :param configparser.ConfigParser config: The config parser.
186
        """
187
        self._target_dir = config.get('sdoc', 'target_dir', fallback=self._target_dir)
188
189
        if not self._target_dir:
190
            raise SDocError("Option 'target_dir' in section 'sdoc' not set correctly in config file '{0!s}'".
191
                            format(self._args.config_filename))
192
193
        if not os.access(self._target_dir, os.W_OK):
194
            raise SDocError("Directory '{0!s}' is not writable".format(self._target_dir))
195
196
    # ------------------------------------------------------------------------------------------------------------------
197
    def _read_config_file(self):
198
        """
199
        Reads the configuration file.
200
        """
201
        config = configparser.ConfigParser()
202
        config.read(self._args.config_filename)
203
204
        # Get the temp and target directory.
205
        self._config_set_temp_dir(config)
206
        self._config_set_target_dir(config)
207
208
        # Create the formatter for generating the document in the target format.
209
        self._config_create_formatter(config)
210
211
        self._formatter_paths.append(os.path.dirname(__file__) + '/sdoc2/formatter')
212
        self._nodes_paths.append(os.path.dirname(__file__) + '/sdoc2/node')
213
214
    # ------------------------------------------------------------------------------------------------------------------
215
    @staticmethod
216
    def _create_node_store():
217
        """
218
        Creates the node store (for storing nodes).
219
        """
220
        sdoc.sdoc2.node_store = NodeStore()
221
222
    # ------------------------------------------------------------------------------------------------------------------
223
    @staticmethod
224
    def importing(path):
225
        """
226
        Imports modules from specific path.
227
228
        :param str path: The specific path.
229
        """
230
        modules = os.listdir(os.path.dirname(__file__) + path)
231
232
        path = path.replace('/', '.')
233
234
        for module in modules:
235
            if module != '__init__.py' and module[-3:] == '.py':
236
                __import__('sdoc' + path + module[:-3], locals(), globals())
237
238
    # ------------------------------------------------------------------------------------------------------------------
239
    def _import_nodes(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...
240
        # @todo improve
241
        self.importing('/sdoc2/node/')
242
243
    # ------------------------------------------------------------------------------------------------------------------
244
    def _import_formatters(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...
245
        # @todo improve
246
        self.importing('/sdoc2/formatter/html/')
247
248
    # ------------------------------------------------------------------------------------------------------------------
249
    @staticmethod
250
    def run_sdoc1(main_filename, temp_filename):
251
        """
252
        Run the SDoc1 parser.
253
254
        :param str main_filename: The name of the file with then main SDoc1 document.
255
        :param str temp_filename: The name of the temporary file where the SDoc2 document must be stored.
256
        """
257
        interpreter1 = SDoc1Interpreter()
258
        interpreter1.process(main_filename, temp_filename)
259
260
    # ------------------------------------------------------------------------------------------------------------------
261
    @staticmethod
262
    def run_sdoc2(temp_filename, one_file, file_per_chapter):
263
        """
264
        Run the SDoc2 parser.
265
266
        :param str temp_filename: The name of the temporary file where the SDoc2 document is stored.
267
        :param bool one_file: Info about generating one output file per one source file.
268
        :param bool file_per_chapter: Info about generating one output file per on chapter of source file.
269
        """
270
        interpreter2 = SDoc2Interpreter()
271
        interpreter2.process(temp_filename)
272
        sdoc.sdoc2.node_store.generate(one_file, file_per_chapter)
273
274
    # ------------------------------------------------------------------------------------------------------------------
275
    def _run_sdoc(self):
276
        """
277
        Runs the SDoc1 and SDoc2 parser.
278
        """
279
        main_filename = self._args.main[0]
280
        temp_filename = self._temp_dir + '/' + os.path.basename(main_filename) + '.sdoc2'
281
282
        self.run_sdoc1(main_filename, temp_filename)
283
284
        self.run_sdoc2(temp_filename, self._formatter.one_file, self._formatter.file_per_chapter)
285
286
        # Activate numbering nodes.
287
        if self._formatter.enumerate:
288
            sdoc.sdoc2.node_store.number_numerable()
289
290
        # Start generating file with specific parameters.
291
        sdoc.sdoc2.node_store.generate(self._formatter.one_file, self._formatter.file_per_chapter)
292
293
    # ------------------------------------------------------------------------------------------------------------------
294 View Code Duplication
    def test_sdoc1(self, main_filename):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
295
        """
296
        Parses a SDoc document and returns a tuple with the stdout and the resulting SDoc2 document.
297
298
        :param str main_filename: The name of the file with then main SDoc1 document.
299
300
        :rtype: (str,str)
301
        """
302
        self._create_node_store()
303
        self._import_nodes()
304
        self._import_formatters()
305
306
        old_stdout, sys.stdout = sys.stdout, StringIO()
307
308
        temp_filename = main_filename + '.sdoc2'
309
        interpreter1 = SDoc1Interpreter()
310
        interpreter1.process(main_filename, temp_filename)
311
312
        output = sys.stdout.getvalue().strip()
313
        sys.stdout = old_stdout
314
315
        with open(temp_filename, 'rt') as fd:
316
            sdoc2 = fd.read()
317
318
        os.unlink(temp_filename)
319
320
        return output, sdoc2
321
322
    # ------------------------------------------------------------------------------------------------------------------
323 View Code Duplication
    def test_sdoc2(self, main_filename):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
324
        """
325
        Parses a SDoc document and returns a tuple with the stdout and the resulting SDoc2 document.
326
327
        :param str main_filename: The name of the file with then main SDoc1 document.
328
329
        :rtype: (str,str)
330
        """
331
        self._create_node_store()
332
        self._import_nodes()
333
        self._import_formatters()
334
335
        old_stdout, sys.stdout = sys.stdout, StringIO()
336
337
        temp_filename = main_filename + '.sdoc2'
338
        interpreter1 = SDoc1Interpreter()
339
        interpreter1.process(main_filename, temp_filename)
340
341
        interpreter2 = SDoc2Interpreter()
342
        interpreter2.process(temp_filename)
343
344
        sdoc.sdoc2.node_store.number_numerable()
345
346
        output = sys.stdout.getvalue().strip()
347
        sys.stdout = old_stdout
348
349
        with open(temp_filename, 'rt') as fd:
350
            sdoc2 = fd.read()
351
352
        os.unlink(temp_filename)
353
354
        return output, sdoc2
355
356
    # ------------------------------------------------------------------------------------------------------------------
357
    def main(self):
358
        """
359
        The main function the SDoc program.
360
        """
361
        try:
362
            self._parse_arguments()
363
364
            self._read_config_file()
365
366
            self._create_node_store()
367
368
            self._import_nodes()
369
370
            self._import_formatters()
371
372
            self._run_sdoc()
373
374
            exit(0)
375
376
        except RuntimeError as err:
377
            print(err)
378
            exit(-1)
379
380
# ----------------------------------------------------------------------------------------------------------------------
381