Browse Source

xml validator for unit and team stashs

Bastien Sevajol 5 years ago
parent
commit
2cf75d0155

+ 2 - 0
config.yaml View File

27
     state_template: "opencombat/state_template.xml"
27
     state_template: "opencombat/state_template.xml"
28
     unit_stash: "opencombat.strategy.unit.stash.UnitStash"
28
     unit_stash: "opencombat.strategy.unit.stash.UnitStash"
29
     team_stash: "opencombat.strategy.team.stash.TeamStash"
29
     team_stash: "opencombat.strategy.team.stash.TeamStash"
30
+    teams_schema: "opencombat/strategy/teams.xsd"
31
+    units_schema: "opencombat/strategy/units.xsd"
30
     cache_dir_path: 'cache'
32
     cache_dir_path: 'cache'
31
     include_path:
33
     include_path:
32
       maps:
34
       maps:

+ 11 - 60
opencombat/state.py View File

7
 from synergine2.config import Config
7
 from synergine2.config import Config
8
 from synergine2.log import get_logger
8
 from synergine2.log import get_logger
9
 
9
 
10
-from opencombat.exception import StateLoadError
11
 from opencombat.exception import NotFoundError
10
 from opencombat.exception import NotFoundError
12
 from opencombat.simulation.base import TileStrategySimulation
11
 from opencombat.simulation.base import TileStrategySimulation
13
 from opencombat.simulation.subject import TileSubject
12
 from opencombat.simulation.subject import TileSubject
14
 from opencombat.util import get_class_from_string_path
13
 from opencombat.util import get_class_from_string_path
15
 from opencombat.util import pretty_xml
14
 from opencombat.util import pretty_xml
16
 from opencombat.util import get_text_xml_element
15
 from opencombat.util import get_text_xml_element
16
+from opencombat.xml import XmlValidator
17
 
17
 
18
 
18
 
19
 class State(object):
19
 class State(object):
195
         self._config = config
195
         self._config = config
196
         self._simulation = simulation
196
         self._simulation = simulation
197
 
197
 
198
+        schema_file_path = self._config.get(
199
+            'global.state_schema',
200
+            'opencombat/state.xsd',
201
+        )
202
+        self._xml_validator = XmlValidator(
203
+            config,
204
+            schema_file_path,
205
+        )
206
+
198
     def get_state(
207
     def get_state(
199
         self,
208
         self,
200
         state_file_path: str,
209
         state_file_path: str,
209
         self,
218
         self,
210
         state_file_path: str,
219
         state_file_path: str,
211
     ) -> Element:
220
     ) -> Element:
212
-        # open and read schema file
213
-        schema_file_path = self._config.get(
214
-            'global.state_schema',
215
-            'opencombat/state.xsd',
216
-        )
217
-        with open(schema_file_path, 'r') as schema_file:
218
-            schema_to_check = schema_file.read()
219
-
220
-        # open and read xml file
221
-        with open(state_file_path, 'r') as xml_file:
222
-            xml_to_check = xml_file.read()
223
-
224
-        xmlschema_doc = etree.fromstring(schema_to_check.encode('utf-8'))
225
-        xmlschema = etree.XMLSchema(xmlschema_doc)
226
-
227
-        try:
228
-            doc = etree.fromstring(xml_to_check.encode('utf-8'))
229
-        # check for file IO error
230
-        except IOError as exc:
231
-            self._logger.error(exc)
232
-            raise StateLoadError('Invalid File "{}": {}'.format(
233
-                state_file_path,
234
-                str(exc),
235
-            ))
236
-        # check for XML syntax errors
237
-        except etree.XMLSyntaxError as exc:
238
-            self._logger.error(exc)
239
-            raise StateLoadError('XML Syntax Error in "{}": {}'.format(
240
-                state_file_path,
241
-                str(exc.error_log),
242
-            ))
243
-        except Exception as exc:
244
-            self._logger.error(exc)
245
-            raise StateLoadError('Unknown error with "{}": {}'.format(
246
-                state_file_path,
247
-                str(exc),
248
-            ))
249
-
250
-        # validate against schema
251
-        try:
252
-            xmlschema.assertValid(doc)
253
-        except etree.DocumentInvalid as exc:
254
-            self._logger.error(exc)
255
-            raise StateLoadError(
256
-                'Schema validation error with "{}": {}'.format(
257
-                    state_file_path,
258
-                    str(exc),
259
-                )
260
-            )
261
-        except Exception as exc:
262
-            self._logger.error(exc)
263
-            raise StateLoadError(
264
-                'Unknown validation error with "{}": {}'.format(
265
-                    state_file_path,
266
-                    str(exc),
267
-                )
268
-            )
269
-
270
-        return doc
221
+        return self._xml_validator.validate_and_return(state_file_path)
271
 
222
 
272
 
223
 
273
 class StateConstructorBuilder(object):
224
 class StateConstructorBuilder(object):

+ 11 - 2
opencombat/strategy/team/stash.py View File

4
 from synergine2.config import Config
4
 from synergine2.config import Config
5
 
5
 
6
 from opencombat.strategy.unit.model import TeamModel
6
 from opencombat.strategy.unit.model import TeamModel
7
+from opencombat.xml import XmlValidator
7
 
8
 
8
 
9
 
9
 class TeamStash(object):
10
 class TeamStash(object):
12
         config: Config,
13
         config: Config,
13
         teams_file_path: str,
14
         teams_file_path: str,
14
     ) -> None:
15
     ) -> None:
15
-        self._confg = config
16
-        # TODO Load xml, validate
16
+        self._config = config
17
         self._teams = None  # typing.List[TeamModel]
17
         self._teams = None  # typing.List[TeamModel]
18
 
18
 
19
+        schema_file_path = self._config.get(
20
+            'global.teams_schema',
21
+            'opencombat/strategy/teams.xsd',
22
+        )
23
+        self._xml_validator = XmlValidator(
24
+            config,
25
+            schema_file_path,
26
+        )
27
+
19
     def get_teams(self) -> typing.List[TeamModel]:
28
     def get_teams(self) -> typing.List[TeamModel]:
20
         pass
29
         pass
21
 
30
 

+ 8 - 3
opencombat/strategy/teams.xsd View File

1
 <?xml version="1.0" encoding="UTF-8" ?>
1
 <?xml version="1.0" encoding="UTF-8" ?>
2
 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
2
 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3
-    <xs:complexType name="teams">
3
+
4
+    <xs:element name="teams" type="teamstype"/>
5
+
6
+    <xs:complexType name="teamstype">
4
         <xs:sequence>
7
         <xs:sequence>
5
             <xs:element name="team" type="teamtype" maxOccurs="unbounded"/>
8
             <xs:element name="team" type="teamtype" maxOccurs="unbounded"/>
6
         </xs:sequence>
9
         </xs:sequence>
11
             <xs:element name="name" type="xs:string" maxOccurs="1"/>
14
             <xs:element name="name" type="xs:string" maxOccurs="1"/>
12
             <xs:element name="units" type="unitstype" maxOccurs="1"/>
15
             <xs:element name="units" type="unitstype" maxOccurs="1"/>
13
         </xs:sequence>
16
         </xs:sequence>
17
+        <xs:attribute name="id" type="xs:string" use="required"/>
18
+        <xs:attribute name="country" type="xs:string" use="required"/>
14
     </xs:complexType>
19
     </xs:complexType>
15
 
20
 
16
     <xs:complexType name="unitstype">
21
     <xs:complexType name="unitstype">
17
         <xs:sequence>
22
         <xs:sequence>
18
-            <xs:element name="unit" type="unittype" maxOccurs="unbounded"/>
23
+            <xs:element name="unit" type="unittype" maxOccurs="unbounded" minOccurs="1"/>
19
         </xs:sequence>
24
         </xs:sequence>
20
     </xs:complexType>
25
     </xs:complexType>
21
 
26
 
22
     <xs:complexType name="unittype">
27
     <xs:complexType name="unittype">
23
         <xs:sequence>
28
         <xs:sequence>
24
-            <xs:element name="id" type="xs:string" maxOccurs="1"/>
29
+            <xs:element name="id" type="xs:string" maxOccurs="1" minOccurs="1"/>
25
         </xs:sequence>
30
         </xs:sequence>
26
     </xs:complexType>
31
     </xs:complexType>
27
 
32
 

+ 11 - 2
opencombat/strategy/unit/stash.py View File

4
 from synergine2.config import Config
4
 from synergine2.config import Config
5
 
5
 
6
 from opencombat.strategy.team.model import UnitModel
6
 from opencombat.strategy.team.model import UnitModel
7
+from opencombat.xml import XmlValidator
7
 
8
 
8
 
9
 
9
 class UnitStash(object):
10
 class UnitStash(object):
12
         config: Config,
13
         config: Config,
13
         units_file_path: str,
14
         units_file_path: str,
14
     ) -> None:
15
     ) -> None:
15
-        self._confg = config
16
-        # TODO Load xml, validate
16
+        self._config = config
17
         self._units = None  # typing.List[UnitModel]
17
         self._units = None  # typing.List[UnitModel]
18
 
18
 
19
+        schema_file_path = self._config.get(
20
+            'global.teams_schema',
21
+            'opencombat/strategy/teams.xsd',
22
+        )
23
+        self._xml_validator = XmlValidator(
24
+            config,
25
+            schema_file_path,
26
+        )
27
+
19
     def get_units(self) -> typing.List[UnitModel]:
28
     def get_units(self) -> typing.List[UnitModel]:
20
         pass
29
         pass
21
 
30
 

+ 21 - 0
opencombat/strategy/units.xsd View File

1
+<?xml version="1.0" encoding="UTF-8" ?>
2
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3
+
4
+    <xs:element name="units" type="unitstype"/>
5
+
6
+    <xs:complexType name="unitstype">
7
+        <xs:sequence>
8
+            <xs:element name="unit" type="unittype" maxOccurs="unbounded" minOccurs="1"/>
9
+        </xs:sequence>
10
+    </xs:complexType>
11
+
12
+    <xs:complexType name="unittype">
13
+        <xs:sequence>
14
+            <xs:element name="name" type="xs:string" maxOccurs="1" minOccurs="1"/>
15
+            <xs:element name="type" type="xs:string" maxOccurs="1" minOccurs="1"/>
16
+        </xs:sequence>
17
+        <xs:attribute name="id" type="xs:string" use="required"/>
18
+        <xs:attribute name="country" type="xs:string" use="required"/>
19
+    </xs:complexType>
20
+
21
+</xs:schema>

+ 75 - 0
opencombat/xml.py View File

1
+# coding: utf-8
2
+from _elementtree import Element
3
+
4
+from lxml import etree
5
+from synergine2.config import Config
6
+from synergine2.log import get_logger
7
+
8
+from opencombat.exception import StateLoadError
9
+
10
+
11
+class XmlValidator(object):
12
+    def __init__(
13
+        self,
14
+        config: Config,
15
+        schema_file_path: str,
16
+    ) -> None:
17
+        self._config = config
18
+        self._logger = get_logger('XmlValidator', config)
19
+        self._schema_file_path = schema_file_path
20
+
21
+    def validate_and_return(self, xml_file_path: str) -> Element:
22
+        with open(self._schema_file_path, 'r') as schema_file:
23
+            schema_to_check = schema_file.read()
24
+
25
+        # open and read xml file
26
+        with open(xml_file_path, 'r') as xml_file:
27
+            xml_to_check = xml_file.read()
28
+
29
+        xmlschema_doc = etree.fromstring(schema_to_check.encode('utf-8'))
30
+        xmlschema = etree.XMLSchema(xmlschema_doc)
31
+
32
+        try:
33
+            doc = etree.fromstring(xml_to_check.encode('utf-8'))
34
+        # check for file IO error
35
+        except IOError as exc:
36
+            self._logger.error(exc)
37
+            raise StateLoadError('Invalid File "{}": {}'.format(
38
+                xml_file_path,
39
+                str(exc),
40
+            ))
41
+        # check for XML syntax errors
42
+        except etree.XMLSyntaxError as exc:
43
+            self._logger.error(exc)
44
+            raise StateLoadError('XML Syntax Error in "{}": {}'.format(
45
+                xml_file_path,
46
+                str(exc.error_log),
47
+            ))
48
+        except Exception as exc:
49
+            self._logger.error(exc)
50
+            raise StateLoadError('Unknown error in "{}": {}'.format(
51
+                xml_file_path,
52
+                str(exc),
53
+            ))
54
+
55
+        # validate against schema
56
+        try:
57
+            xmlschema.assertValid(doc)
58
+        except etree.DocumentInvalid as exc:
59
+            self._logger.error(exc)
60
+            raise StateLoadError(
61
+                'Schema validation error in "{}": {}'.format(
62
+                    xml_file_path,
63
+                    str(exc),
64
+                )
65
+            )
66
+        except Exception as exc:
67
+            self._logger.error(exc)
68
+            raise StateLoadError(
69
+                'Unknown validation error in "{}": {}'.format(
70
+                    xml_file_path,
71
+                    str(exc),
72
+                )
73
+            )
74
+
75
+        return doc

+ 8 - 0
test_config.yaml View File

21
       draw_interior_gap: 2
21
       draw_interior_gap: 2
22
 
22
 
23
 global:
23
 global:
24
+    state_loader: "opencombat.state.StateLoader"
25
+    state_dumper: "opencombat.state.StateDumper"
26
+    state_schema: "opencombat/state.xsd"
27
+    state_template: "opencombat/state_template.xml"
28
+    unit_stash: "opencombat.strategy.unit.stash.UnitStash"
29
+    team_stash: "opencombat.strategy.team.stash.TeamStash"
30
+    teams_schema: "opencombat/strategy/teams.xsd"
31
+    units_schema: "opencombat/strategy/units.xsd"
24
     cache_dir_path: 'cache'
32
     cache_dir_path: 'cache'
25
     include_path:
33
     include_path:
26
       maps:
34
       maps:

+ 0 - 0
tests/strategy/__init__.py View File


+ 13 - 0
tests/strategy/test_teams.py View File

1
+# coding: utf-8
2
+from synergine2.config import Config
3
+
4
+from opencombat.strategy.team.stash import TeamStash
5
+
6
+
7
+def test_units_stash__ok__instantiate(
8
+    config: Config,
9
+):
10
+    TeamStash(
11
+        config,
12
+        'opencombat/strategy/teams.xml',
13
+    )

+ 13 - 0
tests/strategy/test_units.py View File

1
+# coding: utf-8
2
+from synergine2.config import Config
3
+
4
+from opencombat.strategy.unit.stash import UnitStash
5
+
6
+
7
+def test_units_stash__ok__instantiate(
8
+    config: Config,
9
+):
10
+    UnitStash(
11
+        config,
12
+        'opencombat/strategy/units.xml',
13
+    )