-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathxml_fields.py
115 lines (79 loc) · 3.08 KB
/
xml_fields.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# coding=UTF-8
from __future__ import print_function, absolute_import, division
import six
class _Required(object):
"""Only a placeholder to tell apart None default values from required fields"""
def nop(x):
return x
def get_tag_child(elem, tag):
return [e for e in list(elem) if e.tag == tag]
__all__ = [
'_name', '_docs', 'ComputedField', 'ValueField', 'SimpleField',
'_docs_with_label', 'get_tag_child', 'ChildField'
]
class BaseField(object):
"""Base field object"""
def set(self, obj, elem):
obj.__dict__[self.field_name] = self.find(elem)
def __init__(self, field_name):
self.field_name = field_name
def find(self, elem):
raise NotImplementedError('Abstract method!')
class ComputedField(BaseField):
"""
There's no corresponding XML attribute name - value must
be computed from element
"""
def __init__(self, field_name, find_fun):
super(ComputedField, self).__init__(field_name)
self.find = find_fun
class ValueField(BaseField):
"""
Can hide under a pick of different XML attribute names.
Has a type, can have a default value.
"""
def __init__(self, xml_names, field_name, field_type=nop,
default=_Required):
if not isinstance(xml_names, tuple):
xml_names = (xml_names,)
self.xml_names = xml_names
assert field_type is not None
self.field_name = field_name
self.field_type = field_type
self.default = default
def find(self, elem):
return self.field_type(self._find(elem))
def _find(self, elem):
xmln = [xmln for xmln in self.xml_names if xmln in elem.attrib]
if xmln:
return elem.attrib[xmln[0]]
else:
if self.default is _Required:
raise TypeError(
'Did not find field %s in elem tag %s, looked for names %s' % (
self.field_name, elem.tag, self.xml_names))
else:
return self.default
class SimpleField(ValueField):
"""XML attribute is the same as name, has a type and can be default"""
def __init__(self, name, field_type=nop, default=_Required):
super(SimpleField, self).__init__(name, name, field_type, default)
class ChildField(ComputedField):
"""
List of other properties
"""
def __init__(self, name, xml_tag, fun, post_exec=nop):
super(ChildField, self).__init__(name, lambda elem: \
post_exec([fun(c) for c in get_tag_child(elem, xml_tag)]))
def get_docs(elem, label):
"""Parse an XML element. Return documentation"""
for kid in list(elem):
if kid.tag == 'rule':
return get_docs(kid, False)
s = kid.text.strip().split('\n')
return u'\n'.join([u.strip() for u in s if len(u.strip()) > 0])
if label:
return elem.attrib.get('label', None)
_name = SimpleField('name', six.text_type)
_docs = ComputedField('docs', lambda elem: get_docs(elem, False))
_docs_with_label = ComputedField('docs', lambda elem: get_docs(elem, True))