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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
# Copyright (c) 2010-2024 openpyxl
from openpyxl.compat import safe_string
from openpyxl.xml.functions import Element
from openpyxl.utils.indexed_list import IndexedList
from .base import Descriptor, Alias, _convert
from .namespace import namespaced
class Sequence(Descriptor):
"""
A sequence (list or tuple) that may only contain objects of the declared
type
"""
expected_type = type(None)
seq_types = (list, tuple)
idx_base = 0
unique = False
container = list
def __set__(self, instance, seq):
if not isinstance(seq, self.seq_types):
raise TypeError("Value must be a sequence")
seq = self.container(_convert(self.expected_type, value) for value in seq)
if self.unique:
seq = IndexedList(seq)
super().__set__(instance, seq)
def to_tree(self, tagname, obj, namespace=None):
"""
Convert the sequence represented by the descriptor to an XML element
"""
for idx, v in enumerate(obj, self.idx_base):
if hasattr(v, "to_tree"):
el = v.to_tree(tagname, idx)
else:
tagname = namespaced(obj, tagname, namespace)
el = Element(tagname)
el.text = safe_string(v)
yield el
class UniqueSequence(Sequence):
"""
Use a set to keep values unique
"""
seq_types = (list, tuple, set)
container = set
class ValueSequence(Sequence):
"""
A sequence of primitive types that are stored as a single attribute.
"val" is the default attribute
"""
attribute = "val"
def to_tree(self, tagname, obj, namespace=None):
tagname = namespaced(self, tagname, namespace)
for v in obj:
yield Element(tagname, {self.attribute:safe_string(v)})
def from_tree(self, node):
return node.get(self.attribute)
class NestedSequence(Sequence):
"""
Wrap a sequence in an containing object
"""
count = False
def to_tree(self, tagname, obj, namespace=None):
tagname = namespaced(self, tagname, namespace)
container = Element(tagname)
if self.count:
container.set('count', str(len(obj)))
for v in obj:
container.append(v.to_tree())
return container
def from_tree(self, node):
return [self.expected_type.from_tree(el) for el in node]
class MultiSequence(Sequence):
"""
Sequences can contain objects with different tags
"""
def __set__(self, instance, seq):
if not isinstance(seq, (tuple, list)):
raise ValueError("Value must be a sequence")
seq = list(seq)
Descriptor.__set__(self, instance, seq)
def to_tree(self, tagname, obj, namespace=None):
"""
Convert the sequence represented by the descriptor to an XML element
"""
for v in obj:
el = v.to_tree(namespace=namespace)
yield el
class MultiSequencePart(Alias):
"""
Allow a multisequence to be built up from parts
Excluded from the instance __elements__ or __attrs__ as is effectively an Alias
"""
def __init__(self, expected_type, store):
self.expected_type = expected_type
self.store = store
def __set__(self, instance, value):
value = _convert(self.expected_type, value)
instance.__dict__[self.store].append(value)
def __get__(self, instance, cls):
return self
|