Skip to content

Commit

Permalink
Prefix floats when dumping to json to reliably reconstruct them
Browse files Browse the repository at this point in the history
  • Loading branch information
elya5 committed Jan 8, 2024
1 parent 811ba12 commit beae4fd
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 11 deletions.
3 changes: 2 additions & 1 deletion django_unicorn/components/unicorn_template_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
MultipleRootComponentElementError,
NoRootComponentElementError,
)
from django_unicorn.serializer import loads
from django_unicorn.settings import get_minify_html_enabled, get_script_location
from django_unicorn.utils import generate_checksum, sanitize_html

Expand Down Expand Up @@ -136,7 +137,7 @@ def render(self):
)

frontend_context_variables = self.component.get_frontend_context_variables()
frontend_context_variables_dict = orjson.loads(frontend_context_variables)
frontend_context_variables_dict = loads(frontend_context_variables)
checksum = generate_checksum(frontend_context_variables_dict)

# Use `html.parser` and not `lxml` because in testing it was no faster even with `cchardet`
Expand Down
7 changes: 5 additions & 2 deletions django_unicorn/serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from datetime import timedelta
from decimal import Decimal
from functools import lru_cache
import re
from types import MappingProxyType
from typing import Any, Dict, List, Optional, Tuple

Expand Down Expand Up @@ -283,7 +284,7 @@ def _fix_floats(current: Dict, data: Optional[Dict] = None, paths: Optional[List
if idx == len(paths) - 1:
# `path` can be a dictionary key or list index,
# but in either instance it is set the same way
_piece[path] = str(current)
_piece[path] = 'django-unicorn-float-' + str(current)
else:
_piece = _piece[path]

Expand Down Expand Up @@ -431,7 +432,9 @@ def loads(string: str) -> dict:
"""

try:
return orjson.loads(string)
if isinstance(string, bytes):
string = string.decode()
return orjson.loads(re.sub(r'"django-unicorn-float-(\d+.\d+)"', r'\1', string))
except orjson.JSONDecodeError as e:
raise JSONDecodeError from e

Expand Down
23 changes: 15 additions & 8 deletions tests/serializer/test_dumps.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,9 @@ def test_complicated_model():
expected = {
"date": str(model.date),
"datetime": model.datetime.isoformat()[:-3],
"decimal_value": "0.984",
"decimal_value": "django-unicorn-float-0.984",
"duration": "-1 19:00:00",
"float_value": "0.583",
"float_value": "django-unicorn-float-0.583",
"name": "abc",
"pk": 2,
"time": str(model.time)[:-3],
Expand Down Expand Up @@ -646,35 +646,35 @@ def test_get_model_dict_many_to_many_references_model():


def test_float():
expected = '{"name":"0.0"}'
expected = '{"name":"django-unicorn-float-0.0"}'
actual = serializer.dumps({"name": 0.0})

assert expected == actual


def test_dict_float():
expected = '{"name":{"another":"0.0"}}'
expected = '{"name":{"another":"django-unicorn-float-0.0"}}'
actual = serializer.dumps({"name": {"another": 0.0}})

assert expected == actual


def test_list_float():
expected = '{"name":[1,2,"0.0"]}'
expected = '{"name":[1,2,"django-unicorn-float-0.0"]}'
actual = serializer.dumps({"name": [1, 2, 0.0]})

assert expected == actual


def test_nested_list_float():
expected = '{"name":{"blob":[1,2,"0.0"]}}'
expected = '{"name":{"blob":[1,2,"django-unicorn-float-0.0"]}}'
actual = serializer.dumps({"name": {"blob": [1, 2, 0.0]}})

assert expected == actual


def test_nested_list_float_complicated():
expected = '{"another":[{"great":"1.0","ok":["1.6","0.0",4]}],"more":["1.9",2,5],"name":{"blob":[1,2,"0.0"]}}'
expected = '{"another":[{"great":"django-unicorn-float-1.0","ok":["django-unicorn-float-1.6","django-unicorn-float-0.0",4]}],"more":["django-unicorn-float-1.9",2,5],"name":{"blob":[1,2,"django-unicorn-float-0.0"]}}'
actual = serializer.dumps(
{
"name": {"blob": [1, 2, 0.0]},
Expand All @@ -687,7 +687,7 @@ def test_nested_list_float_complicated():


def test_nested_list_float_less_complicated():
expected = '{"another":[{"great":"1.0","ok":["1.6","0.0",4]}]}'
expected = '{"another":[{"great":"django-unicorn-float-1.0","ok":["django-unicorn-float-1.6","django-unicorn-float-0.0",4]}]}'
actual = serializer.dumps(
{
"another": [{"great": 1.0, "ok": [1.6, 0.0, 4]}],
Expand All @@ -697,6 +697,13 @@ def test_nested_list_float_less_complicated():
assert expected == actual


def test_float_loading():
expected = {"name": 0.0}
actual = serializer.loads('{"name":"django-unicorn-float-0.0"}')

assert expected == actual


def test_pydantic():
from pydantic import BaseModel

Expand Down

0 comments on commit beae4fd

Please sign in to comment.