1# Copyright (c) 2010-2024 openpyxl
2
3"""
4Generic serialisable classes
5"""
6from .base import (
7 Convertible,
8 Bool,
9 Descriptor,
10 NoneSet,
11 MinMax,
12 Set,
13 Float,
14 Integer,
15 String,
16 )
17from openpyxl.compat import safe_string
18from openpyxl.xml.functions import Element, localname, whitespace
19
20
21class Nested(Descriptor):
22
23 nested = True
24 attribute = "val"
25
26 def __set__(self, instance, value):
27 if hasattr(value, "tag"):
28 tag = localname(value)
29 if tag != self.name:
30 raise ValueError("Tag does not match attribute")
31
32 value = self.from_tree(value)
33 super().__set__(instance, value)
34
35
36 def from_tree(self, node):
37 return node.get(self.attribute)
38
39
40 def to_tree(self, tagname=None, value=None, namespace=None):
41 namespace = getattr(self, "namespace", namespace)
42 if value is not None:
43 if namespace is not None:
44 tagname = "{%s}%s" % (namespace, tagname)
45 value = safe_string(value)
46 return Element(tagname, {self.attribute:value})
47
48
49class NestedValue(Nested, Convertible):
50 """
51 Nested tag storing the value on the 'val' attribute
52 """
53 pass
54
55
56class NestedText(NestedValue):
57 """
58 Represents any nested tag with the value as the contents of the tag
59 """
60
61
62 def from_tree(self, node):
63 return node.text
64
65
66 def to_tree(self, tagname=None, value=None, namespace=None):
67 namespace = getattr(self, "namespace", namespace)
68 if value is not None:
69 if namespace is not None:
70 tagname = "{%s}%s" % (namespace, tagname)
71 el = Element(tagname)
72 el.text = safe_string(value)
73 whitespace(el)
74 return el
75
76
77class NestedFloat(NestedValue, Float):
78
79 pass
80
81
82class NestedInteger(NestedValue, Integer):
83
84 pass
85
86
87class NestedString(NestedValue, String):
88
89 pass
90
91
92class NestedBool(NestedValue, Bool):
93
94
95 def from_tree(self, node):
96 return node.get("val", True)
97
98
99class NestedNoneSet(Nested, NoneSet):
100
101 pass
102
103
104class NestedSet(Nested, Set):
105
106 pass
107
108
109class NestedMinMax(Nested, MinMax):
110
111 pass
112
113
114class EmptyTag(Nested, Bool):
115
116 """
117 Boolean if a tag exists or not.
118 """
119
120 def from_tree(self, node):
121 return True
122
123
124 def to_tree(self, tagname=None, value=None, namespace=None):
125 if value:
126 namespace = getattr(self, "namespace", namespace)
127 if namespace is not None:
128 tagname = "{%s}%s" % (namespace, tagname)
129 return Element(tagname)