From dc8bd4d3355ff3fc1e6db53025baf607f5b868c9 Mon Sep 17 00:00:00 2001 From: p1c2u Date: Sat, 11 Apr 2020 13:45:46 +0100 Subject: [PATCH] Date-time format unmarshal tz fix --- .../unmarshalling/schemas/unmarshallers.py | 6 +++-- openapi_core/unmarshalling/schemas/util.py | 6 ----- requirements.txt | 1 + requirements_2.7.txt | 1 + setup.cfg | 1 + tests/integration/validation/test_petstore.py | 3 ++- tests/unit/unmarshalling/test_unmarshal.py | 22 +++++++++++++++++-- 7 files changed, 29 insertions(+), 11 deletions(-) diff --git a/openapi_core/unmarshalling/schemas/unmarshallers.py b/openapi_core/unmarshalling/schemas/unmarshallers.py index 5f924d4..a39ed3a 100644 --- a/openapi_core/unmarshalling/schemas/unmarshallers.py +++ b/openapi_core/unmarshalling/schemas/unmarshallers.py @@ -1,6 +1,8 @@ from functools import partial import logging +from isodate.isodatetime import parse_datetime + from openapi_schema_validator._types import ( is_array, is_bool, is_integer, is_object, is_number, is_string, @@ -20,7 +22,7 @@ from openapi_core.unmarshalling.schemas.exceptions import ( ) from openapi_core.unmarshalling.schemas.formatters import Formatter from openapi_core.unmarshalling.schemas.util import ( - forcebool, format_date, format_datetime, format_byte, format_uuid, + forcebool, format_date, format_byte, format_uuid, format_number, ) @@ -77,7 +79,7 @@ class StringUnmarshaller(PrimitiveTypeUnmarshaller): partial(oas30_format_checker.check, format='date'), format_date), SchemaFormat.DATETIME: Formatter.from_callables( partial(oas30_format_checker.check, format='date-time'), - format_datetime), + parse_datetime), SchemaFormat.BINARY: Formatter.from_callables( partial(oas30_format_checker.check, format='binary'), binary_type), SchemaFormat.UUID: Formatter.from_callables( diff --git a/openapi_core/unmarshalling/schemas/util.py b/openapi_core/unmarshalling/schemas/util.py index bdca71f..d5ac76c 100644 --- a/openapi_core/unmarshalling/schemas/util.py +++ b/openapi_core/unmarshalling/schemas/util.py @@ -3,7 +3,6 @@ from base64 import b64decode import datetime from distutils.util import strtobool from six import string_types, text_type, integer_types -import strict_rfc3339 from uuid import UUID @@ -18,11 +17,6 @@ def format_date(value): return datetime.datetime.strptime(value, '%Y-%m-%d').date() -def format_datetime(value): - timestamp = strict_rfc3339.rfc3339_to_timestamp(value) - return datetime.datetime.utcfromtimestamp(timestamp) - - def format_uuid(value): if isinstance(value, UUID): return value diff --git a/requirements.txt b/requirements.txt index 36d15a4..08cebe5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ +isodate==0.6.0 openapi-spec-validator openapi-schema-validator six diff --git a/requirements_2.7.txt b/requirements_2.7.txt index ff492fb..f4eb34c 100644 --- a/requirements_2.7.txt +++ b/requirements_2.7.txt @@ -1,3 +1,4 @@ +isodate==0.6.0 openapi-spec-validator openapi-schema-validator six diff --git a/setup.cfg b/setup.cfg index e58fefe..710f06a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,6 +23,7 @@ python_requires = >= 2.7, != 3.0.*, != 3.1.*, != 3.2.*, != 3.3.*, != 3.4.* setup_requires = setuptools install_requires = + isodate openapi-spec-validator openapi-schema-validator six diff --git a/tests/integration/validation/test_petstore.py b/tests/integration/validation/test_petstore.py index 15f112b..c483c03 100644 --- a/tests/integration/validation/test_petstore.py +++ b/tests/integration/validation/test_petstore.py @@ -3,6 +3,7 @@ import pytest from datetime import datetime from base64 import b64encode from uuid import UUID +from isodate.tzinfo import UTC from six import text_type from openapi_core.casting.schemas.exceptions import CastError @@ -1090,7 +1091,7 @@ class TestPetstore(object): assert parameters == RequestParameters() assert isinstance(body, BaseModel) - assert body.created == datetime(2016, 4, 16, 16, 6, 5) + assert body.created == datetime(2016, 4, 16, 16, 6, 5, tzinfo=UTC) assert body.name == pet_name code = 400 diff --git a/tests/unit/unmarshalling/test_unmarshal.py b/tests/unit/unmarshalling/test_unmarshal.py index e8c7609..64e07ef 100644 --- a/tests/unit/unmarshalling/test_unmarshal.py +++ b/tests/unit/unmarshalling/test_unmarshal.py @@ -1,6 +1,7 @@ import datetime import uuid +from isodate.tzinfo import UTC, FixedOffset import pytest from openapi_core.schema.media_types.models import MediaType @@ -199,13 +200,30 @@ class TestSchemaUnmarshallerCall(object): assert result == datetime.date(2018, 1, 2) - def test_string_format_datetime(self, unmarshaller_factory): + def test_string_format_datetime_invalid(self, unmarshaller_factory): + schema = Schema('string', schema_format='date-time') + value = '2018-01-02T00:00:00' + + with pytest.raises(InvalidSchemaValue): + unmarshaller_factory(schema)(value) + + def test_string_format_datetime_utc(self, unmarshaller_factory): schema = Schema('string', schema_format='date-time') value = '2018-01-02T00:00:00Z' result = unmarshaller_factory(schema)(value) - assert result == datetime.datetime(2018, 1, 2, 0, 0) + tzinfo = UTC + assert result == datetime.datetime(2018, 1, 2, 0, 0, tzinfo=tzinfo) + + def test_string_format_datetime_tz(self, unmarshaller_factory): + schema = Schema('string', schema_format='date-time') + value = '2020-04-01T12:00:00+02:00' + + result = unmarshaller_factory(schema)(value) + + tzinfo = FixedOffset(2) + assert result == datetime.datetime(2020, 4, 1, 12, 0, 0, tzinfo=tzinfo) def test_string_format_custom(self, unmarshaller_factory): formatted = 'x-custom'