Added an AJAX framework within appy.gen, and its first use: a pagination mechanism for producing paginated references in the reference widget.

This commit is contained in:
Gaetan Delannay 2009-10-25 21:42:08 +01:00
parent 4c4b2d0f87
commit 605c42d94e
20 changed files with 546 additions and 187 deletions

View file

@ -21,6 +21,13 @@ import xml.sax
from xml.sax.handler import ContentHandler, ErrorHandler
from xml.sax.xmlreader import InputSource
from StringIO import StringIO
from appy.shared.errors import AppyError
# Error-related constants ------------------------------------------------------
CONVERSION_ERROR = '"%s" value "%s" could not be converted by the XML ' \
'unmarshaller.'
CUSTOM_CONVERSION_ERROR = 'Custom converter for "%s" values produced an ' \
'error while converting value "%s". %s'
# ------------------------------------------------------------------------------
class XmlElement:
@ -150,7 +157,7 @@ class XmlUnmarshaller(XmlParser):
If "object" is specified, it means that the tag contains sub-tags, each
one corresponding to the value of an attribute for this object.
if "tuple" is specified, it will be converted to a list.'''
def __init__(self, klass=None, tagTypes={}):
def __init__(self, klass=None, tagTypes={}, conversionFunctions={}):
XmlParser.__init__(self)
self.klass = klass # If a klass is given here, instead of creating
# a root UnmarshalledObject instance, we will create an instance of this
@ -167,6 +174,19 @@ class XmlUnmarshaller(XmlParser):
# it is not the case of p_xmlContent, you can provide the missing type
# information in p_tagTypes. Here is an example of p_tagTypes:
# {"information": "list", "days": "list", "person": "object"}.
self.conversionFunctions = conversionFunctions
# The parser assumes that data is represented in some standard way. If
# it is not the case, you may provide, in this dict, custom functions
# allowing to convert values of basic types (long, float, DateTime...).
# Every such function must take a single arg which is the value to
# convert and return the converted value. Dict keys are strings
# representing types ('bool', 'int', 'unicode', etc) and dict values are
# conversion functions. Here is an example:
# {'int': convertInteger, 'DateTime': convertDate}
# NOTE: you can even invent a new basic type, put it in self.tagTypes,
# and create a specific conversionFunction for it. This way, you can
# for example convert strings that have specific values (in this case,
# knowing that the value is a 'string' is not sufficient).
def startDocument(self):
self.res = None # The resulting web of Python objects
@ -246,18 +266,37 @@ class XmlUnmarshaller(XmlParser):
def endElement(self, elem):
e = XmlParser.endElement(self, elem)
if e.currentBasicType:
# Get and convert the value of this field
if e.currentBasicType in self.numericTypes:
try:
exec 'value = %s' % e.currentContent.strip()
except SyntaxError:
value = None
elif e.currentBasicType == 'DateTime':
value = DateTime(e.currentContent.strip())
elif e.currentBasicType == 'base64':
value = e.currentContent.decode('base64')
value = e.currentContent.strip()
if not value: value = None
else:
value = e.currentContent.strip()
# If we have a custom converter for values of this type, use it.
if self.conversionFunctions.has_key(e.currentBasicType):
try:
value = self.conversionFunctions[e.currentBasicType](
value)
except Exception, err:
raise AppyError(CUSTOM_CONVERSION_ERROR % (
e.currentBasicType, value, str(err)))
# If not, try a standard conversion
elif e.currentBasicType in self.numericTypes:
try:
exec 'value = %s' % value
except SyntaxError:
raise AppyError(CONVERSION_ERROR % (
e.currentBasicType, value))
except NameError:
raise AppyError(CONVERSION_ERROR % (
e.currentBasicType, value))
# Check that the value is of the correct type. For instance,
# a float value with a comma in it could have been converted
# to a tuple instead of a float.
if not isinstance(value, eval(e.currentBasicType)):
raise AppyError(CONVERSION_ERROR % (
e.currentBasicType, value))
elif e.currentBasicType == 'DateTime':
value = DateTime(value)
elif e.currentBasicType == 'base64':
value = e.currentContent.decode('base64')
# Store the value on the last container
self.storeValue(elem, value)
# Clean the environment