Skip to content

Commit 566fb1a

Browse files
committed
Merge branch 'feature/description-attributes'
2 parents 87a94ac + 6a672af commit 566fb1a

File tree

5 files changed

+130
-70
lines changed

5 files changed

+130
-70
lines changed

NEWS.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
News in version 0.99.1
22
======================
33

4+
API-Incompatible Changes
5+
------------------------
6+
7+
* html_attribute() at al. are now directly implemented using the descriptor
8+
protocol, and not derived from property.
9+
410
Improvements
511
------------
612

htmlgen/attribute.py

Lines changed: 78 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
from htmlgen.timeutil import parse_rfc3339_partial_time
44

55

6-
def html_attribute(attribute_name, default=None):
6+
class html_attribute(object):
7+
78
"""Add an attribute to an HTML element.
89
910
>>> from htmlgen import Element
@@ -30,19 +31,22 @@ def html_attribute(attribute_name, default=None):
3031
3132
"""
3233

33-
def get(self):
34-
return self.get_attribute(attribute_name, default=default)
34+
def __init__(self, attribute_name, default=None):
35+
self._attribute_name = attribute_name
36+
self._default = default
37+
38+
def __get__(self, obj, _=None):
39+
return obj.get_attribute(self._attribute_name, default=self._default)
3540

36-
def set_(self, value):
37-
if value is None or value == default:
38-
self.remove_attribute(attribute_name)
41+
def __set__(self, obj, value):
42+
if value is None or value == self._default:
43+
obj.remove_attribute(self._attribute_name)
3944
else:
40-
self.set_attribute(attribute_name, value)
45+
obj.set_attribute(self._attribute_name, value)
4146

42-
return property(get, set_)
4347

48+
class boolean_html_attribute(object):
4449

45-
def boolean_html_attribute(attribute_name):
4650
"""Add a boolean attribute to an HTML element.
4751
4852
>>> from htmlgen import Element
@@ -59,19 +63,21 @@ def boolean_html_attribute(attribute_name):
5963
6064
"""
6165

62-
def get(self):
63-
return self.get_attribute(attribute_name) == attribute_name
66+
def __init__(self, attribute_name):
67+
self._attribute_name = attribute_name
68+
69+
def __get__(self, obj, _=None):
70+
return obj.get_attribute(self._attribute_name) == self._attribute_name
6471

65-
def set_(self, value):
72+
def __set__(self, obj, value):
6673
if value:
67-
self.set_attribute(attribute_name, attribute_name)
74+
obj.set_attribute(self._attribute_name, self._attribute_name)
6875
else:
69-
self.remove_attribute(attribute_name)
76+
obj.remove_attribute(self._attribute_name)
7077

71-
return property(get, set_)
7278

79+
class int_html_attribute(object):
7380

74-
def int_html_attribute(attribute_name, default=None):
7581
"""Add an attribute to an HTML element that accepts only integers.
7682
7783
>>> from htmlgen import Element
@@ -98,22 +104,25 @@ def int_html_attribute(attribute_name, default=None):
98104
99105
"""
100106

101-
def get(self):
102-
value = self.get_attribute(attribute_name, default=default)
107+
def __init__(self, attribute_name, default=None):
108+
self._attribute_name = attribute_name
109+
self._default = default
110+
111+
def __get__(self, obj, _=None):
112+
value = obj.get_attribute(self._attribute_name, default=self._default)
103113
if value is None:
104114
return None
105115
return int(value)
106116

107-
def set_(self, value):
108-
if value is None or value == default:
109-
self.remove_attribute(attribute_name)
117+
def __set__(self, obj, value):
118+
if value is None or value == self._default:
119+
obj.remove_attribute(self._attribute_name)
110120
else:
111-
self.set_attribute(attribute_name, str(value))
121+
obj.set_attribute(self._attribute_name, str(value))
112122

113-
return property(get, set_)
114123

124+
class float_html_attribute(object):
115125

116-
def float_html_attribute(attribute_name, default=None):
117126
"""Add an attribute to an HTML element that accepts only numbers.
118127
119128
>>> from htmlgen import Element
@@ -140,22 +149,25 @@ def float_html_attribute(attribute_name, default=None):
140149
141150
"""
142151

143-
def get(self):
144-
value = self.get_attribute(attribute_name, default=default)
152+
def __init__(self, attribute_name, default=None):
153+
self._attribute_name = attribute_name
154+
self._default = default
155+
156+
def __get__(self, obj, _=None):
157+
value = obj.get_attribute(self._attribute_name, default=self._default)
145158
if value is None:
146159
return None
147160
return float(value)
148161

149-
def set_(self, value):
150-
if value is None or value == default:
151-
self.remove_attribute(attribute_name)
162+
def __set__(self, obj, value):
163+
if value is None or value == self._default:
164+
obj.remove_attribute(self._attribute_name)
152165
else:
153-
self.set_attribute(attribute_name, str(value))
166+
obj.set_attribute(self._attribute_name, str(value))
154167

155-
return property(get, set_)
156168

169+
class time_html_attribute(object):
157170

158-
def time_html_attribute(attribute_name, default=None):
159171
"""Add an attribute to an HTML element that accepts only time values.
160172
161173
>>> from htmlgen import Element
@@ -183,22 +195,25 @@ def time_html_attribute(attribute_name, default=None):
183195
184196
"""
185197

186-
def get(self):
187-
value = self.get_attribute(attribute_name)
198+
def __init__(self, attribute_name, default=None):
199+
self._attribute_name = attribute_name
200+
self._default = default
201+
202+
def __get__(self, obj, _=None):
203+
value = obj.get_attribute(self._attribute_name)
188204
if value is None:
189-
return default
205+
return self._default
190206
return parse_rfc3339_partial_time(value)
191207

192-
def set_(self, value):
193-
if value is None or value == default:
194-
self.remove_attribute(attribute_name)
208+
def __set__(self, obj, value):
209+
if value is None or value == self._default:
210+
obj.remove_attribute(self._attribute_name)
195211
else:
196-
self.set_attribute(attribute_name, str(value))
212+
obj.set_attribute(self._attribute_name, str(value))
197213

198-
return property(get, set_)
199214

215+
class list_html_attribute(object):
200216

201-
def list_html_attribute(attribute_name):
202217
"""Add an attribute to an HTML element that accepts a list of strings.
203218
204219
>>> from htmlgen import Element
@@ -215,25 +230,29 @@ def list_html_attribute(attribute_name):
215230
216231
"""
217232

218-
def get(self):
219-
value = self.get_attribute(attribute_name)
233+
def __init__(self, attribute_name):
234+
self._attribute_name = attribute_name
235+
236+
def __get__(self, obj, _=None):
237+
value = obj.get_attribute(self._attribute_name)
220238
return value.split(",") if value else []
221239

222-
def set_(self, value):
240+
def __set__(self, obj, value):
223241
if value:
224-
self.set_attribute(attribute_name, ",".join(value))
242+
obj.set_attribute(self._attribute_name, ",".join(value))
225243
else:
226-
self.remove_attribute(attribute_name)
244+
obj.remove_attribute(self._attribute_name)
245+
227246

228-
return property(get, set_)
247+
class data_attribute(html_attribute):
229248

249+
def __init__(self, data_name, default=None):
250+
attribute_name = "data-" + data_name
251+
super(data_attribute, self).__init__(attribute_name, default)
230252

231-
def data_attribute(data_name, default=None):
232-
attribute_name = "data-" + data_name
233-
return html_attribute(attribute_name, default)
234253

254+
class css_class_attribute(object):
235255

236-
def css_class_attribute(css_class):
237256
"""Add a boolean attribute to an HTML element that add a CSS class.
238257
239258
>>> from htmlgen import Element
@@ -251,13 +270,14 @@ def css_class_attribute(css_class):
251270
252271
"""
253272

254-
def get(self):
255-
return self.has_css_class(css_class)
273+
def __init__(self, css_class):
274+
self._css_class = css_class
256275

257-
def set_(self, value):
276+
def __get__(self, obj, _=None):
277+
return obj.has_css_class(self._css_class)
278+
279+
def __set__(self, obj, value):
258280
if value:
259-
self.add_css_classes(css_class)
281+
obj.add_css_classes(self._css_class)
260282
else:
261-
self.remove_css_classes(css_class)
262-
263-
return property(get, set_)
283+
obj.remove_css_classes(self._css_class)

htmlgen/attribute.pyi

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,43 @@
1-
from typing import Any
2-
3-
def html_attribute(attribute_name: str, default: Any = ...) -> property: ...
4-
def boolean_html_attribute(attribute_name: str) -> property: ...
5-
def int_html_attribute(attribute_name: str, default: Any = ...) -> property: ...
6-
def float_html_attribute(attribute_name: str, default: Any = ...) -> property: ...
7-
def time_html_attribute(attribute_name: str, default: Any = ...) -> property: ...
8-
def list_html_attribute(attribute_name: str) -> property: ...
9-
def data_attribute(data_name: str, default: Any = ...) -> property: ...
10-
def css_class_attribute(css_class: str) -> property: ...
1+
import datetime
2+
from typing import Optional, List, Iterable
3+
4+
from htmlgen.element import Element
5+
6+
7+
class html_attribute(object):
8+
def __init__(self, attribute_name: str, default: Optional[str] = ...) -> None: ...
9+
def __get__(self, obj: Element, type: Optional[type] = ...) -> Optional[str]: ...
10+
def __set__(self, obj: Element, value: Optional[str]) -> None: ...
11+
12+
class boolean_html_attribute(object):
13+
def __init__(self, attribute_name: str) -> None: ...
14+
def __get__(self, obj: Element, type_: Optional[type] = ...) -> bool: ...
15+
def __set__(self, obj: Element, value: bool) -> None: ...
16+
17+
class int_html_attribute(object):
18+
def __init__(self, attribute_name: str, default: Optional[int] = ...) -> None: ...
19+
def __get__(self, obj: Element, type_: Optional[type] = ...) -> Optional[int]: ...
20+
def __set__(self, obj: Element, value: Optional[int]) -> None: ...
21+
22+
class float_html_attribute(object):
23+
def __init__(self, attribute_name: str, default: Optional[float] = ...) -> None: ...
24+
def __get__(self, obj: Element, type_: Optional[type] = ...) -> Optional[float]: ...
25+
def __set__(self, obj: Element, value: Optional[float]) -> None: ...
26+
27+
class time_html_attribute(object):
28+
def __init__(self, attribute_name: str, default: Optional[datetime.time] = None) -> None: ...
29+
def __get__(self, obj: Element, type_: Optional[type] = ...) -> Optional[datetime.time]: ...
30+
def __set__(self, obj: Element, value: Optional[datetime.time]) -> None: ...
31+
32+
class list_html_attribute(object):
33+
def __init__(self, attribute_name: str) -> None: ...
34+
def __get__(self, obj: Element, type_: Optional[type] = ...) -> List[str]: ...
35+
def __set__(self, obj: Element, value: Iterable[str]) -> None: ...
36+
37+
class data_attribute(html_attribute):
38+
def __init__(self, data_name: str, default: Optional[str] = None) -> None: ...
39+
40+
class css_class_attribute(object):
41+
def __init__(self, css_class: str) -> None: ...
42+
def __get__(self, obj: Element, type_: Optional[type] = ...) -> bool: ...
43+
def __set__(self, obj: Element, value: bool) -> None: ...

htmlgen/document.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from htmlgen.attribute import html_attribute
2-
from htmlgen.element import Generator, Element, NonVoidElement, VoidElement
2+
from htmlgen.generator import Generator
3+
from htmlgen.element import Element, NonVoidElement, VoidElement
34

45

56
MIME_JAVASCRIPT = "text/javascript"

htmlgen/form.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def __init__(self, type_="text", name=""):
8585
value = html_attribute("value", default="")
8686
readonly = boolean_html_attribute("readonly")
8787
disabled = boolean_html_attribute("disabled")
88-
type = html_attribute("type") # type: ignore
88+
type = html_attribute("type")
8989
placeholder = html_attribute("placeholder")
9090
size = int_html_attribute("size")
9191
focus = boolean_html_attribute("autofocus")

0 commit comments

Comments
 (0)