Skip to content

Commit

Permalink
Unsupported type error handling (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
faph authored Jun 29, 2023
2 parents 5f76d1c + 0d5a7f8 commit 31dc841
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 8 deletions.
19 changes: 11 additions & 8 deletions src/py_adapter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,15 @@
# Elementary serializable data types
Primitives = Union[None, bool, str, int, float]
Logicals = Union[datetime.datetime, datetime.date]
Record = Dict[str, Any] # Recursive definition not possible yet
Array = List[Any] # Recursive definition not possible yet
Record = Dict[str, "Basic"]
Array = List["Basic"]
Basic = Union[Primitives, Logicals, Array, Record]


# TODO: support datetime as nanosecond integer
def to_basic_type(obj: Any, *, datetime_type: Type = datetime.datetime, json_type: Type = str) -> Basic:
"""
Convert an object into a data structure using "basic" types, suitable for serialization
Convert an object into a data structure using "basic" types only as a pre-serialization step.
:param obj: The object to convert
:param datetime_type: The type to convert datetime objects to. Supported types include :class:`int` (timestamp),
Expand All @@ -77,7 +77,7 @@ def to_basic_type(obj: Any, *, datetime_type: Type = datetime.datetime, json_typ

def from_basic_type(basic_obj: Basic, py_type: Type[Obj]) -> Obj:
"""
Convert a data structure with "basic" types into a Python object using an Avro schema
Convert a data structure with "basic" types only into a Python object of a given type
:param basic_obj: Any valid data structure that can be used to create an instance of ``py_type``
:param py_type: The Python class to create an instance from
Expand Down Expand Up @@ -219,10 +219,13 @@ def for_py_type(cls, py_type: Type) -> "_ObjectAdapter":
:param py_type: The Python class to return an object adapter for
"""
# TODO: expose options as necessary
schema = avro.schema.parse(
pas.generate(py_type, options=pas.Option.LOGICAL_JSON_STRING | pas.Option.MILLISECONDS).decode("utf-8")
)
try:
# TODO: expose options as necessary
schema = avro.schema.parse(
pas.generate(py_type, options=pas.Option.LOGICAL_JSON_STRING | pas.Option.MILLISECONDS).decode("utf-8")
)
except TypeError:
raise TypeError(f"{py_type} not supported by py-adapter since it is not supported by py-avro-schema")
return cls(schema)

def adapt(self, data: Basic) -> Any:
Expand Down
19 changes: 19 additions & 0 deletions tests/test_py_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import io
import json
import pathlib
import re
import uuid

import avro.datafile
Expand Down Expand Up @@ -464,3 +465,21 @@ def test_uuid():

def test_uuid_from_empty_string():
assert py_adapter.from_basic_type("", uuid.UUID) is None


def test_unsupported_type():
class Sail:
def __init__(self, name): # untyped __init__ arguments not supported
self.name = name

sail = Sail(name="spinnaker")
expected_serialization = {"name": "spinnaker"}
assert py_adapter.to_basic_type(sail) == expected_serialization # This works
with pytest.raises(
TypeError,
match=re.escape(
"<class 'test_py_adapter.test_unsupported_type.<locals>.Sail'> not supported by py-adapter since it is not "
"supported by py-avro-schema"
),
):
py_adapter.from_basic_type(expected_serialization, Sail) # This does not work

0 comments on commit 31dc841

Please sign in to comment.