# Path: pale/errors/validation.py
# class ArgumentError(BasePaleError):
#     def __init__(self, arg_name, message):
#         error_msg = ("Invalid argument: `%s`.  %s"
#                 % (arg_name, message))
#         self.arg_name = arg_name
#         super(ArgumentError, self).__init__(error_msg)
# 
# Path: pale/fields/base.py
# class BaseField(object):
#     """The base class for all Fields and Arguments.
# 
#     Field objects are used by Resources to define the data they return.
#     They include a name, a type, a short description, and a long
#     description.  Of these instance variables, only `name` is functionally
#     significant, as it's used as the key for the field's value in the
#     outgoing JSON.  The rest of the instance variables are used to generate
#     documentation.
# 
#     Argument objects inherit from Field, in that they share the same base set
#     of instance variables, but are used on the input side of the API, and
#     include validation functionality.
#     """
# 
#     value_type = 'base'
# 
#     def __init__(self,
#                  value_type,
#                  description,
#                  details=None,
#                  property_name=None,
#                  value=None):
#         self.value_type = value_type
#         self.description = description
#         self.details = details
#         self.property_name = property_name
#         self.value_lambda = value
# 
#         if self.value_lambda is not None:
#             assert isinstance(value, types.LambdaType), \
#                     "A Field's `value` parameter must be a lambda"
#             assert self.property_name is None, \
#                     ("Field does not support setting both `property_name` "
#                      "*AND* `value`.  Please pick one or the other")
# 
# 
#     def _fix_up(self, cls, code_name):
#         """Internal helper to name the field after its variable.
# 
#         This is called by _fix_up_fields, which is called by the MetaHasFields
#         metaclass when finishing the construction of a Resource subclass.
# 
#         The `code_name` passed in is the name of the python attribute that
#         the Field has been assigned to in the resource.
# 
#         Note that each BaseField instance must only be assigned to at most
#         one Resource class attribute.
#         """
#         self.name = code_name
# 
# 
#     def render(self, obj, name, context):
#         """The default field renderer.
# 
#         This basic renderer assumes that the object has an attribute with
#         the same name as the field, unless a different field is specified
#         as a `property_name`.
# 
#         The renderer is also passed the context so that it can be
#         propagated to the `_render_serializable` method of nested
#         resources (or, for example, if you decide to implement attribute
#         hiding at the field level instead of at the object level).
# 
#         Callable attributes of `obj` will be called to fetch value.
#         This is useful for fields computed from lambda functions
#         or instance methods.
#         """
#         if self.value_lambda is not None:
#             val = self.value_lambda(obj)
#         else:
#             attr_name = name
#             if self.property_name is not None:
#                 attr_name = self.property_name
#             if isinstance(obj, dict):
#                 val = obj.get(attr_name, None)
#             else:
#                 val = getattr(obj, attr_name, None)
# 
#         if callable(val):
#             try:
#                 val = val()
#             except:
#                 logging.exception("Attempted to call `%s` on obj of type %s.",
#                     attr_name, type(obj))
#                 raise
# 
#         return val
# 
# 
#     def doc_dict(self):
#         """Generate the documentation for this field."""
#         doc = {
#             'type': self.value_type,
#             'description': self.description,
#             'extended_description': self.details
#         }
#         return doc


import json
import logging
from pale.errors import ArgumentError
from pale.fields import BaseField
class JsonDictArgument(BaseArgument):
    allowed_types = (dict, set)

    def __init__(self, *args, **kwargs):
        self.field_map = kwargs.pop('field_map', {})
        self.allow_extra_fields = kwargs.pop('allow_extra_fields', False)
        super(JsonDictArgument, self).__init__(*args, **kwargs)


    def validate(self, item, item_name):
        if isinstance(item, basestring):
            try:
                item = json.loads(item)
            except ValueError as e:
                logging.error("couldn't serialize json from item: '%s'",
                        item)
                raise ArgumentError(item_name,
                        "Invalid JSON string: '%s'" % item)

        self._validate_type(item, item_name)

        if item is None:
            return item

        item_keys = item.keys()
        field_keys = self.field_map.keys()
        extra_keys = [ k for k in item_keys if k not in field_keys ]
        missing_keys = [ k for k in field_keys if k not in item_keys ]

        if len(extra_keys) > 0 and not self.allow_extra_fields:

# Path: pale/errors/validation.py
# class ArgumentError(BasePaleError):
#     def __init__(self, arg_name, message):
#         error_msg = ("Invalid argument: `%s`.  %s"
#                 % (arg_name, message))
#         self.arg_name = arg_name
#         super(ArgumentError, self).__init__(error_msg)
# 
# Path: pale/fields/base.py
# class BaseField(object):
#     """The base class for all Fields and Arguments.
# 
#     Field objects are used by Resources to define the data they return.
#     They include a name, a type, a short description, and a long
#     description.  Of these instance variables, only `name` is functionally
#     significant, as it's used as the key for the field's value in the
#     outgoing JSON.  The rest of the instance variables are used to generate
#     documentation.
# 
#     Argument objects inherit from Field, in that they share the same base set
#     of instance variables, but are used on the input side of the API, and
#     include validation functionality.
#     """
# 
#     value_type = 'base'
# 
#     def __init__(self,
#                  value_type,
#                  description,
#                  details=None,
#                  property_name=None,
#                  value=None):
#         self.value_type = value_type
#         self.description = description
#         self.details = details
#         self.property_name = property_name
#         self.value_lambda = value
# 
#         if self.value_lambda is not None:
#             assert isinstance(value, types.LambdaType), \
#                     "A Field's `value` parameter must be a lambda"
#             assert self.property_name is None, \
#                     ("Field does not support setting both `property_name` "
#                      "*AND* `value`.  Please pick one or the other")
# 
# 
#     def _fix_up(self, cls, code_name):
#         """Internal helper to name the field after its variable.
# 
#         This is called by _fix_up_fields, which is called by the MetaHasFields
#         metaclass when finishing the construction of a Resource subclass.
# 
#         The `code_name` passed in is the name of the python attribute that
#         the Field has been assigned to in the resource.
# 
#         Note that each BaseField instance must only be assigned to at most
#         one Resource class attribute.
#         """
#         self.name = code_name
# 
# 
#     def render(self, obj, name, context):
#         """The default field renderer.
# 
#         This basic renderer assumes that the object has an attribute with
#         the same name as the field, unless a different field is specified
#         as a `property_name`.
# 
#         The renderer is also passed the context so that it can be
#         propagated to the `_render_serializable` method of nested
#         resources (or, for example, if you decide to implement attribute
#         hiding at the field level instead of at the object level).
# 
#         Callable attributes of `obj` will be called to fetch value.
#         This is useful for fields computed from lambda functions
#         or instance methods.
#         """
#         if self.value_lambda is not None:
#             val = self.value_lambda(obj)
#         else:
#             attr_name = name
#             if self.property_name is not None:
#                 attr_name = self.property_name
#             if isinstance(obj, dict):
#                 val = obj.get(attr_name, None)
#             else:
#                 val = getattr(obj, attr_name, None)
# 
#         if callable(val):
#             try:
#                 val = val()
#             except:
#                 logging.exception("Attempted to call `%s` on obj of type %s.",
#                     attr_name, type(obj))
#                 raise
# 
#         return val
# 
# 
#     def doc_dict(self):
#         """Generate the documentation for this field."""
#         doc = {
#             'type': self.value_type,
#             'description': self.description,
#             'extended_description': self.details
#         }
#         return doc


# Path: pale/arguments/base.py
import json
import logging
from pale.errors import ArgumentError
from pale.fields import BaseField

class JsonDictArgument(BaseArgument):
    allowed_types = (dict, set)

    def __init__(self, *args, **kwargs):
        self.field_map = kwargs.pop('field_map', {})
        self.allow_extra_fields = kwargs.pop('allow_extra_fields', False)
        super(JsonDictArgument, self).__init__(*args, **kwargs)


    def validate(self, item, item_name):
        if isinstance(item, basestring):
            try:
                item = json.loads(item)
            except ValueError as e:
                logging.error("couldn't serialize json from item: '%s'",
                        item)
                raise ArgumentError(item_name,
                        "Invalid JSON string: '%s'" % item)

        self._validate_type(item, item_name)

        if item is None:
            return item

        item_keys = item.keys()
        field_keys = self.field_map.keys()
        extra_keys = [ k for k in item_keys if k not in field_keys ]
        missing_keys = [ k for k in field_keys if k not in item_keys ]

        if len(extra_keys) > 0 and not self.allow_extra_fields:

            raise ArgumentError(item_name,