Commit 8ab75a2f by Ryan P Kilby Committed by Carlton Gibson

Add 'STRICT_JSON' API setting.

STRICT_JSON controls the renderer & parser behavior on whether or not
to accept non-standard float values (NaN, Infinity).
parent d740bae9
...@@ -22,6 +22,7 @@ from django.utils.six.moves.urllib import parse as urlparse ...@@ -22,6 +22,7 @@ from django.utils.six.moves.urllib import parse as urlparse
from rest_framework import renderers from rest_framework import renderers
from rest_framework.exceptions import ParseError from rest_framework.exceptions import ParseError
from rest_framework.settings import api_settings
from rest_framework.utils import json from rest_framework.utils import json
...@@ -53,6 +54,7 @@ class JSONParser(BaseParser): ...@@ -53,6 +54,7 @@ class JSONParser(BaseParser):
""" """
media_type = 'application/json' media_type = 'application/json'
renderer_class = renderers.JSONRenderer renderer_class = renderers.JSONRenderer
strict = api_settings.STRICT_JSON
def parse(self, stream, media_type=None, parser_context=None): def parse(self, stream, media_type=None, parser_context=None):
""" """
...@@ -63,7 +65,8 @@ class JSONParser(BaseParser): ...@@ -63,7 +65,8 @@ class JSONParser(BaseParser):
try: try:
decoded_stream = codecs.getreader(encoding)(stream) 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: except ValueError as exc:
raise ParseError('JSON parse error - %s' % six.text_type(exc)) raise ParseError('JSON parse error - %s' % six.text_type(exc))
......
...@@ -61,6 +61,7 @@ class JSONRenderer(BaseRenderer): ...@@ -61,6 +61,7 @@ class JSONRenderer(BaseRenderer):
encoder_class = encoders.JSONEncoder encoder_class = encoders.JSONEncoder
ensure_ascii = not api_settings.UNICODE_JSON ensure_ascii = not api_settings.UNICODE_JSON
compact = api_settings.COMPACT_JSON compact = api_settings.COMPACT_JSON
strict = api_settings.STRICT_JSON
# We don't set a charset because JSON is a binary encoding, # We don't set a charset because JSON is a binary encoding,
# that can be encoded as utf-8, utf-16 or utf-32. # that can be encoded as utf-8, utf-16 or utf-32.
...@@ -101,7 +102,7 @@ class JSONRenderer(BaseRenderer): ...@@ -101,7 +102,7 @@ class JSONRenderer(BaseRenderer):
ret = json.dumps( ret = json.dumps(
data, cls=self.encoder_class, data, cls=self.encoder_class,
indent=indent, ensure_ascii=self.ensure_ascii, 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, # On python 2.x json.dumps() returns bytestrings if ensure_ascii=True,
......
...@@ -110,6 +110,7 @@ DEFAULTS = { ...@@ -110,6 +110,7 @@ DEFAULTS = {
# Encoding # Encoding
'UNICODE_JSON': True, 'UNICODE_JSON': True,
'COMPACT_JSON': True, 'COMPACT_JSON': True,
'STRICT_JSON': True,
'COERCE_DECIMAL_TO_STRING': True, 'COERCE_DECIMAL_TO_STRING': True,
'UPLOADED_FILES_USE_URL': True, 'UPLOADED_FILES_USE_URL': True,
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
import math
import pytest import pytest
from django import forms from django import forms
from django.core.files.uploadhandler import ( from django.core.files.uploadhandler import (
...@@ -9,7 +10,7 @@ from django.core.files.uploadhandler import ( ...@@ -9,7 +10,7 @@ from django.core.files.uploadhandler import (
) )
from django.http.request import RawPostDataException from django.http.request import RawPostDataException
from django.test import TestCase 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.exceptions import ParseError
from rest_framework.parsers import ( from rest_framework.parsers import (
...@@ -42,7 +43,6 @@ class TestFileUploadParser(TestCase): ...@@ -42,7 +43,6 @@ class TestFileUploadParser(TestCase):
def setUp(self): def setUp(self):
class MockRequest(object): class MockRequest(object):
pass pass
from io import BytesIO
self.stream = BytesIO( self.stream = BytesIO(
"Test text file".encode('utf-8') "Test text file".encode('utf-8')
) )
...@@ -129,6 +129,24 @@ class TestFileUploadParser(TestCase): ...@@ -129,6 +129,24 @@ class TestFileUploadParser(TestCase):
self.parser_context['request'].META['HTTP_CONTENT_DISPOSITION'] = disposition 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): class TestPOSTAccessed(TestCase):
def setUp(self): def setUp(self):
self.factory = APIRequestFactory() self.factory = APIRequestFactory()
......
...@@ -358,6 +358,19 @@ class JSONRendererTests(TestCase): ...@@ -358,6 +358,19 @@ class JSONRendererTests(TestCase):
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
JSONRenderer().render(x) 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): def test_without_content_type_args(self):
""" """
Test basic JSON rendering. Test basic JSON rendering.
......
...@@ -198,3 +198,10 @@ class JsonFloatTests(TestCase): ...@@ -198,3 +198,10 @@ class JsonFloatTests(TestCase):
with self.assertRaises(ValueError): with self.assertRaises(ValueError):
json.loads("NaN") json.loads("NaN")
@override_settings(STRICT_JSON=False)
class NonStrictJsonFloatTests(JsonFloatTests):
"""
'STRICT_JSON = False' should not somehow affect internal json behavior
"""
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment