Changeset 105:5d8f1609aaf3


Ignore:
Timestamp:
Nov 9, 2011, 6:44:36 PM (9 years ago)
Author:
Ralph Meijer <ralphm@…>
Branch:
default
Children:
155:8b2ed9549399, 159:bbb746f79718
Message:

Allow wokkel.data_form.Form to be used as a read-only dict.

In [1334124db2fd] wokkel.pubsub.PubSubRequest.options was changed to be a
Form instead of dictionary, which makes existing code using that field to be
incompatible.

To reduce the impact of that change, this change implements various methods in
wokkel.data_form.Form to emulate a read-only dictionary. It now maps the
name of each field to its value, similar to the dictionary returned from
getValues. The latter is now identical to dict(form).

  • * *

Address incompatible change concerning PubSubRequest?.options.

In changeset [1334124db2fd], PubSubRequest.options was changed to hold the
wokkel.data_form.Form that represents the data form sent in the original
request. This makes it easier to type check the form in a step separate from
parsing the request. However, this is an incompatible change.

To remedy this, we move the form to optionsForm and make options a property
that returns the values of the form.

Location:
wokkel
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • wokkel/data_form.py

    r96 r105  
    1313"""
    1414
     15from zope.interface import implements
     16from zope.interface.common import mapping
    1517from twisted.words.protocols.jabber.jid import JID
    1618from twisted.words.xish import domish
     
    4850    @type value: C{unicode}
    4951    @ivar label: Optional label for this option.
    50     @type label: C{unicode} or C{NoneType}.
     52    @type label: C{unicode} or C{NoneType}
    5153    """
    5254
     
    9597    @ivar fieldType: Type of this field. One of C{'boolean'}, C{'fixed'},
    9698                     C{'hidden'}, C{'jid-multi'}, C{'jid-single'},
    97                      C{'list-multi'}, {'list-single'}, C{'text-multi'},
     99                     C{'list-multi'}, C{'list-single'}, C{'text-multi'},
    98100                     C{'text-private'}, C{'text-single'}.
    99101
    100102                     The default is C{'text-single'}.
    101103    @type fieldType: C{str}
    102     @ivar var: Field name. Optional if L{fieldType} is C{'fixed'}.
     104    @ivar var: Field name. Optional if C{fieldType} is C{'fixed'}.
    103105    @type var: C{str}
    104106    @ivar label: Human readable label for this field.
     
    109111    @ivar options: List of possible values to choose from in a response
    110112                   to this form as a list of L{Option}s.
    111     @type options: C{list}.
     113    @type options: C{list}
    112114    @ivar desc: Human readable description for this field.
    113115    @type desc: C{unicode}
    114116    @ivar required: Whether the field is required to be provided in a
    115117                    response to this form.
    116     @type required: C{bool}.
     118    @type required: C{bool}
    117119    """
    118120
     
    366368    is recorded in the C{formNamespace} instance variable.
    367369
     370    A L{Form} also acts as read-only dictionary, with the values of fields
     371    keyed by their name. See L{__getitem__}.
     372
    368373    @ivar formType: Type of form. One of C{'form'}, C{'submit'}, {'cancel'},
    369374                    or {'result'}.
     
    379384    @ivar formNamespace: The optional namespace of the field names for this
    380385        form. This goes in the special field named C{'FORM_TYPE'}, if set.
    381     @type formNamespace: C{str}.
     386    @type formNamespace: C{str}
    382387
    383388    @ivar fields: Dictionary of named fields. Note that this is meant to be
     
    390395    @type fieldList: C{list}
    391396    """
     397
     398    implements(mapping.IIterableMapping,
     399               mapping.IEnumerableMapping,
     400               mapping.IReadMapping,
     401               mapping.IItemMapping)
    392402
    393403    def __init__(self, formType, title=None, instructions=None,
     
    469479        is rendered using L{toElement}, these fields will have no C{'type'}
    470480        attribute, and it is up to the receiving party to interpret the values
    471         properly (e.g. by knowing about the FORM_TYPE in L{formNamespace} and
     481        properly (e.g. by knowing about the FORM_TYPE in C{formNamespace} and
    472482        the field name).
    473483
     
    568578
    569579
     580    def __iter__(self):
     581        return iter(self.fields)
     582
     583
     584    def __len__(self):
     585        return len(self.fields)
     586
     587
     588    def __getitem__(self, key):
     589        """
     590        Called to implement evaluation of self[key].
     591
     592        This returns the value of the field with the name in C{key}. For
     593        multi-value fields, the value is a list, otherwise a single value.
     594
     595        If a field has no type, and the field has multiple values, the value
     596        of the list of values. Otherwise, it will be a single value.
     597
     598        Raises C{KeyError} if there is no field with the name in C{key}.
     599        """
     600        field = self.fields[key]
     601
     602        if (field.fieldType in ('jid-multi', 'list-multi', 'text-multi') or
     603            (field.fieldType is None and len(field.values) > 1)):
     604            value = field.values
     605        else:
     606            value = field.value
     607
     608        return value
     609
     610
     611    def get(self, key, default=None):
     612        try:
     613            return self[key]
     614        except KeyError:
     615            return default
     616
     617
     618    def __contains__(self, key):
     619        return key in self.fields
     620
     621
     622    def iterkeys(self):
     623        return iter(self)
     624
     625
     626    def itervalues(self):
     627        for key in self:
     628            yield self[key]
     629
     630
     631    def iteritems(self):
     632        for key in self:
     633            yield (key, self[key])
     634
     635
     636    def keys(self):
     637        return list(self)
     638
     639
     640    def values(self):
     641        return list(self.itervalues())
     642
     643
     644    def items(self):
     645        return list(self.iteritems())
     646
     647
    570648    def getValues(self):
    571649        """
     
    573651
    574652        For all named fields, the corresponding value or values are
    575         returned in a dictionary keyed by the field name. For multi-value
    576         fields, the dictionary value is a list, otherwise a single value.
    577 
    578         If a field has no type, and the field has multiple values, the value of
    579         the dictionary entry is the list of values. Otherwise, it will be a
    580         single value.
    581 
     653        returned in a dictionary keyed by the field name. This is equivalent
     654        do C{dict(f)}, where C{f} is a L{Form}.
     655
     656        @see: L{__getitem__}
    582657        @rtype: C{dict}
    583658        """
    584         values = {}
    585 
    586         for name, field in self.fields.iteritems():
    587             if (field.fieldType in ('jid-multi', 'list-multi', 'text-multi') or
    588                 (field.fieldType is None and len(field.values) > 1)):
    589                 value = field.values
    590             else:
    591                 value = field.value
    592 
    593             values[name] = value
    594 
    595         return values
     659        return dict(self)
    596660
    597661
  • wokkel/disco.py

    r103 r105  
    210210                item = data_form.Form.fromElement(child)
    211211
    212             if item:
     212            if item is not None:
    213213                info.append(item)
    214214
  • wokkel/pubsub.py

    r97 r105  
    385385        """
    386386        form = data_form.findForm(verbElement, NS_PUBSUB_NODE_CONFIG)
    387         if form and form.formType == 'submit':
     387        if form is not None and form.formType == 'submit':
    388388            values = form.getValues()
    389389            self.nodeType = values.get('pubsub#node_type', 'leaf')
     
    397397        """
    398398        form = data_form.findForm(verbElement, NS_PUBSUB_NODE_CONFIG)
    399         if form:
     399        if form is not None:
    400400            if form.formType in ('submit', 'cancel'):
    401401                self.options = form
     
    413413            if element.uri == NS_PUBSUB and element.name == 'configure':
    414414                form = data_form.findForm(element, NS_PUBSUB_NODE_CONFIG)
    415                 if form:
     415                if form is not None:
    416416                    if form.formType != 'submit':
    417417                        raise BadRequest(text=u"Unexpected form type '%s'" %
     
    497497        """
    498498        form = data_form.findForm(verbElement, NS_PUBSUB_SUBSCRIBE_OPTIONS)
    499         if form:
     499        if form is not None:
    500500            if form.formType in ('submit', 'cancel'):
    501501                self.options = form
     
    516516                form = data_form.findForm(element,
    517517                                          NS_PUBSUB_SUBSCRIBE_OPTIONS)
    518                 if form:
     518                if form is not None:
    519519                    if form.formType != 'submit':
    520520                        raise BadRequest(text=u"Unexpected form type '%s'" %
     
    527527
    528528    def _render_optionsWithSubscribe(self, verbElement):
    529         if self.options:
     529        if self.options is not None:
    530530            optionsElement = verbElement.parent.addElement('options')
    531531            self._render_options(optionsElement)
     
    11951195
    11961196            handler = getattr(self, handlerName)
    1197 
    11981197            args = [getattr(request, arg) for arg in argNames]
    1199             if 'options' in argNames:
    1200                 args[argNames.index('options')] = request.options.getValues()
    1201 
    12021198            d = handler(*args)
    12031199
  • wokkel/test/test_data_form.py

    r96 r105  
    55Tests for {wokkel.data_form}.
    66"""
     7
     8from zope.interface import verify
     9from zope.interface.common.mapping import IIterableMapping
    710
    811from twisted.trial import unittest
     
    473476        element = domish.Element((NS_X_DATA, 'field'))
    474477        element['var'] = 'test'
    475         child = element.addElement(('myns', 'value'))
     478        element.addElement(('myns', 'value'))
    476479        field = data_form.Field.fromElement(element)
    477480
     
    989992
    990993
    991     def test_getValues(self):
    992         """
    993         Each named field is represented in the values, keyed by name.
     994    def test_interface(self):
     995        """
     996        L{Form}s act as a read-only dictionary.
     997        """
     998        form = data_form.Form('submit')
     999        verify.verifyObject(IIterableMapping, form)
     1000
     1001
     1002    def test_getitem(self):
     1003        """
     1004        Using Form as a mapping will yield the value of fields keyed by name.
    9941005        """
    9951006        fields = [data_form.Field(var='botname', value='The Jabber Bot'),
     
    9981009                                                values=['news', 'search'])]
    9991010        form = data_form.Form('submit', fields=fields)
    1000         values = form.getValues()
     1011        self.assertEqual('The Jabber Bot', form['botname'])
     1012        self.assertTrue(form['public'])
     1013        self.assertEqual(['news', 'search'], form['features'])
     1014
     1015
     1016    def test_getitemOneValueTypeMulti(self):
     1017        """
     1018        A single value for a multi-value field type is returned in a list.
     1019        """
     1020        fields = [data_form.Field('list-multi', var='features',
     1021                                                values=['news'])]
     1022        form = data_form.Form('submit', fields=fields)
     1023        self.assertEqual(['news'], form['features'])
     1024
     1025
     1026    def test_getitemMultipleValuesNoType(self):
     1027        """
     1028        Multiple values for a field without type are returned in a list.
     1029        """
     1030        fields = [data_form.Field(None, var='features',
     1031                                        values=['news', 'search'])]
     1032        form = data_form.Form('submit', fields=fields)
     1033        self.assertEqual(['news', 'search'], form['features'])
     1034
     1035
     1036    def test_getitemMultipleValuesTypeSingle(self):
     1037        """
     1038        Multiple values for a single-value field type returns the first value.
     1039        """
     1040        fields = [data_form.Field('text-single', var='features',
     1041                                        values=['news', 'search'])]
     1042        form = data_form.Form('submit', fields=fields)
     1043        self.assertEqual('news', form['features'])
     1044
     1045
     1046    def test_get(self):
     1047        """
     1048        Getting the value of a known field succeeds.
     1049        """
     1050        fields = [data_form.Field(var='botname', value='The Jabber Bot')]
     1051        form = data_form.Form('submit', fields=fields)
     1052        self.assertEqual('The Jabber Bot', form.get('botname'))
     1053
     1054
     1055    def test_getUnknownNone(self):
     1056        """
     1057        Getting the value of a unknown field returns None.
     1058        """
     1059        fields = [data_form.Field(var='botname', value='The Jabber Bot')]
     1060        form = data_form.Form('submit', fields=fields)
     1061        self.assertIdentical(None, form.get('features'))
     1062
     1063
     1064    def test_getUnknownDefault(self):
     1065        """
     1066        Getting the value of a unknown field returns specified default.
     1067        """
     1068        fields = [data_form.Field(var='botname', value='The Jabber Bot')]
     1069        form = data_form.Form('submit', fields=fields)
     1070        self.assertTrue(form.get('public', True))
     1071
     1072
     1073    def test_contains(self):
     1074        """
     1075        A form contains a known field.
     1076        """
     1077        fields = [data_form.Field(var='botname', value='The Jabber Bot')]
     1078        form = data_form.Form('submit', fields=fields)
     1079        self.assertIn('botname', form)
     1080
     1081
     1082    def test_containsNot(self):
     1083        """
     1084        A form does not contains an unknown field.
     1085        """
     1086        fields = [data_form.Field(var='botname', value='The Jabber Bot')]
     1087        form = data_form.Form('submit', fields=fields)
     1088        self.assertNotIn('features', form)
     1089
     1090
     1091    def test_iterkeys(self):
     1092        """
     1093        Iterating over the keys of a form yields all field names.
     1094        """
     1095        fields = [data_form.Field(var='botname', value='The Jabber Bot'),
     1096                  data_form.Field('boolean', var='public', value=True),
     1097                  data_form.Field('list-multi', var='features',
     1098                                                values=['news', 'search'])]
     1099        form = data_form.Form('submit', fields=fields)
     1100        self.assertEqual(set(['botname', 'public', 'features']),
     1101                         set(form.iterkeys()))
     1102
     1103
     1104    def test_itervalues(self):
     1105        """
     1106        Iterating over the values of a form yields all field values.
     1107        """
     1108        fields = [data_form.Field(var='botname', value='The Jabber Bot'),
     1109                  data_form.Field('boolean', var='public', value=True)]
     1110        form = data_form.Form('submit', fields=fields)
     1111        self.assertEqual(set(['The Jabber Bot', True]),
     1112                         set(form.itervalues()))
     1113
     1114
     1115    def test_iteritems(self):
     1116        """
     1117        Iterating over the values of a form yields all item tuples.
     1118        """
     1119        fields = [data_form.Field(var='botname', value='The Jabber Bot'),
     1120                  data_form.Field('boolean', var='public', value=True)]
     1121        form = data_form.Form('submit', fields=fields)
     1122        self.assertEqual(set([('botname', 'The Jabber Bot'),
     1123                              ('public', True)]),
     1124                         set(form.iteritems()))
     1125
     1126
     1127    def test_keys(self):
     1128        """
     1129        Getting the keys of a form yields a list of field names.
     1130        """
     1131        fields = [data_form.Field(var='botname', value='The Jabber Bot'),
     1132                  data_form.Field('boolean', var='public', value=True),
     1133                  data_form.Field('list-multi', var='features',
     1134                                                values=['news', 'search'])]
     1135        form = data_form.Form('submit', fields=fields)
     1136        keys = form.keys()
     1137        self.assertIsInstance(keys, list)
     1138        self.assertEqual(set(['botname', 'public', 'features']),
     1139                         set(keys))
     1140
     1141
     1142    def test_values(self):
     1143        """
     1144        Getting the values of a form yields a list of field values.
     1145        """
     1146        fields = [data_form.Field(var='botname', value='The Jabber Bot'),
     1147                  data_form.Field('boolean', var='public', value=True)]
     1148        form = data_form.Form('submit', fields=fields)
     1149        values = form.values()
     1150        self.assertIsInstance(values, list)
     1151        self.assertEqual(set(['The Jabber Bot', True]), set(values))
     1152
     1153
     1154    def test_items(self):
     1155        """
     1156        Iterating over the values of a form yields a list of all item tuples.
     1157        """
     1158        fields = [data_form.Field(var='botname', value='The Jabber Bot'),
     1159                  data_form.Field('boolean', var='public', value=True)]
     1160        form = data_form.Form('submit', fields=fields)
     1161        items = form.items()
     1162        self.assertIsInstance(items, list)
     1163        self.assertEqual(set([('botname', 'The Jabber Bot'),
     1164                              ('public', True)]),
     1165                         set(items))
     1166
     1167
     1168    def test_getValues(self):
     1169        """
     1170        L{Form.getValues} returns a dict of all field values.
     1171        """
     1172        fields = [data_form.Field(var='botname', value='The Jabber Bot'),
     1173                  data_form.Field('boolean', var='public', value=True),
     1174                  data_form.Field('list-multi', var='features',
     1175                                                values=['news', 'search'])]
     1176        form = data_form.Form('submit', fields=fields)
    10011177        self.assertEqual({'botname': 'The Jabber Bot',
    10021178                          'public': True,
    10031179                          'features': ['news', 'search']},
    1004                          values)
    1005 
    1006 
    1007     def test_getValuesOneValueTypeMulti(self):
    1008         """
    1009         A single value for a multi-value field type is returned in a list.
    1010         """
    1011         fields = [data_form.Field('list-multi', var='features',
    1012                                                 values=['news'])]
    1013         form = data_form.Form('submit', fields=fields)
    1014         values = form.getValues()
    1015         self.assertEqual({'features': ['news']}, values)
    1016 
    1017 
    1018     def test_getValuesMultipleValuesNoType(self):
    1019         """
    1020         Multiple values for a field without type are returned in a list.
    1021         """
    1022         fields = [data_form.Field(None, var='features',
    1023                                         values=['news', 'search'])]
    1024         form = data_form.Form('submit', fields=fields)
    1025         values = form.getValues()
    1026         self.assertEqual({'features': ['news', 'search']}, values)
    1027 
    1028 
    1029     def test_getValuesMultipleValuesTypeSingle(self):
    1030         """
    1031         Multiple values for a single-value field type returns the first value.
    1032         """
    1033         fields = [data_form.Field('text-single', var='features',
    1034                                         values=['news', 'search'])]
    1035         form = data_form.Form('submit', fields=fields)
    1036         values = form.getValues()
    1037         self.assertEqual({'features': 'news'}, values)
     1180                         form.getValues())
    10381181
    10391182
  • wokkel/test/test_pubsub.py

    r97 r105  
    16181618
    16191619        request = pubsub.PubSubRequest.fromElement(parseXml(xml))
    1620         values = request.options.getValues()
     1620        values = request.options
    16211621        self.assertIn('pubsub#access_model', values)
    16221622        self.assertEqual(u'open', values['pubsub#access_model'])
     
    29142914        def configureSet(request):
    29152915            self.assertEquals(['pubsub#deliver_payloads'],
    2916                               request.options.fields.keys())
     2916                              request.options.keys())
    29172917
    29182918        self.resource.getConfigurationOptions = getConfigurationOptions
     
    37093709
    37103710        def setConfiguration(requestor, service, nodeIdentifier, options):
    3711             self.assertEquals({'pubsub#deliver_payloads': False,
    3712                                'pubsub#persist_items': True}, options)
    3713 
     3711            self.assertIn('pubsub#deliver_payloads', options)
     3712            self.assertFalse(options['pubsub#deliver_payloads'])
     3713            self.assertIn('pubsub#persist_items', options)
     3714            self.assertTrue(options['pubsub#persist_items'])
    37143715
    37153716        self.service.getConfigurationOptions = getConfigurationOptions
Note: See TracChangeset for help on using the changeset viewer.