2021-03-11 15:09:18 +00:00
|
|
|
import sys
|
|
|
|
from dataclasses import asdict, dataclass
|
|
|
|
from functools import partial
|
|
|
|
from json import dumps as sdumps
|
2022-07-24 20:07:54 +01:00
|
|
|
from string import ascii_lowercase
|
|
|
|
from typing import Dict
|
2021-03-11 15:09:18 +00:00
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
try:
|
2022-07-24 20:07:54 +01:00
|
|
|
import ujson
|
2021-03-11 15:09:18 +00:00
|
|
|
from ujson import dumps as udumps
|
|
|
|
|
2023-10-25 04:13:13 +01:00
|
|
|
ujson_version = tuple(map(int, ujson.__version__.strip(ascii_lowercase).split(".")))
|
2022-07-24 20:07:54 +01:00
|
|
|
|
2021-03-11 15:09:18 +00:00
|
|
|
NO_UJSON = False
|
|
|
|
DEFAULT_DUMPS = udumps
|
|
|
|
except ModuleNotFoundError:
|
|
|
|
NO_UJSON = True
|
|
|
|
DEFAULT_DUMPS = partial(sdumps, separators=(",", ":"))
|
2022-07-24 20:07:54 +01:00
|
|
|
ujson_version = None
|
2021-03-11 15:09:18 +00:00
|
|
|
|
|
|
|
from sanic import Sanic
|
|
|
|
from sanic.response import BaseHTTPResponse, json
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
class Foo:
|
|
|
|
bar: str
|
|
|
|
|
|
|
|
def __json__(self):
|
|
|
|
return udumps(asdict(self))
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
def foo():
|
|
|
|
return Foo(bar="bar")
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
2022-07-24 20:07:54 +01:00
|
|
|
def payload(foo: Foo):
|
2021-03-11 15:09:18 +00:00
|
|
|
return {"foo": foo}
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
|
|
def default_back_to_ujson():
|
|
|
|
yield
|
|
|
|
BaseHTTPResponse._dumps = DEFAULT_DUMPS
|
|
|
|
|
|
|
|
|
|
|
|
def test_change_encoder():
|
2021-12-23 22:30:27 +00:00
|
|
|
Sanic("Test", dumps=sdumps)
|
2021-03-11 15:09:18 +00:00
|
|
|
assert BaseHTTPResponse._dumps == sdumps
|
|
|
|
|
|
|
|
|
|
|
|
def test_change_encoder_to_some_custom():
|
|
|
|
def my_custom_encoder():
|
|
|
|
return "foo"
|
|
|
|
|
2021-12-23 22:30:27 +00:00
|
|
|
Sanic("Test", dumps=my_custom_encoder)
|
2021-03-11 15:09:18 +00:00
|
|
|
assert BaseHTTPResponse._dumps == my_custom_encoder
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.skipif(NO_UJSON is True, reason="ujson not installed")
|
2022-07-24 20:07:54 +01:00
|
|
|
def test_json_response_ujson(payload: Dict[str, Foo]):
|
2021-03-11 15:09:18 +00:00
|
|
|
"""ujson will look at __json__"""
|
|
|
|
response = json(payload)
|
|
|
|
assert response.body == b'{"foo":{"bar":"bar"}}'
|
|
|
|
|
2023-10-25 04:13:13 +01:00
|
|
|
with pytest.raises(TypeError, match="Object of type Foo is not JSON serializable"):
|
2021-03-11 15:09:18 +00:00
|
|
|
json(payload, dumps=sdumps)
|
|
|
|
|
2021-12-23 22:30:27 +00:00
|
|
|
Sanic("Test", dumps=sdumps)
|
2023-10-25 04:13:13 +01:00
|
|
|
with pytest.raises(TypeError, match="Object of type Foo is not JSON serializable"):
|
2021-03-11 15:09:18 +00:00
|
|
|
json(payload)
|
|
|
|
|
|
|
|
|
2022-07-24 20:07:54 +01:00
|
|
|
@pytest.mark.skipif(
|
|
|
|
NO_UJSON is True or ujson_version >= (5, 4, 0),
|
|
|
|
reason=(
|
|
|
|
"ujson not installed or version is 5.4.0 or newer, "
|
|
|
|
"which can handle arbitrary size integers"
|
|
|
|
),
|
|
|
|
)
|
2021-03-11 15:09:18 +00:00
|
|
|
def test_json_response_json():
|
|
|
|
"""One of the easiest ways to tell the difference is that ujson cannot
|
|
|
|
serialize over 64 bits"""
|
|
|
|
too_big_for_ujson = 111111111111111111111
|
|
|
|
|
|
|
|
with pytest.raises(OverflowError, match="int too big to convert"):
|
|
|
|
json(too_big_for_ujson)
|
|
|
|
|
|
|
|
response = json(too_big_for_ujson, dumps=sdumps)
|
|
|
|
assert sys.getsizeof(response.body) == 54
|
|
|
|
|
2021-12-23 22:30:27 +00:00
|
|
|
Sanic("Test", dumps=sdumps)
|
2021-03-11 15:09:18 +00:00
|
|
|
response = json(too_big_for_ujson)
|
|
|
|
assert sys.getsizeof(response.body) == 54
|