diff --git a/setup.py b/setup.py index df023b3..5ea9770 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ def read(filename): extras_require = { "marshmallow": ["marshmallow>=2.15.0"], - "pydantic:python_version >= '3.6'": ["pydantic>=1.6.1"], + "pydantic:python_version >= '3.6'": ["pydantic>=2.0.0"], "aiohttp:python_version <= '3.4'": [], "aiohttp:python_version >= '3.4'": "aiohttp>=2.3.0", "twisted:python_version != '3.3' and python_version != '3.4'": "twisted>=17.1.0", diff --git a/tests/integration/test_retry_aiohttp.py b/tests/integration/test_retry_aiohttp.py index 05895cd..823d872 100644 --- a/tests/integration/test_retry_aiohttp.py +++ b/tests/integration/test_retry_aiohttp.py @@ -5,17 +5,27 @@ from uplink.clients import io from . import test_retry +import sys @pytest.mark.asyncio async def test_retry_with_asyncio(mock_client, mock_response): - import asyncio + # Check the Python version and define coroutine accordingly + if sys.version_info >= (3, 5): + # For Python 3.5 and newer + async def coroutine(): + return mock_response + elif (3, 3) <= sys.version_info < (3, 5): + # For Python 3.3 and 3.4 + import asyncio + @asyncio.coroutine + def coroutine(): + yield + return mock_response + else: + # Not applicable for Python 2 + return - @asyncio.coroutine - def coroutine(): - return mock_response - - # Setup mock_response.with_json({"id": 123, "name": "prkumar"}) mock_client.with_side_effect([Exception, coroutine()]) mock_client.with_io(io.AsyncioStrategy()) diff --git a/tests/unit/test_converters.py b/tests/unit/test_converters.py index 4925940..ed86b40 100644 --- a/tests/unit/test_converters.py +++ b/tests/unit/test_converters.py @@ -26,7 +26,7 @@ def test_convert(self): class TestStandardConverter(object): def test_create_response_body_converter_with_converter( - self, converter_mock + self, converter_mock ): # Setup factory = standard.StandardConverter() @@ -83,7 +83,7 @@ class TestConverterFactoryRegistry(object): backend = converters.ConverterFactoryRegistry._converter_factory_registry def test_init_args_are_passed_to_factory( - self, converter_factory_mock, converter_mock + self, converter_factory_mock, converter_mock ): args = ("arg1", "arg2") kwargs = {"arg3": "arg3"} @@ -178,7 +178,7 @@ def test_init_without_marshmallow(self): @for_marshmallow_2_and_3 def test_create_request_body_converter( - self, mocker, schema_mock_and_argument, is_marshmallow_3 + self, mocker, schema_mock_and_argument, is_marshmallow_3 ): # Setup schema_mock, argument = schema_mock_and_argument @@ -210,7 +210,7 @@ def test_create_request_body_converter_without_schema(self): @for_marshmallow_2_and_3 def test_create_response_body_converter( - self, mocker, schema_mock_and_argument, is_marshmallow_3 + self, mocker, schema_mock_and_argument, is_marshmallow_3 ): # Setup schema_mock, argument = schema_mock_and_argument @@ -242,7 +242,7 @@ def test_create_response_body_converter( c.convert(data) def test_create_response_body_converter_with_unsupported_response( - self, schema_mock_and_argument + self, schema_mock_and_argument ): # Setup schema_mock, argument = schema_mock_and_argument @@ -474,15 +474,6 @@ def test_dict_converter(self): sys.version_info < (3, 6), reason="requires python3.6 or higher" ) class TestPydanticConverter(object): - @pytest.fixture - def pydantic_model_mock(self, mocker): - class Model(pydantic.BaseModel): - def __new__(cls, *args, **kwargs): - return model - - model = mocker.Mock(spec=Model) - return model, Model - def test_init_without_pydantic(self, mocker): mocker.patch.object( converters.PydanticConverter, @@ -494,30 +485,29 @@ def test_init_without_pydantic(self, mocker): with pytest.raises(ImportError): converters.PydanticConverter() - def test_create_request_body_converter(self, pydantic_model_mock): + def test_create_request_body_converter(self): + class Model(pydantic.BaseModel): + id: int = 0 + expected_result = {"id": 0} - request_body = {} - model_mock, model = pydantic_model_mock - model_mock.dict.return_value = expected_result + request_body = {} converter = converters.PydanticConverter() - request_converter = converter.create_request_body_converter(model) + request_converter = converter.create_request_body_converter(Model) result = request_converter.convert(request_body) assert result == expected_result - model_mock.dict.assert_called_once() - model_mock.dict.assert_called_once() def test_convert_complex_model(self): from json import loads from datetime import datetime class ComplexModel(pydantic.BaseModel): - when = datetime.utcnow() # type: datetime - where = "http://example.com" # type: pydantic.AnyUrl - some = [1] # type: typing.List[int] + when: datetime = datetime.utcnow() + where: pydantic.AnyUrl = "http://example.com" + some: list[int] = [1] model = ComplexModel() request_body = {} @@ -532,23 +522,20 @@ class ComplexModel(pydantic.BaseModel): assert result == expected_result - def test_create_request_body_converter_with_original_model( - self, pydantic_model_mock - ): + def test_create_request_body_converter_with_original_model(self): expected_result = {"id": 0} - model_mock, model = pydantic_model_mock - model_mock.dict.return_value = expected_result + class Model(pydantic.BaseModel): + id: int = 0 - request_body = model() + request_body = Model() converter = converters.PydanticConverter() - request_converter = converter.create_request_body_converter(model) + request_converter = converter.create_request_body_converter(Model) result = request_converter.convert(request_body) assert result == expected_result - model_mock.dict.assert_called_once() def test_create_request_body_converter_without_schema(self, mocker): expected_result = None @@ -558,44 +545,37 @@ def test_create_request_body_converter_without_schema(self, mocker): assert result is expected_result - def test_create_response_body_converter(self, mocker, pydantic_model_mock): - expected_result = "data" - model_mock, model = pydantic_model_mock + def test_create_response_body_converter(self, mocker): + class Model(pydantic.BaseModel): + id: int - parse_obj_mock = mocker.patch.object( - model, "parse_obj", return_value=expected_result - ) + expected_result = Model(id=1) response = mocker.Mock(spec=["json"]) - response.json.return_value = {} + response.json.return_value = {"id": 1} converter = converters.PydanticConverter() - c = converter.create_response_body_converter(model) + c = converter.create_response_body_converter(Model) result = c.convert(response) response.json.assert_called_once() - parse_obj_mock.assert_called_once_with(response.json()) assert result == expected_result def test_create_response_body_converter_invalid_response( - self, mocker, pydantic_model_mock + self, mocker ): - data = {"quick": "fox"} - _, model = pydantic_model_mock + data = {"id": "qwe"} # Not int - parse_obj_mock = mocker.patch.object( - model, "parse_obj", side_effect=pydantic.ValidationError([], model) - ) + class Model(pydantic.BaseModel): + id: int converter = converters.PydanticConverter() - c = converter.create_response_body_converter(model) + c = converter.create_response_body_converter(Model) with pytest.raises(pydantic.ValidationError): c.convert(data) - parse_obj_mock.assert_called_once_with(data) - def test_create_response_body_converter_without_schema(self): expected_result = None converter = converters.PydanticConverter() @@ -604,12 +584,15 @@ def test_create_response_body_converter_without_schema(self): assert result is expected_result - def test_create_string_converter(self, pydantic_model_mock): + def test_create_string_converter(self): expected_result = None - _, model = pydantic_model_mock + + class Model(pydantic.BaseModel): + id: int + converter = converters.PydanticConverter() - c = converter.create_string_converter(model, None) + c = converter.create_string_converter(Model, None) assert c is expected_result diff --git a/uplink/converters/pydantic_.py b/uplink/converters/pydantic_.py index 4254ac9..4f2df42 100644 --- a/uplink/converters/pydantic_.py +++ b/uplink/converters/pydantic_.py @@ -9,7 +9,7 @@ def _encode_pydantic(obj): - from pydantic.json import pydantic_encoder + from pydantic_core import to_jsonable_python # json atoms if isinstance(obj, (str, int, float, bool)) or obj is None: @@ -22,7 +22,7 @@ def _encode_pydantic(obj): return [_encode_pydantic(i) for i in obj] # pydantic types - return _encode_pydantic(pydantic_encoder(obj)) + return _encode_pydantic(to_jsonable_python(obj)) class _PydanticRequestBody(Converter):