Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion hjson/decoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,12 @@ def scantfnns(context, s, end):
integer, frac, exp = m.groups()
if frac or exp:
res = context.parse_float(integer + (frac or '') + (exp or ''))
if int(res) == res and abs(res)<1e10: res = int(res)
# Only collapse to int when the exponent is what made the
# value a whole number (e.g. "17.01e2" -> 1701). A plain
# decimal literal like "1.0" or "10.00" was written as a
# float on purpose and should stay one, matching what
# dumps() produces for it and what json.loads() would do.
if exp and int(res) == res and abs(res)<1e10: res = int(res)
else:
res = context.parse_int(integer)
return res, end
Expand Down
2 changes: 1 addition & 1 deletion hjson/tests/assets/comments_result.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
rem2: "// test"
rem3: "/* test */"
num1: 0
num2: 0
num2: 0.0
num3: 2
true1: true
true2: true
Expand Down
2 changes: 1 addition & 1 deletion hjson/tests/assets/comments_result.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"rem2": "// test",
"rem3": "/* test */",
"num1": 0,
"num2": 0,
"num2": 0.0,
"num3": 2,
"true1": true,
"true2": true,
Expand Down
24 changes: 24 additions & 0 deletions hjson/tests/test_decode.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,30 @@ def test_float(self):
self.assertTrue(isinstance(rval, float))
self.assertEqual(rval, 1.0)

def test_decimal_point_is_not_dropped(self):
# A literal that was written with a decimal point should stay a
# float even when its fractional part is all zeros, since the
# author explicitly chose the float form. Silently turning it
# into an int both loses that distinction and breaks round
# tripping: dumps(1.0) already produces "1.0", so loads("1.0")
# ought to hand back a float rather than an int.
for text, expected in (
("1.0", 1.0),
("10.00", 10.0),
("-3.0", -3.0),
):
rval = json.loads(text)
self.assertTrue(isinstance(rval, float), (text, rval))
self.assertEqual(rval, expected)

def test_exponent_can_still_collapse_to_int(self):
# When it's the exponent that turns the value into a whole
# number, keeping it an int is fine: the decimal point in the
# source wasn't the last word on the value's magnitude.
rval = json.loads("17.01e2")
self.assertTrue(isinstance(rval, int))
self.assertEqual(rval, 1701)

def test_decoder_optimizations(self):
# Several optimizations were made that skip over calls to
# the whitespace regex, so this test is designed to try and
Expand Down