Skip to content

Commit 48c2faa

Browse files
committed
feat: allow precision specification
1 parent 40e60a1 commit 48c2faa

File tree

2 files changed

+219
-7
lines changed

2 files changed

+219
-7
lines changed

babel/numbers.py

+47-7
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,7 @@ def format_decimal(
520520
format: str | NumberPattern | None = None,
521521
locale: Locale | str | None = LC_NUMERIC,
522522
decimal_quantization: bool = True,
523+
precision: int | None = None,
523524
group_separator: bool = True,
524525
*,
525526
numbering_system: Literal["default"] | str = "latn",
@@ -560,11 +561,19 @@ def format_decimal(
560561
>>> format_decimal(12345.67, locale='en_US', group_separator=True)
561562
u'12,345.67'
562563
564+
When you bypass locale truncation, you can specify precision:
565+
566+
>>> format_decimal(1.2346, locale='en_US', decimal_quantization=False, precision=2)
567+
u'1.23'
568+
>>> format_decimal(0.3, locale='en_US', decimal_quantization=False, precision=2)
569+
u'0.30'
570+
563571
:param number: the number to format
564572
:param format:
565573
:param locale: the `Locale` object or locale identifier
566574
:param decimal_quantization: Truncate and round high-precision numbers to
567575
the format pattern. Defaults to `True`.
576+
:param precision: Optionally specify precision when decimal_quantization is False.
568577
:param group_separator: Boolean to switch group separator on/off in a locale's
569578
number format.
570579
:param numbering_system: The numbering system used for formatting number symbols. Defaults to "latn".
@@ -576,7 +585,7 @@ def format_decimal(
576585
format = locale.decimal_formats[format]
577586
pattern = parse_pattern(format)
578587
return pattern.apply(
579-
number, locale, decimal_quantization=decimal_quantization, group_separator=group_separator, numbering_system=numbering_system)
588+
number, locale, decimal_quantization=decimal_quantization, precision=precision, group_separator=group_separator, numbering_system=numbering_system)
580589

581590

582591
def format_compact_decimal(
@@ -674,6 +683,7 @@ def format_currency(
674683
currency_digits: bool = True,
675684
format_type: Literal["name", "standard", "accounting"] = "standard",
676685
decimal_quantization: bool = True,
686+
precision: int | None = None,
677687
group_separator: bool = True,
678688
*,
679689
numbering_system: Literal["default"] | str = "latn",
@@ -755,6 +765,11 @@ def format_currency(
755765
>>> format_currency(1099.9876, 'USD', locale='en_US', decimal_quantization=False)
756766
u'$1,099.9876'
757767
768+
When you bypass locale truncation, you can specify precision:
769+
770+
>>> format_currency(1099.9876, 'USD', locale='en_US', decimal_quantization=False, precision=3)
771+
u'$1,099.988'
772+
758773
:param number: the number to format
759774
:param currency: the currency code
760775
:param format: the format string to use
@@ -763,6 +778,7 @@ def format_currency(
763778
:param format_type: the currency format type to use
764779
:param decimal_quantization: Truncate and round high-precision numbers to
765780
the format pattern. Defaults to `True`.
781+
:param precision: Optionally specify precision when decimal_quantization is False.
766782
:param group_separator: Boolean to switch group separator on/off in a locale's
767783
number format.
768784
:param numbering_system: The numbering system used for formatting number symbols. Defaults to "latn".
@@ -772,7 +788,7 @@ def format_currency(
772788
if format_type == 'name':
773789
return _format_currency_long_name(number, currency, format=format,
774790
locale=locale, currency_digits=currency_digits,
775-
decimal_quantization=decimal_quantization, group_separator=group_separator,
791+
decimal_quantization=decimal_quantization, precision=precision, group_separator=group_separator,
776792
numbering_system=numbering_system)
777793
locale = Locale.parse(locale)
778794
if format:
@@ -785,7 +801,7 @@ def format_currency(
785801

786802
return pattern.apply(
787803
number, locale, currency=currency, currency_digits=currency_digits,
788-
decimal_quantization=decimal_quantization, group_separator=group_separator, numbering_system=numbering_system)
804+
decimal_quantization=decimal_quantization, precision=precision, group_separator=group_separator, numbering_system=numbering_system)
789805

790806

791807
def _format_currency_long_name(
@@ -796,6 +812,7 @@ def _format_currency_long_name(
796812
currency_digits: bool = True,
797813
format_type: Literal["name", "standard", "accounting"] = "standard",
798814
decimal_quantization: bool = True,
815+
precision: int | None = None,
799816
group_separator: bool = True,
800817
*,
801818
numbering_system: Literal["default"] | str = "latn",
@@ -825,7 +842,7 @@ def _format_currency_long_name(
825842

826843
number_part = pattern.apply(
827844
number, locale, currency=currency, currency_digits=currency_digits,
828-
decimal_quantization=decimal_quantization, group_separator=group_separator, numbering_system=numbering_system)
845+
decimal_quantization=decimal_quantization, precision=precision, group_separator=group_separator, numbering_system=numbering_system)
829846

830847
return unit_pattern.format(number_part, display_name)
831848

@@ -887,6 +904,7 @@ def format_percent(
887904
format: str | NumberPattern | None = None,
888905
locale: Locale | str | None = LC_NUMERIC,
889906
decimal_quantization: bool = True,
907+
precision: int | None = None,
890908
group_separator: bool = True,
891909
*,
892910
numbering_system: Literal["default"] | str = "latn",
@@ -922,11 +940,17 @@ def format_percent(
922940
>>> format_percent(229291.1234, locale='pt_BR', group_separator=True)
923941
u'22.929.112%'
924942
943+
When you bypass locale truncation, you can specify precision:
944+
945+
>>> format_percent(0.0111, locale='en_US', decimal_quantization=False, precision=3)
946+
u'1.110%'
947+
925948
:param number: the percent number to format
926949
:param format:
927950
:param locale: the `Locale` object or locale identifier
928951
:param decimal_quantization: Truncate and round high-precision numbers to
929952
the format pattern. Defaults to `True`.
953+
:param precision: Optionally specify precision when decimal_quantization is False.
930954
:param group_separator: Boolean to switch group separator on/off in a locale's
931955
number format.
932956
:param numbering_system: The numbering system used for formatting number symbols. Defaults to "latn".
@@ -938,7 +962,7 @@ def format_percent(
938962
format = locale.percent_formats[None]
939963
pattern = parse_pattern(format)
940964
return pattern.apply(
941-
number, locale, decimal_quantization=decimal_quantization, group_separator=group_separator,
965+
number, locale, decimal_quantization=decimal_quantization, precision=precision, group_separator=group_separator,
942966
numbering_system=numbering_system,
943967
)
944968

@@ -949,6 +973,7 @@ def format_scientific(
949973
locale: Locale | str | None = LC_NUMERIC,
950974
decimal_quantization: bool = True,
951975
*,
976+
precision: int | None = None,
952977
numbering_system: Literal["default"] | str = "latn",
953978
) -> str:
954979
"""Return value formatted in scientific notation for a specific locale.
@@ -972,11 +997,17 @@ def format_scientific(
972997
>>> format_scientific(1234.9876, u'#.##E0', locale='en_US', decimal_quantization=False)
973998
u'1.2349876E3'
974999
1000+
When you bypass locale truncation, you can specify precision:
1001+
1002+
>>> format_scientific(000.00100, locale='en_US', decimal_quantization=False, precision=3)
1003+
u'1.000E-3'
1004+
9751005
:param number: the number to format
9761006
:param format:
9771007
:param locale: the `Locale` object or locale identifier
9781008
:param decimal_quantization: Truncate and round high-precision numbers to
9791009
the format pattern. Defaults to `True`.
1010+
:param precision: Optionally specify precision when decimal_quantization is False.
9801011
:param numbering_system: The numbering system used for formatting number symbols. Defaults to "latn".
9811012
The special value "default" will use the default numbering system of the locale.
9821013
:raise `UnsupportedNumberingSystemError`: If the numbering system is not supported by the locale.
@@ -986,7 +1017,7 @@ def format_scientific(
9861017
format = locale.scientific_formats[None]
9871018
pattern = parse_pattern(format)
9881019
return pattern.apply(
989-
number, locale, decimal_quantization=decimal_quantization, numbering_system=numbering_system)
1020+
number, locale, decimal_quantization=decimal_quantization, precision=precision, numbering_system=numbering_system)
9901021

9911022

9921023
class NumberFormatError(ValueError):
@@ -1346,6 +1377,7 @@ def apply(
13461377
currency: str | None = None,
13471378
currency_digits: bool = True,
13481379
decimal_quantization: bool = True,
1380+
precision: int | None = None,
13491381
force_frac: tuple[int, int] | None = None,
13501382
group_separator: bool = True,
13511383
*,
@@ -1371,6 +1403,8 @@ def apply(
13711403
strictly matching the CLDR definition for
13721404
the locale.
13731405
:type decimal_quantization: bool
1406+
:param precision: Optionally specify precision when decimal_quantization is False.
1407+
:type precision: int|None
13741408
:param force_frac: DEPRECATED - a forced override for `self.frac_prec`
13751409
for a single formatting invocation.
13761410
:param numbering_system: The numbering system used for formatting number symbols. Defaults to "latn".
@@ -1407,14 +1441,20 @@ def apply(
14071441
else:
14081442
frac_prec = self.frac_prec
14091443

1444+
if decimal_quantization and precision is not None:
1445+
raise ValueError("To specify precision, decimal_quantization should be set to False.")
1446+
14101447
# Bump decimal precision to the natural precision of the number if it
14111448
# exceeds the one we're about to use. This adaptative precision is only
14121449
# triggered if the decimal quantization is disabled or if a scientific
14131450
# notation pattern has a missing mandatory fractional part (as in the
14141451
# default '#E0' pattern). This special case has been extensively
14151452
# discussed at https://github.com/python-babel/babel/pull/494#issuecomment-307649969 .
14161453
if not decimal_quantization or (self.exp_prec and frac_prec == (0, 0)):
1417-
frac_prec = (frac_prec[0], max([frac_prec[1], get_decimal_precision(value)]))
1454+
if not decimal_quantization and precision is not None:
1455+
frac_prec = (precision, precision)
1456+
else:
1457+
frac_prec = (frac_prec[0], max([frac_prec[1], get_decimal_precision(value)]))
14181458

14191459
# Render scientific notation.
14201460
if self.exp_prec:

0 commit comments

Comments
 (0)