# Path: aiohttp_json_api/fields/base.py
# class Attribute(BaseField):
#     """
#     .. seealso::
# 
#         http://jsonapi.org/format/#document-resource-object-attributes
# 
#     An attribute is always part of the resource's JSON API attributes object,
#     unless *meta* is set, in which case the attribute appears in the resource's
#     meta object.
# 
#     Per default, an attribute is mapped to a property on the resource object.
#     You can customize this behaviour by implementing your own *getter* and
#     *setter*:
# 
#     .. code-block:: python3
# 
#         class Article(BaseSchema):
# 
#             title = Attribute()
# 
#     Does the same as:
# 
#     .. code-block:: python3
# 
#         class Article(BaseSchema):
# 
#             title = Attribute()
# 
#             @gets('title')
#             def title(self, article):
#                 return article.title
# 
#             @sets('title')
#             def title(self, article, new_title):
#                 article.title = new_title
#                 return None
# 
#     :arg bool meta:
#         If true, the attribute is part of the resource's *meta* object.
#     :arg bool load_only:
#         If `True` skip this field during serialization, otherwise
#         its value will be present in the serialized data.
#     :arg \*\*kwargs:
#         The init arguments for the :class:`BaseField`.
#     """
# 
#     def __init__(self, *, meta: bool = False, load_only=False, **kwargs):
#         super(Attribute, self).__init__(**kwargs)
#         self.meta = bool(meta)
#         self.load_only = load_only
#         self._trafaret = None
# 
# Path: aiohttp_json_api/fields/trafarets.py
# class DecimalTrafaret(t.Float):
#     convertable = t.str_types + (numbers.Real, int)
#     value_type = decimal.Decimal
# 
#     def __init__(self, places=None, rounding=None, allow_nan=False, **kwargs):
#         self.allow_nan = allow_nan
#         self.places = decimal.Decimal((0, (1,), -places)) \
#             if places is not None else None
#         self.rounding = rounding
#         super(DecimalTrafaret, self).__init__(**kwargs)
# 
#     def _converter(self, value):
#         if not isinstance(value, self.convertable):
#             self._failure(f'value is not {self.value_type.__name__}',
#                           value=value)
#         try:
#             return self.value_type(value)
#         except (ValueError, decimal.InvalidOperation):
#             self._failure(
#                 f"value can't be converted to {self.value_type.__name__}",
#                 value=value
#             )
# 
#     def check_and_return(self, data):
#         data = super(DecimalTrafaret, self).check_and_return(data)
# 
#         if self.allow_nan:
#             if data.is_nan():
#                 return decimal.Decimal('NaN')  # avoid sNaN, -sNaN and -NaN
#         else:
#             if data.is_nan() or data.is_infinite():
#                 self._failure('Special numeric values are not permitted.',
#                               value=data)
# 
#         if self.places is not None and data.is_finite():
#             try:
#                 data = data.quantize(self.places, rounding=self.rounding)
#             except decimal.InvalidOperation:
#                 self._failure('Decimal can not be properly quantized.',
#                               value=data)
# 
#         return data
# 
# Path: aiohttp_json_api/errors.py
# class InvalidType(ValidationError):
#     """
#     JSON API invalid type error. HTTP 400 Bad request.
# 
#     Raised if an input value (part of a JSON API document) has the wrong type.
# 
#     This type of exception is often raised during decoding.
# 
#     :seealso: http://jsonapi.org/format/#document-structure
#     """
# 
# class InvalidValue(ValidationError):
#     """
#     JSON API invalid value error. HTTP 400 Bad request.
# 
#     Raised if an input value (part of a JSON API document) has an invalid
#     value.
# 
#     :seealso: http://jsonapi.org/format/#document-structure
#     """
# 
# Path: aiohttp_json_api/helpers.py
# def is_collection(obj, exclude=()):
#     """Return True if ``obj`` is a collection type."""
#     return (not isinstance(obj, (Mapping,) + exclude) and
#             is_iterable_but_not_string(obj))


import collections
import datetime
import fractions
import uuid
import trafaret as t
from enum import Enum
from trafaret.contrib import rfc_3339
from yarl import URL
from .base import Attribute
from .trafarets import DecimalTrafaret
from ..errors import InvalidType, InvalidValue
from ..helpers import is_collection


class Decimal(Attribute):
    """Encodes and decodes a :class:`decimal.Decimal` as a string."""

    def __init__(self, *, gte=None, lte=None, gt=None, lt=None, **kwargs):
        super(Decimal, self).__init__(**kwargs)
        self._trafaret = DecimalTrafaret(gte=gte, lte=lte, gt=gt, lt=lt)
        if self.allow_none:
            self._trafaret |= t.Null()

    def pre_validate(self, schema, data, sp):
        try:
            self._trafaret.check(data)
        except t.DataError as error:
            raise InvalidValue(detail=error.as_dict(), source_pointer=sp)

    def deserialize(self, schema, data, sp, **kwargs):
        if self.allow_none and data is None:
            return None

        return self._trafaret.check(data)

    def serialize(self, schema, data, **kwargs):
        if self.allow_none and data is None:
            return None

        return str(self._trafaret.check(data))



# Path: aiohttp_json_api/fields/base.py
# class Attribute(BaseField):
#     """
#     .. seealso::
# 
#         http://jsonapi.org/format/#document-resource-object-attributes
# 
#     An attribute is always part of the resource's JSON API attributes object,
#     unless *meta* is set, in which case the attribute appears in the resource's
#     meta object.
# 
#     Per default, an attribute is mapped to a property on the resource object.
#     You can customize this behaviour by implementing your own *getter* and
#     *setter*:
# 
#     .. code-block:: python3
# 
#         class Article(BaseSchema):
# 
#             title = Attribute()
# 
#     Does the same as:
# 
#     .. code-block:: python3
# 
#         class Article(BaseSchema):
# 
#             title = Attribute()
# 
#             @gets('title')
#             def title(self, article):
#                 return article.title
# 
#             @sets('title')
#             def title(self, article, new_title):
#                 article.title = new_title
#                 return None
# 
#     :arg bool meta:
#         If true, the attribute is part of the resource's *meta* object.
#     :arg bool load_only:
#         If `True` skip this field during serialization, otherwise
#         its value will be present in the serialized data.
#     :arg \*\*kwargs:
#         The init arguments for the :class:`BaseField`.
#     """
# 
#     def __init__(self, *, meta: bool = False, load_only=False, **kwargs):
#         super(Attribute, self).__init__(**kwargs)
#         self.meta = bool(meta)
#         self.load_only = load_only
#         self._trafaret = None
# 
# Path: aiohttp_json_api/fields/trafarets.py
# class DecimalTrafaret(t.Float):
#     convertable = t.str_types + (numbers.Real, int)
#     value_type = decimal.Decimal
# 
#     def __init__(self, places=None, rounding=None, allow_nan=False, **kwargs):
#         self.allow_nan = allow_nan
#         self.places = decimal.Decimal((0, (1,), -places)) \
#             if places is not None else None
#         self.rounding = rounding
#         super(DecimalTrafaret, self).__init__(**kwargs)
# 
#     def _converter(self, value):
#         if not isinstance(value, self.convertable):
#             self._failure(f'value is not {self.value_type.__name__}',
#                           value=value)
#         try:
#             return self.value_type(value)
#         except (ValueError, decimal.InvalidOperation):
#             self._failure(
#                 f"value can't be converted to {self.value_type.__name__}",
#                 value=value
#             )
# 
#     def check_and_return(self, data):
#         data = super(DecimalTrafaret, self).check_and_return(data)
# 
#         if self.allow_nan:
#             if data.is_nan():
#                 return decimal.Decimal('NaN')  # avoid sNaN, -sNaN and -NaN
#         else:
#             if data.is_nan() or data.is_infinite():
#                 self._failure('Special numeric values are not permitted.',
#                               value=data)
# 
#         if self.places is not None and data.is_finite():
#             try:
#                 data = data.quantize(self.places, rounding=self.rounding)
#             except decimal.InvalidOperation:
#                 self._failure('Decimal can not be properly quantized.',
#                               value=data)
# 
#         return data
# 
# Path: aiohttp_json_api/errors.py
# class InvalidType(ValidationError):
#     """
#     JSON API invalid type error. HTTP 400 Bad request.
# 
#     Raised if an input value (part of a JSON API document) has the wrong type.
# 
#     This type of exception is often raised during decoding.
# 
#     :seealso: http://jsonapi.org/format/#document-structure
#     """
# 
# class InvalidValue(ValidationError):
#     """
#     JSON API invalid value error. HTTP 400 Bad request.
# 
#     Raised if an input value (part of a JSON API document) has an invalid
#     value.
# 
#     :seealso: http://jsonapi.org/format/#document-structure
#     """
# 
# Path: aiohttp_json_api/helpers.py
# def is_collection(obj, exclude=()):
#     """Return True if ``obj`` is a collection type."""
#     return (not isinstance(obj, (Mapping,) + exclude) and
#             is_iterable_but_not_string(obj))


# Path: aiohttp_json_api/fields/attributes.py
import collections
import datetime
import fractions
import uuid
import trafaret as t
from enum import Enum
from trafaret.contrib import rfc_3339
from yarl import URL
from .base import Attribute
from .trafarets import DecimalTrafaret
from ..errors import InvalidType, InvalidValue
from ..helpers import is_collection



class Decimal(Attribute):
    """Encodes and decodes a :class:`decimal.Decimal` as a string."""

    def __init__(self, *, gte=None, lte=None, gt=None, lt=None, **kwargs):
        super(Decimal, self).__init__(**kwargs)
        self._trafaret = DecimalTrafaret(gte=gte, lte=lte, gt=gt, lt=lt)
        if self.allow_none:
            self._trafaret |= t.Null()

    def pre_validate(self, schema, data, sp):
        try:
            self._trafaret.check(data)
        except t.DataError as error:
            raise InvalidValue(detail=error.as_dict(), source_pointer=sp)

    def deserialize(self, schema, data, sp, **kwargs):
        if self.allow_none and data is None:
            return None

        return self._trafaret.check(data)

    def serialize(self, schema, data, **kwargs):
        if self.allow_none and data is None:
            return None

        return str(self._trafaret.check(data))



class Fraction(Attribute):