Add 'STRICT_JSON' API setting.

STRICT_JSON controls the renderer & parser behavior on whether or not
to accept non-standard float values (NaN, Infinity).
This commit is contained in:
Ryan P Kilby 2017-07-10 15:23:12 -04:00 committed by Carlton Gibson
parent d740bae95a
commit 8ab75a2f01
6 changed files with 48 additions and 5 deletions

View File

@ -22,6 +22,7 @@ from django.utils.six.moves.urllib import parse as urlparse
from rest_framework import renderers
from rest_framework.exceptions import ParseError
from rest_framework.settings import api_settings
from rest_framework.utils import json
@ -53,6 +54,7 @@ class JSONParser(BaseParser):
"""
media_type = 'application/json'
renderer_class = renderers.JSONRenderer
strict = api_settings.STRICT_JSON
def parse(self, stream, media_type=None, parser_context=None):
"""
@ -63,7 +65,8 @@ class JSONParser(BaseParser):
try:
decoded_stream = codecs.getreader(encoding)(stream)
return json.load(decoded_stream)
parse_constant = json.strict_constant if self.strict else None
return json.load(decoded_stream, parse_constant=parse_constant)
except ValueError as exc:
raise ParseError('JSON parse error - %s' % six.text_type(exc))

View File

@ -61,6 +61,7 @@ class JSONRenderer(BaseRenderer):
encoder_class = encoders.JSONEncoder
ensure_ascii = not api_settings.UNICODE_JSON
compact = api_settings.COMPACT_JSON
strict = api_settings.STRICT_JSON
# We don't set a charset because JSON is a binary encoding,
# that can be encoded as utf-8, utf-16 or utf-32.
@ -101,7 +102,7 @@ class JSONRenderer(BaseRenderer):
ret = json.dumps(
data, cls=self.encoder_class,
indent=indent, ensure_ascii=self.ensure_ascii,
separators=separators
allow_nan=not self.strict, separators=separators
)
# On python 2.x json.dumps() returns bytestrings if ensure_ascii=True,

View File

@ -110,6 +110,7 @@ DEFAULTS = {
# Encoding
'UNICODE_JSON': True,
'COMPACT_JSON': True,
'STRICT_JSON': True,
'COERCE_DECIMAL_TO_STRING': True,
'UPLOADED_FILES_USE_URL': True,

View File

@ -1,7 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import math
import pytest
from django import forms
from django.core.files.uploadhandler import (
@ -9,7 +10,7 @@ from django.core.files.uploadhandler import (
)
from django.http.request import RawPostDataException
from django.test import TestCase
from django.utils.six.moves import StringIO
from django.utils.six import BytesIO, StringIO
from rest_framework.exceptions import ParseError
from rest_framework.parsers import (
@ -42,7 +43,6 @@ class TestFileUploadParser(TestCase):
def setUp(self):
class MockRequest(object):
pass
from io import BytesIO
self.stream = BytesIO(
"Test text file".encode('utf-8')
)
@ -129,6 +129,24 @@ class TestFileUploadParser(TestCase):
self.parser_context['request'].META['HTTP_CONTENT_DISPOSITION'] = disposition
class TestJSONParser(TestCase):
def bytes(self, value):
return BytesIO(value.encode('utf-8'))
def test_float_strictness(self):
parser = JSONParser()
# Default to strict
for value in ['Infinity', '-Infinity', 'NaN']:
with pytest.raises(ParseError):
parser.parse(self.bytes(value))
parser.strict = False
assert parser.parse(self.bytes('Infinity')) == float('inf')
assert parser.parse(self.bytes('-Infinity')) == float('-inf')
assert math.isnan(parser.parse(self.bytes('NaN')))
class TestPOSTAccessed(TestCase):
def setUp(self):
self.factory = APIRequestFactory()

View File

@ -358,6 +358,19 @@ class JSONRendererTests(TestCase):
with self.assertRaises(TypeError):
JSONRenderer().render(x)
def test_float_strictness(self):
renderer = JSONRenderer()
# Default to strict
for value in [float('inf'), float('-inf'), float('nan')]:
with pytest.raises(ValueError):
renderer.render(value)
renderer.strict = False
assert renderer.render(float('inf')) == b'Infinity'
assert renderer.render(float('-inf')) == b'-Infinity'
assert renderer.render(float('nan')) == b'NaN'
def test_without_content_type_args(self):
"""
Test basic JSON rendering.

View File

@ -198,3 +198,10 @@ class JsonFloatTests(TestCase):
with self.assertRaises(ValueError):
json.loads("NaN")
@override_settings(STRICT_JSON=False)
class NonStrictJsonFloatTests(JsonFloatTests):
"""
'STRICT_JSON = False' should not somehow affect internal json behavior
"""