From: Martin A. <svn...@pl...> - 2009-04-12 16:07:06
|
Author: optilude Date: Sun Apr 12 16:07:02 2009 New Revision: 26487 Modified: plone.tiles/trunk/plone/tiles/data.py plone.tiles/trunk/plone/tiles/data.txt Log: Add tests + fixes for the data encode/decode methods Modified: plone.tiles/trunk/plone/tiles/data.py ============================================================================== --- plone.tiles/trunk/plone/tiles/data.py (original) +++ plone.tiles/trunk/plone/tiles/data.py Sun Apr 12 16:07:02 2009 @@ -114,6 +114,7 @@ for name, field in getFieldsInOrder(schema): if name in ignore or name not in data: continue + converter = type_to_converter.get(field.__class__, None) if converter is None: raise KeyError(u"Cannot URL encode %s of type %s" % (name, field.__class__,)) @@ -122,6 +123,10 @@ if converter: encoded_name = "%s:%s" % (name, converter,) + value = data[name] + if value is None: + continue + if ISequence.providedBy(field): value_type_converter = type_to_converter.get(field.value_type.__class__, None) if value_type_converter is None: @@ -130,18 +135,23 @@ if value_type_converter: encoded_name = "%s:%s:%s" % (name, value_type_converter, converter,) + + for item in value: + + if isinstance(item, bool): + item = item and '1' or '' + + encode.append((encoded_name, item,)) + + else: + - value = data[name] - - # The :bool converter just does bool() value, but urlencode() does - # str() on the object. The result is False => 'False' => True :( - if isinstance(value, bool): - if value: - value = '1' - else: - value = '' + # The :bool converter just does bool() value, but urlencode() does + # str() on the object. The result is False => 'False' => True :( + if isinstance(value, bool): + value = value and '1' or '' - encode.append((encoded_name, data[name])) + encode.append((encoded_name, value)) return urllib.urlencode(encode) Modified: plone.tiles/trunk/plone/tiles/data.txt ============================================================================== --- plone.tiles/trunk/plone/tiles/data.txt (original) +++ plone.tiles/trunk/plone/tiles/data.txt Sun Apr 12 16:07:02 2009 @@ -0,0 +1,121 @@ +====================== +Data encoding/decoding +====================== + +This test exercises the encode() and decode() methods in plone.tiles.data. + + >>> from zope.interface import Interface + >>> from zope import schema + + >>> from plone.tiles.data import encode, decode + +Encoding +-------- + +First, we'll create a simple schema that exercises several field types: + + >>> class ISimple(Interface): + ... text_line = schema.TextLine(title=u"Text") + ... ascii_line = schema.ASCIILine(title=u"ASCII") + ... text = schema.Text(title=u"Text", missing_value=u"Missing") + ... ascii = schema.ASCII(title=u"ASCII") + ... int = schema.Int(title=u"Int") + ... float = schema.Float(title=u"Float") + ... bool = schema.Bool(title=u"Bool") + +A simple encode produces a query string: + + >>> data = dict(text_line=u'A', ascii_line='B', text=u'C\nD', ascii='E\nF', int=3, float=1.2, bool=False) + >>> encode(data, ISimple) + 'text_line=A&ascii_line=B&text%3Atext=C%0AD&ascii%3Atext=E%0AF&int%3Along=3&float%3Afloat=1.2&bool%3Aboolean=' + +Notice how a boolean is encoded as an empty value. If it were true, it'd be +encoded as 1: + + >>> data = dict(text_line=u'A', ascii_line='B', text=u'C\nD', ascii='E\nF', int=3, float=1.2, bool=True) + >>> encode(data, ISimple) + 'text_line=A&ascii_line=B&text%3Atext=C%0AD&ascii%3Atext=E%0AF&int%3Along=3&float%3Afloat=1.2&bool%3Aboolean=1' + +If the data dictionary has values not in the interface, they are ignored: + + >>> data = dict(text_line=u'A', ascii_line='B', text=u'C\nD', ascii='E\nF', int=3, float=1.2, bool=True, foo=123) + >>> encode(data, ISimple) + 'text_line=A&ascii_line=B&text%3Atext=C%0AD&ascii%3Atext=E%0AF&int%3Along=3&float%3Afloat=1.2&bool%3Aboolean=1' + +If the data dictionary omits some fields, they are ignored. + + >>> data = dict(text_line=u'A', ascii_line='B', text=u'C\nD', ascii='E\nF', float=1.2, bool=True, foo=123) + >>> encode(data, ISimple) + 'text_line=A&ascii_line=B&text%3Atext=C%0AD&ascii%3Atext=E%0AF&float%3Afloat=1.2&bool%3Aboolean=1' + +It is also possible to explicitly ignore some fields: + + >>> data = dict(text_line=u'A', ascii_line='B', text=u'C\nD', ascii='E\nF', float=1.2, bool=True, foo=123) + >>> encode(data, ISimple, ignore=('text_line', 'text',)) + 'ascii_line=B&ascii%3Atext=E%0AF&float%3Afloat=1.2&bool%3Aboolean=1' + +Lists and tuples may also be encoded. The value type will be encoded as well. + + >>> class ISequences(Interface): + ... list = schema.List(title=u"List", value_type=schema.ASCIILine(title=u"Text")) + ... tuple = schema.Tuple(title=u"List", value_type=schema.Int(title=u"Int")) + + >>> data = dict(list=['a', 'b'], tuple=(1,2,3)) + >>> encode(data, ISequences) + 'list%3Alist=a&list%3Alist=b&tuple%3Along%3Atuple=1&tuple%3Along%3Atuple=2&tuple%3Along%3Atuple=3' + +Unsupported fields will raise a KeyError. This also applies to the value_type +of a list or tupel: + + >>> class IUnsupported(Interface): + ... decimal = schema.Decimal(title=u"Decimal") + ... list = schema.List(title=u"Set", value_type=schema.Decimal(title=u"Decimal")) + ... bytes_line = schema.BytesLine(title=u"Bytes line") + + >>> from decimal import Decimal + >>> data = dict(decimal=Decimal(2), list=[Decimal(1), Decimal(2),], bytes_line='abc') + >>> encode(data, IUnsupported) # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + KeyError: u"Cannot URL encode decimal of type <class 'zope.schema._field.Decimal'>" + + >>> encode(data, IUnsupported, ignore=('decimal',)) # doctest: +ELLIPSIS + Traceback (most recent call last): + ... + KeyError: u"Cannot URL encode value type for list of type <class 'zope.schema._field.List'> : <class 'zope.schema._field.Decimal'>" + + >>> encode(data, IUnsupported, ignore=('decimal', 'list',)) + 'bytes_line=abc' + +Decoding +-------- + +The decoder exists because the Zope form marshalers are not perfect: for +instance, they cannot adequately deal with the differences between unicode +and ascii. zope.schema is picky about that sort of thing. + +Let's use a data dictionary that may have come back from a query string like +the first example above. + + >>> data = dict(text_line=u'A', ascii_line=u'B', text=u'C\nD', ascii=u'E\nF', int=3, float=1.2, bool=False) + >>> sorted(decode(data, ISimple).items()) + [('ascii', 'E\nF'), ('ascii_line', 'B'), ('bool', False), ('float', 1.2), ('int', 3), ('text', u'C\nD'), ('text_line', u'A')] + +If any values are missing from the input dictionary, they will default to +missing_value. + + >>> data = dict(text_line=u'A', ascii_line=u'B', int=3, float=1.2, bool=False) + >>> sorted(decode(data, ISimple).items()) + [('ascii', None), ('ascii_line', 'B'), ('bool', False), ('float', 1.2), ('int', 3), ('text', u'Missing'), ('text_line', u'A')] + +If you pass missing=False, the values are ignored instead. + + >>> data = dict(text_line=u'A', ascii_line=u'B', int=3, float=1.2, bool=False) + >>> sorted(decode(data, ISimple, missing=False).items()) + [('ascii_line', 'B'), ('bool', False), ('float', 1.2), ('int', 3), ('text_line', u'A')] + +Decoding also works for lists and their value types: + + >>> data = dict(list=[u'a', u'b']) + >>> sorted(decode(data, ISequences, missing=False).items()) + [('list', ['a', 'b'])] |