111 lines
2.9 KiB
Python
111 lines
2.9 KiB
Python
|
# -*- coding: utf-8 -*-
|
||
|
|
||
|
"""
|
||
|
misc utility functions and classes
|
||
|
"""
|
||
|
|
||
|
|
||
|
# utils ######################################################################
|
||
|
|
||
|
def _indent(s, level=1, tab="\t"):
|
||
|
"""indent blocks"""
|
||
|
indent = tab * level
|
||
|
return "\n".join("%s%s" % (indent, line) for line in s.split("\n"))
|
||
|
|
||
|
|
||
|
def _u(v, encoding="utf8"):
|
||
|
"""provides a unicode string from anything."""
|
||
|
if isinstance(v, str):
|
||
|
return v
|
||
|
elif isinstance(v, (list, tuple)):
|
||
|
return " ".join(_u(vi, encoding) for vi in v)
|
||
|
elif v is None:
|
||
|
return "none"
|
||
|
else:
|
||
|
return str(v)
|
||
|
|
||
|
|
||
|
# base classes ###############################################################
|
||
|
|
||
|
class _Base(object):
|
||
|
"""equality based on state rather than id"""
|
||
|
|
||
|
_state_attributes = []
|
||
|
|
||
|
def _state(self):
|
||
|
return {name: getattr(self, name)
|
||
|
for name in self._state_attributes}
|
||
|
|
||
|
def __eq__(self, other):
|
||
|
try:
|
||
|
return other._state() == self._state()
|
||
|
except AttributeError:
|
||
|
return False
|
||
|
|
||
|
def __ne__(self, other):
|
||
|
return not self.__eq__(other)
|
||
|
|
||
|
# def __hash__(self): return hash(self._state())
|
||
|
|
||
|
def __hash__(self):
|
||
|
raise RuntimeError("state is not hashable")
|
||
|
|
||
|
|
||
|
class _Element(_Base):
|
||
|
"""element with xml serialization support"""
|
||
|
|
||
|
_state_attributes = ["tag"]
|
||
|
attributes = []
|
||
|
|
||
|
def __init__(self):
|
||
|
super(_Element, self).__init__()
|
||
|
self.id = None
|
||
|
|
||
|
@property
|
||
|
def tag(self):
|
||
|
"""the svg tag that represents this element
|
||
|
"""
|
||
|
raise NotImplementedError # this tag is suppsed to be defined in derived classes
|
||
|
|
||
|
def _xml(self, defs):
|
||
|
"""xml serialization"""
|
||
|
u = "<%s %s" % (self.tag, self._xml_attributes(defs))
|
||
|
content = self._xml_content(defs)
|
||
|
if content.strip():
|
||
|
u += ">\n" + \
|
||
|
_indent(content) + "\n" + \
|
||
|
"</%s>" % self.tag
|
||
|
else:
|
||
|
u += "/>"
|
||
|
return u
|
||
|
|
||
|
def _xml_content(self, defs):
|
||
|
"""xml serialization of content"""
|
||
|
return ""
|
||
|
|
||
|
def _xml_attributes(self, defs):
|
||
|
"""xml serialization of attributes"""
|
||
|
return " ".join(self._xml_attribute(name, defs) for name in self.attributes)
|
||
|
|
||
|
def _xml_attribute(self, name, defs):
|
||
|
"""unicode serialization of attribute/value pair"""
|
||
|
attribute = getattr(self, name)
|
||
|
if name == "href":
|
||
|
name = "xlink:href"
|
||
|
try:
|
||
|
href = "#%s" % attribute.id
|
||
|
except:
|
||
|
pass
|
||
|
else:
|
||
|
defs.append(attribute)
|
||
|
attribute = href
|
||
|
try:
|
||
|
u = attribute._xml_attr(defs)
|
||
|
except AttributeError:
|
||
|
u = _u(attribute)
|
||
|
return "%s='%s'" % (name.replace("_", "-"), u) if u else ""
|
||
|
|
||
|
def _xml_attr(self, defs):
|
||
|
defs.append(self)
|
||
|
return "url(#%s)" % self.id
|