Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.10/site-packages/django/utils/numberformat.py: 7%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

58 statements  

1from decimal import Decimal 

2 

3from django.conf import settings 

4from django.utils.safestring import mark_safe 

5 

6 

7def format( 

8 number, 

9 decimal_sep, 

10 decimal_pos=None, 

11 grouping=0, 

12 thousand_sep="", 

13 force_grouping=False, 

14 use_l10n=None, 

15): 

16 """ 

17 Get a number (as a number or string), and return it as a string, 

18 using formats defined as arguments: 

19 

20 * decimal_sep: Decimal separator symbol (for example ".") 

21 * decimal_pos: Number of decimal positions 

22 * grouping: Number of digits in every group limited by thousand separator. 

23 For non-uniform digit grouping, it can be a sequence with the number 

24 of digit group sizes following the format used by the Python locale 

25 module in locale.localeconv() LC_NUMERIC grouping (e.g. (3, 2, 0)). 

26 * thousand_sep: Thousand separator symbol (for example ",") 

27 """ 

28 if number is None or number == "": 

29 return mark_safe(number) 

30 if use_l10n is None: 

31 use_l10n = True 

32 use_grouping = use_l10n and settings.USE_THOUSAND_SEPARATOR 

33 use_grouping = use_grouping or force_grouping 

34 use_grouping = use_grouping and grouping != 0 

35 # Make the common case fast 

36 if isinstance(number, int) and not use_grouping and not decimal_pos: 

37 return mark_safe(number) 

38 # sign 

39 sign = "" 

40 # Treat potentially very large/small floats as Decimals. 

41 if isinstance(number, float) and "e" in str(number).lower(): 

42 number = Decimal(str(number)) 

43 if isinstance(number, Decimal): 

44 if decimal_pos is not None: 

45 # If the provided number is too small to affect any of the visible 

46 # decimal places, consider it equal to '0'. 

47 cutoff = Decimal("0." + "1".rjust(decimal_pos, "0")) 

48 if abs(number) < cutoff: 

49 number = Decimal("0") 

50 

51 # Format values with more than 200 digits (an arbitrary cutoff) using 

52 # scientific notation to avoid high memory usage in {:f}'.format(). 

53 _, digits, exponent = number.as_tuple() 

54 if abs(exponent) + len(digits) > 200: 

55 number = "{:e}".format(number) 

56 coefficient, exponent = number.split("e") 

57 # Format the coefficient. 

58 coefficient = format( 

59 coefficient, 

60 decimal_sep, 

61 decimal_pos, 

62 grouping, 

63 thousand_sep, 

64 force_grouping, 

65 use_l10n, 

66 ) 

67 return "{}e{}".format(coefficient, exponent) 

68 else: 

69 str_number = "{:f}".format(number) 

70 else: 

71 str_number = str(number) 

72 if str_number[0] == "-": 

73 sign = "-" 

74 str_number = str_number[1:] 

75 # decimal part 

76 if "." in str_number: 

77 int_part, dec_part = str_number.split(".") 

78 if decimal_pos is not None: 

79 dec_part = dec_part[:decimal_pos] 

80 else: 

81 int_part, dec_part = str_number, "" 

82 if decimal_pos is not None: 

83 dec_part += "0" * (decimal_pos - len(dec_part)) 

84 dec_part = dec_part and decimal_sep + dec_part 

85 # grouping 

86 if use_grouping: 

87 try: 

88 # if grouping is a sequence 

89 intervals = list(grouping) 

90 except TypeError: 

91 # grouping is a single value 

92 intervals = [grouping, 0] 

93 active_interval = intervals.pop(0) 

94 int_part_gd = "" 

95 cnt = 0 

96 for digit in int_part[::-1]: 

97 if cnt and cnt == active_interval: 

98 if intervals: 

99 active_interval = intervals.pop(0) or active_interval 

100 int_part_gd += thousand_sep[::-1] 

101 cnt = 0 

102 int_part_gd += digit 

103 cnt += 1 

104 int_part = int_part_gd[::-1] 

105 return sign + int_part + dec_part