# Path: chartmogul/resource.py
# class DataObject(object):
# 
#     def __init__(self, **kwargs):
#         """
#         Any arguments are translated into class attributes.
#         """
#         self._set_attrs(**kwargs)
# 
#     def _set_attrs(self, **kwargs):
#         """
#         Dynamically creates attributes of the class, so it's not necessary
#         to repeat their list besides the serialization schema.
#         """
#         for key, value in kwargs.items():
#             setattr(self, key, value)
# 
#     def __repr__(self):
#         """
#         Pretty-prints any object as
#         <ClassName{property='string',number=4,date=datetime.datetime(2003, 8, 4, 21, 41, 43)}>
#         """
#         return "<" + self.__class__.__name__ + "{" + \
#             ", ".join([attr + "=" + repr(getattr(self, attr)) for attr in sorted(dir(self))
#                        if not attr.startswith('_') and not callable(getattr(self, attr))]) + \
#                "}>"
# 
# class Resource(DataObject):
#     _path = ''
# 
#     @classmethod
#     def _load(cls, response):
#         """
#         Python doesn't allow arbitrary fields on arrays and having special classes
#         for array & cursor attributes is not necessary => namedtuple.
#         (immutable, therefore all set on creation by generator)
#         """
#         response.raise_for_status()
#         if response.status_code == 204 or response.status_code == 202:
#             return None
#         try:
#             jsonObj = response.json()
#         except ValueError:  # Couldn't parse JSON, probably just text message.
#             return response.content
#         return cls._loadJSON(jsonObj)
# 
#     @classmethod
#     def _loadJSON(cls, jsonObj):
#         # has load_many capability & is many entries result?
#         if '_root_key' in dir(cls) is not None and cls._root_key in jsonObj:
#             return cls._many(cls._schema.load(jsonObj[cls._root_key], many=True), **{
#                              key: jsonObj[key] for key in LIST_PARAMS if key in jsonObj})
#         else:
#             return cls._schema.load(jsonObj)
# 
#     @classmethod
#     def _preProcessParams(cls, params):
#         for key, replacement in ESCAPED_QUERY_KEYS.items():
#             if key in params:
#                 params[replacement] = params[key]
#                 del params[key]
#         return params
# 
#     @classmethod
#     def _request(cls, config, method, http_verb, path, data=None, **kwargs):
#         if http_verb == 'get':
#             params = cls._preProcessParams(kwargs)
#             data = None
#         else:
#             params = None
#             if data is not None:
#                 data = dumps(data, default=json_serial)
# 
#         return Promise(
#             lambda resolve,
#             _: resolve(
#                 getattr(
#                     requests_retry_session(
#                         config.max_retries,
#                         config.backoff_factor),
#                     http_verb)(
#                     config.uri + path,
#                     data=data,
#                     headers={
#                         'content-type': 'application/json', 'User-Agent': 'chartmogul-python/' + __version__},
#                     params=params,
#                     auth=config.auth,
#                     timeout=config.request_timeout))).then(
#             cls._load).catch(annotateHTTPError)
# 
#     @classmethod
#     def _expandPath(cls, path, kwargs):
#         t = URITemplate(path)
#         return t.expand(kwargs)
# 
#     @classmethod
#     def _validate_arguments(cls, method, kwargs):
#         # This enforces user to pass argument, otherwise we could call
#         # wrong URL.
#         if method in [
#             'destroy',
#             'cancel',
#             'retrieve',
#                 'update'] and 'uuid' not in kwargs:
#             raise ArgumentMissingError("Please pass 'uuid' parameter")
#         if method in ['create', 'modify'] and 'data' not in kwargs:
#             raise ArgumentMissingError("Please pass 'data' parameter")
#         if method in [
#                 'destroy_all'] and 'data_source_uuid' not in kwargs and 'customer_uuid' not in kwargs:
#             raise ArgumentMissingError(
#                 "Please pass 'data_source_uuid' and 'customer_uuid' parameters")
# 
#     @classmethod
#     def _method(cls, method, http_verb, path=None):
#         @classmethod
#         def fc(cls, config, **kwargs):
#             if config is None or not isinstance(config, Config):
#                 raise ConfigurationError(
#                     "First argument should be"
#                     " instance of chartmogul.Config class!")
# 
#             pathTemp = path  # due to Python closure
#             if pathTemp is None:
#                 pathTemp = cls._path
# 
#             cls._validate_arguments(method, kwargs)
# 
#             pathTemp = Resource._expandPath(pathTemp, kwargs)
#             # UUID is always path parameter only.
#             if 'uuid' in kwargs:
#                 del kwargs['uuid']
# 
#             return cls._request(config, method, http_verb, pathTemp, **kwargs)
#         return fc
# 
# Path: chartmogul/api/attributes.py
# class Attributes(Resource):
#     """
#     https://dev.chartmogul.com/v1.0/reference#customer-attributes
#     """
#     _path = "/customers{/uuid}/attributes"
# 
#     class _Schema(Schema):
#         tags = fields.List(fields.String())
#         stripe = fields.Dict()
#         clearbit = fields.Dict()
#         custom = fields.Dict()
# 
#         @post_load
#         def make(self, data, **kwargs):
#             return Attributes(**data)
# 
#     _schema = _Schema(unknown=EXCLUDE)


from marshmallow import Schema, fields, post_load, EXCLUDE
from ..resource import DataObject, Resource
from collections import namedtuple
from .attributes import Attributes
"""
Beside the Customer class this module uses also subdocument classes.
These are used only on deserializing the server response, so the client has
proper types (eg. parsed dates instead of strings). Creating customer only
requires Customer class and simple dict/array data.
Validation is done on server.
"""



# Path: chartmogul/resource.py
# class DataObject(object):
# 
#     def __init__(self, **kwargs):
#         """
#         Any arguments are translated into class attributes.
#         """
#         self._set_attrs(**kwargs)
# 
#     def _set_attrs(self, **kwargs):
#         """
#         Dynamically creates attributes of the class, so it's not necessary
#         to repeat their list besides the serialization schema.
#         """
#         for key, value in kwargs.items():
#             setattr(self, key, value)
# 
#     def __repr__(self):
#         """
#         Pretty-prints any object as
#         <ClassName{property='string',number=4,date=datetime.datetime(2003, 8, 4, 21, 41, 43)}>
#         """
#         return "<" + self.__class__.__name__ + "{" + \
#             ", ".join([attr + "=" + repr(getattr(self, attr)) for attr in sorted(dir(self))
#                        if not attr.startswith('_') and not callable(getattr(self, attr))]) + \
#                "}>"
# 
# class Resource(DataObject):
#     _path = ''
# 
#     @classmethod
#     def _load(cls, response):
#         """
#         Python doesn't allow arbitrary fields on arrays and having special classes
#         for array & cursor attributes is not necessary => namedtuple.
#         (immutable, therefore all set on creation by generator)
#         """
#         response.raise_for_status()
#         if response.status_code == 204 or response.status_code == 202:
#             return None
#         try:
#             jsonObj = response.json()
#         except ValueError:  # Couldn't parse JSON, probably just text message.
#             return response.content
#         return cls._loadJSON(jsonObj)
# 
#     @classmethod
#     def _loadJSON(cls, jsonObj):
#         # has load_many capability & is many entries result?
#         if '_root_key' in dir(cls) is not None and cls._root_key in jsonObj:
#             return cls._many(cls._schema.load(jsonObj[cls._root_key], many=True), **{
#                              key: jsonObj[key] for key in LIST_PARAMS if key in jsonObj})
#         else:
#             return cls._schema.load(jsonObj)
# 
#     @classmethod
#     def _preProcessParams(cls, params):
#         for key, replacement in ESCAPED_QUERY_KEYS.items():
#             if key in params:
#                 params[replacement] = params[key]
#                 del params[key]
#         return params
# 
#     @classmethod
#     def _request(cls, config, method, http_verb, path, data=None, **kwargs):
#         if http_verb == 'get':
#             params = cls._preProcessParams(kwargs)
#             data = None
#         else:
#             params = None
#             if data is not None:
#                 data = dumps(data, default=json_serial)
# 
#         return Promise(
#             lambda resolve,
#             _: resolve(
#                 getattr(
#                     requests_retry_session(
#                         config.max_retries,
#                         config.backoff_factor),
#                     http_verb)(
#                     config.uri + path,
#                     data=data,
#                     headers={
#                         'content-type': 'application/json', 'User-Agent': 'chartmogul-python/' + __version__},
#                     params=params,
#                     auth=config.auth,
#                     timeout=config.request_timeout))).then(
#             cls._load).catch(annotateHTTPError)
# 
#     @classmethod
#     def _expandPath(cls, path, kwargs):
#         t = URITemplate(path)
#         return t.expand(kwargs)
# 
#     @classmethod
#     def _validate_arguments(cls, method, kwargs):
#         # This enforces user to pass argument, otherwise we could call
#         # wrong URL.
#         if method in [
#             'destroy',
#             'cancel',
#             'retrieve',
#                 'update'] and 'uuid' not in kwargs:
#             raise ArgumentMissingError("Please pass 'uuid' parameter")
#         if method in ['create', 'modify'] and 'data' not in kwargs:
#             raise ArgumentMissingError("Please pass 'data' parameter")
#         if method in [
#                 'destroy_all'] and 'data_source_uuid' not in kwargs and 'customer_uuid' not in kwargs:
#             raise ArgumentMissingError(
#                 "Please pass 'data_source_uuid' and 'customer_uuid' parameters")
# 
#     @classmethod
#     def _method(cls, method, http_verb, path=None):
#         @classmethod
#         def fc(cls, config, **kwargs):
#             if config is None or not isinstance(config, Config):
#                 raise ConfigurationError(
#                     "First argument should be"
#                     " instance of chartmogul.Config class!")
# 
#             pathTemp = path  # due to Python closure
#             if pathTemp is None:
#                 pathTemp = cls._path
# 
#             cls._validate_arguments(method, kwargs)
# 
#             pathTemp = Resource._expandPath(pathTemp, kwargs)
#             # UUID is always path parameter only.
#             if 'uuid' in kwargs:
#                 del kwargs['uuid']
# 
#             return cls._request(config, method, http_verb, pathTemp, **kwargs)
#         return fc
# 
# Path: chartmogul/api/attributes.py
# class Attributes(Resource):
#     """
#     https://dev.chartmogul.com/v1.0/reference#customer-attributes
#     """
#     _path = "/customers{/uuid}/attributes"
# 
#     class _Schema(Schema):
#         tags = fields.List(fields.String())
#         stripe = fields.Dict()
#         clearbit = fields.Dict()
#         custom = fields.Dict()
# 
#         @post_load
#         def make(self, data, **kwargs):
#             return Attributes(**data)
# 
#     _schema = _Schema(unknown=EXCLUDE)


# Path: chartmogul/api/customer.py
from marshmallow import Schema, fields, post_load, EXCLUDE
from ..resource import DataObject, Resource
from collections import namedtuple
from .attributes import Attributes

"""
Beside the Customer class this module uses also subdocument classes.
These are used only on deserializing the server response, so the client has
proper types (eg. parsed dates instead of strings). Creating customer only
requires Customer class and simple dict/array data.
Validation is done on server.
"""



class Address(DataObject):