|
1
|
|
|
# -*- coding: utf-8 -*- |
|
2
|
|
|
|
|
3
|
1 |
|
import six |
|
4
|
|
|
|
|
5
|
1 |
|
from abc import ABCMeta, abstractmethod |
|
6
|
|
|
|
|
7
|
|
|
|
|
8
|
1 |
|
class SectionParser(six.with_metaclass(ABCMeta)): |
|
9
|
|
|
""" |
|
10
|
|
|
Abstract base parser of a single section in a mission file. |
|
11
|
|
|
|
|
12
|
|
|
A common approach to parse a section can be described in the following way: |
|
13
|
|
|
|
|
14
|
|
|
#. Pass a section name (e.g. 'MAIN') to :meth:`start` method. If parser can |
|
15
|
|
|
process a section with such name, it will return `True` and then you can |
|
16
|
|
|
proceed. |
|
17
|
|
|
#. Pass section lines one-by-one to :meth:`parse_line`. |
|
18
|
|
|
#. When you are done, get your parsed data by calling :meth:`stop`. This |
|
19
|
|
|
will tell the parser that no more data will be given and the parsing can |
|
20
|
|
|
be finished. |
|
21
|
|
|
|
|
22
|
|
|
| |
|
23
|
|
|
**Example**: |
|
24
|
|
|
|
|
25
|
|
|
.. code-block:: python |
|
26
|
|
|
|
|
27
|
|
|
section_name = "Test section" |
|
28
|
|
|
lines = ["foo", "bar", "baz", "qux", ] |
|
29
|
|
|
parser = SomeParser() |
|
30
|
|
|
|
|
31
|
|
|
if parser.start(section_name): |
|
32
|
|
|
for line in lines: |
|
33
|
|
|
parser.parse_line(line) |
|
34
|
|
|
result = parser.stop() |
|
35
|
|
|
|
|
36
|
|
|
""" |
|
37
|
|
|
#: Tells whether a parser was started. |
|
38
|
1 |
|
running = False |
|
39
|
|
|
|
|
40
|
|
|
#: An internal buffer which can be redefined. |
|
41
|
1 |
|
data = None |
|
42
|
|
|
|
|
43
|
1 |
|
def start(self, section_name): |
|
44
|
|
|
""" |
|
45
|
|
|
Try to start a parser. If a section with given name can be parsed, the |
|
46
|
|
|
parser will initialize it's internal data structures and set |
|
47
|
|
|
:attr:`running` to `True`. |
|
48
|
|
|
|
|
49
|
|
|
:param str section_name: a name of section which is going to be parsed |
|
50
|
|
|
|
|
51
|
|
|
:returns: `True` if section with a given name can be parsed by parser, |
|
52
|
|
|
`False` otherwise |
|
53
|
|
|
:rtype: :class:`bool` |
|
54
|
|
|
""" |
|
55
|
1 |
|
result = self.check_section_name(section_name) |
|
56
|
1 |
|
if result: |
|
57
|
1 |
|
self.running = True |
|
58
|
1 |
|
self.init_parser(section_name) |
|
59
|
1 |
|
return result |
|
60
|
|
|
|
|
61
|
1 |
|
@abstractmethod |
|
62
|
|
|
def check_section_name(self, section_name): |
|
63
|
|
|
""" |
|
64
|
|
|
Check whether a section with a given name can be parsed. |
|
65
|
|
|
|
|
66
|
|
|
:param str section_name: a name of section which is going to be parsed |
|
67
|
|
|
|
|
68
|
|
|
:returns: `True` if section with a given name can be parsed by parser, |
|
69
|
|
|
`False` otherwise |
|
70
|
|
|
:rtype: :class:`bool` |
|
71
|
|
|
""" |
|
72
|
|
|
|
|
73
|
1 |
|
@abstractmethod |
|
74
|
|
|
def init_parser(self, section_name): |
|
75
|
|
|
""" |
|
76
|
|
|
Abstract method which is called by :meth:`start` to initialize |
|
77
|
|
|
internal data structures. |
|
78
|
|
|
|
|
79
|
|
|
:param str section_name: a name of section which is going to be parsed |
|
80
|
|
|
|
|
81
|
|
|
:returns: ``None`` |
|
82
|
|
|
""" |
|
83
|
|
|
|
|
84
|
1 |
|
@abstractmethod |
|
85
|
|
|
def parse_line(self, line): |
|
86
|
|
|
""" |
|
87
|
|
|
Abstract method which is called manually to parse a line from mission |
|
88
|
|
|
section. |
|
89
|
|
|
|
|
90
|
|
|
:param str line: a single line to parse |
|
91
|
|
|
|
|
92
|
|
|
:returns: ``None`` |
|
93
|
|
|
""" |
|
94
|
|
|
|
|
95
|
1 |
|
def stop(self): |
|
96
|
|
|
""" |
|
97
|
|
|
Stops parser and returns fully processed data. |
|
98
|
|
|
|
|
99
|
|
|
:returns: a data structure returned by :meth:`clean` method |
|
100
|
|
|
|
|
101
|
|
|
:raises RuntimeError: if parser was not started |
|
102
|
|
|
""" |
|
103
|
1 |
|
if not self.running: |
|
104
|
1 |
|
raise RuntimeError("Cannot stop parser which is not running") |
|
105
|
|
|
|
|
106
|
1 |
|
self.running = False |
|
107
|
1 |
|
return self.clean() |
|
108
|
|
|
|
|
109
|
1 |
|
def clean(self): |
|
110
|
|
|
""" |
|
111
|
|
|
Returns fully parsed data. Is called by :meth:`stop` method. |
|
112
|
|
|
|
|
113
|
|
|
:returns: a data structure which is specific for every subclass |
|
114
|
|
|
""" |
|
115
|
1 |
|
return self.data |
|
116
|
|
|
|
|
117
|
|
|
|
|
118
|
1 |
|
class ValuesParser(six.with_metaclass(ABCMeta, SectionParser)): |
|
119
|
|
|
""" |
|
120
|
|
|
This is a base class for parsers which assume that a section, which is |
|
121
|
|
|
going to be parsed, consists of key-value pairs with unique keys, one pair |
|
122
|
|
|
per line. |
|
123
|
|
|
|
|
124
|
|
|
**Section definition example**:: |
|
125
|
|
|
|
|
126
|
|
|
[section name] |
|
127
|
|
|
key1 value1 |
|
128
|
|
|
key2 value2 |
|
129
|
|
|
key3 value3 |
|
130
|
|
|
""" |
|
131
|
|
|
|
|
132
|
1 |
|
def init_parser(self, section_name): |
|
133
|
|
|
""" |
|
134
|
|
|
Implements abstract method. See :meth:`SectionParser.init_parser` for |
|
135
|
|
|
semantics. |
|
136
|
|
|
|
|
137
|
|
|
Initializes a dictionary to store raw keys and their values. |
|
138
|
|
|
""" |
|
139
|
1 |
|
self.data = {} |
|
140
|
|
|
|
|
141
|
1 |
|
def parse_line(self, line): |
|
142
|
|
|
""" |
|
143
|
|
|
Implements abstract method. See :meth:`SectionParser.parse_line` for |
|
144
|
|
|
semantics. |
|
145
|
|
|
|
|
146
|
|
|
Splits line into key-value pair and puts it into internal dictionary. |
|
147
|
|
|
""" |
|
148
|
1 |
|
key, value = line.strip().split() |
|
149
|
1 |
|
self.data.update({key: value}) |
|
150
|
|
|
|
|
151
|
|
|
|
|
152
|
1 |
|
class CollectingParser(six.with_metaclass(ABCMeta, SectionParser)): |
|
153
|
|
|
""" |
|
154
|
|
|
This is a base class for parsers which assume that a section, which is |
|
155
|
|
|
going to be parsed, consists of homogeneous lines which describe different |
|
156
|
|
|
objects with one set of attributes. |
|
157
|
|
|
|
|
158
|
|
|
**Section definition example**:: |
|
159
|
|
|
|
|
160
|
|
|
[section name] |
|
161
|
|
|
object1_attr1 object1_attr2 object1_attr3 object1_attr4 |
|
162
|
|
|
object2_attr1 object2_attr2 object2_attr3 object2_attr4 |
|
163
|
|
|
object3_attr1 object3_attr2 object3_attr3 object3_attr4 |
|
164
|
|
|
""" |
|
165
|
|
|
|
|
166
|
1 |
|
def init_parser(self, section_name): |
|
167
|
|
|
""" |
|
168
|
|
|
Implements abstract method. See :meth:`SectionParser.init_parser` for |
|
169
|
|
|
semantics. |
|
170
|
|
|
|
|
171
|
|
|
Initializes a list for storing collection of objects. |
|
172
|
|
|
""" |
|
173
|
1 |
|
self.data = [] |
|
174
|
|
|
|
|
175
|
1 |
|
def parse_line(self, line): |
|
176
|
|
|
""" |
|
177
|
|
|
Implements abstract method. See :meth:`SectionParser.parse_line` for |
|
178
|
|
|
semantics. |
|
179
|
|
|
|
|
180
|
|
|
Just puts entire line to internal buffer. You probably will want to |
|
181
|
|
|
redefine this method to do some extra job on each line. |
|
182
|
|
|
""" |
|
183
|
|
|
self.data.append(line.strip()) |
|
184
|
|
|
|