Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions src/celpy/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,23 +43,26 @@ class CELJSONEncoder(json.JSONEncoder):

This is **not** a reversible transformation. Some things are coerced to strings
without any more detailed type marker.
Specifically timestamps, durations, and bytes.
Specifically timestamps, durations, bytes, and integers outside the interoperable range.
"""

@staticmethod
def to_python(
cel_object: celtypes.Value,
) -> Union[celtypes.Value, List[Any], Dict[Any, Any], bool]:
) -> Union[celtypes.Value, List[Any], Dict[Any, Any], bool, str]:
"""Recursive walk through the CEL object, replacing :py:class:`celpy.celtypes.BoolType` with native ``bool`` instances.
This lets the :py:mod:`json` module correctly represent the obects
with JSON ``true`` and ``false``.

This will also replace :py:class:`celpy.celtypes.ListType` and :py:class:`celpy.celtypes.MapType` with native ``list`` and ``dict``.
CEL integers outside ``-(2**53 - 1)`` to ``2**53 - 1`` become decimal strings.
All other CEL objects will be left intact. This creates an intermediate hybrid
beast that's not quite a :py:class:`celpy.celtypes.Value` because a few things have been replaced.
"""
if isinstance(cel_object, celtypes.BoolType):
return True if cel_object else False
elif isinstance(cel_object, (celtypes.IntType, celtypes.UintType)):
return str(cel_object) if abs(cel_object) > 2**53 - 1 else cel_object
elif isinstance(cel_object, celtypes.ListType):
return [CELJSONEncoder.to_python(item) for item in cel_object]
elif isinstance(cel_object, celtypes.MapType):
Expand All @@ -72,7 +75,7 @@ def to_python(

def encode(self, cel_object: celtypes.Value) -> str:
"""
Override built-in encode to create proper Python ``bool`` objects.
Override built-in encode to convert booleans and large integers.
"""
return super().encode(CELJSONEncoder.to_python(cel_object))

Expand Down
25 changes: 25 additions & 0 deletions tests/test_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,31 @@ def test_encoder():
)


@pytest.mark.parametrize(
"cel_value, expected",
[
(celpy.celtypes.IntType(-2**63), '"-9223372036854775808"'),
(celpy.celtypes.IntType(-(2**53 + 1)), '"-9007199254740993"'),
(celpy.celtypes.IntType(-2**53), '"-9007199254740992"'),
(celpy.celtypes.IntType(-(2**53 - 1)), "-9007199254740991"),
(celpy.celtypes.IntType(2**53 - 1), "9007199254740991"),
(celpy.celtypes.IntType(2**53), '"9007199254740992"'),
(celpy.celtypes.IntType(2**53 + 1), '"9007199254740993"'),
(celpy.celtypes.IntType(2**63 - 1), '"9223372036854775807"'),
(celpy.celtypes.UintType(2**53 - 1), "9007199254740991"),
(celpy.celtypes.UintType(2**53), '"9007199254740992"'),
(celpy.celtypes.UintType(2**53 + 1), '"9007199254740993"'),
(celpy.celtypes.UintType(2**64 - 1), '"18446744073709551615"'),
],
)
def test_encoder_integer_range(cel_value, expected):
assert json.dumps(cel_value, cls=celpy.CELJSONEncoder) == expected
cel_obj = celpy.celtypes.MapType(
{celpy.celtypes.StringType("numbers"): celpy.celtypes.ListType([cel_value])}
)
assert json.dumps(cel_obj, cls=celpy.CELJSONEncoder) == f'{{"numbers": [{expected}]}}'


def test_encoder_unknown():
cel_obj = sentinel.no_json
with pytest.raises(TypeError):
Expand Down