summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.txt7
-rw-r--r--conf.py4
-rw-r--r--setup.py2
-rw-r--r--simplejson/__init__.py2
-rw-r--r--simplejson/_speedups.c47
-rw-r--r--simplejson/encoder.py9
-rw-r--r--simplejson/tests/__init__.py1
-rw-r--r--simplejson/tests/test_subclass.py37
8 files changed, 101 insertions, 8 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index 76b7d78..ab95258 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,3 +1,10 @@
+Version 3.7.0 released 2015-05-18
+
+* simplejson no longer trusts custom str/repr methods for int, long, float
+ subclasses. These instances are now formatted as if they were exact
+ instances of those types.
+ https://github.com/simplejson/simplejson/issues/118
+
Version 3.6.5 released 2014-10-24
* Importing bug fix for reference leak when an error occurs during
diff --git a/conf.py b/conf.py
index a233b94..5ddd810 100644
--- a/conf.py
+++ b/conf.py
@@ -42,9 +42,9 @@ copyright = '2014, Bob Ippolito'
# other places throughout the built documents.
#
# The short X.Y version.
-version = '3.6'
+version = '3.7'
# The full version, including alpha/beta/rc tags.
-release = '3.6.5'
+release = '3.7.0'
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
diff --git a/setup.py b/setup.py
index de86d0c..77ad402 100644
--- a/setup.py
+++ b/setup.py
@@ -11,7 +11,7 @@ from distutils.errors import CCompilerError, DistutilsExecError, \
DistutilsPlatformError
IS_PYPY = hasattr(sys, 'pypy_translation_info')
-VERSION = '3.6.5'
+VERSION = '3.7.0'
DESCRIPTION = "Simple, fast, extensible JSON encoder/decoder for Python"
with open('README.rst', 'r') as f:
diff --git a/simplejson/__init__.py b/simplejson/__init__.py
index 8c0b698..0ba6ab0 100644
--- a/simplejson/__init__.py
+++ b/simplejson/__init__.py
@@ -98,7 +98,7 @@ Using simplejson.tool from the shell to validate and pretty-print::
Expecting property name: line 1 column 3 (char 2)
"""
from __future__ import absolute_import
-__version__ = '3.6.5'
+__version__ = '3.7.0'
__all__ = [
'dump', 'dumps', 'load', 'loads',
'JSONDecoder', 'JSONDecodeError', 'JSONEncoder',
diff --git a/simplejson/_speedups.c b/simplejson/_speedups.c
index 0b2d81c..65cf444 100644
--- a/simplejson/_speedups.c
+++ b/simplejson/_speedups.c
@@ -10,6 +10,7 @@
#define PyString_AS_STRING PyBytes_AS_STRING
#define PyString_FromStringAndSize PyBytes_FromStringAndSize
#define PyInt_Check(obj) 0
+#define PyInt_CheckExact(obj) 0
#define JSON_UNICHR Py_UCS4
#define JSON_InternFromString PyUnicode_InternFromString
#define JSON_Intern_GET_SIZE PyUnicode_GET_SIZE
@@ -660,7 +661,19 @@ encoder_stringify_key(PyEncoderObject *s, PyObject *key)
return _encoded_const(key);
}
else if (PyInt_Check(key) || PyLong_Check(key)) {
- return PyObject_Str(key);
+ if (!(PyInt_CheckExact(key) || PyLong_CheckExact(key))) {
+ /* See #118, do not trust custom str/repr */
+ PyObject *tmp = PyObject_CallFunctionObjArgs((PyObject *)&PyLong_Type, key, NULL);
+ if (tmp == NULL) {
+ return NULL;
+ }
+ PyObject *res = PyObject_Str(tmp);
+ Py_DECREF(tmp);
+ return res;
+ }
+ else {
+ return PyObject_Str(key);
+ }
}
else if (s->use_decimal && PyObject_TypeCheck(key, (PyTypeObject *)s->Decimal)) {
return PyObject_Str(key);
@@ -2637,7 +2650,7 @@ encoder_init(PyObject *self, PyObject *args, PyObject *kwds)
s->tuple_as_array = PyObject_IsTrue(tuple_as_array);
if (PyInt_Check(int_as_string_bitcount) || PyLong_Check(int_as_string_bitcount)) {
static const unsigned int long_long_bitsize = SIZEOF_LONG_LONG * 8;
- int int_as_string_bitcount_val = PyLong_AsLong(int_as_string_bitcount);
+ int int_as_string_bitcount_val = (int)PyLong_AsLong(int_as_string_bitcount);
if (int_as_string_bitcount_val > 0 && int_as_string_bitcount_val < long_long_bitsize) {
s->max_long_size = PyLong_FromUnsignedLongLong(1ULL << int_as_string_bitcount_val);
s->min_long_size = PyLong_FromLongLong(-1LL << int_as_string_bitcount_val);
@@ -2800,7 +2813,19 @@ encoder_encode_float(PyEncoderObject *s, PyObject *obj)
}
}
/* Use a better float format here? */
- return PyObject_Repr(obj);
+ if (PyFloat_CheckExact(obj)) {
+ return PyObject_Repr(obj);
+ }
+ else {
+ /* See #118, do not trust custom str/repr */
+ PyObject *tmp = PyObject_CallFunctionObjArgs((PyObject *)&PyFloat_Type, obj, NULL);
+ if (tmp == NULL) {
+ return NULL;
+ }
+ PyObject *res = PyObject_Repr(tmp);
+ Py_DECREF(tmp);
+ return res;
+ }
}
static PyObject *
@@ -2840,7 +2865,21 @@ encoder_listencode_obj(PyEncoderObject *s, JSON_Accu *rval, PyObject *obj, Py_ss
rv = _steal_accumulate(rval, encoded);
}
else if (PyInt_Check(obj) || PyLong_Check(obj)) {
- PyObject *encoded = PyObject_Str(obj);
+ PyObject *encoded;
+ if (PyInt_CheckExact(obj) || PyLong_CheckExact(obj)) {
+ encoded = PyObject_Str(obj);
+ }
+ else {
+ /* See #118, do not trust custom str/repr */
+ PyObject *tmp = PyObject_CallFunctionObjArgs((PyObject *)&PyLong_Type, obj, NULL);
+ if (tmp == NULL) {
+ encoded = NULL;
+ }
+ else {
+ encoded = PyObject_Str(tmp);
+ Py_DECREF(tmp);
+ }
+ }
if (encoded != NULL) {
encoded = maybe_quote_bigint(s, encoded, obj);
if (encoded == NULL)
diff --git a/simplejson/encoder.py b/simplejson/encoder.py
index db18244..7f0b037 100644
--- a/simplejson/encoder.py
+++ b/simplejson/encoder.py
@@ -311,6 +311,9 @@ class JSONEncoder(object):
elif o == _neginf:
text = '-Infinity'
else:
+ if type(o) != float:
+ # See #118, do not trust custom str/repr
+ o = float(o)
return _repr(o)
if ignore_nan:
@@ -412,6 +415,9 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
or
_int_as_string_bitcount < 1
)
+ if type(value) not in integer_types:
+ # See #118, do not trust custom str/repr
+ value = int(value)
if (
skip_quoting or
(-1 << _int_as_string_bitcount)
@@ -501,6 +507,9 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
elif key is None:
key = 'null'
elif isinstance(key, integer_types):
+ if key not in integer_types:
+ # See #118, do not trust custom str/repr
+ key = int(key)
key = str(key)
elif _use_decimal and isinstance(key, Decimal):
key = str(key)
diff --git a/simplejson/tests/__init__.py b/simplejson/tests/__init__.py
index c7551e8..8c1a4f1 100644
--- a/simplejson/tests/__init__.py
+++ b/simplejson/tests/__init__.py
@@ -62,6 +62,7 @@ def all_tests_suite():
'simplejson.tests.test_namedtuple',
'simplejson.tests.test_tool',
'simplejson.tests.test_for_json',
+ 'simplejson.tests.test_subclass',
]))
suite = get_suite()
import simplejson
diff --git a/simplejson/tests/test_subclass.py b/simplejson/tests/test_subclass.py
new file mode 100644
index 0000000..2bae3b6
--- /dev/null
+++ b/simplejson/tests/test_subclass.py
@@ -0,0 +1,37 @@
+from unittest import TestCase
+import simplejson as json
+
+from decimal import Decimal
+
+class AlternateInt(int):
+ def __repr__(self):
+ return 'invalid json'
+ __str__ = __repr__
+
+
+class AlternateFloat(float):
+ def __repr__(self):
+ return 'invalid json'
+ __str__ = __repr__
+
+
+# class AlternateDecimal(Decimal):
+# def __repr__(self):
+# return 'invalid json'
+
+
+class TestSubclass(TestCase):
+ def test_int(self):
+ self.assertEqual(json.dumps(AlternateInt(1)), '1')
+ self.assertEqual(json.dumps(AlternateInt(-1)), '-1')
+ self.assertEqual(json.loads(json.dumps({AlternateInt(1): 1})), {'1': 1})
+
+ def test_float(self):
+ self.assertEqual(json.dumps(AlternateFloat(1.0)), '1.0')
+ self.assertEqual(json.dumps(AlternateFloat(-1.0)), '-1.0')
+ self.assertEqual(json.loads(json.dumps({AlternateFloat(1.0): 1})), {'1.0': 1})
+
+ # NOTE: Decimal subclasses are not supported as-is
+ # def test_decimal(self):
+ # self.assertEqual(json.dumps(AlternateDecimal('1.0')), '1.0')
+ # self.assertEqual(json.dumps(AlternateDecimal('-1.0')), '-1.0')