Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP - Replace ujson with orjson as default json library #1509

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 4 additions & 1 deletion sanic/request.py
Expand Up @@ -15,7 +15,10 @@


try:
from ujson import loads as json_loads
if sys.version_info < (3, 6):
from orjson import loads as json_loads
else:
from ujson import loads as json_loads
except ImportError:
if sys.version_info[:2] == (3, 5):

Expand Down
25 changes: 23 additions & 2 deletions sanic/response.py
@@ -1,6 +1,7 @@
from functools import partial
from mimetypes import guess_type
from os import path
from sys import version_info
from urllib.parse import quote_plus

from aiofiles import open as open_async
Expand All @@ -11,13 +12,16 @@


try:
from ujson import dumps as json_dumps
if version_info < (3, 6):
from ujson import dumps as _json_dumps
else:
from orjson import dumps as _json_dumps
except BaseException:
from json import dumps

# This is done in order to ensure that the JSON response is
# kept consistent across both ujson and inbuilt json usage.
json_dumps = partial(dumps, separators=(",", ":"))
_json_dumps = partial(dumps, separators=(",", ":"))


class BaseHTTPResponse:
Expand Down Expand Up @@ -200,6 +204,21 @@ def cookies(self):
return self._cookies


def json_dumps(*args, **kwargs):
"""
This method provides a non breaking change for the python
imports being done by the existing code if any.

If any of the current code was doing a
`from sanic.response import json_dumps`, the behavior can
change since orjson.dumps returns bytes by default as return
"""
data = _json_dumps(*args, **kwargs)
if isinstance(data, bytes):
data = data.decode("utf-8")
return data


def json(
body,
status=200,
Expand All @@ -214,6 +233,8 @@ def json(
:param body: Response data to be serialized.
:param status: Response code.
:param headers: Custom Headers.
:param content_type: Content Type to be used for `HTTPResponse`
:param dumps: Method to use for serializing payload into JSON
:param kwargs: Remaining arguments that are passed to the json encoder.
"""
return HTTPResponse(
Expand Down
2 changes: 1 addition & 1 deletion sanic/testing.py
@@ -1,9 +1,9 @@
from json import JSONDecodeError
from socket import socket

from sanic.exceptions import MethodNotSupported
from sanic.log import logger
from sanic.response import text
from socket import socket


HOST = "127.0.0.1"
Expand Down
13 changes: 12 additions & 1 deletion setup.py
Expand Up @@ -73,18 +73,23 @@ def open_local(paths, mode="r", encoding="utf8"):
env_dependency = (
'; sys_platform != "win32" ' 'and implementation_name == "cpython"'
)
ujson = "ujson>=1.35" + env_dependency

ujson = "ujson>=1.35" + env_dependency + ' and python_version < "3.6"'
orjson = 'orjson>=2.0.1; implementation_name == "cpython" and python_version > "3.5"'
uvloop = "uvloop>=0.5.3" + env_dependency

requirements = [
"httptools>=0.0.10",
uvloop,
ujson,
orjson,
"aiofiles>=0.3.0",
"websockets>=6.0,<7.0",
"multidict>=4.0,<5.0",
]

print(requirements)

tests_require = [
"pytest==4.1.0",
"multidict>=4.0,<5.0",
Expand All @@ -94,10 +99,16 @@ def open_local(paths, mode="r", encoding="utf8"):
"beautifulsoup4",
uvloop,
ujson,
orjson,
"pytest-sanic",
"pytest-sugar",
]

if strtobool(os.environ.get("SANIC_NO_ORJSON", "no")):
print("Installing without orjson")
requirements.remove(orjson)
tests_require.remove(orjson)

if strtobool(os.environ.get("SANIC_NO_UJSON", "no")):
print("Installing without uJSON")
requirements.remove(ujson)
Expand Down
1 change: 1 addition & 0 deletions tox.ini
Expand Up @@ -4,6 +4,7 @@ envlist = py35, py36, py37, {py35,py36,py37}-no-ext, lint, check
[testenv]
usedevelop = True
setenv =
{py35,py36,py37}-no-ext: SANIC_NO_ORJSON=1
{py35,py36,py37}-no-ext: SANIC_NO_UJSON=1
{py35,py36,py37}-no-ext: SANIC_NO_UVLOOP=1
deps =
Expand Down