Skip to content

Commit

Permalink
test invalid json (#76)
Browse files Browse the repository at this point in the history
* test invalid json

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* bugfix, update tests

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* add `convert_nan`

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* bump version

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
PythonFZ and pre-commit-ci[bot] authored Nov 8, 2024
1 parent c9fff3b commit 042bed9
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 9 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "znsocket",
"version": "0.2.6",
"version": "0.2.7",
"description": "JS interface for the python znsocket package",
"main": "js/index.js",
"types": "js/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "znsocket"
version = "0.2.6"
version = "0.2.7"
description = "Python implementation of a Redis-compatible API using websockets."
authors = ["Fabian Zills <[email protected]>"]
license = "Apache-2.0"
Expand Down
37 changes: 37 additions & 0 deletions tests/test_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -394,3 +394,40 @@ def test_dct_merge(client, request):
new_dct = dct | dct2
assert new_dct == {"a": "1", "b": "3", "c": "4"}
assert isinstance(new_dct, dict)


@pytest.mark.parametrize("client", ["znsclient", "znsclient_w_redis", "redisclient"])
def test_dict_refresh_update(client, request, znsclient):
r = request.getfixturevalue(client)
dct = znsocket.Dict(r=r, key="dct:test", socket=znsclient)
dct2 = znsocket.Dict(
r=r, key="dct:test", socket=znsocket.Client.from_url(znsclient.address)
)
mock = MagicMock()
dct2.on_refresh(mock)

dct.update({"a": 1, "b": [1, 2, 3]})
assert dct == {"a": 1, "b": [1, 2, 3]}
znsclient.sio.sleep(0.2)
mock.assert_called_with({"keys": ["a", "b"]})


@pytest.mark.parametrize("client", ["znsclient", "znsclient_w_redis", "redisclient"])
def test_invalid_json(client, request):
c = request.getfixturevalue(client)
dct = znsocket.Dict(r=c, key="list:test")

with pytest.raises(ValueError):
dct.update({"a": float("inf")})
with pytest.raises(ValueError):
dct.update({"a": float("nan")})
with pytest.raises(ValueError):
dct.update({"a": float("-inf")})

dct.convert_nan = True

dct.update({"inf": float("inf"), "nan": float("nan"), "-inf": float("-inf")})

assert dct["inf"] is None
assert dct["nan"] is None
assert dct["-inf"] is None
28 changes: 28 additions & 0 deletions tests/test_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ def test_list_extend(client, request):
lst.extend([5, 6.28, "7"])
assert lst == [1, 2, 3, 4, 5, 6.28, "7"]

lst.append(None)
assert lst[-1] is None


@pytest.mark.parametrize(
"client", ["znsclient", "znsclient_w_redis", "redisclient", "empty"]
Expand Down Expand Up @@ -540,3 +543,28 @@ def test_list_refresh_extend_self_trigger(client, request, znsclient):
znsclient.sio.sleep(0.01)
assert len(lst) == 3
mock.assert_not_called()


@pytest.mark.parametrize("client", ["znsclient", "znsclient_w_redis", "redisclient"])
def test_invalid_json(client, request):
c = request.getfixturevalue(client)
dct = znsocket.List(r=c, key="list:test")

with pytest.raises(ValueError):
dct.append(float("inf"))
with pytest.raises(ValueError):
dct.append(float("nan"))
with pytest.raises(ValueError):
dct.append(float("-inf"))

dct.convert_nan = True

dct.append(float("inf"))
dct.append(float("nan"))
dct.append(float("-inf"))

assert len(dct) == 3

assert dct[0] is None
assert dct[1] is None
assert dct[2] is None
55 changes: 48 additions & 7 deletions znsocket/objects/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,37 @@ class ZnSocketObject:

def _encode(self, data: t.Any) -> str:
if self.converter is not None:
return json.dumps(data, cls=znjson.ZnEncoder.from_converters(self.converter))
return json.dumps(data)
try:
return json.dumps(
data,
cls=znjson.ZnEncoder.from_converters(self.converter),
allow_nan=False,
)
except ValueError:
if self.convert_nan:
value = json.dumps(
data,
cls=znjson.ZnEncoder.from_converters(self.converter),
allow_nan=True,
)
return (
value.replace("NaN", "null")
.replace("-Infinity", "null")
.replace("Infinity", "null")
)
raise

try:
return json.dumps(data, allow_nan=False)
except ValueError:
if self.convert_nan:
value = json.dumps(data, allow_nan=True)
return (
value.replace("NaN", "null")
.replace("-Infinity", "null")
.replace("Infinity", "null")
)
raise


def _decode(self, data: str) -> t.Any:
Expand All @@ -42,6 +71,7 @@ def __init__(
repr_type: ListRepr = "length",
converter: list[t.Type[znjson.ConverterBase]] | None = None,
max_commands_per_call: int = 1_000_000,
convert_nan: bool = False,
):
"""Synchronized list object.
Expand Down Expand Up @@ -72,6 +102,9 @@ def __init__(
Reduce for large list operations to avoid
hitting the message size limit.
Only applies when using `znsocket.Client`.
convert_nan: bool
Convert NaN and Infinity to None. Both are no native
JSON values and can not be encoded/decoded.
"""
self.redis = r
Expand All @@ -80,6 +113,7 @@ def __init__(
self.socket = socket if socket else (r if isinstance(r, Client) else None)
self.converter = converter
self._on_refresh = lambda x: None
self.convert_nan = convert_nan

if isinstance(r, Client):
self._pipeline_kwargs = {"max_commands_per_call": max_commands_per_call}
Expand Down Expand Up @@ -113,11 +147,9 @@ def __getitem__(self, index: int | list | slice) -> t.Any | list[t.Any]:
items = []
for value in data:
if value is None:
item = None
else:
item = _decode(self, value)
if item is None:
raise IndexError("list index out of range")

item = _decode(self, value)
if isinstance(item, str):
if item.startswith("znsocket.List:"):
key = item.split(":", 1)[1]
Expand Down Expand Up @@ -325,6 +357,7 @@ def __init__(
callbacks: DictCallbackTypedDict | None = None,
repr_type: DictRepr = "keys",
converter: list[t.Type[znjson.ConverterBase]] | None = None,
convert_nan: bool = False,
):
"""Synchronized dict object.
Expand All @@ -346,16 +379,19 @@ def __init__(
repr_type: "keys"|"minimal"|"full"
Control the `repr` appearance of the object.
Reduce for better performance.
converter: list[znjson.ConverterBase]|None
Optional list of znjson converters
to use for encoding/decoding the data.
convert_nan: bool
Convert NaN and Infinity to None. Both are no native
JSON values and can not be encoded/decoded.
"""
self.redis = r
self.socket = socket if socket else (r if isinstance(r, Client) else None)
self.converter = converter
self.key = key
self.repr_type = repr_type
self.convert_nan = convert_nan
self._callbacks = {
"setitem": None,
"delitem": None,
Expand Down Expand Up @@ -507,6 +543,11 @@ def update(self, *args, **kwargs):
pipeline.hset(self.key, key, _encode(self, value))
pipeline.execute()

if self.socket is not None:
refresh: RefreshTypeDict = {"keys": list(other.keys())}
refresh_data: RefreshDataTypeDict = {"target": self.key, "data": refresh}
self.socket.sio.emit(f"refresh", refresh_data, namespace="/znsocket")

def __or__(self, value: "dict|Dict") -> dict:
if isinstance(value, Dict):
value = dict(value)
Expand Down

0 comments on commit 042bed9

Please sign in to comment.