533
505
@param name: (keyword) The name of the API call that this schema
534
506
represents. Accessible via the C{name} attribute.
535
@param parameters: (keyword) The parameters of the API, as a list of
536
named L{Parameter} instances.
507
@param parameters: (keyword) The parameters of the API, as a mapping
508
of parameter names to L{Parameter} instances.
537
509
@param doc: (keyword) The documentation of this API Call. Accessible
538
510
via the C{doc} attribute.
539
@param result: (keyword) A description of the result of this API
540
call. Accessible via the C{result} attribute.
511
@param result: (keyword) A description of the result of this API call,
512
in the same format as C{parameters}. Accessible via the C{result}
541
514
@param errors: (keyword) A sequence of exception classes that the API
542
515
can potentially raise. Accessible as a L{set} via the C{errors}
690
648
'name': self.name,
692
'parameters': self._parameters[:],
650
'parameters': self._parameters.copy(),
693
651
'result': self.result.copy() if self.result else {},
694
652
'errors': self.errors.copy() if self.errors else set()}
695
if 'parameters' in kwargs:
696
new_params = kwargs.pop('parameters')
697
new_kwargs['parameters'].extend(new_params)
653
new_kwargs['parameters'].update(kwargs.pop('parameters', {}))
698
654
new_kwargs['result'].update(kwargs.pop('result', {}))
699
655
new_kwargs['errors'].update(kwargs.pop('errors', set()))
700
656
new_kwargs.update(kwargs)
703
659
parameters = self._convert_old_schema(schema_items)
704
new_kwargs['parameters'].extend(parameters)
660
new_kwargs['parameters'].update(parameters)
705
661
return Schema(**new_kwargs)
707
663
def _convert_old_schema(self, parameters):
722
677
fields={"baz": List(item=Integer()),
723
"shimmy": Integer()}))]
678
"shimmy": Integer()}))}
725
680
By design, the old schema syntax ignored the names "bar" and "quux".
727
# 'merged' here is an associative list that maps parameter names to
728
# Parameter instances, OR sub-associative lists which represent nested
729
# lists and structures.
733
# [("foo", Integer("foo"))]
735
# [Integer("foo.bar")]
736
# (which represents a list of integers called "foo" with a meaningless
737
# index name of "bar") becomes
738
# [("foo", [("bar", Integer("foo.bar"))])].
740
683
for parameter in parameters:
741
segments = parameter.name.split('.')
742
_merge_associative_list(merged, segments, parameter)
743
result = [self._inner_convert_old_schema(node, 1) for node in merged]
684
crap[parameter.name] = parameter
685
nest = self._convert_flat_to_nest(crap)
686
return self._inner_convert_old_schema(nest, 0).fields
746
def _inner_convert_old_schema(self, node, depth):
688
def _inner_convert_old_schema(self, mapping, depth):
748
690
Internal recursion helper for L{_convert_old_schema}.
750
@param node: A node in the associative list tree as described in
751
_convert_old_schema. A two tuple of (name, parameter).
752
@param depth: The depth that the node is at. This is important to know
753
if we're currently processing a list or a structure. ("foo.N" is a
754
list called "foo", "foo.N.fieldname" describes a field in a list of
757
name, parameter_description = node
758
if not isinstance(parameter_description, list):
759
# This is a leaf, i.e., an actual L{Parameter} instance.
760
return parameter_description
692
if not isinstance(mapping, dict):
761
694
if depth % 2 == 0:
762
# we're processing a structure.
764
for node in parameter_description:
765
fields[node[0]] = self._inner_convert_old_schema(
767
return Structure(name, fields=fields)
696
for k, v in mapping.iteritems():
697
fields[k] = self._inner_convert_old_schema(v, depth + 1)
698
return Structure("anonymous_structure", fields=fields)
769
# we're processing a list.
770
if not isinstance(parameter_description, list):
771
raise TypeError("node %r must be an associative list"
772
% (parameter_description,))
773
if not len(parameter_description) == 1:
775
"Multiple different index names specified: %r"
776
% ([item[0] for item in parameter_description],))
777
subnode = parameter_description[0]
778
item = self._inner_convert_old_schema(subnode, depth + 1)
700
if not isinstance(mapping, dict):
701
raise TypeError("mapping %r must be a dict" % (mapping,))
702
if not len(mapping) == 1:
703
raise ValueError("Multiple different index names specified: %r"
705
item = mapping.values()[0]
706
item = self._inner_convert_old_schema(item, depth + 1)
707
name = item.name.split('.', 1)[0]
779
708
return List(name=name, item=item, optional=item.optional)
782
def _merge_associative_list(alist, path, value):
784
Merge a value into an associative list at the given path, maintaining
785
insertion order. Examples will explain it::
788
>>> _merge_associative_list(alist, ["foo", "bar"], "barvalue")
789
>>> _merge_associative_list(alist, ["foo", "baz"], "bazvalue")
790
>>> alist == [("foo", [("bar", "barvalue"), ("baz", "bazvalue")])]
792
@param alist: An associative list of names to values.
793
@param path: A path through sub-alists which we ultimately want to point to
795
@param value: The value to set.
796
@return: None. This operation mutates the associative list in place.
798
for key in path[:-1]:
805
alist.append((key, subalist))
807
alist.append((path[-1], value))