""" open/DurusWorks/qp/fill/widget.qpy """ from qpy import xml, stringify from qp.fill.html import htmltag from qp.http.request import Upload from qp.lib.spec import string_classes from qp.pub.common import get_fields, get_request import sys def subname(prefix, name): """Create a unique name for a sub-widget or sub-component.""" return "%s__%s" % (prefix, name) def merge_attrs(base, overrides): """({string: any}, {string: any}) -> {string: any} """ items = [] if base: items.extend(list(base.items())) if overrides: items.extend(list(overrides.items())) attrs = {} for name, val in items: if name.endswith('_'): name = name[:-1] attrs[name] = val return attrs def redecode(s): """ Form inputs sometimes receive strings values that have been re-encoded as utf8 multiple times. A character that looks like an A with a mark above it, for example, might appear. This function attempts to return the original form of the unicode string. """ if not s: return s def has_bad(x): for character in x: if 128 <= ord(character) <= 256: return True for level in range(5): if has_bad(s): try: s = s.encode('latin1').decode('utf8') except (UnicodeDecodeError, UnicodeEncodeError): return s return s class WidgetValueError(Exception): """May be raised a widget has problems parsing its value.""" def __init__(self, msg): self.msg = msg def __str__(self): return stringify(self.msg) class Widget (object): """Abstract base class for web widgets. Instance attributes: name : string value : any error : string title : string hint : string required : bool attrs : {string: any} _parsed : bool Feel free to access these directly; to set them, use the 'set_*()' modifier methods. """ def __init__(self, name, value=None, title="", hint="", required=False, render_br=None, redecode=False, attrs=None, **kwattrs): # render_br is here as a keyword for the sole purpose of stopping # it from being rendered as an attr when called from code that # was really written for Quixote widgets. It will probably be # removed from a future release. assert self.__class__ is not Widget, "abstract class" self.name = name self.value = value self.error = None self.title = title self.hint = hint self.required = required self.redecode = redecode self.attrs = merge_attrs(attrs, kwattrs) self._parsed = False def __repr__(self): return "<%s at %x: %s>" % (self.__class__.__name__, id(self), self.name) def __str__(self): return "%s: %s" % (self.__class__.__name__, self.name) def __iter__(self): yield self def get_name(self): return self.name def set_value(self, value): self.value = value def set_error(self, error): self.error = error def get_error(self, request=None): self.parse(request=request) return self.error def has_error(self, request=None): return bool(self.get_error(request=request)) def clear_error(self, request=None): self.parse(request=request) self.error = None def set_title(self, title): self.title = title def get_title(self): return self.title def set_hint(self, hint): self.hint = hint def get_hint(self): return self.hint def is_required(self): return self.required def is_submitted(self, request=None): if request is None: request = get_request() return self.name in get_fields() def parse(self, request=None): if not self._parsed: self._parsed = True if request is None: request = get_request() if self.is_submitted(request=request): try: self._parse(request) except WidgetValueError: exc = sys.exc_info()[1] self.set_error(stringify(exc)) if (self.required and self.value is None and not self.has_error()): if isinstance(self.required, string_classes): self.set_error(self.required) else: self.set_error('required') return self.value def _parse(self, request): # subclasses may override but this is not part of the public API value = request.get_field(self.name) if isinstance(value, string_classes) and value.strip(): self.value = value else: self.value = None def clear(self, request=None): if request is None: request = get_request() if self.name in get_fields(): del get_fields()[self.name] if self._parsed: self._parse(request) def render_title:xml(self, title): if title: '\n' if self.required: title += '*' '