Skip to content

Commit

Permalink
Merge branch 'feature/description-attributes'
Browse files Browse the repository at this point in the history
  • Loading branch information
srittau committed Nov 11, 2017
2 parents 87a94ac + 6a672af commit 566fb1a
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 70 deletions.
6 changes: 6 additions & 0 deletions NEWS.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
News in version 0.99.1
======================

API-Incompatible Changes
------------------------

* html_attribute() at al. are now directly implemented using the descriptor
protocol, and not derived from property.

Improvements
------------

Expand Down
136 changes: 78 additions & 58 deletions htmlgen/attribute.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
from htmlgen.timeutil import parse_rfc3339_partial_time


def html_attribute(attribute_name, default=None):
class html_attribute(object):

"""Add an attribute to an HTML element.
>>> from htmlgen import Element
Expand All @@ -30,19 +31,22 @@ def html_attribute(attribute_name, default=None):
"""

def get(self):
return self.get_attribute(attribute_name, default=default)
def __init__(self, attribute_name, default=None):
self._attribute_name = attribute_name
self._default = default

def __get__(self, obj, _=None):
return obj.get_attribute(self._attribute_name, default=self._default)

def set_(self, value):
if value is None or value == default:
self.remove_attribute(attribute_name)
def __set__(self, obj, value):
if value is None or value == self._default:
obj.remove_attribute(self._attribute_name)
else:
self.set_attribute(attribute_name, value)
obj.set_attribute(self._attribute_name, value)

return property(get, set_)

class boolean_html_attribute(object):

def boolean_html_attribute(attribute_name):
"""Add a boolean attribute to an HTML element.
>>> from htmlgen import Element
Expand All @@ -59,19 +63,21 @@ def boolean_html_attribute(attribute_name):
"""

def get(self):
return self.get_attribute(attribute_name) == attribute_name
def __init__(self, attribute_name):
self._attribute_name = attribute_name

def __get__(self, obj, _=None):
return obj.get_attribute(self._attribute_name) == self._attribute_name

def set_(self, value):
def __set__(self, obj, value):
if value:
self.set_attribute(attribute_name, attribute_name)
obj.set_attribute(self._attribute_name, self._attribute_name)
else:
self.remove_attribute(attribute_name)
obj.remove_attribute(self._attribute_name)

return property(get, set_)

class int_html_attribute(object):

def int_html_attribute(attribute_name, default=None):
"""Add an attribute to an HTML element that accepts only integers.
>>> from htmlgen import Element
Expand All @@ -98,22 +104,25 @@ def int_html_attribute(attribute_name, default=None):
"""

def get(self):
value = self.get_attribute(attribute_name, default=default)
def __init__(self, attribute_name, default=None):
self._attribute_name = attribute_name
self._default = default

def __get__(self, obj, _=None):
value = obj.get_attribute(self._attribute_name, default=self._default)
if value is None:
return None
return int(value)

def set_(self, value):
if value is None or value == default:
self.remove_attribute(attribute_name)
def __set__(self, obj, value):
if value is None or value == self._default:
obj.remove_attribute(self._attribute_name)
else:
self.set_attribute(attribute_name, str(value))
obj.set_attribute(self._attribute_name, str(value))

return property(get, set_)

class float_html_attribute(object):

def float_html_attribute(attribute_name, default=None):
"""Add an attribute to an HTML element that accepts only numbers.
>>> from htmlgen import Element
Expand All @@ -140,22 +149,25 @@ def float_html_attribute(attribute_name, default=None):
"""

def get(self):
value = self.get_attribute(attribute_name, default=default)
def __init__(self, attribute_name, default=None):
self._attribute_name = attribute_name
self._default = default

def __get__(self, obj, _=None):
value = obj.get_attribute(self._attribute_name, default=self._default)
if value is None:
return None
return float(value)

def set_(self, value):
if value is None or value == default:
self.remove_attribute(attribute_name)
def __set__(self, obj, value):
if value is None or value == self._default:
obj.remove_attribute(self._attribute_name)
else:
self.set_attribute(attribute_name, str(value))
obj.set_attribute(self._attribute_name, str(value))

return property(get, set_)

class time_html_attribute(object):

def time_html_attribute(attribute_name, default=None):
"""Add an attribute to an HTML element that accepts only time values.
>>> from htmlgen import Element
Expand Down Expand Up @@ -183,22 +195,25 @@ def time_html_attribute(attribute_name, default=None):
"""

def get(self):
value = self.get_attribute(attribute_name)
def __init__(self, attribute_name, default=None):
self._attribute_name = attribute_name
self._default = default

def __get__(self, obj, _=None):
value = obj.get_attribute(self._attribute_name)
if value is None:
return default
return self._default
return parse_rfc3339_partial_time(value)

def set_(self, value):
if value is None or value == default:
self.remove_attribute(attribute_name)
def __set__(self, obj, value):
if value is None or value == self._default:
obj.remove_attribute(self._attribute_name)
else:
self.set_attribute(attribute_name, str(value))
obj.set_attribute(self._attribute_name, str(value))

return property(get, set_)

class list_html_attribute(object):

def list_html_attribute(attribute_name):
"""Add an attribute to an HTML element that accepts a list of strings.
>>> from htmlgen import Element
Expand All @@ -215,25 +230,29 @@ def list_html_attribute(attribute_name):
"""

def get(self):
value = self.get_attribute(attribute_name)
def __init__(self, attribute_name):
self._attribute_name = attribute_name

def __get__(self, obj, _=None):
value = obj.get_attribute(self._attribute_name)
return value.split(",") if value else []

def set_(self, value):
def __set__(self, obj, value):
if value:
self.set_attribute(attribute_name, ",".join(value))
obj.set_attribute(self._attribute_name, ",".join(value))
else:
self.remove_attribute(attribute_name)
obj.remove_attribute(self._attribute_name)


return property(get, set_)
class data_attribute(html_attribute):

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

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

class css_class_attribute(object):

def css_class_attribute(css_class):
"""Add a boolean attribute to an HTML element that add a CSS class.
>>> from htmlgen import Element
Expand All @@ -251,13 +270,14 @@ def css_class_attribute(css_class):
"""

def get(self):
return self.has_css_class(css_class)
def __init__(self, css_class):
self._css_class = css_class

def set_(self, value):
def __get__(self, obj, _=None):
return obj.has_css_class(self._css_class)

def __set__(self, obj, value):
if value:
self.add_css_classes(css_class)
obj.add_css_classes(self._css_class)
else:
self.remove_css_classes(css_class)

return property(get, set_)
obj.remove_css_classes(self._css_class)
53 changes: 43 additions & 10 deletions htmlgen/attribute.pyi
Original file line number Diff line number Diff line change
@@ -1,10 +1,43 @@
from typing import Any

def html_attribute(attribute_name: str, default: Any = ...) -> property: ...
def boolean_html_attribute(attribute_name: str) -> property: ...
def int_html_attribute(attribute_name: str, default: Any = ...) -> property: ...
def float_html_attribute(attribute_name: str, default: Any = ...) -> property: ...
def time_html_attribute(attribute_name: str, default: Any = ...) -> property: ...
def list_html_attribute(attribute_name: str) -> property: ...
def data_attribute(data_name: str, default: Any = ...) -> property: ...
def css_class_attribute(css_class: str) -> property: ...
import datetime
from typing import Optional, List, Iterable

from htmlgen.element import Element


class html_attribute(object):
def __init__(self, attribute_name: str, default: Optional[str] = ...) -> None: ...
def __get__(self, obj: Element, type: Optional[type] = ...) -> Optional[str]: ...
def __set__(self, obj: Element, value: Optional[str]) -> None: ...

class boolean_html_attribute(object):
def __init__(self, attribute_name: str) -> None: ...
def __get__(self, obj: Element, type_: Optional[type] = ...) -> bool: ...
def __set__(self, obj: Element, value: bool) -> None: ...

class int_html_attribute(object):
def __init__(self, attribute_name: str, default: Optional[int] = ...) -> None: ...
def __get__(self, obj: Element, type_: Optional[type] = ...) -> Optional[int]: ...
def __set__(self, obj: Element, value: Optional[int]) -> None: ...

class float_html_attribute(object):
def __init__(self, attribute_name: str, default: Optional[float] = ...) -> None: ...
def __get__(self, obj: Element, type_: Optional[type] = ...) -> Optional[float]: ...
def __set__(self, obj: Element, value: Optional[float]) -> None: ...

class time_html_attribute(object):
def __init__(self, attribute_name: str, default: Optional[datetime.time] = None) -> None: ...
def __get__(self, obj: Element, type_: Optional[type] = ...) -> Optional[datetime.time]: ...
def __set__(self, obj: Element, value: Optional[datetime.time]) -> None: ...

class list_html_attribute(object):
def __init__(self, attribute_name: str) -> None: ...
def __get__(self, obj: Element, type_: Optional[type] = ...) -> List[str]: ...
def __set__(self, obj: Element, value: Iterable[str]) -> None: ...

class data_attribute(html_attribute):
def __init__(self, data_name: str, default: Optional[str] = None) -> None: ...

class css_class_attribute(object):
def __init__(self, css_class: str) -> None: ...
def __get__(self, obj: Element, type_: Optional[type] = ...) -> bool: ...
def __set__(self, obj: Element, value: bool) -> None: ...
3 changes: 2 additions & 1 deletion htmlgen/document.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from htmlgen.attribute import html_attribute
from htmlgen.element import Generator, Element, NonVoidElement, VoidElement
from htmlgen.generator import Generator
from htmlgen.element import Element, NonVoidElement, VoidElement


MIME_JAVASCRIPT = "text/javascript"
Expand Down
2 changes: 1 addition & 1 deletion htmlgen/form.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def __init__(self, type_="text", name=""):
value = html_attribute("value", default="")
readonly = boolean_html_attribute("readonly")
disabled = boolean_html_attribute("disabled")
type = html_attribute("type") # type: ignore
type = html_attribute("type")
placeholder = html_attribute("placeholder")
size = int_html_attribute("size")
focus = boolean_html_attribute("autofocus")
Expand Down

0 comments on commit 566fb1a

Please sign in to comment.