summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorEthan Furman <ethan@stoneleaf.us>2013-08-10 20:01:45 (GMT)
committerEthan Furman <ethan@stoneleaf.us>2013-08-10 20:01:45 (GMT)
commita4998a70416c27730e75c0a4225ee2c3552b1618 (patch)
tree5f04a47e0c77fdc0d0e05ff4a09d9a3e3c42702e /Lib
parentfbcf4d78c881176345483111503c4bc7765d4ff8 (diff)
downloadcpython-a4998a70416c27730e75c0a4225ee2c3552b1618.zip
cpython-a4998a70416c27730e75c0a4225ee2c3552b1618.tar.gz
cpython-a4998a70416c27730e75c0a4225ee2c3552b1618.tar.bz2
Close #18264: int- and float-derived enums now converted to int or float.
Diffstat (limited to 'Lib')
-rw-r--r--Lib/json/encoder.py27
-rw-r--r--Lib/test/test_json/test_enum.py81
2 files changed, 100 insertions, 8 deletions
diff --git a/Lib/json/encoder.py b/Lib/json/encoder.py
index 39b550d..0513838 100644
--- a/Lib/json/encoder.py
+++ b/Lib/json/encoder.py
@@ -175,6 +175,7 @@ class JSONEncoder(object):
def encode(self, o):
"""Return a JSON string representation of a Python data structure.
+ >>> from json.encoder import JSONEncoder
>>> JSONEncoder().encode({"foo": ["bar", "baz"]})
'{"foo": ["bar", "baz"]}'
@@ -298,9 +299,13 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
elif value is False:
yield buf + 'false'
elif isinstance(value, int):
- yield buf + str(value)
+ # Subclasses of int/float may override __str__, but we still
+ # want to encode them as integers/floats in JSON. One example
+ # within the standard library is IntEnum.
+ yield buf + str(int(value))
elif isinstance(value, float):
- yield buf + _floatstr(value)
+ # see comment above for int
+ yield buf + _floatstr(float(value))
else:
yield buf
if isinstance(value, (list, tuple)):
@@ -346,7 +351,8 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
# JavaScript is weakly typed for these, so it makes sense to
# also allow them. Many encoders seem to do something like this.
elif isinstance(key, float):
- key = _floatstr(key)
+ # see comment for int/float in _make_iterencode
+ key = _floatstr(float(key))
elif key is True:
key = 'true'
elif key is False:
@@ -354,7 +360,8 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
elif key is None:
key = 'null'
elif isinstance(key, int):
- key = str(key)
+ # see comment for int/float in _make_iterencode
+ key = str(int(key))
elif _skipkeys:
continue
else:
@@ -374,9 +381,11 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
elif value is False:
yield 'false'
elif isinstance(value, int):
- yield str(value)
+ # see comment for int/float in _make_iterencode
+ yield str(int(value))
elif isinstance(value, float):
- yield _floatstr(value)
+ # see comment for int/float in _make_iterencode
+ yield _floatstr(float(value))
else:
if isinstance(value, (list, tuple)):
chunks = _iterencode_list(value, _current_indent_level)
@@ -402,9 +411,11 @@ def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
elif o is False:
yield 'false'
elif isinstance(o, int):
- yield str(o)
+ # see comment for int/float in _make_iterencode
+ yield str(int(o))
elif isinstance(o, float):
- yield _floatstr(o)
+ # see comment for int/float in _make_iterencode
+ yield _floatstr(float(o))
elif isinstance(o, (list, tuple)):
yield from _iterencode_list(o, _current_indent_level)
elif isinstance(o, dict):
diff --git a/Lib/test/test_json/test_enum.py b/Lib/test/test_json/test_enum.py
new file mode 100644
index 0000000..66c6480
--- /dev/null
+++ b/Lib/test/test_json/test_enum.py
@@ -0,0 +1,81 @@
+from enum import Enum, IntEnum
+from test.test_json import PyTest, CTest
+
+SMALL = 1
+BIG = 1<<32
+HUGE = 1<<64
+REALLY_HUGE = 1<<96
+
+class BigNum(IntEnum):
+ small = SMALL
+ big = BIG
+ huge = HUGE
+ really_huge = REALLY_HUGE
+
+E = 2.718281
+PI = 3.141593
+TAU = 2 * PI
+
+class FloatNum(float, Enum):
+ e = E
+ pi = PI
+ tau = TAU
+
+class TestEnum:
+
+ def test_floats(self):
+ for enum in FloatNum:
+ self.assertEqual(self.dumps(enum), repr(enum.value))
+ self.assertEqual(float(self.dumps(enum)), enum)
+ self.assertEqual(self.loads(self.dumps(enum)), enum)
+
+ def test_ints(self):
+ for enum in BigNum:
+ self.assertEqual(self.dumps(enum), str(enum.value))
+ self.assertEqual(int(self.dumps(enum)), enum)
+ self.assertEqual(self.loads(self.dumps(enum)), enum)
+
+ def test_list(self):
+ self.assertEqual(
+ self.dumps(list(BigNum)),
+ str([SMALL, BIG, HUGE, REALLY_HUGE]),
+ )
+ self.assertEqual(self.dumps(list(FloatNum)), str([E, PI, TAU]))
+
+ def test_dict_keys(self):
+ s, b, h, r = BigNum
+ e, p, t = FloatNum
+ d = {
+ s:'tiny', b:'large', h:'larger', r:'largest',
+ e:"Euler's number", p:'pi', t:'tau',
+ }
+ nd = self.loads(self.dumps(d))
+ self.assertEqual(nd[str(SMALL)], 'tiny')
+ self.assertEqual(nd[str(BIG)], 'large')
+ self.assertEqual(nd[str(HUGE)], 'larger')
+ self.assertEqual(nd[str(REALLY_HUGE)], 'largest')
+ self.assertEqual(nd[repr(E)], "Euler's number")
+ self.assertEqual(nd[repr(PI)], 'pi')
+ self.assertEqual(nd[repr(TAU)], 'tau')
+
+ def test_dict_values(self):
+ d = dict(
+ tiny=BigNum.small,
+ large=BigNum.big,
+ larger=BigNum.huge,
+ largest=BigNum.really_huge,
+ e=FloatNum.e,
+ pi=FloatNum.pi,
+ tau=FloatNum.tau,
+ )
+ nd = self.loads(self.dumps(d))
+ self.assertEqual(nd['tiny'], SMALL)
+ self.assertEqual(nd['large'], BIG)
+ self.assertEqual(nd['larger'], HUGE)
+ self.assertEqual(nd['largest'], REALLY_HUGE)
+ self.assertEqual(nd['e'], E)
+ self.assertEqual(nd['pi'], PI)
+ self.assertEqual(nd['tau'], TAU)
+
+class TestPyEnum(TestEnum, PyTest): pass
+class TestCEnum(TestEnum, CTest): pass