pywargame 0.3.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. pywargame/__init__.py +2 -0
  2. pywargame/common/__init__.py +3 -0
  3. pywargame/common/collector.py +87 -0
  4. pywargame/common/dicedraw.py +363 -0
  5. pywargame/common/drawdice.py +40 -0
  6. pywargame/common/singleton.py +22 -0
  7. pywargame/common/test.py +25 -0
  8. pywargame/common/verbose.py +59 -0
  9. pywargame/common/verboseguard.py +53 -0
  10. pywargame/cyberboard/__init__.py +18 -0
  11. pywargame/cyberboard/archive.py +283 -0
  12. pywargame/cyberboard/base.py +63 -0
  13. pywargame/cyberboard/board.py +462 -0
  14. pywargame/cyberboard/cell.py +200 -0
  15. pywargame/cyberboard/collect.py +49 -0
  16. pywargame/cyberboard/collectgbx0pwd.py +30 -0
  17. pywargame/cyberboard/collectgbxext.py +30 -0
  18. pywargame/cyberboard/collectgsnexp.py +32 -0
  19. pywargame/cyberboard/collectgsnext.py +30 -0
  20. pywargame/cyberboard/draw.py +396 -0
  21. pywargame/cyberboard/exporter.py +1132 -0
  22. pywargame/cyberboard/extractor.py +240 -0
  23. pywargame/cyberboard/features.py +17 -0
  24. pywargame/cyberboard/gamebox.py +81 -0
  25. pywargame/cyberboard/gbxexp.py +76 -0
  26. pywargame/cyberboard/gbxext.py +64 -0
  27. pywargame/cyberboard/gsnexp.py +147 -0
  28. pywargame/cyberboard/gsnext.py +59 -0
  29. pywargame/cyberboard/head.py +111 -0
  30. pywargame/cyberboard/image.py +76 -0
  31. pywargame/cyberboard/main.py +47 -0
  32. pywargame/cyberboard/mark.py +102 -0
  33. pywargame/cyberboard/palette.py +36 -0
  34. pywargame/cyberboard/piece.py +169 -0
  35. pywargame/cyberboard/player.py +36 -0
  36. pywargame/cyberboard/scenario.py +115 -0
  37. pywargame/cyberboard/testgrid.py +156 -0
  38. pywargame/cyberboard/tile.py +121 -0
  39. pywargame/cyberboard/tray.py +68 -0
  40. pywargame/cyberboard/windows.py +41 -0
  41. pywargame/cyberboard/zeropwd.py +45 -0
  42. pywargame/cyberboard.py +2728 -0
  43. pywargame/gbx0pwd.py +2776 -0
  44. pywargame/gbxextract.py +2795 -0
  45. pywargame/gsnexport.py +16499 -0
  46. pywargame/gsnextract.py +2790 -0
  47. pywargame/latex/__init__.py +2 -0
  48. pywargame/latex/collect.py +34 -0
  49. pywargame/latex/latexexporter.py +4010 -0
  50. pywargame/latex/main.py +184 -0
  51. pywargame/vassal/__init__.py +66 -0
  52. pywargame/vassal/base.py +139 -0
  53. pywargame/vassal/board.py +243 -0
  54. pywargame/vassal/buildfile.py +60 -0
  55. pywargame/vassal/chart.py +79 -0
  56. pywargame/vassal/chessclock.py +197 -0
  57. pywargame/vassal/collect.py +98 -0
  58. pywargame/vassal/collectpatch.py +28 -0
  59. pywargame/vassal/command.py +21 -0
  60. pywargame/vassal/documentation.py +322 -0
  61. pywargame/vassal/dumpcollect.py +28 -0
  62. pywargame/vassal/dumpvsav.py +28 -0
  63. pywargame/vassal/element.py +439 -0
  64. pywargame/vassal/exporter.py +89 -0
  65. pywargame/vassal/extension.py +101 -0
  66. pywargame/vassal/folder.py +103 -0
  67. pywargame/vassal/game.py +940 -0
  68. pywargame/vassal/gameelements.py +1091 -0
  69. pywargame/vassal/globalkey.py +127 -0
  70. pywargame/vassal/globalproperty.py +433 -0
  71. pywargame/vassal/grid.py +573 -0
  72. pywargame/vassal/map.py +1061 -0
  73. pywargame/vassal/mapelements.py +1020 -0
  74. pywargame/vassal/merge.py +57 -0
  75. pywargame/vassal/merger.py +460 -0
  76. pywargame/vassal/moduledata.py +275 -0
  77. pywargame/vassal/mrgcollect.py +31 -0
  78. pywargame/vassal/patch.py +44 -0
  79. pywargame/vassal/patchcollect.py +28 -0
  80. pywargame/vassal/player.py +83 -0
  81. pywargame/vassal/save.py +495 -0
  82. pywargame/vassal/skel.py +380 -0
  83. pywargame/vassal/trait.py +224 -0
  84. pywargame/vassal/traits/__init__.py +36 -0
  85. pywargame/vassal/traits/area.py +50 -0
  86. pywargame/vassal/traits/basic.py +35 -0
  87. pywargame/vassal/traits/calculatedproperty.py +22 -0
  88. pywargame/vassal/traits/cargo.py +29 -0
  89. pywargame/vassal/traits/click.py +41 -0
  90. pywargame/vassal/traits/clone.py +28 -0
  91. pywargame/vassal/traits/delete.py +24 -0
  92. pywargame/vassal/traits/deselect.py +32 -0
  93. pywargame/vassal/traits/dynamicproperty.py +112 -0
  94. pywargame/vassal/traits/globalcommand.py +55 -0
  95. pywargame/vassal/traits/globalhotkey.py +26 -0
  96. pywargame/vassal/traits/globalproperty.py +54 -0
  97. pywargame/vassal/traits/hide.py +67 -0
  98. pywargame/vassal/traits/label.py +76 -0
  99. pywargame/vassal/traits/layer.py +105 -0
  100. pywargame/vassal/traits/mark.py +20 -0
  101. pywargame/vassal/traits/mask.py +85 -0
  102. pywargame/vassal/traits/mat.py +26 -0
  103. pywargame/vassal/traits/moved.py +35 -0
  104. pywargame/vassal/traits/movefixed.py +51 -0
  105. pywargame/vassal/traits/nonrect.py +95 -0
  106. pywargame/vassal/traits/nostack.py +55 -0
  107. pywargame/vassal/traits/place.py +104 -0
  108. pywargame/vassal/traits/prototype.py +20 -0
  109. pywargame/vassal/traits/report.py +34 -0
  110. pywargame/vassal/traits/restrictaccess.py +28 -0
  111. pywargame/vassal/traits/restrictcommand.py +32 -0
  112. pywargame/vassal/traits/return.py +40 -0
  113. pywargame/vassal/traits/rotate.py +62 -0
  114. pywargame/vassal/traits/sendto.py +59 -0
  115. pywargame/vassal/traits/sheet.py +129 -0
  116. pywargame/vassal/traits/skel.py +9 -0
  117. pywargame/vassal/traits/stack.py +28 -0
  118. pywargame/vassal/traits/submenu.py +27 -0
  119. pywargame/vassal/traits/trail.py +61 -0
  120. pywargame/vassal/traits/trigger.py +72 -0
  121. pywargame/vassal/turn.py +272 -0
  122. pywargame/vassal/upgrade.py +191 -0
  123. pywargame/vassal/vmod.py +323 -0
  124. pywargame/vassal/vsav.py +100 -0
  125. pywargame/vassal/widget.py +358 -0
  126. pywargame/vassal/withtraits.py +634 -0
  127. pywargame/vassal/xml.py +4 -0
  128. pywargame/vassal/zone.py +399 -0
  129. pywargame/vassal.py +12500 -0
  130. pywargame/vmodpatch.py +12548 -0
  131. pywargame/vsavdump.py +12533 -0
  132. pywargame/vslmerge.py +13015 -0
  133. pywargame/wgexport.py +16689 -0
  134. pywargame/ztexport.py +14351 -0
  135. pywargame/zuntzu/__init__.py +5 -0
  136. pywargame/zuntzu/base.py +82 -0
  137. pywargame/zuntzu/collect.py +38 -0
  138. pywargame/zuntzu/countersheet.py +250 -0
  139. pywargame/zuntzu/dicehand.py +48 -0
  140. pywargame/zuntzu/exporter.py +936 -0
  141. pywargame/zuntzu/gamebox.py +154 -0
  142. pywargame/zuntzu/map.py +36 -0
  143. pywargame/zuntzu/piece.py +37 -0
  144. pywargame/zuntzu/scenario.py +208 -0
  145. pywargame/zuntzu/ztexp.py +115 -0
  146. pywargame-0.3.1.dist-info/METADATA +353 -0
  147. pywargame-0.3.1.dist-info/RECORD +150 -0
  148. pywargame-0.3.1.dist-info/WHEEL +5 -0
  149. pywargame-0.3.1.dist-info/licenses/LICENSE +5 -0
  150. pywargame-0.3.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,439 @@
1
+ ## BEGIN_IMPORT
2
+ from common import VerboseGuard
3
+ from . base import *
4
+ ## END_IMPORT
5
+
6
+ # ====================================================================
7
+ class Element:
8
+ BUILD = 'VASSAL.build.'
9
+ MODULE = BUILD + 'module.'
10
+ WIDGET = BUILD + 'widget.'
11
+ FOLDER = MODULE + 'folder.'
12
+ MAP = MODULE + 'map.'
13
+ PICKER = MAP + 'boardPicker.'
14
+ BOARD = PICKER + 'board.'
15
+ known_tags = {}
16
+ unique_attrs = {}
17
+
18
+ def __init__(self,parent,tag,node=None,**kwargs):
19
+ '''Create a new element
20
+
21
+ Parameters
22
+ ----------
23
+ parent : Element
24
+ Parent element to add this element to
25
+ tag : str
26
+ Element tag
27
+ node : xml.dom.Node
28
+ If not None, then read attributes from that. Otherwise
29
+ set elements according to kwargs
30
+ kwargs : dict
31
+ Attribute keys and values. Only used if node is None
32
+ '''
33
+ #from xml.dom.minidom import Document
34
+
35
+ if parent is not None:
36
+ self._tag = tag
37
+ self._root = (parent if isinstance(parent,xmlns.Document) else
38
+ parent._root)
39
+ self._node = (node if node is not None else
40
+ parent.addNode(tag,**kwargs))
41
+ else:
42
+ self._root = None
43
+ self._node = None
44
+ self._tag = None
45
+
46
+ # ----------------------------------------------------------------
47
+ @classmethod
48
+ def _make_unique(cls,tag,*values):
49
+ return tag + ('_'+'_'.join(values) if len(values)>0 else '')
50
+
51
+ def _unique(self):
52
+ uattr = Element.unique_attrs.get(self._tag,[])
53
+ return self._make_unique(self._tag,
54
+ *[self.getAttribute(a) for a in uattr])
55
+
56
+ def __hash__(self):
57
+ return hash(self._unique())
58
+
59
+ def __eq__(self,other):
60
+ '''Equality comparison - check if to elements are the same.
61
+
62
+ This is based on the tag of the elements first, then on the
63
+ attributes. However, not all attributes should be compared
64
+ for equality - only thise that are meant to be unique.
65
+
66
+ '''
67
+ #print(f'Compare {self} to {other} ({self._tag},{other._tag})')
68
+ if not isinstance(other,Element):
69
+ return False
70
+ return self.__hash__() == other.__hash__()
71
+ #if self._tag != other._tag:
72
+ # return False
73
+
74
+ # to be done
75
+ #
76
+ # uattr = Element.unique_attrs[self._tag]
77
+ # attr = self .getAttributes()
78
+ # oattr = other.getAttributes()
79
+
80
+ return True
81
+
82
+
83
+ # ----------------------------------------------------------------
84
+ # Attributes
85
+ def __contains__(self,key):
86
+ '''Check if element has attribute key'''
87
+ return self.hasAttribute(key)
88
+
89
+ def __getitem__(self,key):
90
+ '''Get attribute key value'''
91
+ return self.getAttribute(key)
92
+
93
+ def __setitem__(self,key,value):
94
+ '''Set attribute key value'''
95
+ self.setAttribute(key,value)
96
+
97
+ def hasAttribute(self,k):
98
+ '''Check if element has attribute '''
99
+ return self._node.hasAttribute(k)
100
+
101
+ def getAttribute(self,k):
102
+ '''Get attribute key value'''
103
+ return self._node.getAttribute(k)
104
+
105
+ def setAttribute(self,k,v):
106
+ '''Set attribute key value'''
107
+ self._node.setAttribute(k,str(v).lower()
108
+ if isinstance(v,bool) else str(v))
109
+
110
+ def setAttributes(self,**kwargs):
111
+ '''Set attributes to dictionary key and value'''
112
+ for k,v in kwargs.items():
113
+ self.setAttribute(k,v)
114
+
115
+ def getAttributes(self):
116
+ '''Get attributes as dict'''
117
+ return self._node.attributes
118
+
119
+ # ----------------------------------------------------------------
120
+ # Plain nodes
121
+ def getChildren(self):
122
+ '''Get child nodes (xml.dom.Node)'''
123
+ return self._node.childNodes
124
+
125
+ # ----------------------------------------------------------------
126
+ # Getters
127
+ #
128
+ # First generics
129
+ def getAsDict(self,tag='',key=None,enable=True):
130
+ '''Get elements with a specific tag as a dictionary
131
+ where the key is given by attribute key'''
132
+ cont = self._node.getElementsByTagName(tag)
133
+ if not enable or key is None:
134
+ return cont
135
+
136
+ return {e.getAttribute(key): e for e in cont}
137
+
138
+ def getAsOne(self,tag='',single=True):
139
+ '''Get elements with a specific tag, as a list.
140
+ If single is true, then assume we only have one such
141
+ child element, or fail.'''
142
+ cont = self._node.getElementsByTagName(tag)
143
+ if single and len(cont) != 1:
144
+ return None
145
+ return cont
146
+
147
+ def getElementsByKey(self,cls,key='',asdict=True):
148
+ '''Get elments of a specific class as a dictionary,
149
+ where the key is set by the key attribute.'''
150
+ cont = self.getAsDict(cls.TAG,key,asdict)
151
+ if cont is None: return None
152
+
153
+ if not asdict: return [cls(self,node=n) for n in cont]
154
+
155
+ return {k : cls(self,node=n) for k, n in cont.items()}
156
+
157
+ def getAllElements(self,cls,single=True):
158
+ '''Get elements with a specific tag, as a list. If single is
159
+ true, then assume we only have one such child element, or
160
+ fail.
161
+
162
+ If `cls` is None, then return _all_ child elements.
163
+
164
+ '''
165
+ #from xml.dom.minidom import Text, Element as XMLElement
166
+ if cls is None:
167
+ ret = []
168
+ for node in self.getChildren():
169
+ if isinstance(node,xmlns.Text):
170
+ continue
171
+
172
+ if not hasattr(node,'tagName'):
173
+ print(f'Do not know how to deal with {type(node)}')
174
+ continue
175
+
176
+ tag = node.tagName
177
+ cls = Element.getTagClass(tag)
178
+ if cls is None:
179
+ raise RuntimeError(f'No class reflection of tag {tag}')
180
+
181
+ ret.append(cls(self,node=node))
182
+
183
+ return ret
184
+
185
+ cont = self.getAsOne(cls.TAG,single=single)
186
+ if cont is None: return None
187
+ return [cls(self,node=n) for n in cont]
188
+
189
+ def getSpecificElements(self,cls,key,*names,asdict=True):
190
+ '''Get all elements of specific class and that has the
191
+ attribute key, and the attribute value is in names
192
+
193
+ '''
194
+ cont = self.getAsOne(cls.TAG,single=False)
195
+ cand = [cls(self,node=n) for n in cont
196
+ if n.getAttribute(key) in names]
197
+ if asdict:
198
+ return {c[key] : c for c in cand}
199
+ return cand
200
+
201
+ def getParent(self,cls=None,checkTag=True):
202
+ if self._node.parentNode is None:
203
+ return None
204
+ if cls is None:
205
+ cls = self.getTagClass(self._node.parentNode.tagName)
206
+ checkTag = False
207
+ if cls is None:
208
+ return None
209
+ if checkTag and self._node.parentNode.tagName != cls.TAG:
210
+ return None
211
+ return cls(self,node=self._node.parentNode)
212
+
213
+ def getParentOfClass(self,cls):
214
+ '''Searches back until we find the parent with the right
215
+ class, or none
216
+ '''
217
+ try:
218
+ iter(cls)
219
+ except:
220
+ cls = [cls]
221
+ t = {c.TAG: c for c in cls}
222
+ p = self._node.parentNode
223
+ while p is not None:
224
+ c = t.get(p.tagName,None)
225
+ if c is not None: return c(self,node=p)
226
+ p = p.parentNode
227
+ return None
228
+
229
+ @classmethod
230
+ def getTagClass(cls,tag):
231
+ '''Get class corresponding to the tag'''
232
+ # if tag not in cls.known_tags: return None;
233
+ # Older VASSAL may have funny tag-names
234
+ return cls.known_tags.get(tag,None)
235
+
236
+ # ----------------------------------------------------------------
237
+ # Adders
238
+ def addNode(self,tag,**attr):
239
+ '''Add a note to this element
240
+
241
+ Parameters
242
+ ----------
243
+ tag : str
244
+ Node tag name
245
+ attr : dict
246
+ Attributes to set
247
+ '''
248
+ e = self._root.createElement(tag)
249
+ if self._node: self._node.appendChild(e)
250
+
251
+ for k, v in attr.items():
252
+ e.setAttribute(k,str(v).lower() if isinstance(v,bool) else str(v))
253
+
254
+ return e
255
+
256
+ def addText(self,text):
257
+ '''Add a text child node to an element'''
258
+ t = self._root.createTextNode(text)
259
+ self._node.appendChild(t)
260
+ return t
261
+
262
+ def hasText(self):
263
+ return self._node.firstChild is not None and \
264
+ self._node.firstChild.nodeType == self._node.firstChild.TEXT_NODE
265
+
266
+ def getText(self):
267
+ '''Get contained text node content'''
268
+ if self._node.firstChild is None or \
269
+ self._node.firstChild.nodeType != self._node.firstChild.TEXT_NODE:
270
+ return ''
271
+ return self._node.firstChild.nodeValue
272
+
273
+ def setText(self,txt):
274
+ '''Set contained text node content'''
275
+ if self._node.firstChild is None or \
276
+ self._node.firstChild.nodeType != self._node.firstChild.TEXT_NODE:
277
+ return
278
+ self._node.firstChild.nodeValue = txt
279
+
280
+
281
+ def add(self,cls,**kwargs):
282
+ '''Add an element and return wrapped in cls object'''
283
+ return cls(self,node=None,**kwargs)
284
+
285
+ def append(self,elem):
286
+ '''Append and element'''
287
+ if self._node.appendChild(elem._node):
288
+ return elem
289
+ return False
290
+
291
+ # ----------------------------------------------------------------
292
+ def remove(self,elem):
293
+ '''Remove an element'''
294
+ try:
295
+ self._node.removeChild(elem._node)
296
+ except:
297
+ return None
298
+ return elem
299
+ # ----------------------------------------------------------------
300
+ def insertBefore(self,toadd,ref):
301
+ '''Insert an element before another element'''
302
+ try:
303
+ self._node.insertBefore(toadd._node,ref._node)
304
+ except:
305
+ return None
306
+ return toadd
307
+
308
+ # ----------------------------------------------------------------
309
+ def print(self,file=None,recursive=False,indent=''):
310
+ '''Print this element, and possibly its child elements.
311
+
312
+ If `file` is None, then print to stdout. If `recursive` is
313
+ `True`, then also print child elements. If `recursive` is an
314
+ integer, then print this many deep levels of child elements.
315
+
316
+ '''
317
+ if file is None:
318
+ from sys import stdout
319
+ file = stdout
320
+
321
+ from io import StringIO
322
+ from textwrap import indent as i
323
+
324
+ stream = StringIO()
325
+
326
+ print(f'Element TAG={self.TAG} CLS={self.__class__.__name__}',
327
+ file=stream)
328
+ attrs = self.getAttributes()
329
+ #print(type(attrs))
330
+ ln = max([len(n) for n in attrs.keys()]+[0])
331
+ for name,value in attrs.items():
332
+ print(f' {name:{ln}s}: {value}',file=stream)
333
+
334
+ if isinstance(recursive,bool):
335
+ recursive = 1024 if recursive else 0# Large number
336
+
337
+ if recursive > 1:
338
+ for child in self.getAllElements(cls=None):
339
+ child.print(file=stream,
340
+ recursive=recursive-1,
341
+ indent=' ')
342
+ else:
343
+ n = len(self.getChildren())
344
+ if n > 0:
345
+ print(f' {n} child elements',file=stream)
346
+
347
+ print(i(stream.getvalue(),indent).rstrip(),file=file)
348
+
349
+
350
+
351
+ # --------------------------------------------------------------------
352
+ class DummyElement(Element):
353
+ def __init__(self,parent,node=None,**kwargs):
354
+ '''A dummy element we can use to select elements of different
355
+ classes
356
+
357
+ '''
358
+ super(DummyElement,self).__init__(parent,'Dummy',node=node)
359
+
360
+ # --------------------------------------------------------------------
361
+ class ToolbarElement(Element):
362
+ def __init__(self,
363
+ parent,
364
+ tag,
365
+ node = None,
366
+ name = '', # Toolbar element name
367
+ tooltip = '', # Tool tip
368
+ text = '', # Button text
369
+ icon = '', # Button icon,
370
+ hotkey = '', # Named key or key stroke
371
+ canDisable = False,
372
+ propertyGate = '',
373
+ disabledIcon = '',
374
+ **kwargs):
375
+ '''Base class for toolbar elements.
376
+
377
+ Parameters
378
+ ----------
379
+ parent : Element
380
+ Parent element if any
381
+ tag : str
382
+ Element tag
383
+ node : XMLNode
384
+ Possible node - when reading back
385
+ name : str
386
+ Name of element (user reminder). If not set, and tooltip is set,
387
+ set to tooltip
388
+ toolttip : str
389
+ Tool tip when hovering. If not set, and name is set, then
390
+ use name as tooltip.
391
+ text : str
392
+ Text of button
393
+ icon : str
394
+ Image path for button image
395
+ hotkey : str
396
+ Named key or key-sequence
397
+ canDisable : bool
398
+ If true, then the element can be disabled
399
+ propertyGate : str
400
+ Name of a global property. When this property is `true`,
401
+ then this element is _disabled_. Note that this _must_ be
402
+ the name of a property - it cannot be a BeanShell
403
+ expression.
404
+ disabledIcon : str
405
+ Path to image to use when the element is disabled.
406
+ kwargs : dict
407
+ Other attributes to set on the element
408
+ '''
409
+ if name == '' and tooltip != '': name = tooltip
410
+ if name != '' and tooltip == '': tooltip = name
411
+
412
+ # Build arguments for super class
413
+ args = {
414
+ 'node': node,
415
+ 'name': name,
416
+ 'icon': icon,
417
+ 'tooltip': tooltip,
418
+ 'hotkey': hotkey,
419
+ 'canDisable': canDisable,
420
+ 'propertyGate': propertyGate,
421
+ 'disabledIcon': disabledIcon }
422
+ bt = kwargs.pop('buttonText',None)
423
+ # If the element expects buttonText attribute, then do not set
424
+ # the text attribute - some elements interpret that as a
425
+ # legacy name attribute,
426
+ if bt is not None:
427
+ args['buttonText'] = bt
428
+ else:
429
+ args['text'] = text
430
+ args.update(kwargs)
431
+
432
+ super(ToolbarElement,self).__init__(parent,
433
+ tag,
434
+ **args)
435
+ # print('Attributes\n','\n'.join([f'- {k}="{v}"' for k,v in self._node.attributes.items()]))
436
+
437
+ #
438
+ # EOF
439
+ #
@@ -0,0 +1,89 @@
1
+ ## BEGIN_IMPORT
2
+ from vassal.vmod import VMod
3
+ from common import VerboseGuard, Verbose
4
+ ## END_IMPORT
5
+
6
+ class Exporter:
7
+ def __init__(self):
8
+ '''Base class for exporters'''
9
+ pass
10
+
11
+
12
+ def setup(self):
13
+ '''Should be defined to set-up for processing, for example
14
+ generating images and such. This is executed in a context
15
+ where the VMod file has been opened for writing via
16
+ `self._vmod`. Thus, files can be added to the module at this
17
+ stage.
18
+ '''
19
+ pass
20
+
21
+ def createBuildFile(self,ignores='(.*markers?|all|commons|[ ]+)'):
22
+ '''Should be defined to make the `buildFile.xml` document
23
+
24
+ Parameters
25
+ ----------
26
+ ignores : str
27
+ Regular expression to match ignored categories for factions
28
+ determination. Python's re.fullmatch is applied to this
29
+ regular exression against chit categories. If the pattern
30
+ is matched, then the chit is not considered to belong to a
31
+ faction.
32
+
33
+ '''
34
+ pass
35
+
36
+ def createModuleData(self):
37
+ '''Should be defined to make the `moduledata` document'''
38
+ pass
39
+
40
+ def run(self,vmodname,patch=None):
41
+ '''Run the exporter to generate the module
42
+ '''
43
+ with VMod(vmodname,'w') as vmod:
44
+ self._vmod = vmod
45
+ self.setup()
46
+ self.createBuildFile()
47
+ self.createModuleData()
48
+ self.runPatch(patch)
49
+ self._vmod.addFiles(**{VMod.BUILD_FILE :
50
+ self._build.encode(),
51
+ VMod.MODULE_DATA :
52
+ self._moduleData.encode()})
53
+ Verbose().message('Created VMOD')
54
+
55
+
56
+ def runPatch(self,patch):
57
+ '''Run user specified patch script. The script should define
58
+
59
+ ```
60
+ def patch(buildFile,moduleData,vmod,verbose):
61
+ ```
62
+
63
+ where `buildFile` is the `buildFile.xml` and `moduleData` are
64
+ the XML documents as `xml.dom.Document` objects, `vmod` is a
65
+ `VMod` instance, and `verbose` is a `bool` selecting verbose
66
+ mode or not.
67
+ '''
68
+ if patch is None or patch == '':
69
+ return
70
+
71
+ from importlib.util import spec_from_file_location, module_from_spec
72
+ from pathlib import Path
73
+ from sys import modules
74
+
75
+ p = Path(patch)
76
+ with VerboseGuard(f'Will patch module with {p.stem}.patch function') \
77
+ as v:
78
+
79
+ spec = spec_from_file_location(p.stem, p.absolute())
80
+ module = module_from_spec(spec)
81
+ spec.loader.exec_module(module)
82
+ modules[p.stem] = module
83
+
84
+ # Patch must accept xml.dom.document,xml.dom.document,ZipFile
85
+ module.patch(self._build,
86
+ self._moduleData,
87
+ self._vmod,
88
+ Verbose().verbose)
89
+
@@ -0,0 +1,101 @@
1
+ ## BEGIN_IMPORT
2
+ from common import VerboseGuard
3
+ from . base import *
4
+ from . element import Element
5
+ from . game import Game
6
+ ## END_IMPORT
7
+
8
+ # ====================================================================
9
+ class Extension(Element):
10
+ TAG = Element.MODULE+'ModuleExtension'
11
+ def __init__(self,
12
+ parent = None,
13
+ node = None,
14
+ anyModule = False,
15
+ version = '',
16
+ description = '',
17
+ module = '',
18
+ moduleVersion = '',
19
+ vassalVersion = '',
20
+ nextPieceSlotId = 0,
21
+ extensionId = 0,
22
+ asDocument = False):
23
+ super().__init__(parent,self.TAG,node)
24
+
25
+ self._tag = 'extension'
26
+ if self._node is None:
27
+ #from xml.dom.minidom import Document
28
+
29
+
30
+ self._root = xmlns.Document()
31
+ self._node = self._root
32
+ self.setAttributes(
33
+ anyModule = anyModule,
34
+ version = version,
35
+ description = description,
36
+ module = module,
37
+ moduleVersion = moduleVersion,
38
+ vassalVersion = vassalVersion,
39
+ nextPieceSlotId = nextPieceSlotId,
40
+ extensionId = extensionId)
41
+
42
+ def addExtensionElement(self,**kwargs):
43
+ '''Add an extension element'''
44
+
45
+ return self.add(ExtensionElement,**kwargs)
46
+
47
+ # ----------------------------------------------------------------
48
+ def getExtensionElements(self,asdict=True):
49
+ '''Get all or a sole `GlobalPropertie` element(s) from this
50
+
51
+ Parameters
52
+ ----------
53
+ asdict : bool
54
+ If `True`, return a dictonary that maps key to
55
+ `ExtensionElement` elements. If `False`, return a list of
56
+ all `ExtensionElement` children.
57
+
58
+ Returns
59
+ -------
60
+ children : dict or list
61
+ Dictionary or list of `Extension` children
62
+
63
+ '''
64
+ return self.getElementsByKey(ExtensionElement,'target',asdict)
65
+
66
+ registerElement(Extension)
67
+
68
+ # --------------------------------------------------------------------
69
+ class ExtensionElement(Element):
70
+ TAG = Element.MODULE + 'ExtensionElement'
71
+ UNIQUE = ['target']
72
+
73
+ def __init__(self,
74
+ extension,
75
+ node = None,
76
+ target = ''):
77
+ super().__init__(extension,
78
+ self.TAG,
79
+ node = node,
80
+ target = target)
81
+
82
+
83
+ def getTarget(self):
84
+ return self['target']
85
+
86
+ @property
87
+ def target(self):
88
+ return self.getTarget()
89
+
90
+ def getSelect(self):
91
+ parts = self.target.split('/')
92
+ specs = [p.split(':') for p in parts]
93
+ return specs
94
+
95
+
96
+ registerElement(ExtensionElement)
97
+
98
+ # --------------------------------------------------------------------
99
+ #
100
+ # EOF
101
+ #