[Txaws-dev] [Merge] lp:~radix/txaws/parameter-enrichment into lp:txaws
Christopher Armstrong has proposed merging lp:~radix/txaws/parameter-enrichment into lp:txaws. Requested reviews: txAWS Technical List (txaws-tech) txAWS Technical List (txaws-tech) Related bugs: Bug #984660 in txAWS: "Enrich schema declarations to allow for describing all the details of an API" https://bugs.launchpad.net/txaws/+bug/984660 For more details, see: https://code.launchpad.net/~radix/txaws/parameter-enrichment/+merge/103592 This is one part of schema enrichment. It adds List and Structure Parameter types, and significantly refactors the way that paramater parsing and formatting is done. -- https://code.launchpad.net/~radix/txaws/parameter-enrichment/+merge/103592 Your team txAWS Technical List is requested to review the proposed merge of lp:~radix/txaws/parameter-enrichment into lp:txaws. === modified file 'txaws/server/schema.py' --- txaws/server/schema.py 2012-03-27 11:51:09 + +++ txaws/server/schema.py 2012-04-26 01:41:19 + @@ -26,6 +26,12 @@ super(MissingParameterError, self).__init__(message) +class InconsistentParameterError(SchemaError): +def __init__(self, name): +message = "Parameter %s is used inconsistently" % (name,) +super(InconsistentParameterError, self).__init__(message) + + class InvalidParameterValueError(SchemaError): """Raised when the value of a parameter is invalid.""" @@ -51,6 +57,20 @@ super(UnknownParameterError, self).__init__(message) +class UnknownParametersError(Exception): +""" +Raised when extra unknown fields are passed to L{Structure.parse}. + +@ivar result: The already coerced result representing the known parameters. +@ivar unknown: The unknown parameters. +""" +def __init__(self, result, unknown): +self.result = result +self.unknown = unknown +message = "The parameters %s are not recognized" % (unknown,) +super(UnknownParametersError, self).__init__(message) + + class Parameter(object): """A single parameter in an HTTP request. @@ -67,7 +87,9 @@ @param validator: A callable to validate the parameter, returning a bool. """ -def __init__(self, name, optional=False, default=None, +supports_multiple = False + +def __init__(self, name=None, optional=False, default=None, min=None, max=None, allow_none=False, validator=None): self.name = name self.optional = optional @@ -182,7 +204,7 @@ lower_than_min_template = "Value must be at least %s." greater_than_max_template = "Value exceeds maximum of %s." -def __init__(self, name, optional=False, default=None, +def __init__(self, name=None, optional=False, default=None, min=0, max=None, allow_none=False, validator=None): super(Integer, self).__init__(name, optional, default, min, max, allow_none, validator) @@ -228,8 +250,10 @@ kind = "enum" -def __init__(self, name, mapping, optional=False, default=None): +def __init__(self, name=None, mapping=None, optional=False, default=None): super(Enum, self).__init__(name, optional=optional, default=default) +if mapping is None: +raise TypeError("Must provide mapping") self.mapping = mapping self.reverse = dict((value, key) for key, value in mapping.iteritems()) @@ -260,18 +284,145 @@ return datetime.strftime(utc_value, "%Y-%m-%dT%H:%M:%SZ") +class List(Parameter): +""" +A homogenous list of instances of a parameterized type. + +There is a strange behavior that lists can have any starting index and any +gaps are ignored. Conventionally they are 1-based, and so indexes proceed +like 1, 2, 3... However, any non-negative index can be used and the +ordering will be used to determine the true index. So:: + +{5: 'a', 7: 'b', 9: 'c'} + +becomes:: + +['a', 'b', 'c'] +""" + +kind = "list" +supports_multiple = True + +def __init__(self, name=None, item=None, optional=False, default=None): +""" +@param item: A L{Parameter} instance which will be used to parse and +format the values in the list. +""" +if item is None: +raise TypeError("Must provide item") +super(List, self).__init__(name, optional=optional, default=default) +self.item = item + +def parse(self, value): +""" +Convert a dictionary of {relative index: value} to a list of parsed +C{value}s. +""" +indices = [] +if not isinstance(value, di
[Txaws-dev] [Merge] lp:~radix/txaws/parameter-enrichment into lp:txaws
You have been requested to review the proposed merge of lp:~radix/txaws/parameter-enrichment into lp:txaws. For more details, see: https://code.launchpad.net/~radix/txaws/parameter-enrichment/+merge/103592 This is one part of schema enrichment. It adds List and Structure Parameter types, and significantly refactors the way that paramater parsing and formatting is done. -- https://code.launchpad.net/~radix/txaws/parameter-enrichment/+merge/103592 Your team txAWS Technical List is requested to review the proposed merge of lp:~radix/txaws/parameter-enrichment into lp:txaws. === modified file 'txaws/server/schema.py' --- txaws/server/schema.py 2012-03-27 11:51:09 + +++ txaws/server/schema.py 2012-04-26 01:41:19 + @@ -26,6 +26,12 @@ super(MissingParameterError, self).__init__(message) +class InconsistentParameterError(SchemaError): +def __init__(self, name): +message = "Parameter %s is used inconsistently" % (name,) +super(InconsistentParameterError, self).__init__(message) + + class InvalidParameterValueError(SchemaError): """Raised when the value of a parameter is invalid.""" @@ -51,6 +57,20 @@ super(UnknownParameterError, self).__init__(message) +class UnknownParametersError(Exception): +""" +Raised when extra unknown fields are passed to L{Structure.parse}. + +@ivar result: The already coerced result representing the known parameters. +@ivar unknown: The unknown parameters. +""" +def __init__(self, result, unknown): +self.result = result +self.unknown = unknown +message = "The parameters %s are not recognized" % (unknown,) +super(UnknownParametersError, self).__init__(message) + + class Parameter(object): """A single parameter in an HTTP request. @@ -67,7 +87,9 @@ @param validator: A callable to validate the parameter, returning a bool. """ -def __init__(self, name, optional=False, default=None, +supports_multiple = False + +def __init__(self, name=None, optional=False, default=None, min=None, max=None, allow_none=False, validator=None): self.name = name self.optional = optional @@ -182,7 +204,7 @@ lower_than_min_template = "Value must be at least %s." greater_than_max_template = "Value exceeds maximum of %s." -def __init__(self, name, optional=False, default=None, +def __init__(self, name=None, optional=False, default=None, min=0, max=None, allow_none=False, validator=None): super(Integer, self).__init__(name, optional, default, min, max, allow_none, validator) @@ -228,8 +250,10 @@ kind = "enum" -def __init__(self, name, mapping, optional=False, default=None): +def __init__(self, name=None, mapping=None, optional=False, default=None): super(Enum, self).__init__(name, optional=optional, default=default) +if mapping is None: +raise TypeError("Must provide mapping") self.mapping = mapping self.reverse = dict((value, key) for key, value in mapping.iteritems()) @@ -260,18 +284,145 @@ return datetime.strftime(utc_value, "%Y-%m-%dT%H:%M:%SZ") +class List(Parameter): +""" +A homogenous list of instances of a parameterized type. + +There is a strange behavior that lists can have any starting index and any +gaps are ignored. Conventionally they are 1-based, and so indexes proceed +like 1, 2, 3... However, any non-negative index can be used and the +ordering will be used to determine the true index. So:: + +{5: 'a', 7: 'b', 9: 'c'} + +becomes:: + +['a', 'b', 'c'] +""" + +kind = "list" +supports_multiple = True + +def __init__(self, name=None, item=None, optional=False, default=None): +""" +@param item: A L{Parameter} instance which will be used to parse and +format the values in the list. +""" +if item is None: +raise TypeError("Must provide item") +super(List, self).__init__(name, optional=optional, default=default) +self.item = item + +def parse(self, value): +""" +Convert a dictionary of {relative index: value} to a list of parsed +C{value}s. +""" +indices = [] +if not isinstance(value, dict): +raise InvalidParameterValueError("%r should be a dict." % (value,)) +for index in value.keys(): +try: +indices.append(int(index)) +except ValueError: +raise UnknownParameterError(index) +result = [None] * len(value) +for index_index, index in enumerate(sorted(indices)): +v = value[str(index)] +if index < 0: +raise UnknownParameterError(index) +result[index_index] = self.item.coerce(v) +return result + +def format(self, value): +
Re: [Txaws-dev] [Merge] lp:~radix/txaws/parameter-enrichment into lp:txaws
Looks like I broke the second return value from 'extract' -- It's returning a nested structure when it should still be returning the flat structure. I'll fix that. -- https://code.launchpad.net/~radix/txaws/parameter-enrichment/+merge/103592 Your team txAWS Technical List is requested to review the proposed merge of lp:~radix/txaws/parameter-enrichment into lp:txaws. ___ Mailing list: https://launchpad.net/~txaws-dev Post to : txaws-dev@lists.launchpad.net Unsubscribe : https://launchpad.net/~txaws-dev More help : https://help.launchpad.net/ListHelp
Re: [Txaws-dev] [Merge] lp:~radix/txaws/parameter-enrichment into lp:txaws
I've fixed the 'rest' return format. The only tests in landscape and clouddeck that are still failing are because of names in error messages. I think this is a pretty tolerable incompatibility, but I'm going to see what I can do to do maintain compatibility here. Please take another look in the meantime (and if another reviewer wants to take a look, that'd be appreciated). -- https://code.launchpad.net/~radix/txaws/parameter-enrichment/+merge/103592 Your team txAWS Technical List is requested to review the proposed merge of lp:~radix/txaws/parameter-enrichment into lp:txaws. ___ Mailing list: https://launchpad.net/~txaws-dev Post to : txaws-dev@lists.launchpad.net Unsubscribe : https://launchpad.net/~txaws-dev More help : https://help.launchpad.net/ListHelp
Re: [Txaws-dev] [Merge] lp:~radix/txaws/parameter-enrichment into lp:txaws
Oh, and to address your comments explicitly: [1] fixed (name.split('.',1)[0], actually) [2] fixed [3] I've made [] the default default for List, but I'm not sure if 'default' should be forwarded from the item to the list. After investigating it for a while, the original behavior of default= on a list item (i.e. numbered parameter) seems broken... Given a schema of Unicode("foo.n", optional=True, default=u"foo"), after extracting an empty dict the value of arguments.foo == Arguments({}). This is a little bit concerning given that we have code that assumes it works (or at least, specifies defaults). Given that the existing behavior is weird, I don't think it's worth putting any effort into it; everyone should specify lists as List(..., optional=True, default=[...]) from now on. Does this seem reasonable? [4] Wow, that's a seriously dumb one. Fixed. [5] The reason we don't convert it to a list is in this part of the code we don't know that it's meant to be interpreted as a list. Plus, it's just meant to be an internal data structure used during parsing and formatting, and never exposed to users. (however, it was accidentally being exposed to users as the 'rest' return value from extract(), which I've fixed). Thanks! -- https://code.launchpad.net/~radix/txaws/parameter-enrichment/+merge/103592 Your team txAWS Technical List is requested to review the proposed merge of lp:~radix/txaws/parameter-enrichment into lp:txaws. ___ Mailing list: https://launchpad.net/~txaws-dev Post to : txaws-dev@lists.launchpad.net Unsubscribe : https://launchpad.net/~txaws-dev More help : https://help.launchpad.net/ListHelp
[Txaws-dev] [Merge] lp:~radix/txaws/additional-schema-info into lp:txaws
Christopher Armstrong has proposed merging lp:~radix/txaws/additional-schema-info into lp:txaws with lp:~radix/txaws/parameter-enrichment as a prerequisite. Requested reviews: txAWS Technical List (txaws-tech) txAWS Technical List (txaws-tech) Related bugs: Bug #984660 in txAWS: "Enrich schema declarations to allow for describing all the details of an API" https://bugs.launchpad.net/txaws/+bug/984660 For more details, see: https://code.launchpad.net/~radix/txaws/additional-schema-info/+merge/104144 This simple branch adds the following constructor parameters and attributes: - 'doc' on Parameter and all subclasses - 'name' on Schema - 'doc' on Schema - 'result' on Schema - 'errors' on Schema In addition, the 'extend' method has been updated to accept all these new parameters. For parameters, result, and errors, it merges them into the existing values instead of replacing them. -- https://code.launchpad.net/~radix/txaws/additional-schema-info/+merge/104144 Your team txAWS Technical List is requested to review the proposed merge of lp:~radix/txaws/additional-schema-info into lp:txaws. === modified file 'txaws/server/schema.py' --- txaws/server/schema.py 2012-04-30 16:36:20 + +++ txaws/server/schema.py 2012-04-30 16:36:20 + @@ -90,7 +90,8 @@ supports_multiple = False def __init__(self, name=None, optional=False, default=None, - min=None, max=None, allow_none=False, validator=None): + min=None, max=None, allow_none=False, validator=None, + doc=None): self.name = name self.optional = optional self.default = default @@ -98,6 +99,7 @@ self.max = max self.allow_none = allow_none self.validator = validator +self.doc = doc def coerce(self, value): """Coerce a single value according to this parameter's settings. @@ -205,9 +207,10 @@ greater_than_max_template = "Value exceeds maximum of %s." def __init__(self, name=None, optional=False, default=None, - min=0, max=None, allow_none=False, validator=None): + min=0, max=None, allow_none=False, validator=None, + doc=None): super(Integer, self).__init__(name, optional, default, min, max, - allow_none, validator) + allow_none, validator, doc=doc) def parse(self, value): return int(value) @@ -250,8 +253,10 @@ kind = "enum" -def __init__(self, name=None, mapping=None, optional=False, default=None): -super(Enum, self).__init__(name, optional=optional, default=default) +def __init__(self, name=None, mapping=None, optional=False, default=None, + doc=None): +super(Enum, self).__init__(name, optional=optional, default=default, + doc=doc) if mapping is None: raise TypeError("Must provide mapping") self.mapping = mapping @@ -303,14 +308,16 @@ kind = "list" supports_multiple = True -def __init__(self, name=None, item=None, optional=False, default=None): +def __init__(self, name=None, item=None, optional=False, default=None, + doc=None): """ @param item: A L{Parameter} instance which will be used to parse and format the values in the list. """ if item is None: raise TypeError("Must provide item") -super(List, self).__init__(name, optional=optional, default=default) +super(List, self).__init__(name, optional=optional, default=default, + doc=doc) self.item = item if default is None: self.default = [] @@ -363,14 +370,15 @@ kind = "structure" supports_multiple = True -def __init__(self, name=None, fields=None, optional=False, default=None): +def __init__(self, name=None, fields=None, optional=False, default=None, + doc=None): """ @param fields: A mapping of field name to field L{Parameter} instance. """ if fields is None: raise TypeError("Must provide fields") super(Structure, self).__init__(name, optional=optional, -default=default) +default=default, doc=doc) self.fields = fields def parse(self, value): @@ -466,11 +474,10 @@ def __init__(self, *_parameters, **kwargs): """Initialize a new L{Schema} instance. -Any number of L{Parameter} instances can be passed. The parameter path -is used as the target in L{Schema.extract
[Txaws-dev] [Merge] lp:~radix/txaws/additional-schema-info into lp:txaws
You have been requested to review the proposed merge of lp:~radix/txaws/additional-schema-info into lp:txaws. For more details, see: https://code.launchpad.net/~radix/txaws/additional-schema-info/+merge/104144 This simple branch adds the following constructor parameters and attributes: - 'doc' on Parameter and all subclasses - 'name' on Schema - 'doc' on Schema - 'result' on Schema - 'errors' on Schema In addition, the 'extend' method has been updated to accept all these new parameters. For parameters, result, and errors, it merges them into the existing values instead of replacing them. -- https://code.launchpad.net/~radix/txaws/additional-schema-info/+merge/104144 Your team txAWS Technical List is requested to review the proposed merge of lp:~radix/txaws/additional-schema-info into lp:txaws. === modified file 'txaws/server/schema.py' --- txaws/server/schema.py 2012-04-30 16:36:20 + +++ txaws/server/schema.py 2012-04-30 16:36:20 + @@ -90,7 +90,8 @@ supports_multiple = False def __init__(self, name=None, optional=False, default=None, - min=None, max=None, allow_none=False, validator=None): + min=None, max=None, allow_none=False, validator=None, + doc=None): self.name = name self.optional = optional self.default = default @@ -98,6 +99,7 @@ self.max = max self.allow_none = allow_none self.validator = validator +self.doc = doc def coerce(self, value): """Coerce a single value according to this parameter's settings. @@ -205,9 +207,10 @@ greater_than_max_template = "Value exceeds maximum of %s." def __init__(self, name=None, optional=False, default=None, - min=0, max=None, allow_none=False, validator=None): + min=0, max=None, allow_none=False, validator=None, + doc=None): super(Integer, self).__init__(name, optional, default, min, max, - allow_none, validator) + allow_none, validator, doc=doc) def parse(self, value): return int(value) @@ -250,8 +253,10 @@ kind = "enum" -def __init__(self, name=None, mapping=None, optional=False, default=None): -super(Enum, self).__init__(name, optional=optional, default=default) +def __init__(self, name=None, mapping=None, optional=False, default=None, + doc=None): +super(Enum, self).__init__(name, optional=optional, default=default, + doc=doc) if mapping is None: raise TypeError("Must provide mapping") self.mapping = mapping @@ -303,14 +308,16 @@ kind = "list" supports_multiple = True -def __init__(self, name=None, item=None, optional=False, default=None): +def __init__(self, name=None, item=None, optional=False, default=None, + doc=None): """ @param item: A L{Parameter} instance which will be used to parse and format the values in the list. """ if item is None: raise TypeError("Must provide item") -super(List, self).__init__(name, optional=optional, default=default) +super(List, self).__init__(name, optional=optional, default=default, + doc=doc) self.item = item if default is None: self.default = [] @@ -363,14 +370,15 @@ kind = "structure" supports_multiple = True -def __init__(self, name=None, fields=None, optional=False, default=None): +def __init__(self, name=None, fields=None, optional=False, default=None, + doc=None): """ @param fields: A mapping of field name to field L{Parameter} instance. """ if fields is None: raise TypeError("Must provide fields") super(Structure, self).__init__(name, optional=optional, -default=default) +default=default, doc=doc) self.fields = fields def parse(self, value): @@ -466,11 +474,10 @@ def __init__(self, *_parameters, **kwargs): """Initialize a new L{Schema} instance. -Any number of L{Parameter} instances can be passed. The parameter path -is used as the target in L{Schema.extract} and L{Schema.bundle}. For -example:: +Any number of L{Parameter} instances can be passed. The parameter names +are used in L{Schema.extract} and L{Schema.bundle}. For example:: - schema = Schema(Unicode('Name')) + schema = Schema(name="SetName", parameters={"Name": Unicode()}) means that the result of L{Schema.extract} would have a C{Name} attribute. Similarly, L{Schema.bundle} would look for a C{Name} @@ -478,12 +485,34 @@ A more complex example:: - schema