Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow deserialization of undefined object #482

Merged
merged 12 commits into from
Feb 23, 2024
3 changes: 2 additions & 1 deletion doc/source/api/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,15 @@ This section summarizes OpenAPI-Common helper functions.
Exceptions and warnings
=======================

This section summarizes OpenAPI-Common attributes.
This section summarizes OpenAPI-Common exceptions and warnings.

.. autosummary::
:toctree: _autosummary

ApiException
ApiConnectionException
AuthenticationWarning
UndefinedObjectWarning


============
Expand Down
8 changes: 7 additions & 1 deletion src/ansys/openapi/common/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@

from ._api_client import ApiClient
from ._base import ApiBase, ApiClientBase, ModelBase, Unset, Unset_Type
from ._exceptions import ApiConnectionException, ApiException, AuthenticationWarning
from ._exceptions import (
ApiConnectionException,
ApiException,
AuthenticationWarning,
UndefinedObjectWarning,
)
from ._session import ApiClientFactory, OIDCSessionBuilder
from ._util import SessionConfiguration, generate_user_agent

Expand All @@ -22,6 +27,7 @@
"ApiBase",
"ApiClientBase",
"ModelBase",
"UndefinedObjectWarning",
"Unset",
"Unset_Type",
]
18 changes: 16 additions & 2 deletions src/ansys/openapi/common/_api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,16 @@
cast,
)
from urllib.parse import quote
import warnings

from dateutil.parser import parse
import requests

with warnings.catch_warnings():
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was causing issues when I was trying to trap specific warnings.

warnings.simplefilter("ignore", category=DeprecationWarning)
from dateutil.parser import parse

from ._base import ApiClientBase, DeserializedType, ModelBase, PrimitiveType, SerializedType, Unset
from ._exceptions import ApiException
from ._exceptions import ApiException, UndefinedObjectWarning
from ._util import SessionConfiguration, handle_response


Expand Down Expand Up @@ -346,10 +350,20 @@ def __deserialize(self, data: SerializedType, klass_name: str) -> DeserializedTy

* String class name
* String type definition for list or dictionary
* "object" literal, which returns the dictionary as-is
"""
if data is None:
return None

if klass_name == "object":
warnings.warn(
"Attempting to deserialize an object with no defined type. Returning "
"the raw data as a dictionary. Check your OpenAPI definition and ensure "
"all types are fully defined.",
UndefinedObjectWarning,
)
return data

list_match = self.LIST_MATCH_REGEX.match(klass_name)
if list_match is not None:
assert isinstance(data, list)
Expand Down
12 changes: 12 additions & 0 deletions src/ansys/openapi/common/_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,15 @@ def __str__(self) -> str:
def __repr__(self) -> str:
"""Printable representation of the object."""
return f"ApiException({self.status_code}, '{self.reason_phrase}', '{self.body}')"


class UndefinedObjectWarning(UserWarning):
"""
Provides a warning for when a model is incompletely described in the OpenAPI definition.

The data received from the server cannot be fully deserialized, and so the response is provided
as an un-deserialized dictionary.

This warning can be safely suppressed if the required detail cannot be added to the OpenAPI
definition, but in this case the deserialization must be defined manually.
"""
16 changes: 15 additions & 1 deletion tests/test_api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@
from requests_mock.request import _RequestObjectProxy
from requests_mock.response import _FakeConnection, _IOReader

from ansys.openapi.common import ApiClient, ApiException, SessionConfiguration
from ansys.openapi.common import (
ApiClient,
ApiException,
SessionConfiguration,
UndefinedObjectWarning,
)

TEST_URL = "http://localhost/api/v1.svc"
UA_STRING = "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Firefox/47.0"
Expand Down Expand Up @@ -390,6 +395,15 @@ def test_deserialize_primitive_of_wrong_type_does_nothing(self, data, target_typ
assert isinstance(output, type(data))
assert output == data

def test_deserialize_undefined_object_returns_dict_and_warns(self):
data = {"foo": "bar", "baz": [1, 2, 3]}
with pytest.warns(
UndefinedObjectWarning,
match="Attempting to deserialize an object with no defined type",
):
output = self._client._ApiClient__deserialize(data, "object")
assert output == data


class TestResponseParsing:
from requests.adapters import HTTPAdapter
Expand Down
Loading