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

1"""Serialize a Python numerical value as an ECMAScript v6 or later string.""" 

2 

3import math 

4from typing import Union, no_type_check 

5 

6DASH = '-' 

7D_POINT = '.' 

8DIGIT_ZERO = '0' 

9MIN_INTEGER_DIGITS = 0 

10MAX_INTEGER_DIGITS = 21 

11REPR_SWITCH_ABOVE = -7 

12 

13 

14@no_type_check 

15def serialize(number: Union[float, int]) -> str: 

16 """Serialize a Python builtin number as an ECMAScript v6 or later string. 

17 

18 Implementation Note(s): 

19 

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})') 

31 

32 as_float = float(number) 

33 if as_float == 0: 

34 return DIGIT_ZERO 

35 

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:]) 

46 

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 :] 

51 

52 if f_part == DIGIT_ZERO: 

53 d_point, f_part = '', '' 

54 

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 

58 

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 

62 

63 return f'{sign}{i_part}{d_point}{f_part}{e_part}'