JSON encoder change via app (#2055)
This commit is contained in:
parent
d76925cf35
commit
b1a57a8b62
|
@ -84,6 +84,7 @@ class Sanic(BaseSanic):
|
|||
log_config: Optional[Dict[str, Any]] = None,
|
||||
configure_logging: bool = True,
|
||||
register: Optional[bool] = None,
|
||||
dumps: Optional[Callable[..., str]] = None,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
|
||||
|
@ -117,8 +118,6 @@ class Sanic(BaseSanic):
|
|||
self.websocket_tasks: Set[Future] = set()
|
||||
self.named_request_middleware: Dict[str, Deque[MiddlewareType]] = {}
|
||||
self.named_response_middleware: Dict[str, Deque[MiddlewareType]] = {}
|
||||
# self.named_request_middleware: Dict[str, MiddlewareType] = {}
|
||||
# self.named_response_middleware: Dict[str, MiddlewareType] = {}
|
||||
self._test_manager = None
|
||||
self._test_client = None
|
||||
self._asgi_client = None
|
||||
|
@ -133,6 +132,9 @@ class Sanic(BaseSanic):
|
|||
|
||||
self.router.ctx.app = self
|
||||
|
||||
if dumps:
|
||||
BaseHTTPResponse._dumps = dumps
|
||||
|
||||
@property
|
||||
def loop(self):
|
||||
"""
|
||||
|
|
|
@ -61,7 +61,8 @@ class BlueprintGroup(MutableSequence):
|
|||
Create a new Blueprint Group
|
||||
|
||||
:param url_prefix: URL: to be prefixed before all the Blueprint Prefix
|
||||
:param version: API Version for the blueprint group. This will be inherited by each of the Blueprint
|
||||
:param version: API Version for the blueprint group. This will be
|
||||
inherited by each of the Blueprint
|
||||
:param strict_slashes: URL Strict slash behavior indicator
|
||||
"""
|
||||
self._blueprints = []
|
||||
|
@ -90,8 +91,8 @@ class BlueprintGroup(MutableSequence):
|
|||
@property
|
||||
def version(self) -> Optional[Union[str, int, float]]:
|
||||
"""
|
||||
API Version for the Blueprint Group. This will be applied only in case if the Blueprint doesn't already have
|
||||
a version specified
|
||||
API Version for the Blueprint Group. This will be applied only in case
|
||||
if the Blueprint doesn't already have a version specified
|
||||
|
||||
:return: Version information
|
||||
"""
|
||||
|
@ -162,7 +163,8 @@ class BlueprintGroup(MutableSequence):
|
|||
|
||||
def _sanitize_blueprint(self, bp: "sanic.Blueprint") -> "sanic.Blueprint":
|
||||
"""
|
||||
Sanitize the Blueprint Entity to override the Version and strict slash behaviors as required.
|
||||
Sanitize the Blueprint Entity to override the Version and strict slash
|
||||
behaviors as required.
|
||||
|
||||
:param bp: Sanic Blueprint entity Object
|
||||
:return: Modified Blueprint
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
from collections import defaultdict
|
||||
from typing import Dict, List, Optional, Iterable
|
||||
from typing import Dict, Iterable, List, Optional
|
||||
|
||||
from sanic_routing.route import Route # type: ignore
|
||||
|
||||
from sanic.base import BaseSanic
|
||||
from sanic.blueprint_group import BlueprintGroup
|
||||
from sanic.models.futures import FutureRoute, FutureStatic
|
||||
from sanic.models.handler_types import (
|
||||
ListenerType,
|
||||
MiddlewareType,
|
||||
RouteHandler,
|
||||
)
|
||||
from sanic.models.futures import FutureRoute, FutureStatic
|
||||
|
||||
|
||||
class Blueprint(BaseSanic):
|
||||
|
@ -99,7 +99,8 @@ class Blueprint(BaseSanic):
|
|||
:param blueprints: blueprints to be registered as a group
|
||||
:param url_prefix: URL route to be prepended to all sub-prefixes
|
||||
:param version: API Version to be used for Blueprint group
|
||||
:param strict_slashes: Indicate strict slash termination behavior for URL
|
||||
:param strict_slashes: Indicate strict slash termination behavior
|
||||
for URL
|
||||
"""
|
||||
|
||||
def chain(nested) -> Iterable[Blueprint]:
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
from pathlib import PurePath
|
||||
from typing import NamedTuple, List, Union, Iterable, Optional
|
||||
from typing import Iterable, List, NamedTuple, Optional, Union
|
||||
|
||||
from sanic.models.handler_types import (
|
||||
ErrorMiddlewareType,
|
||||
ListenerType,
|
||||
MiddlewareType,
|
||||
ErrorMiddlewareType,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -39,6 +39,8 @@ class BaseHTTPResponse:
|
|||
The base class for all HTTP Responses
|
||||
"""
|
||||
|
||||
_dumps = json_dumps
|
||||
|
||||
def __init__(self):
|
||||
self.asgi: bool = False
|
||||
self.body: Optional[bytes] = None
|
||||
|
@ -66,8 +68,8 @@ class BaseHTTPResponse:
|
|||
response.cookies["test"]["domain"] = ".yummy-yummy-cookie.com"
|
||||
response.cookies["test"]["httponly"] = True
|
||||
|
||||
`See user guide
|
||||
<https://sanicframework.org/guide/basics/cookies.html>`_
|
||||
`See user guide re: cookies
|
||||
<https://sanicframework.org/guide/basics/cookies.html>`__
|
||||
|
||||
:return: the cookie jar
|
||||
:rtype: CookieJar
|
||||
|
@ -251,7 +253,7 @@ def json(
|
|||
status: int = 200,
|
||||
headers: Optional[Dict[str, str]] = None,
|
||||
content_type: str = "application/json",
|
||||
dumps: Callable[..., str] = json_dumps,
|
||||
dumps: Optional[Callable[..., str]] = None,
|
||||
**kwargs,
|
||||
) -> HTTPResponse:
|
||||
"""
|
||||
|
@ -262,6 +264,8 @@ def json(
|
|||
:param headers: Custom Headers.
|
||||
:param kwargs: Remaining arguments that are passed to the json encoder.
|
||||
"""
|
||||
if not dumps:
|
||||
dumps = BaseHTTPResponse._dumps
|
||||
return HTTPResponse(
|
||||
dumps(body, **kwargs),
|
||||
headers=headers,
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
from pytest import raises
|
||||
|
||||
from sanic.app import Sanic
|
||||
from sanic.blueprints import Blueprint
|
||||
from sanic.blueprint_group import BlueprintGroup
|
||||
from sanic.blueprints import Blueprint
|
||||
from sanic.request import Request
|
||||
from sanic.response import HTTPResponse, text
|
||||
|
||||
|
|
92
tests/test_json_encoding.py
Normal file
92
tests/test_json_encoding.py
Normal file
|
@ -0,0 +1,92 @@
|
|||
import sys
|
||||
|
||||
from dataclasses import asdict, dataclass
|
||||
from functools import partial
|
||||
from json import dumps as sdumps
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
try:
|
||||
from ujson import dumps as udumps
|
||||
|
||||
NO_UJSON = False
|
||||
DEFAULT_DUMPS = udumps
|
||||
except ModuleNotFoundError:
|
||||
NO_UJSON = True
|
||||
DEFAULT_DUMPS = partial(sdumps, separators=(",", ":"))
|
||||
|
||||
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
|
||||
def payload(foo):
|
||||
return {"foo": foo}
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def default_back_to_ujson():
|
||||
yield
|
||||
BaseHTTPResponse._dumps = DEFAULT_DUMPS
|
||||
|
||||
|
||||
def test_change_encoder():
|
||||
Sanic("...", dumps=sdumps)
|
||||
assert BaseHTTPResponse._dumps == sdumps
|
||||
|
||||
|
||||
def test_change_encoder_to_some_custom():
|
||||
def my_custom_encoder():
|
||||
return "foo"
|
||||
|
||||
Sanic("...", dumps=my_custom_encoder)
|
||||
assert BaseHTTPResponse._dumps == my_custom_encoder
|
||||
|
||||
|
||||
@pytest.mark.skipif(NO_UJSON is True, reason="ujson not installed")
|
||||
def test_json_response_ujson(payload):
|
||||
"""ujson will look at __json__"""
|
||||
response = json(payload)
|
||||
assert response.body == b'{"foo":{"bar":"bar"}}'
|
||||
|
||||
with pytest.raises(
|
||||
TypeError, match="Object of type Foo is not JSON serializable"
|
||||
):
|
||||
json(payload, dumps=sdumps)
|
||||
|
||||
Sanic("...", dumps=sdumps)
|
||||
with pytest.raises(
|
||||
TypeError, match="Object of type Foo is not JSON serializable"
|
||||
):
|
||||
json(payload)
|
||||
|
||||
|
||||
@pytest.mark.skipif(NO_UJSON is True, reason="ujson not installed")
|
||||
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
|
||||
|
||||
Sanic("...", dumps=sdumps)
|
||||
response = json(too_big_for_ujson)
|
||||
assert sys.getsizeof(response.body) == 54
|
Loading…
Reference in New Issue
Block a user