quixote html.py,1.9,1.10
Neil Schemenauer <nascheme-fVcApmY9cLvQ3/1i3zOLAti2O/[email protected]>
| Newsgroups | gmane.comp.web.quixote.cvs |
|---|---|
| Message-ID | <[email protected]> |
Update of /home/cvs/quixote
In directory hewson:/tmp/cvs-serv20326
Modified Files:
html.py
Log Message:
Add htmltext class and supporting functions (htmlescape, htmltag, href).
Index: html.py
===================================================================
RCS file: /home/cvs/quixote/html.py,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -d -r1.9 -r1.10
--- html.py 9 Jul 2002 19:56:59 -0000 1.9
+++ html.py 14 Oct 2002 22:59:07 -0000 1.10
@@ -42,37 +42,219 @@
"""
-# created 20010821, NAS (from mems.ui.lib.util and mems.ui.lib.html)
-
__revision__ = "$Id$"
+import sys
import urllib
-from types import UnicodeType
+from types import UnicodeType, TupleType, StringType
+import re
+if sys.hexversion < 0x20200b1:
-def html_quote(value, fallback=None):
- """html_quote(value : any [, fallback : string]) -> string
+ # inefficient 2.2 compatibility hacks
- Quotes 'value' for use in an HTML page. The special characters &,
- <, > are replaced by SGML entities. If value is None, then the
- behavior depends on the fallback argument. If it is not supplied
- then an error is raised. Otherwise, the fallback value is returned
- unquoted.
+ class object:
+ pass
+
+ class staticmethod:
+ def __init__(self, func):
+ self.__call__ = func
+
+ def zip(*args):
+ return map(None, *args)
+
+ def classof(o):
+ if hasattr(o, "__class__"):
+ return o.__class__
+ else:
+ return type(o)
+
+else:
+
+ classof = type
+
+
+_format_codes = 'diouxXeEfFgGcrs%'
+_format_re = re.compile(r'%%[^%s]*([%s])' % (_format_codes, _format_codes))
+
+
+class htmltext(object):
"""
- if value is None:
- if fallback is None:
- raise ValueError, "value is None and no fallback supplied"
+
+ """
+
+ __slots__ = ['s']
+
+ def quote(s):
+ s = s.replace("&", "&")
+ s = s.replace("<", "<")
+ s = s.replace(">", ">")
+ s = s.replace('"', """)
+ return s
+ quote = staticmethod(quote)
+
+ def __new__(klass, s):
+ inst = object.__new__(klass)
+ inst.s = str(s)
+ return inst
+
+ if sys.hexversion < 0x20200b1:
+ def __init__(self, s):
+ self.s = str(s)
+
+ # XXX make read-only
+ #def __setattr__(self, name, value):
+ # raise AttributeError, 'immutable object'
+
+ def __getstate__(self):
+ raise ValueError, 'htmltext objects should not be pickled'
+
+ def __repr__(self):
+ return '<htmltext %r>' % self.s
+
+ def __str__(self):
+ return self.s
+
+ def __len__(self):
+ return len(self.s)
+
+ def __cmp__(self, other):
+ return cmp(self.s, other)
+
+ def __hash__(self):
+ return hash(self.s)
+
+ def __mod__(self, args):
+ codes = [code[-1] for code in _format_re.findall(self.s)
+ if code != '%']
+ if len(codes) == 1 and not isinstance(args, TupleType):
+ args = (args,)
+ quoted_args = []
+ for arg, code in zip(args, codes):
+ if classof(arg) is self.__class__:
+ quoted_args.append(arg)
+ elif code == 's':
+ quoted_args.append(self.quote(str(arg)))
+ elif code == 'r':
+ quoted_args.append(_QuotedRepr(arg, self.quote))
+ elif code == 'c':
+ raise ValueError, "htmltext does not allow '%c' format"
+ else:
+ quoted_args.append(arg)
+ return self.__class__(self.s % tuple(quoted_args))
+
+ def __add__(self, other):
+ if isinstance(other, StringType):
+ return self.__class__(self.s + self.quote(other))
+ elif classof(other) is self.__class__:
+ return self.__class__(self.s + other.s)
else:
- return fallback
- if isinstance(value, UnicodeType):
- value = value.encode('iso-8859-1')
+ return NotImplemented
+
+ def __radd__(self, other):
+ if isinstance(other, StringType):
+ return self.__class__(self.quote(other) + self.s)
+ else:
+ return NotImplemented
+
+ def __mul__(self, n):
+ return self.__class__(self.s * n)
+
+ def join(self, items):
+ quoted_items = []
+ for item in items:
+ if classof(item) is self.__class__:
+ quoted_items.append(str(item))
+ elif isinstance(item, StringType):
+ quoted_items.append(self.quote(item))
+ else:
+ raise TypeError, 'join request string arguments'
+ return self.__class__(self.s.join(quoted_items))
+
+ def startswith(self, s):
+ if isinstance(s, htmltext):
+ s = s.s
+ else:
+ s = self.quote(s)
+ return self.s.startswith(s)
+
+ def endswith(self, s):
+ if isinstance(s, htmltext):
+ s = s.s
+ else:
+ s = self.quote(s)
+ return self.s.endswith(s)
+
+ def replace(self, old, new, maxsplit=-1):
+ if isinstance(old, htmltext):
+ old = old.s
+ else:
+ old = self.quote(old)
+ if isinstance(new, htmltext):
+ new = new.s
+ else:
+ new = self.quote(new)
+ return self.__class__(self.s.replace(old, new))
+
+ def lower(self):
+ return self.__class__(self.s.lower())
+
+ def upper(self):
+ return self.__class__(self.s.upper())
+
+class _QuotedRepr:
+ # helper for htmltext class
+ def __init__(self, value, quote):
+ self.value = value
+ self.quote = quote
+
+ def __repr__(self):
+ return self.quote(`self.value`)
+
+
+def htmlescape(s):
+ """htmlescape(s) -> htmltext
+
+ Return an 'htmltext' object using the argument. If the argument is not
+ already a 'htmltext' object then the HTML markup characters ", <, >, and &
+ are first escaped.
+ """
+ if classof(s) is htmltext:
+ return s
+ elif isinstance(s, UnicodeType):
+ s = s.encode('iso-8859-1')
else:
- value = str(value)
- value = value.replace("&", "&") # must be done first
- value = value.replace("<", "<")
- value = value.replace(">", ">")
- value = value.replace('"', """)
- return value
+ s = str(s)
+ # inline htmltext.quote for speed
+ s = s.replace("&", "&") # must be done first
+ s = s.replace("<", "<")
+ s = s.replace(">", ">")
+ s = s.replace('"', """)
+ return htmltext(s)
+
+
+ValuelessAttr = ["valueless_attr"] # magic singleton object
+
+def htmltag (tag, xml_end=0, **attrs):
+ """Create a HTML tag.
+ """
+ r = "<%s" % tag
+ for (attr, val) in attrs.items():
+ if val is ValuelessAttr:
+ val = attr
+ if val is not None:
+ r += ' %s="%s"' % (attr, htmltext.quote(str(val)))
+ if xml_end:
+ r += " />"
+ else:
+ r += ">"
+ return htmltext(r)
+
+
+def href (url, text, title=None, **attrs):
+ return (htmltag("a", href=url, title=title, **attrs) +
+ htmltext(text) +
+ htmltext("</a>"))
def url_quote(value, fallback=None):
@@ -95,6 +277,36 @@
return urllib.quote(value)
+#
+# The rest of this module is for Quixote applications that were written
+# before 'htmltext'. If you are writing a new application, ignore them.
+#
+
+def html_quote(value, fallback=None):
+ """html_quote(value : any [, fallback : string]) -> str
+
+ Quotes 'value' for use in an HTML page. The special characters &,
+ <, > are replaced by SGML entities. If value is None, then the
+ behavior depends on the fallback argument. If it is not supplied
+ then an error is raised. Otherwise, the fallback value is returned
+ unquoted.
+ """
+ if value is None:
+ if fallback is None:
+ raise ValueError, "value is None and no fallback supplied"
+ else:
+ return fallback
+ elif isinstance(value, UnicodeType):
+ value = value.encode('iso-8859-1')
+ else:
+ value = str(value)
+ value = value.replace("&", "&") # must be done first
+ value = value.replace("<", "<")
+ value = value.replace(">", ">")
+ value = value.replace('"', """)
+ return value
+
+
def value_quote(value):
"""Quote HTML attribute values. This function is of marginal
utility since html_quote can be used.
@@ -113,8 +325,6 @@
return render_tag("a", href=url, title=title, name=name,
**kwargs) + str(text) + "</a>"
-
-ValuelessAttr = ["valueless_attr"] # magic singleton object
def render_tag (tag, xml_end=0, **attrs):
r = "<%s" % tag