Skip to content
Snippets Groups Projects
Commit c739746c authored by Luca Boccassi's avatar Luca Boccassi
Browse files

New upstream version 0.6.21

parent c0e4846a
No related branches found
No related tags found
No related merge requests found
......@@ -20,6 +20,30 @@ To install:
Release History
---------------
2021-01-26 Version 0.6.21
+++++++++++++++++++++++++
**Bug Fixes**
- Fixes `failsafe_deserialize` introduced in `0.6.20` #232
2021-01-25 Version 0.6.20
+++++++++++++++++++++++++
**Features**
- Add `failsafe_deserialize` method to the `Deserializer` object. #232
- Serialize `datetime`, `date`, `time`, `timedelta` and `Decimal` correctly when serializing `object` . #224
2020-09-08 Version 0.6.19
+++++++++++++++++++++++++
**Bugfixes**
- Fix serialization of random Model object #220
- Fix serialization of unicode string in Py2 and object mode #221
2020-07-27 Version 0.6.18
+++++++++++++++++++++++++
......
......@@ -9,4 +9,6 @@ pytest-asyncio;python_full_version>="3.5.2"
mypy;python_full_version>="3.5.2"
pylint
aiohttp;python_full_version>="3.5.2"
trio;python_full_version>="3.5.2"
\ No newline at end of file
# async in msrest was experimental, we won't update
trio==0.14.0;python_version == '3.5'
trio==0.16.0;python_version >= '3.6'
......@@ -951,9 +951,21 @@ class Serializer(object):
return self.serialize_basic(attr, self.basic_types[obj_type], **kwargs)
if obj_type is _long_type:
return self.serialize_long(attr)
if obj_type is unicode_str:
return self.serialize_unicode(attr)
if obj_type is datetime.datetime:
return self.serialize_iso(attr)
if obj_type is datetime.date:
return self.serialize_date(attr)
if obj_type is datetime.time:
return self.serialize_time(attr)
if obj_type is datetime.timedelta:
return self.serialize_duration(attr)
if obj_type is decimal.Decimal:
return self.serialize_decimal(attr)
# If it's a model or I know this dependency, serialize as a Model
elif obj_type in self.dependencies.values() or isinstance(obj_type, Model):
elif obj_type in self.dependencies.values() or isinstance(attr, Model):
return self._serialize(attr)
if obj_type == dict:
......@@ -1474,6 +1486,26 @@ class Deserializer(object):
pass # Target is not a Model, no classify
return target, target.__class__.__name__
def failsafe_deserialize(self, target_obj, data, content_type=None):
"""Ignores any errors encountered in deserialization,
and falls back to not deserializing the object. Recommended
for use in error deserialization, as we want to return the
HttpResponseError to users, and not have them deal with
a deserialization error.
:param str target_obj: The target object type to deserialize to.
:param str/dict data: The response data to deseralize.
:param str content_type: Swagger "produces" if available.
"""
try:
return self(target_obj, data, content_type=content_type)
except:
_LOGGER.warning(
"Ran into a deserialization error. Ignoring since this is failsafe deserialization",
exc_info=True
)
return None
@staticmethod
def _unpack_content(raw_data, content_type=None):
"""Extract the correct structure for deserialization.
......
......@@ -40,7 +40,7 @@ except ImportError:
from urllib.parse import urlparse
import xml.etree.ElementTree as ET
from typing import TYPE_CHECKING, Generic, TypeVar, cast, IO, List, Union, Any, Mapping, Dict, Optional, Tuple, Callable, Iterator # pylint: disable=unused-import
from typing import TYPE_CHECKING, Generic, TypeVar, cast, IO, List, Union, Any, Mapping, Dict, Optional, Tuple, Callable, Iterator, MutableMapping # pylint: disable=unused-import
HTTPResponseType = TypeVar("HTTPResponseType", bound='HTTPClientResponse')
......@@ -325,7 +325,7 @@ class HTTPClientResponse(object):
self.request = request
self.internal_response = internal_response
self.status_code = None # type: Optional[int]
self.headers = {} # type: Dict[str, str]
self.headers = {} # type: MutableMapping[str, str]
self.reason = None # type: Optional[str]
def body(self):
......
......@@ -26,6 +26,7 @@
from typing import Any, Callable, AsyncIterator, Optional
import aiohttp
from multidict import CIMultiDict
from . import AsyncHTTPSender, ClientRequest, AsyncClientResponse
......@@ -69,7 +70,7 @@ class AioHttpClientResponse(AsyncClientResponse):
super(AioHttpClientResponse, self).__init__(request, aiohttp_response)
# https://aiohttp.readthedocs.io/en/stable/client_reference.html#aiohttp.ClientResponse
self.status_code = aiohttp_response.status
self.headers = aiohttp_response.headers
self.headers = CIMultiDict(aiohttp_response.headers)
self.reason = aiohttp_response.reason
self._body = None
......
......@@ -25,4 +25,4 @@
# --------------------------------------------------------------------------
#: version of this package. Use msrest.__version__ instead
msrest_version = "0.6.18"
msrest_version = "0.6.21"
......@@ -28,7 +28,7 @@ from setuptools import setup, find_packages
setup(
name='msrest',
version='0.6.18',
version='0.6.21',
author='Microsoft Corporation',
packages=find_packages(exclude=["tests", "tests.*"]),
url=("https://github.com/Azure/msrest-for-python"),
......@@ -45,6 +45,7 @@ setup(
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'License :: OSI Approved :: MIT License',
'Topic :: Software Development'],
install_requires=[
......
......@@ -25,6 +25,7 @@
#
#--------------------------------------------------------------------------
from decimal import Decimal
import sys
import json
import isodate
......@@ -312,6 +313,28 @@ class TestRuntimeSerialized(unittest.TestCase):
assert s.query("filter", [',', ',', ','], "[str]", div=",") == "%2C,%2C,%2C"
assert s.query("filter", [',', ',', ','], "[str]", div="|", skip_quote=True) == ",|,|,"
def test_serialize_custom_model(self):
class CustomSample(Model):
_validation = {
'a': {'required': True},
}
_attribute_map = {
'a': {'key': 'a', 'type': 'str'},
}
def __init__(self, a):
self.a = a
s = Serializer()
model = CustomSample("helloworld")
serialized = s._serialize(model)
assert serialized is not None
assert isinstance(serialized, dict)
assert serialized['a'] == "helloworld"
def test_serialize_direct_model(self):
testobj = self.TestObj()
testobj.attr_a = "myid"
......@@ -1344,6 +1367,8 @@ class TestRuntimeSerialized(unittest.TestCase):
except NameError:
long_type = int
s = Serializer()
assert s.serialize_data(long_type(1), 'object') == long_type(1)
class TestModel(Model):
_attribute_map = {'data': {'key': 'data', 'type': 'object'}}
......@@ -1354,6 +1379,59 @@ class TestRuntimeSerialized(unittest.TestCase):
'data': {'id': long_type(1)}
}
def test_unicode_as_type_object(self):
"""Test irrelevant on Python 3. But still doing it to test regresssion.
https://github.com/Azure/msrest-for-python/issue/221
"""
s = Serializer()
assert s.serialize_data(u"\ua015", 'object') == u"\ua015"
class TestModel(Model):
_attribute_map = {'data': {'key': 'data', 'type': 'object'}}
m = TestModel(data = {'id': u"\ua015"})
serialized = m.serialize()
assert serialized == {
'data': {'id': u"\ua015"}
}
def test_datetime_types_as_type_object(self):
"""https://github.com/Azure/msrest-for-python/issues/223
"""
class TestModel(Model):
_attribute_map = {'data': {'key': 'data', 'type': 'object'}}
m = TestModel(data = {
'datetime': isodate.parse_datetime('2012-02-24T00:53:52.780Z'),
'date': date(2019,5,1),
'time': time(11,12,13),
'timedelta': timedelta(56)
})
serialized = m.serialize()
assert serialized['data'] == {
'datetime': '2012-02-24T00:53:52.780Z',
'date': '2019-05-01',
'time': '11:12:13',
'timedelta': 'P56D'
}
def test_decimal_types_as_type_object(self):
"""https://github.com/Azure/msrest-for-python/issues/223
"""
class TestModel(Model):
_attribute_map = {'data': {'key': 'data', 'type': 'object'}}
m = TestModel(data = {
'decimal': Decimal('1.1'),
})
serialized = m.serialize()
assert serialized['data'] == {
'decimal': 1.1
}
def test_json_with_xml_map(self):
basic_json = {'age': 37, 'country': 'france'}
......@@ -2455,6 +2533,33 @@ class TestRuntimeDeserialized(unittest.TestCase):
m = TestModel.deserialize({'data': {'id': long_type(1)}})
assert m.data['id'] == long_type(1)
def test_failsafe_deserialization(self):
class Error(Model):
_attribute_map = {
"status": {"key": "status", "type": "int"},
"message": {"key": "message", "type": "str"},
}
def __init__(self, **kwargs):
super(Error, self).__init__(**kwargs)
self.status = kwargs.get("status", None)
self.message = kwargs.get("message", None)
with pytest.raises(DeserializationError):
self.d(Error, json.dumps(''), 'text/html')
# should fail
deserialized = self.d.failsafe_deserialize(Error, json.dumps(''), 'text/html')
assert deserialized is None
# should not fail
error = {"status": 400, "message": "should deserialize"}
deserialized = self.d.failsafe_deserialize(Error, json.dumps(error), 'application/json')
assert deserialized.status == 400
assert deserialized.message == "should deserialize"
class TestModelInstanceEquality(unittest.TestCase):
def test_model_instance_equality(self):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment