diff options
author | Ethan Furman <ethan@stoneleaf.us> | 2013-08-10 20:01:45 (GMT) |
---|---|---|
committer | Ethan Furman <ethan@stoneleaf.us> | 2013-08-10 20:01:45 (GMT) |
commit | a4998a70416c27730e75c0a4225ee2c3552b1618 (patch) | |
tree | 5f04a47e0c77fdc0d0e05ff4a09d9a3e3c42702e /Lib | |
parent | fbcf4d78c881176345483111503c4bc7765d4ff8 (diff) | |
download | cpython-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.py | 27 | ||||
-rw-r--r-- | Lib/test/test_json/test_enum.py | 81 |
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 |