Skip to content

Commit

Permalink
Remove UJSONResponse (#1047)
Browse files Browse the repository at this point in the history
* Remove UJSONResponse

* Add documentation about custom JSON serialization
  • Loading branch information
JayH5 committed Nov 8, 2020
1 parent 8bfc2a3 commit 99b3778
Show file tree
Hide file tree
Showing 7 changed files with 16 additions and 44 deletions.
3 changes: 0 additions & 3 deletions README.md
Expand Up @@ -95,7 +95,6 @@ Starlette does not have any hard dependencies, but the following are optional:
* [`itsdangerous`][itsdangerous] - Required for `SessionMiddleware` support.
* [`pyyaml`][pyyaml] - Required for `SchemaGenerator` support.
* [`graphene`][graphene] - Required for `GraphQLApp` support.
* [`ujson`][ujson] - Required if you want to use `UJSONResponse`.

You can install all of these with `pip3 install starlette[full]`.

Expand Down Expand Up @@ -140,7 +139,6 @@ as [one of the fastest Python frameworks available](https://www.techempower.com/

For high throughput loads you should:

* Make sure to install `ujson` and use `UJSONResponse`.
* Run using gunicorn using the `uvicorn` worker class.
* Use one or two workers per-CPU core. (You might need to experiment with this.)
* Disable access logging.
Expand Down Expand Up @@ -178,4 +176,3 @@ gunicorn -k uvicorn.workers.UvicornH11Worker ...
[itsdangerous]: https://pythonhosted.org/itsdangerous/
[sqlalchemy]: https://www.sqlalchemy.org
[pyyaml]: https://pyyaml.org/wiki/PyYAMLDocumentation
[ujson]: https://github.com/esnme/ultrajson
3 changes: 0 additions & 3 deletions docs/index.md
Expand Up @@ -89,7 +89,6 @@ Starlette does not have any hard dependencies, but the following are optional:
* [`itsdangerous`][itsdangerous] - Required for `SessionMiddleware` support.
* [`pyyaml`][pyyaml] - Required for `SchemaGenerator` support.
* [`graphene`][graphene] - Required for `GraphQLApp` support.
* [`ujson`][ujson] - Required if you want to use `UJSONResponse`.

You can install all of these with `pip3 install starlette[full]`.

Expand Down Expand Up @@ -134,7 +133,6 @@ as [one of the fastest Python frameworks available](https://www.techempower.com/

For high throughput loads you should:

* Make sure to install `ujson` and use `UJSONResponse`.
* Run using Gunicorn using the `uvicorn` worker class.
* Use one or two workers per-CPU core. (You might need to experiment with this.)
* Disable access logging.
Expand Down Expand Up @@ -172,4 +170,3 @@ gunicorn -k uvicorn.workers.UvicornH11Worker ...
[itsdangerous]: https://pythonhosted.org/itsdangerous/
[sqlalchemy]: https://www.sqlalchemy.org
[pyyaml]: https://pyyaml.org/wiki/PyYAMLDocumentation
[ujson]: https://github.com/esnme/ultrajson
28 changes: 16 additions & 12 deletions docs/responses.md
Expand Up @@ -92,26 +92,30 @@ async def app(scope, receive, send):
await response(scope, receive, send)
```

### UJSONResponse
#### Custom JSON serialization

A JSON response class that uses the optimised `ujson` library for serialisation.
If you need fine-grained control over JSON serialization, you can subclass
`JSONResponse` and override the `render` method.

Using `ujson` will result in faster JSON serialisation, but is also less careful
than Python's built-in implementation in how it handles some edge-cases.

In general you *probably* want to stick with `JSONResponse` by default unless
you are micro-optimising a particular endpoint.
For example, if you wanted to use a third-party JSON library such as
[orjson](https://pypi.org/project/orjson/):

```python
from starlette.responses import UJSONResponse
from typing import Any

import orjson
from starlette.responses import JSONResponse

async def app(scope, receive, send):
assert scope['type'] == 'http'
response = UJSONResponse({'hello': 'world'})
await response(scope, receive, send)

class OrjsonResponse(JSONResponse):
def render(self, content: Any) -> bytes:
return orjson.dumps(content)
```

In general you *probably* want to stick with `JSONResponse` by default unless
you are micro-optimising a particular endpoint or need to serialize non-standard
object types.

### RedirectResponse

Returns an HTTP redirect. Uses a 307 status code by default.
Expand Down
1 change: 0 additions & 1 deletion requirements.txt
Expand Up @@ -6,7 +6,6 @@ jinja2
python-multipart
pyyaml
requests
ujson

# Testing
autoflake
Expand Down
1 change: 0 additions & 1 deletion setup.py
Expand Up @@ -57,7 +57,6 @@ def get_packages(package):
"python-multipart",
"pyyaml",
"requests",
"ujson",
]
},
classifiers=[
Expand Down
13 changes: 0 additions & 13 deletions starlette/responses.py
Expand Up @@ -24,11 +24,6 @@
aiofiles = None # type: ignore
aio_stat = None # type: ignore

try:
import ujson
except ImportError: # pragma: nocover
ujson = None # type: ignore


# Compatibility wrapper for `mimetypes.guess_type` to support `os.PathLike` on <py3.8
def guess_type(
Expand Down Expand Up @@ -172,14 +167,6 @@ def render(self, content: typing.Any) -> bytes:
).encode("utf-8")


class UJSONResponse(JSONResponse):
media_type = "application/json"

def render(self, content: typing.Any) -> bytes:
assert ujson is not None, "ujson must be installed to use UJSONResponse"
return ujson.dumps(content, ensure_ascii=False).encode("utf-8")


class RedirectResponse(Response):
def __init__(
self,
Expand Down
11 changes: 0 additions & 11 deletions tests/test_responses.py
Expand Up @@ -12,7 +12,6 @@
RedirectResponse,
Response,
StreamingResponse,
UJSONResponse,
)
from starlette.testclient import TestClient

Expand All @@ -37,16 +36,6 @@ async def app(scope, receive, send):
assert response.content == b"xxxxx"


def test_ujson_response():
async def app(scope, receive, send):
response = UJSONResponse({"hello": "world"})
await response(scope, receive, send)

client = TestClient(app)
response = client.get("/")
assert response.json() == {"hello": "world"}


def test_json_none_response():
async def app(scope, receive, send):
response = JSONResponse(None)
Expand Down

0 comments on commit 99b3778

Please sign in to comment.