<commit_before># -*- coding: utf-8 -*-
import datetime as dt
import uuid
import decimal
import math

import pytest

from marshmallow import EXCLUDE, INCLUDE, RAISE, fields, utils, Schema, validate
from marshmallow.exceptions import ValidationError
from marshmallow.compat import basestring
from marshmallow.validate import Equal

from tests.base import (
    assert_almost_equal,
    assert_date_equal,
    assert_datetime_equal,
    assert_time_equal,
    central,
    ALL_FIELDS,
)

class TestDeserializingNone:

    @pytest.mark.parametrize('FieldClass', ALL_FIELDS)
    def test_fields_allow_none_deserialize_to_none(self, FieldClass):
        if FieldClass == fields.FormattedString:
            field = FieldClass(src_str='foo', allow_none=True)
        else:
            field = FieldClass(allow_none=True)
        field.deserialize(None) is None

    # https://github.com/marshmallow-code/marshmallow/issues/111
    @pytest.mark.parametrize('FieldClass', ALL_FIELDS)
    def test_fields_dont_allow_none_by_default(self, FieldClass):
        # by default, allow_none=False
        if FieldClass == fields.FormattedString:
            field = FieldClass(src_str='foo')
        else:
            field = FieldClass()
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(None)
        assert 'Field may not be null.' in str(excinfo)

    def test_allow_none_is_true_if_missing_is_true(self):
        field = fields.Field(missing=None)
        assert field.allow_none is True
        field.deserialize(None) is None

    def test_list_field_deserialize_none_to_empty_list(self):
        field = fields.List(fields.String(allow_none=True), allow_none=True)
        assert field.deserialize(None) is None


class TestFieldDeserialization:

    def test_float_field_deserialization(self):
        field = fields.Float()
        assert_almost_equal(field.deserialize('12.3'), 12.3)
        assert_almost_equal(field.deserialize(12.3), 12.3)

    @pytest.mark.parametrize(
        'in_val',
        [
            'bad',
            '',
            {},
        ],
    )
    def test_invalid_float_field_deserialization(self, in_val):
        field = fields.Float()
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(in_val)
        assert excinfo.value.args[0] == 'Not a valid number.'

    def test_integer_field_deserialization(self):
        field = fields.Integer()
        assert field.deserialize('42') == 42
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize('42.0')
        assert excinfo.value.args[0] == 'Not a valid integer.'
        with pytest.raises(ValidationError):
            field.deserialize('bad')
        assert excinfo.value.args[0] == 'Not a valid integer.'
        with pytest.raises(ValidationError):
            field.deserialize({})
        assert excinfo.value.args[0] == 'Not a valid integer.'

    def test_strict_integer_field_deserialization(self):
        field = fields.Integer(strict=True)
        assert field.deserialize(42) == 42
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(42.0)
        assert excinfo.value.args[0] == 'Not a valid integer.'
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(decimal.Decimal('42.0'))
        assert excinfo.value.args[0] == 'Not a valid integer.'
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize('42')
        assert excinfo.value.args[0] == 'Not a valid integer.'

    def test_decimal_field_deserialization(self):
        m1 = 12
        m2 = '12.355'
        m3 = decimal.Decimal(1)
        m4 = 3.14
        m5 = 'abc'
        m6 = [1, 2]

        field = fields.Decimal()
        assert isinstance(field.deserialize(m1), decimal.Decimal)
        assert field.deserialize(m1) == decimal.Decimal(12)
        assert isinstance(field.deserialize(m2), decimal.Decimal)
        assert field.deserialize(m2) == decimal.Decimal('12.355')
        assert isinstance(field.deserialize(m3), decimal.Decimal)
        assert field.deserialize(m3) == decimal.Decimal(1)
        assert isinstance(field.deserialize(m4), decimal.Decimal)
        assert field.deserialize(m4).as_tuple() == (0, (3, 1, 4), -2)
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(m5)
        assert excinfo.value.args[0] == 'Not a valid number.'
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(m6)
        assert excinfo.value.args[0] == 'Not a valid number.'

    def test_decimal_field_with_places(self):
        m1 = 12
        m2 = '12.355'
        m3 = decimal.Decimal(1)
        m4 = 'abc'
        m5 = [1, 2]

        field = fields.Decimal(1)
        assert isinstance(field.deserialize(m1), decimal.Decimal)
        assert field.deserialize(m1) == decimal.Decimal(12)
        assert isinstance(field.deserialize(m2), decimal.Decimal)
        assert field.deserialize(m2) == decimal.Decimal('12.4')
        assert isinstance(field.deserialize(m3), decimal.Decimal)
        assert field.deserialize(m3) == decimal.Decimal(1)
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(m4)
        assert excinfo.value.args[0] == 'Not a valid number.'
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(m5)
        assert excinfo.value.args[0] == 'Not a valid number.'

    def test_decimal_field_with_places_and_rounding(self):
        m1 = 12
        m2 = '12.355'
        m3 = decimal.Decimal(1)
        m4 = 'abc'
        m5 = [1, 2]

        field = fields.Decimal(1, decimal.ROUND_DOWN)
        assert isinstance(field.deserialize(m1), decimal.Decimal)
        assert field.deserialize(m1) == decimal.Decimal(12)
        assert isinstance(field.deserialize(m2), decimal.Decimal)
        assert field.deserialize(m2) == decimal.Decimal('12.3')
        assert isinstance(field.deserialize(m3), decimal.Decimal)
        assert field.deserialize(m3) == decimal.Decimal(1)
        with pytest.raises(ValidationError):
            field.deserialize(m4)
        with pytest.raises(ValidationError):
            field.deserialize(m5)

    def test_decimal_field_deserialization_string(self):
        m1 = 12
        m2 = '12.355'
        m3 = decimal.Decimal(1)
        m4 = 'abc'
        m5 = [1, 2]

        field = fields.Decimal(as_string=True)
        assert isinstance(field.deserialize(m1), decimal.Decimal)
        assert field.deserialize(m1) == decimal.Decimal(12)
        assert isinstance(field.deserialize(m2), decimal.Decimal)
        assert field.deserialize(m2) == decimal.Decimal('12.355')
        assert isinstance(field.deserialize(m3), decimal.Decimal)
        assert field.deserialize(m3) == decimal.Decimal(1)
        with pytest.raises(ValidationError):
            field.deserialize(m4)
        with pytest.raises(ValidationError):
            field.deserialize(m5)

    def test_decimal_field_special_values(self):
        m1 = '-NaN'
        m2 = 'NaN'
        m3 = '-sNaN'
        m4 = 'sNaN'
        m5 = '-Infinity'
        m6 = 'Infinity'
        m7 = '-0'

        field = fields.Decimal(places=2, allow_nan=True)

        m1d = field.deserialize(m1)
        assert isinstance(m1d, decimal.Decimal)
        assert m1d.is_qnan() and not m1d.is_signed()

        m2d = field.deserialize(m2)
        assert isinstance(m2d, decimal.Decimal)
        assert m2d.is_qnan() and not m2d.is_signed()

        m3d = field.deserialize(m3)
        assert isinstance(m3d, decimal.Decimal)
        assert m3d.is_qnan() and not m3d.is_signed()

        m4d = field.deserialize(m4)
        assert isinstance(m4d, decimal.Decimal)
        assert m4d.is_qnan() and not m4d.is_signed()

        m5d = field.deserialize(m5)
        assert isinstance(m5d, decimal.Decimal)
        assert m5d.is_infinite() and m5d.is_signed()

        m6d = field.deserialize(m6)
        assert isinstance(m6d, decimal.Decimal)
        assert m6d.is_infinite() and not m6d.is_signed()

        m7d = field.deserialize(m7)
        assert isinstance(m7d, decimal.Decimal)
        assert m7d.is_zero() and m7d.is_signed()

    def test_decimal_field_special_values_not_permitted(self):
        m1 = '-NaN'
        m2 = 'NaN'
        m3 = '-sNaN'
        m4 = 'sNaN'
        m5 = '-Infinity'
        m6 = 'Infinity'
        m7 = '-0'

        field = fields.Decimal(places=2)

        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(m1)
        assert str(excinfo.value.args[0]) == (
            'Special numeric values (nan or infinity) are not permitted.'
        )
        with pytest.raises(ValidationError):
            field.deserialize(m2)
        with pytest.raises(ValidationError):
            field.deserialize(m3)
        with pytest.raises(ValidationError):
            field.deserialize(m4)
        with pytest.raises(ValidationError):
            field.deserialize(m5)
        with pytest.raises(ValidationError):
            field.deserialize(m6)

        m7d = field.deserialize(m7)
        assert isinstance(m7d, decimal.Decimal)
        assert m7d.is_zero() and m7d.is_signed()

    @pytest.mark.parametrize('allow_nan', (None, False, True))
    @pytest.mark.parametrize('value', ('nan', '-nan', 'inf', '-inf'))
    def test_float_field_allow_nan(self, value, allow_nan):

        if allow_nan is None:
            # Test default case is False
            field = fields.Float()
        else:
            field = fields.Float(allow_nan=allow_nan)

        if allow_nan is True:
            res = field.deserialize(value)
            assert isinstance(res, float)
            if value.endswith('nan'):
                assert math.isnan(res)
            else:
                assert res == float(value)
        else:
            with pytest.raises(ValidationError) as excinfo:
                field.deserialize(value)
            assert str(excinfo.value.args[0]) == (
                'Special numeric values (nan or infinity) are not permitted.'
            )

    def test_string_field_deserialization(self):
        field = fields.String()
        assert field.deserialize('foo') == 'foo'
        assert field.deserialize(b'foo') == 'foo'

        # https://github.com/marshmallow-code/marshmallow/issues/231
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(42)
        assert excinfo.value.args[0] == 'Not a valid string.'

        with pytest.raises(ValidationError):
            field.deserialize({})

    def test_boolean_field_deserialization(self):
        field = fields.Boolean()
        assert field.deserialize(True) is True
        assert field.deserialize(False) is False
        assert field.deserialize('True') is True
        assert field.deserialize('False') is False
        assert field.deserialize('true') is True
        assert field.deserialize('false') is False
        assert field.deserialize('1') is True
        assert field.deserialize('0') is False
        assert field.deserialize('on') is True
        assert field.deserialize('ON') is True
        assert field.deserialize('On') is True
        assert field.deserialize('off') is False
        assert field.deserialize('OFF') is False
        assert field.deserialize('Off') is False
        assert field.deserialize('y') is True
        assert field.deserialize('Y') is True
        assert field.deserialize('yes') is True
        assert field.deserialize('YES') is True
        assert field.deserialize('Yes') is True
        assert field.deserialize('n') is False
        assert field.deserialize('N') is False
        assert field.deserialize('no') is False
        assert field.deserialize('NO') is False
        assert field.deserialize('No') is False
        assert field.deserialize(1) is True
        assert field.deserialize(0) is False

        with pytest.raises(ValidationError) as excinfo:
            field.deserialize({})
        assert excinfo.value.args[0] == 'Not a valid boolean.'

        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(42)

        with pytest.raises(ValidationError) as excinfo:
            field.deserialize('invalid-string')

    def test_boolean_field_deserialization_with_custom_truthy_values(self):
        class MyBoolean(fields.Boolean):
            truthy = set(['yep'])
        field = MyBoolean()
        assert field.deserialize('yep') is True

        field = fields.Boolean(truthy=('yep',))
        assert field.deserialize('yep') is True
        assert field.deserialize(False) is False

    @pytest.mark.parametrize(
        'in_val',
        [
            'notvalid',
            123,
        ],
    )
    def test_boolean_field_deserialization_with_custom_truthy_values_invalid(
            self, in_val,
    ):
        class MyBoolean(fields.Boolean):
            truthy = set(['yep'])
        field = MyBoolean()
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(in_val)
        expected_msg = 'Not a valid boolean.'
        assert str(excinfo.value.args[0]) == expected_msg

        field = fields.Boolean(truthy=('yep',))
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(in_val)
        expected_msg = 'Not a valid boolean.'
        assert str(excinfo.value.args[0]) == expected_msg

        field2 = MyBoolean(error_messages={'invalid': 'bad input'})
        with pytest.raises(ValidationError) as excinfo:
            field2.deserialize(in_val)
        assert str(excinfo.value.args[0]) == 'bad input'

        field2 = fields.Boolean(
            truthy=('yep',),
            error_messages={'invalid': 'bad input'},
        )

    def test_boolean_field_deserialization_with_empty_truthy(self):
        field = fields.Boolean(truthy=())
        assert field.deserialize('yep') is True
        assert field.deserialize(True) is True
        assert field.deserialize(False) is False

    def test_boolean_field_deserialization_with_custom_falsy_values(self):
        field = fields.Boolean(falsy=('nope',))
        assert field.deserialize('nope') is False
        assert field.deserialize(True) is True

    @pytest.mark.parametrize(
        'in_value',
        [
            'not-a-datetime',
            42,
            '',
            [],
            '2018-01-01',
            dt.datetime.now().strftime('%H:%M:%S %Y-%m-%d'),
            dt.datetime.now().strftime('%m-%d-%Y %H:%M:%S'),
        ],
    )
    def test_invalid_datetime_deserialization(self, in_value):
        field = fields.DateTime()
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(in_value)
        msg = 'Not a valid datetime.'
        assert msg in str(excinfo)

    def test_datetime_passed_year_is_invalid(self):
        field = fields.DateTime()
        with pytest.raises(ValidationError):
            field.deserialize('1916')

    def test_datetime_passed_date_is_invalid(self):
        field = fields.DateTime()
        with pytest.raises(ValidationError):
            field.deserialize('2017-04-13')

    def test_custom_date_format_datetime_field_deserialization(self):

        dtime = dt.datetime.now()
        datestring = dtime.strftime('%H:%M:%S %Y-%m-%d')

        field = fields.DateTime(format='%d-%m-%Y %H:%M:%S')
        #deserialization should fail when datestring is not of same format
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(datestring)
        msg = 'Not a valid datetime.'.format(datestring)
        assert msg in str(excinfo)
        field = fields.DateTime(format='%H:%M:%S %Y-%m-%d')
        assert_datetime_equal(field.deserialize(datestring), dtime)

        field = fields.DateTime()
        assert msg in str(excinfo)

    @pytest.mark.parametrize('fmt', ['rfc', 'rfc822'])
    def test_rfc_datetime_field_deserialization(self, fmt):
        dtime = dt.datetime.now()
        datestring = utils.rfcformat(dtime)
        field = fields.DateTime(format=fmt)
        assert_datetime_equal(field.deserialize(datestring), dtime)

    @pytest.mark.parametrize('fmt', ['iso', 'iso8601'])
    def test_iso_datetime_field_deserialization(self, fmt):
        dtime = dt.datetime.now()
        datestring = dtime.isoformat()
        field = fields.DateTime(format=fmt)
        assert_datetime_equal(field.deserialize(datestring), dtime)

    def test_localdatetime_field_deserialization(self):
        dtime = dt.datetime.now()
        localized_dtime = central.localize(dtime)
        field = fields.DateTime(format='iso')
        result = field.deserialize(localized_dtime.isoformat())
        assert_datetime_equal(result, dtime)
        # If dateutil is used, the datetime will not be naive
        if utils.dateutil_available:
            assert result.tzinfo is not None

    def test_time_field_deserialization(self):
        field = fields.Time()
        t = dt.time(1, 23, 45)
        t_formatted = t.isoformat()
        result = field.deserialize(t_formatted)
        assert isinstance(result, dt.time)
        assert_time_equal(result, t)
        # With microseconds
        t2 = dt.time(1, 23, 45, 6789)
        t2_formatted = t2.isoformat()
        result2 = field.deserialize(t2_formatted)
        assert_time_equal(result2, t2)

    @pytest.mark.parametrize(
        'in_data',
        [
            'badvalue',
            '',
            [],
            42,
        ],
    )
    def test_invalid_time_field_deserialization(self, in_data):
        field = fields.Time()
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(in_data)
        assert excinfo.value.args[0] == 'Not a valid time.'

    def test_invalid_timedelta_precision(self):
        with pytest.raises(ValueError) as excinfo:
            fields.TimeDelta('invalid')
        assert 'The precision must be "days",' in str(excinfo)

    def test_timedelta_field_deserialization(self):
        field = fields.TimeDelta()
        result = field.deserialize('42')
        assert isinstance(result, dt.timedelta)
        assert result.days == 0
        assert result.seconds == 42
        assert result.microseconds == 0

        field = fields.TimeDelta(fields.TimeDelta.SECONDS)
        result = field.deserialize(100000)
        assert result.days == 1
        assert result.seconds == 13600
        assert result.microseconds == 0

        field = fields.TimeDelta(fields.TimeDelta.DAYS)
        result = field.deserialize('-42')
        assert isinstance(result, dt.timedelta)
        assert result.days == -42
        assert result.seconds == 0
        assert result.microseconds == 0

        field = fields.TimeDelta(fields.TimeDelta.MICROSECONDS)
        result = field.deserialize(10 ** 6 + 1)
        assert isinstance(result, dt.timedelta)
        assert result.days == 0
        assert result.seconds == 1
        assert result.microseconds == 1

        field = fields.TimeDelta(fields.TimeDelta.MICROSECONDS)
        result = field.deserialize(86400 * 10 ** 6 + 1)
        assert isinstance(result, dt.timedelta)
        assert result.days == 1
        assert result.seconds == 0
        assert result.microseconds == 1

        field = fields.TimeDelta()
        result = field.deserialize(12.9)
        assert isinstance(result, dt.timedelta)
        assert result.days == 0
        assert result.seconds == 12
        assert result.microseconds == 0

        field = fields.TimeDelta(fields.TimeDelta.WEEKS)
        result = field.deserialize(1)
        assert isinstance(result, dt.timedelta)
        assert result.days == 7
        assert result.seconds == 0
        assert result.microseconds == 0

        field = fields.TimeDelta(fields.TimeDelta.HOURS)
        result = field.deserialize(25)
        assert isinstance(result, dt.timedelta)
        assert result.days == 1
        assert result.seconds == 3600
        assert result.microseconds == 0

        field = fields.TimeDelta(fields.TimeDelta.MINUTES)
        result = field.deserialize(1441)
        assert isinstance(result, dt.timedelta)
        assert result.days == 1
        assert result.seconds == 60
        assert result.microseconds == 0

        field = fields.TimeDelta(fields.TimeDelta.MILLISECONDS)
        result = field.deserialize(123456)
        assert isinstance(result, dt.timedelta)
        assert result.days == 0
        assert result.seconds == 123
        assert result.microseconds == 456000

    @pytest.mark.parametrize(
        'in_value',
        [
            '',
            'badvalue',
            [],
            9999999999,
        ],
    )
    def test_invalid_timedelta_field_deserialization(self, in_value):
        field = fields.TimeDelta(fields.TimeDelta.DAYS)
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(in_value)
        assert excinfo.value.args[0] == 'Not a valid period of time.'

    @pytest.mark.parametrize('format', (None, '%Y-%m-%d'))
    def test_date_field_deserialization(self, format):
        field = fields.Date(format=format)
        d = dt.date(2014, 8, 21)
        iso_date = d.isoformat()
        result = field.deserialize(iso_date)
        assert type(result) == dt.date
        assert_date_equal(result, d)

    @pytest.mark.parametrize(
        'in_value',
        [
            '',
            123,
            [],
            dt.date(2014, 8, 21).strftime('%d-%m-%Y'),
        ],
    )
    def test_invalid_date_field_deserialization(self, in_value):
        field = fields.Date()
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(in_value)
        msg = 'Not a valid date.'
        assert excinfo.value.args[0] == msg

    def test_dict_field_deserialization(self):
        field = fields.Dict()
        assert field.deserialize({'foo': 'bar'}) == {'foo': 'bar'}
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize('baddict')
        assert excinfo.value.args[0] == 'Not a valid mapping type.'

    def test_structured_dict_value_deserialization(self):
        field = fields.Dict(values=fields.List(fields.Str))
        assert field.deserialize({'foo': ['bar', 'baz']}) == {'foo': ['bar', 'baz']}
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize({'foo': [1, 2], 'bar': 'baz', 'ham': ['spam']})
        assert excinfo.value.args[0] == {
            'foo': {'value': {0: ['Not a valid string.'], 1: ['Not a valid string.']}},
            'bar': {'value': ['Not a valid list.']}, }
        assert excinfo.value.valid_data == {'foo': [], 'ham': ['spam']}

    def test_structured_dict_key_deserialization(self):
        field = fields.Dict(keys=fields.Str)
        assert field.deserialize({'foo': 'bar'}) == {'foo': 'bar'}
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize({1: 'bar', 'foo': 'baz'})
        assert excinfo.value.args[0] == {1: {'key': ['Not a valid string.']}}
        assert excinfo.value.valid_data == {'foo': 'baz'}

    def test_structured_dict_key_value_deserialization(self):
        field = fields.Dict(
            keys=fields.Str(validate=[validate.Email(), validate.Regexp(r'.*@test\.com$')]),
            values=fields.Decimal,
        )
        assert field.deserialize({'<EMAIL>': 1}) == {'<EMAIL>': decimal.Decimal(1)}
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize({1: 'bar'})
        assert excinfo.value.args[0] == {1: {
            'key': ['Not a valid string.'],
            'value': ['Not a valid number.'],
        }}
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize({'<EMAIL>': 'bar'})
        assert excinfo.value.args[0] == {'<EMAIL>': {'value': ['Not a valid number.']}}
        assert excinfo.value.valid_data == {}
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize({1: 1})
        assert excinfo.value.args[0] == {1: {'key': ['Not a valid string.']}}
        assert excinfo.value.valid_data == {}
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize({'foo': 'bar'})
        assert excinfo.value.args[0] == {'foo': {
            'key': ['Not a valid email address.', 'String does not match expected pattern.'],
            'value': ['Not a valid number.'],
        }}
        assert excinfo.value.valid_data == {}

    def test_url_field_deserialization(self):
        field = fields.Url()
        assert field.deserialize('https://duckduckgo.com') == 'https://duckduckgo.com'
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize('badurl')
        assert excinfo.value.args[0][0] == 'Not a valid URL.'
        # Relative URLS not allowed by default
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize('/foo/bar')
        assert excinfo.value.args[0][0] == 'Not a valid URL.'

    def test_relative_url_field_deserialization(self):
        field = fields.Url(relative=True)
        assert field.deserialize('/foo/bar') == '/foo/bar'

    def test_url_field_schemes_argument(self):
        field = fields.URL()
        url = 'ws://test.test'
        with pytest.raises(ValidationError):
            field.deserialize(url)
        field2 = fields.URL(schemes=set(['http', 'https', 'ws']))
        assert field2.deserialize(url) == url

    def test_email_field_deserialization(self):
        field = fields.Email()
        assert field.deserialize('<EMAIL>') == '<EMAIL>'
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize('invalidemail')
        assert excinfo.value.args[0][0] == 'Not a valid email address.'

    def test_function_field_deserialization_is_noop_by_default(self):
        field = fields.Function(lambda x: None)
        # Default is noop
        assert field.deserialize('foo') == 'foo'
        assert field.deserialize(42) == 42

    def test_function_field_deserialization_with_callable(self):
        field = fields.Function(
            lambda x: None,
            deserialize=lambda val: val.upper(),
        )
        assert field.deserialize('foo') == 'FOO'

    def test_function_field_deserialization_with_context(self):
        class Parent(Schema):
            pass
        field = fields.Function(
            lambda x: None,
            deserialize=lambda val, context: val.upper() + context['key'],
        )
        field.parent = Parent(context={'key': 'BAR'})
        assert field.deserialize('foo') == 'FOOBAR'

    def test_function_field_passed_deserialize_only_is_load_only(self):
        field = fields.Function(deserialize=lambda val: val.upper())
        assert field.load_only is True

    def test_function_field_passed_deserialize_and_serialize_is_not_load_only(self):
        field = fields.Function(
            serialize=lambda val: val.lower(),
            deserialize=lambda val: val.upper(),
        )
        assert field.load_only is False

    def test_uuid_field_deserialization(self):
        field = fields.UUID()
        uuid_str = str(uuid.uuid4())
        result = field.deserialize(uuid_str)
        assert isinstance(result, uuid.UUID)
        assert str(result) == uuid_str

        uuid4 = uuid.uuid4()
        result = field.deserialize(uuid4)
        assert isinstance(result, uuid.UUID)
        assert result == uuid4

        uuid_bytes = b']\xc7wW\x132O\xf9\xa5\xbe\x13\x1f\x02\x18\xda\xbf'
        result = field.deserialize(uuid_bytes)
        assert isinstance(result, uuid.UUID)
        assert result.bytes == uuid_bytes

    @pytest.mark.parametrize(
        'in_value',
        [
            'malformed',
            123,
            [],
            b'tooshort',
        ],
    )
    def test_invalid_uuid_deserialization(self, in_value):
        field = fields.UUID()
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(in_value)

        assert excinfo.value.args[0] == 'Not a valid UUID.'

    def test_deserialization_function_must_be_callable(self):
        with pytest.raises(ValueError):
            fields.Function(
                lambda x: None,
                deserialize='notvalid',
            )

    def test_method_field_deserialization_is_noop_by_default(self):
        class MiniUserSchema(Schema):
            uppername = fields.Method('uppercase_name')

            def uppercase_name(self, obj):
                return obj.upper()
        s = MiniUserSchema()
        assert s.fields['uppername'].deserialize('steve') == 'steve'

    def test_deserialization_method(self):
        class MiniUserSchema(Schema):
            uppername = fields.Method('uppercase_name', deserialize='lowercase_name')

            def uppercase_name(self, obj):
                return obj.name.upper()

            def lowercase_name(self, value):
                return value.lower()

        s = MiniUserSchema()
        assert s.fields['uppername'].deserialize('STEVE') == 'steve'

    def test_deserialization_method_must_be_a_method(self):
        class BadSchema(Schema):
            uppername = fields.Method('uppercase_name', deserialize='lowercase_name')

        s = BadSchema()
        with pytest.raises(ValueError):
            s.fields['uppername'].deserialize('STEVE')

    def test_method_field_deserialize_only(self):
        class MethodDeserializeOnly(Schema):
            def lowercase_name(self, value):
                return value.lower()

        m = fields.Method(deserialize='lowercase_name')
        m.parent = MethodDeserializeOnly()

        assert m.deserialize('ALEC') == 'alec'

    def test_datetime_list_field_deserialization(self):
        dtimes = dt.datetime.now(), dt.datetime.now(), dt.datetime.utcnow()
        dstrings = [each.isoformat() for each in dtimes]
        field = fields.List(fields.DateTime())
        result = field.deserialize(dstrings)
        assert all([isinstance(each, dt.datetime) for each in result])
        for actual, expected in zip(result, dtimes):
            assert_date_equal(actual, expected)

    def test_list_field_deserialize_invalid_item(self):
        field = fields.List(fields.DateTime)
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(['badvalue'])
        assert excinfo.value.args[0] == {0: ['Not a valid datetime.']}

        field = fields.List(fields.Str())
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(['good', 42])
        assert excinfo.value.args[0] == {1: ['Not a valid string.']}

    def test_list_field_deserialize_multiple_invalid_items(self):
        field = fields.List(
            fields.Int(
                validate=validate.Range(10, 20, error='Value {input} not in range'),
            ),
        )
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize([10, 5, 25])
        assert len(excinfo.value.args[0]) == 2
        assert excinfo.value.args[0][1] == ['Value 5 not in range']
        assert excinfo.value.args[0][2] == ['Value 25 not in range']

    @pytest.mark.parametrize(
        'value',
        [
            'notalist',
            42,
            {},
        ],
    )
    def test_list_field_deserialize_value_that_is_not_a_list(self, value):
        field = fields.List(fields.Str())
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(value)
        assert excinfo.value.args[0] == 'Not a valid list.'

    def test_constant_field_deserialization(self):<commit_msg>Add deserialization tests<commit_after># -*- coding: utf-8 -*-
import datetime as dt
import uuid
import decimal
import math

import pytest

from marshmallow import EXCLUDE, INCLUDE, RAISE, fields, utils, Schema, validate
from marshmallow.exceptions import ValidationError
from marshmallow.compat import basestring
from marshmallow.validate import Equal

from tests.base import (
    assert_almost_equal,
    assert_date_equal,
    assert_datetime_equal,
    assert_time_equal,
    central,
    ALL_FIELDS,
)

class TestDeserializingNone:

    @pytest.mark.parametrize('FieldClass', ALL_FIELDS)
    def test_fields_allow_none_deserialize_to_none(self, FieldClass):
        if FieldClass == fields.FormattedString:
            field = FieldClass(src_str='foo', allow_none=True)
        else:
            field = FieldClass(allow_none=True)
        field.deserialize(None) is None

    # https://github.com/marshmallow-code/marshmallow/issues/111
    @pytest.mark.parametrize('FieldClass', ALL_FIELDS)
    def test_fields_dont_allow_none_by_default(self, FieldClass):
        # by default, allow_none=False
        if FieldClass == fields.FormattedString:
            field = FieldClass(src_str='foo')
        else:
            field = FieldClass()
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(None)
        assert 'Field may not be null.' in str(excinfo)

    def test_allow_none_is_true_if_missing_is_true(self):
        field = fields.Field(missing=None)
        assert field.allow_none is True
        field.deserialize(None) is None

    def test_list_field_deserialize_none_to_empty_list(self):
        field = fields.List(fields.String(allow_none=True), allow_none=True)
        assert field.deserialize(None) is None

    def test_tuple_field_deserialize_none_to_nont(self):
        field = fields.Tuple([fields.String()], allow_none=True)
        assert field.deserialize(None) is None


class TestFieldDeserialization:

    def test_float_field_deserialization(self):
        field = fields.Float()
        assert_almost_equal(field.deserialize('12.3'), 12.3)
        assert_almost_equal(field.deserialize(12.3), 12.3)

    @pytest.mark.parametrize(
        'in_val',
        [
            'bad',
            '',
            {},
        ],
    )
    def test_invalid_float_field_deserialization(self, in_val):
        field = fields.Float()
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(in_val)
        assert excinfo.value.args[0] == 'Not a valid number.'

    def test_integer_field_deserialization(self):
        field = fields.Integer()
        assert field.deserialize('42') == 42
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize('42.0')
        assert excinfo.value.args[0] == 'Not a valid integer.'
        with pytest.raises(ValidationError):
            field.deserialize('bad')
        assert excinfo.value.args[0] == 'Not a valid integer.'
        with pytest.raises(ValidationError):
            field.deserialize({})
        assert excinfo.value.args[0] == 'Not a valid integer.'

    def test_strict_integer_field_deserialization(self):
        field = fields.Integer(strict=True)
        assert field.deserialize(42) == 42
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(42.0)
        assert excinfo.value.args[0] == 'Not a valid integer.'
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(decimal.Decimal('42.0'))
        assert excinfo.value.args[0] == 'Not a valid integer.'
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize('42')
        assert excinfo.value.args[0] == 'Not a valid integer.'

    def test_decimal_field_deserialization(self):
        m1 = 12
        m2 = '12.355'
        m3 = decimal.Decimal(1)
        m4 = 3.14
        m5 = 'abc'
        m6 = [1, 2]

        field = fields.Decimal()
        assert isinstance(field.deserialize(m1), decimal.Decimal)
        assert field.deserialize(m1) == decimal.Decimal(12)
        assert isinstance(field.deserialize(m2), decimal.Decimal)
        assert field.deserialize(m2) == decimal.Decimal('12.355')
        assert isinstance(field.deserialize(m3), decimal.Decimal)
        assert field.deserialize(m3) == decimal.Decimal(1)
        assert isinstance(field.deserialize(m4), decimal.Decimal)
        assert field.deserialize(m4).as_tuple() == (0, (3, 1, 4), -2)
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(m5)
        assert excinfo.value.args[0] == 'Not a valid number.'
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(m6)
        assert excinfo.value.args[0] == 'Not a valid number.'

    def test_decimal_field_with_places(self):
        m1 = 12
        m2 = '12.355'
        m3 = decimal.Decimal(1)
        m4 = 'abc'
        m5 = [1, 2]

        field = fields.Decimal(1)
        assert isinstance(field.deserialize(m1), decimal.Decimal)
        assert field.deserialize(m1) == decimal.Decimal(12)
        assert isinstance(field.deserialize(m2), decimal.Decimal)
        assert field.deserialize(m2) == decimal.Decimal('12.4')
        assert isinstance(field.deserialize(m3), decimal.Decimal)
        assert field.deserialize(m3) == decimal.Decimal(1)
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(m4)
        assert excinfo.value.args[0] == 'Not a valid number.'
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(m5)
        assert excinfo.value.args[0] == 'Not a valid number.'

    def test_decimal_field_with_places_and_rounding(self):
        m1 = 12
        m2 = '12.355'
        m3 = decimal.Decimal(1)
        m4 = 'abc'
        m5 = [1, 2]

        field = fields.Decimal(1, decimal.ROUND_DOWN)
        assert isinstance(field.deserialize(m1), decimal.Decimal)
        assert field.deserialize(m1) == decimal.Decimal(12)
        assert isinstance(field.deserialize(m2), decimal.Decimal)
        assert field.deserialize(m2) == decimal.Decimal('12.3')
        assert isinstance(field.deserialize(m3), decimal.Decimal)
        assert field.deserialize(m3) == decimal.Decimal(1)
        with pytest.raises(ValidationError):
            field.deserialize(m4)
        with pytest.raises(ValidationError):
            field.deserialize(m5)

    def test_decimal_field_deserialization_string(self):
        m1 = 12
        m2 = '12.355'
        m3 = decimal.Decimal(1)
        m4 = 'abc'
        m5 = [1, 2]

        field = fields.Decimal(as_string=True)
        assert isinstance(field.deserialize(m1), decimal.Decimal)
        assert field.deserialize(m1) == decimal.Decimal(12)
        assert isinstance(field.deserialize(m2), decimal.Decimal)
        assert field.deserialize(m2) == decimal.Decimal('12.355')
        assert isinstance(field.deserialize(m3), decimal.Decimal)
        assert field.deserialize(m3) == decimal.Decimal(1)
        with pytest.raises(ValidationError):
            field.deserialize(m4)
        with pytest.raises(ValidationError):
            field.deserialize(m5)

    def test_decimal_field_special_values(self):
        m1 = '-NaN'
        m2 = 'NaN'
        m3 = '-sNaN'
        m4 = 'sNaN'
        m5 = '-Infinity'
        m6 = 'Infinity'
        m7 = '-0'

        field = fields.Decimal(places=2, allow_nan=True)

        m1d = field.deserialize(m1)
        assert isinstance(m1d, decimal.Decimal)
        assert m1d.is_qnan() and not m1d.is_signed()

        m2d = field.deserialize(m2)
        assert isinstance(m2d, decimal.Decimal)
        assert m2d.is_qnan() and not m2d.is_signed()

        m3d = field.deserialize(m3)
        assert isinstance(m3d, decimal.Decimal)
        assert m3d.is_qnan() and not m3d.is_signed()

        m4d = field.deserialize(m4)
        assert isinstance(m4d, decimal.Decimal)
        assert m4d.is_qnan() and not m4d.is_signed()

        m5d = field.deserialize(m5)
        assert isinstance(m5d, decimal.Decimal)
        assert m5d.is_infinite() and m5d.is_signed()

        m6d = field.deserialize(m6)
        assert isinstance(m6d, decimal.Decimal)
        assert m6d.is_infinite() and not m6d.is_signed()

        m7d = field.deserialize(m7)
        assert isinstance(m7d, decimal.Decimal)
        assert m7d.is_zero() and m7d.is_signed()

    def test_decimal_field_special_values_not_permitted(self):
        m1 = '-NaN'
        m2 = 'NaN'
        m3 = '-sNaN'
        m4 = 'sNaN'
        m5 = '-Infinity'
        m6 = 'Infinity'
        m7 = '-0'

        field = fields.Decimal(places=2)

        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(m1)
        assert str(excinfo.value.args[0]) == (
            'Special numeric values (nan or infinity) are not permitted.'
        )
        with pytest.raises(ValidationError):
            field.deserialize(m2)
        with pytest.raises(ValidationError):
            field.deserialize(m3)
        with pytest.raises(ValidationError):
            field.deserialize(m4)
        with pytest.raises(ValidationError):
            field.deserialize(m5)
        with pytest.raises(ValidationError):
            field.deserialize(m6)

        m7d = field.deserialize(m7)
        assert isinstance(m7d, decimal.Decimal)
        assert m7d.is_zero() and m7d.is_signed()

    @pytest.mark.parametrize('allow_nan', (None, False, True))
    @pytest.mark.parametrize('value', ('nan', '-nan', 'inf', '-inf'))
    def test_float_field_allow_nan(self, value, allow_nan):

        if allow_nan is None:
            # Test default case is False
            field = fields.Float()
        else:
            field = fields.Float(allow_nan=allow_nan)

        if allow_nan is True:
            res = field.deserialize(value)
            assert isinstance(res, float)
            if value.endswith('nan'):
                assert math.isnan(res)
            else:
                assert res == float(value)
        else:
            with pytest.raises(ValidationError) as excinfo:
                field.deserialize(value)
            assert str(excinfo.value.args[0]) == (
                'Special numeric values (nan or infinity) are not permitted.'
            )

    def test_string_field_deserialization(self):
        field = fields.String()
        assert field.deserialize('foo') == 'foo'
        assert field.deserialize(b'foo') == 'foo'

        # https://github.com/marshmallow-code/marshmallow/issues/231
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(42)
        assert excinfo.value.args[0] == 'Not a valid string.'

        with pytest.raises(ValidationError):
            field.deserialize({})

    def test_boolean_field_deserialization(self):
        field = fields.Boolean()
        assert field.deserialize(True) is True
        assert field.deserialize(False) is False
        assert field.deserialize('True') is True
        assert field.deserialize('False') is False
        assert field.deserialize('true') is True
        assert field.deserialize('false') is False
        assert field.deserialize('1') is True
        assert field.deserialize('0') is False
        assert field.deserialize('on') is True
        assert field.deserialize('ON') is True
        assert field.deserialize('On') is True
        assert field.deserialize('off') is False
        assert field.deserialize('OFF') is False
        assert field.deserialize('Off') is False
        assert field.deserialize('y') is True
        assert field.deserialize('Y') is True
        assert field.deserialize('yes') is True
        assert field.deserialize('YES') is True
        assert field.deserialize('Yes') is True
        assert field.deserialize('n') is False
        assert field.deserialize('N') is False
        assert field.deserialize('no') is False
        assert field.deserialize('NO') is False
        assert field.deserialize('No') is False
        assert field.deserialize(1) is True
        assert field.deserialize(0) is False

        with pytest.raises(ValidationError) as excinfo:
            field.deserialize({})
        assert excinfo.value.args[0] == 'Not a valid boolean.'

        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(42)

        with pytest.raises(ValidationError) as excinfo:
            field.deserialize('invalid-string')

    def test_boolean_field_deserialization_with_custom_truthy_values(self):
        class MyBoolean(fields.Boolean):
            truthy = set(['yep'])
        field = MyBoolean()
        assert field.deserialize('yep') is True

        field = fields.Boolean(truthy=('yep',))
        assert field.deserialize('yep') is True
        assert field.deserialize(False) is False

    @pytest.mark.parametrize(
        'in_val',
        [
            'notvalid',
            123,
        ],
    )
    def test_boolean_field_deserialization_with_custom_truthy_values_invalid(
            self, in_val,
    ):
        class MyBoolean(fields.Boolean):
            truthy = set(['yep'])
        field = MyBoolean()
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(in_val)
        expected_msg = 'Not a valid boolean.'
        assert str(excinfo.value.args[0]) == expected_msg

        field = fields.Boolean(truthy=('yep',))
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(in_val)
        expected_msg = 'Not a valid boolean.'
        assert str(excinfo.value.args[0]) == expected_msg

        field2 = MyBoolean(error_messages={'invalid': 'bad input'})
        with pytest.raises(ValidationError) as excinfo:
            field2.deserialize(in_val)
        assert str(excinfo.value.args[0]) == 'bad input'

        field2 = fields.Boolean(
            truthy=('yep',),
            error_messages={'invalid': 'bad input'},
        )

    def test_boolean_field_deserialization_with_empty_truthy(self):
        field = fields.Boolean(truthy=())
        assert field.deserialize('yep') is True
        assert field.deserialize(True) is True
        assert field.deserialize(False) is False

    def test_boolean_field_deserialization_with_custom_falsy_values(self):
        field = fields.Boolean(falsy=('nope',))
        assert field.deserialize('nope') is False
        assert field.deserialize(True) is True

    @pytest.mark.parametrize(
        'in_value',
        [
            'not-a-datetime',
            42,
            '',
            [],
            '2018-01-01',
            dt.datetime.now().strftime('%H:%M:%S %Y-%m-%d'),
            dt.datetime.now().strftime('%m-%d-%Y %H:%M:%S'),
        ],
    )
    def test_invalid_datetime_deserialization(self, in_value):
        field = fields.DateTime()
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(in_value)
        msg = 'Not a valid datetime.'
        assert msg in str(excinfo)

    def test_datetime_passed_year_is_invalid(self):
        field = fields.DateTime()
        with pytest.raises(ValidationError):
            field.deserialize('1916')

    def test_datetime_passed_date_is_invalid(self):
        field = fields.DateTime()
        with pytest.raises(ValidationError):
            field.deserialize('2017-04-13')

    def test_custom_date_format_datetime_field_deserialization(self):

        dtime = dt.datetime.now()
        datestring = dtime.strftime('%H:%M:%S %Y-%m-%d')

        field = fields.DateTime(format='%d-%m-%Y %H:%M:%S')
        #deserialization should fail when datestring is not of same format
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(datestring)
        msg = 'Not a valid datetime.'.format(datestring)
        assert msg in str(excinfo)
        field = fields.DateTime(format='%H:%M:%S %Y-%m-%d')
        assert_datetime_equal(field.deserialize(datestring), dtime)

        field = fields.DateTime()
        assert msg in str(excinfo)

    @pytest.mark.parametrize('fmt', ['rfc', 'rfc822'])
    def test_rfc_datetime_field_deserialization(self, fmt):
        dtime = dt.datetime.now()
        datestring = utils.rfcformat(dtime)
        field = fields.DateTime(format=fmt)
        assert_datetime_equal(field.deserialize(datestring), dtime)

    @pytest.mark.parametrize('fmt', ['iso', 'iso8601'])
    def test_iso_datetime_field_deserialization(self, fmt):
        dtime = dt.datetime.now()
        datestring = dtime.isoformat()
        field = fields.DateTime(format=fmt)
        assert_datetime_equal(field.deserialize(datestring), dtime)

    def test_localdatetime_field_deserialization(self):
        dtime = dt.datetime.now()
        localized_dtime = central.localize(dtime)
        field = fields.DateTime(format='iso')
        result = field.deserialize(localized_dtime.isoformat())
        assert_datetime_equal(result, dtime)
        # If dateutil is used, the datetime will not be naive
        if utils.dateutil_available:
            assert result.tzinfo is not None

    def test_time_field_deserialization(self):
        field = fields.Time()
        t = dt.time(1, 23, 45)
        t_formatted = t.isoformat()
        result = field.deserialize(t_formatted)
        assert isinstance(result, dt.time)
        assert_time_equal(result, t)
        # With microseconds
        t2 = dt.time(1, 23, 45, 6789)
        t2_formatted = t2.isoformat()
        result2 = field.deserialize(t2_formatted)
        assert_time_equal(result2, t2)

    @pytest.mark.parametrize(
        'in_data',
        [
            'badvalue',
            '',
            [],
            42,
        ],
    )
    def test_invalid_time_field_deserialization(self, in_data):
        field = fields.Time()
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(in_data)
        assert excinfo.value.args[0] == 'Not a valid time.'

    def test_invalid_timedelta_precision(self):
        with pytest.raises(ValueError) as excinfo:
            fields.TimeDelta('invalid')
        assert 'The precision must be "days",' in str(excinfo)

    def test_timedelta_field_deserialization(self):
        field = fields.TimeDelta()
        result = field.deserialize('42')
        assert isinstance(result, dt.timedelta)
        assert result.days == 0
        assert result.seconds == 42
        assert result.microseconds == 0

        field = fields.TimeDelta(fields.TimeDelta.SECONDS)
        result = field.deserialize(100000)
        assert result.days == 1
        assert result.seconds == 13600
        assert result.microseconds == 0

        field = fields.TimeDelta(fields.TimeDelta.DAYS)
        result = field.deserialize('-42')
        assert isinstance(result, dt.timedelta)
        assert result.days == -42
        assert result.seconds == 0
        assert result.microseconds == 0

        field = fields.TimeDelta(fields.TimeDelta.MICROSECONDS)
        result = field.deserialize(10 ** 6 + 1)
        assert isinstance(result, dt.timedelta)
        assert result.days == 0
        assert result.seconds == 1
        assert result.microseconds == 1

        field = fields.TimeDelta(fields.TimeDelta.MICROSECONDS)
        result = field.deserialize(86400 * 10 ** 6 + 1)
        assert isinstance(result, dt.timedelta)
        assert result.days == 1
        assert result.seconds == 0
        assert result.microseconds == 1

        field = fields.TimeDelta()
        result = field.deserialize(12.9)
        assert isinstance(result, dt.timedelta)
        assert result.days == 0
        assert result.seconds == 12
        assert result.microseconds == 0

        field = fields.TimeDelta(fields.TimeDelta.WEEKS)
        result = field.deserialize(1)
        assert isinstance(result, dt.timedelta)
        assert result.days == 7
        assert result.seconds == 0
        assert result.microseconds == 0

        field = fields.TimeDelta(fields.TimeDelta.HOURS)
        result = field.deserialize(25)
        assert isinstance(result, dt.timedelta)
        assert result.days == 1
        assert result.seconds == 3600
        assert result.microseconds == 0

        field = fields.TimeDelta(fields.TimeDelta.MINUTES)
        result = field.deserialize(1441)
        assert isinstance(result, dt.timedelta)
        assert result.days == 1
        assert result.seconds == 60
        assert result.microseconds == 0

        field = fields.TimeDelta(fields.TimeDelta.MILLISECONDS)
        result = field.deserialize(123456)
        assert isinstance(result, dt.timedelta)
        assert result.days == 0
        assert result.seconds == 123
        assert result.microseconds == 456000

    @pytest.mark.parametrize(
        'in_value',
        [
            '',
            'badvalue',
            [],
            9999999999,
        ],
    )
    def test_invalid_timedelta_field_deserialization(self, in_value):
        field = fields.TimeDelta(fields.TimeDelta.DAYS)
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(in_value)
        assert excinfo.value.args[0] == 'Not a valid period of time.'

    @pytest.mark.parametrize('format', (None, '%Y-%m-%d'))
    def test_date_field_deserialization(self, format):
        field = fields.Date(format=format)
        d = dt.date(2014, 8, 21)
        iso_date = d.isoformat()
        result = field.deserialize(iso_date)
        assert type(result) == dt.date
        assert_date_equal(result, d)

    @pytest.mark.parametrize(
        'in_value',
        [
            '',
            123,
            [],
            dt.date(2014, 8, 21).strftime('%d-%m-%Y'),
        ],
    )
    def test_invalid_date_field_deserialization(self, in_value):
        field = fields.Date()
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(in_value)
        msg = 'Not a valid date.'
        assert excinfo.value.args[0] == msg

    def test_dict_field_deserialization(self):
        field = fields.Dict()
        assert field.deserialize({'foo': 'bar'}) == {'foo': 'bar'}
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize('baddict')
        assert excinfo.value.args[0] == 'Not a valid mapping type.'

    def test_structured_dict_value_deserialization(self):
        field = fields.Dict(values=fields.List(fields.Str))
        assert field.deserialize({'foo': ['bar', 'baz']}) == {'foo': ['bar', 'baz']}
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize({'foo': [1, 2], 'bar': 'baz', 'ham': ['spam']})
        assert excinfo.value.args[0] == {
            'foo': {'value': {0: ['Not a valid string.'], 1: ['Not a valid string.']}},
            'bar': {'value': ['Not a valid list.']}, }
        assert excinfo.value.valid_data == {'foo': [], 'ham': ['spam']}

    def test_structured_dict_key_deserialization(self):
        field = fields.Dict(keys=fields.Str)
        assert field.deserialize({'foo': 'bar'}) == {'foo': 'bar'}
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize({1: 'bar', 'foo': 'baz'})
        assert excinfo.value.args[0] == {1: {'key': ['Not a valid string.']}}
        assert excinfo.value.valid_data == {'foo': 'baz'}

    def test_structured_dict_key_value_deserialization(self):
        field = fields.Dict(
            keys=fields.Str(validate=[validate.Email(), validate.Regexp(r'.*@test\.com$')]),
            values=fields.Decimal,
        )
        assert field.deserialize({'<EMAIL>': 1}) == {'<EMAIL>': decimal.Decimal(1)}
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize({1: 'bar'})
        assert excinfo.value.args[0] == {1: {
            'key': ['Not a valid string.'],
            'value': ['Not a valid number.'],
        }}
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize({'<EMAIL>': 'bar'})
        assert excinfo.value.args[0] == {'<EMAIL>': {'value': ['Not a valid number.']}}
        assert excinfo.value.valid_data == {}
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize({1: 1})
        assert excinfo.value.args[0] == {1: {'key': ['Not a valid string.']}}
        assert excinfo.value.valid_data == {}
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize({'foo': 'bar'})
        assert excinfo.value.args[0] == {'foo': {
            'key': ['Not a valid email address.', 'String does not match expected pattern.'],
            'value': ['Not a valid number.'],
        }}
        assert excinfo.value.valid_data == {}

    def test_url_field_deserialization(self):
        field = fields.Url()
        assert field.deserialize('https://duckduckgo.com') == 'https://duckduckgo.com'
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize('badurl')
        assert excinfo.value.args[0][0] == 'Not a valid URL.'
        # Relative URLS not allowed by default
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize('/foo/bar')
        assert excinfo.value.args[0][0] == 'Not a valid URL.'

    def test_relative_url_field_deserialization(self):
        field = fields.Url(relative=True)
        assert field.deserialize('/foo/bar') == '/foo/bar'

    def test_url_field_schemes_argument(self):
        field = fields.URL()
        url = 'ws://test.test'
        with pytest.raises(ValidationError):
            field.deserialize(url)
        field2 = fields.URL(schemes=set(['http', 'https', 'ws']))
        assert field2.deserialize(url) == url

    def test_email_field_deserialization(self):
        field = fields.Email()
        assert field.deserialize('<EMAIL>') == '<EMAIL>'
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize('invalidemail')
        assert excinfo.value.args[0][0] == 'Not a valid email address.'

    def test_function_field_deserialization_is_noop_by_default(self):
        field = fields.Function(lambda x: None)
        # Default is noop
        assert field.deserialize('foo') == 'foo'
        assert field.deserialize(42) == 42

    def test_function_field_deserialization_with_callable(self):
        field = fields.Function(
            lambda x: None,
            deserialize=lambda val: val.upper(),
        )
        assert field.deserialize('foo') == 'FOO'

    def test_function_field_deserialization_with_context(self):
        class Parent(Schema):
            pass
        field = fields.Function(
            lambda x: None,
            deserialize=lambda val, context: val.upper() + context['key'],
        )
        field.parent = Parent(context={'key': 'BAR'})
        assert field.deserialize('foo') == 'FOOBAR'

    def test_function_field_passed_deserialize_only_is_load_only(self):
        field = fields.Function(deserialize=lambda val: val.upper())
        assert field.load_only is True

    def test_function_field_passed_deserialize_and_serialize_is_not_load_only(self):
        field = fields.Function(
            serialize=lambda val: val.lower(),
            deserialize=lambda val: val.upper(),
        )
        assert field.load_only is False

    def test_uuid_field_deserialization(self):
        field = fields.UUID()
        uuid_str = str(uuid.uuid4())
        result = field.deserialize(uuid_str)
        assert isinstance(result, uuid.UUID)
        assert str(result) == uuid_str

        uuid4 = uuid.uuid4()
        result = field.deserialize(uuid4)
        assert isinstance(result, uuid.UUID)
        assert result == uuid4

        uuid_bytes = b']\xc7wW\x132O\xf9\xa5\xbe\x13\x1f\x02\x18\xda\xbf'
        result = field.deserialize(uuid_bytes)
        assert isinstance(result, uuid.UUID)
        assert result.bytes == uuid_bytes

    @pytest.mark.parametrize(
        'in_value',
        [
            'malformed',
            123,
            [],
            b'tooshort',
        ],
    )
    def test_invalid_uuid_deserialization(self, in_value):
        field = fields.UUID()
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(in_value)

        assert excinfo.value.args[0] == 'Not a valid UUID.'

    def test_deserialization_function_must_be_callable(self):
        with pytest.raises(ValueError):
            fields.Function(
                lambda x: None,
                deserialize='notvalid',
            )

    def test_method_field_deserialization_is_noop_by_default(self):
        class MiniUserSchema(Schema):
            uppername = fields.Method('uppercase_name')

            def uppercase_name(self, obj):
                return obj.upper()
        s = MiniUserSchema()
        assert s.fields['uppername'].deserialize('steve') == 'steve'

    def test_deserialization_method(self):
        class MiniUserSchema(Schema):
            uppername = fields.Method('uppercase_name', deserialize='lowercase_name')

            def uppercase_name(self, obj):
                return obj.name.upper()

            def lowercase_name(self, value):
                return value.lower()

        s = MiniUserSchema()
        assert s.fields['uppername'].deserialize('STEVE') == 'steve'

    def test_deserialization_method_must_be_a_method(self):
        class BadSchema(Schema):
            uppername = fields.Method('uppercase_name', deserialize='lowercase_name')

        s = BadSchema()
        with pytest.raises(ValueError):
            s.fields['uppername'].deserialize('STEVE')

    def test_method_field_deserialize_only(self):
        class MethodDeserializeOnly(Schema):
            def lowercase_name(self, value):
                return value.lower()

        m = fields.Method(deserialize='lowercase_name')
        m.parent = MethodDeserializeOnly()

        assert m.deserialize('ALEC') == 'alec'

    def test_datetime_list_field_deserialization(self):
        dtimes = dt.datetime.now(), dt.datetime.now(), dt.datetime.utcnow()
        dstrings = [each.isoformat() for each in dtimes]
        field = fields.List(fields.DateTime())
        result = field.deserialize(dstrings)
        assert all([isinstance(each, dt.datetime) for each in result])
        for actual, expected in zip(result, dtimes):
            assert_date_equal(actual, expected)

    def test_list_field_deserialize_invalid_item(self):
        field = fields.List(fields.DateTime)
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(['badvalue'])
        assert excinfo.value.args[0] == {0: ['Not a valid datetime.']}

        field = fields.List(fields.Str())
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(['good', 42])
        assert excinfo.value.args[0] == {1: ['Not a valid string.']}

    def test_list_field_deserialize_multiple_invalid_items(self):
        field = fields.List(
            fields.Int(
                validate=validate.Range(10, 20, error='Value {input} not in range'),
            ),
        )
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize([10, 5, 25])
        assert len(excinfo.value.args[0]) == 2
        assert excinfo.value.args[0][1] == ['Value 5 not in range']
        assert excinfo.value.args[0][2] == ['Value 25 not in range']

    @pytest.mark.parametrize(
        'value',
        [
            'notalist',
            42,
            {},
        ],
    )
    def test_list_field_deserialize_value_that_is_not_a_list(self, value):
        field = fields.List(fields.Str())
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(value)
        assert excinfo.value.args[0] == 'Not a valid list.'

    def test_datetime_int_tuple_field_deserialization(self):
        dtime = dt.datetime.now()
        data = dtime.isoformat(), 42
        field = fields.Tuple([fields.DateTime(), fields.Integer()])
        result = field.deserialize(data)

        assert isinstance(result, tuple)
        assert len(result) == 2
        for val, type_, true_val in zip(result, (dt.datetime, int), (dtime, 42)):
            assert isinstance(val, type_)
            assert val == true_val

    def test_tuple_field_deserialize_invalid_item(self):
        field = fields.Tuple([fields.DateTime])
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(['badvalue'])
        assert excinfo.value.args[0] == {0: ['Not a valid datetime.']}

        field = fields.Tuple([fields.Str(), fields.Integer()])
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(['good', 'bad'])
        assert excinfo.value.args[0] == {1: ['Not a valid integer.']}

    def test_tuple_field_deserialize_multiple_invalid_items(self):
        validator = validate.Range(10, 20, error='Value {input} not in range')
        field = fields.Tuple([
            fields.Int(validate=validator),
            fields.Int(validate=validator),
            fields.Int(validate=validator),
        ])

        with pytest.raises(ValidationError) as excinfo:
            field.deserialize([10, 5, 25])
        assert len(excinfo.value.args[0]) == 2
        assert excinfo.value.args[0][1] == ['Value 5 not in range']
        assert excinfo.value.args[0][2] == ['Value 25 not in range']

    @pytest.mark.parametrize(
        'value',
        [
            'notalist',
            42,
            {},
        ],
    )
    def test_tuple_field_deserialize_value_that_is_not_a_collection(self, value):
        field = fields.Tuple([fields.Str()])
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize(value)
        assert excinfo.value.args[0] == 'Not a valid tuple.'

    def test_tuple_field_deserialize_invalid_length(self):
        field = fields.Tuple([fields.Str(), fields.Str()])
        with pytest.raises(ValidationError) as excinfo:
            field.deserialize([42])
        assert excinfo.value.args[0] == 'Length must be 2.'


    def test_constant_field_deserialization(self):
{'fraction_non_alphanumeric': 0.11578112609040445, 'fraction_numerical': 0.016287439760873542, 'mean_word_length': 2.466902823305488, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 15, 'https://': 8, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3602324', 'n_tokens_mistral': 19837, 'n_tokens_neox': 19606, 'n_words': 3991}