Coverage for tallipoika/py2es6.py: 100.00%
38 statements
« prev ^ index » next coverage.py v7.4.1, created at 2024-02-04 23:32:02 +00:00
« prev ^ index » next coverage.py v7.4.1, created at 2024-02-04 23:32:02 +00:00
1"""Serialize a Python numerical value as an ECMAScript v6 or later string."""
3import math
4from typing import Union, no_type_check
6DASH = '-'
7D_POINT = '.'
8DIGIT_ZERO = '0'
9MIN_INTEGER_DIGITS = 0
10MAX_INTEGER_DIGITS = 21
11REPR_SWITCH_ABOVE = -7
14@no_type_check
15def serialize(number: Union[float, int]) -> str:
16 """Serialize a Python builtin number as an ECMAScript v6 or later string.
18 Implementation Note(s):
20 - (builtin) numbers are for now int and float but may be extended to other types that can be mapped to JSON numbers
21 - separate into the components:
22 + sign
23 + integral part
24 + decimal point
25 + fractional part
26 + exponent part
27 - return the concatenation of the components (some components may be empty)
28 """
29 if not math.isfinite(number):
30 raise ValueError(f'invalid number ({number})')
32 as_float = float(number)
33 if as_float == 0:
34 return DIGIT_ZERO
36 as_text = str(as_float)
37 sign = DASH if as_text[0] == DASH else ''
38 magnitude = as_text[1:] if sign else as_text
39 mantissa = magnitude
40 e_part, e_number = '', 0
41 if (exp_ndx := magnitude.find('e')) > 0:
42 mantissa, e_part = magnitude[0:exp_ndx], magnitude[exp_ndx:]
43 if e_part[2:3] == '0': # remove leading zero of exponent representation
44 e_part = f'{e_part[:2]}{e_part[3:]}'
45 e_number = int(e_part[1:])
47 i_part, d_point, f_part = mantissa, '', ''
48 if (dec_ndx := mantissa.find(D_POINT)) > 0:
49 d_point = D_POINT
50 i_part, f_part = mantissa[:dec_ndx], mantissa[dec_ndx + 1 :]
52 if f_part == DIGIT_ZERO:
53 d_point, f_part = '', ''
55 if MIN_INTEGER_DIGITS < e_number < MAX_INTEGER_DIGITS:
56 up_shifts = e_number - len(i_part) - len(f_part) + 1
57 return f'{sign}{i_part}{f_part}{DIGIT_ZERO * up_shifts}' # neither decimal point nor exponential representation
59 if REPR_SWITCH_ABOVE < e_number < 0:
60 down_shifts = -e_number - 1
61 return f'{sign}{DIGIT_ZERO}{D_POINT}{DIGIT_ZERO * down_shifts}{i_part}{f_part}' # no exponential representation
63 return f'{sign}{i_part}{d_point}{f_part}{e_part}'