[Txaws-dev] [Merge] lp:~radix/txaws/parameter-enrichment into lp:txaws

2012-04-25 Thread Christopher Armstrong
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

2012-04-25 Thread Christopher Armstrong
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

2012-04-26 Thread Christopher Armstrong
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

2012-04-26 Thread Christopher Armstrong
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

2012-04-26 Thread Christopher Armstrong
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

2012-04-30 Thread Christopher Armstrong
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

2012-04-30 Thread Christopher Armstrong
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