]> git.ipfire.org Git - thirdparty/babel.git/commitdiff
Defer lookups of default locale values
authorAarni Koskela <akx@iki.fi>
Wed, 8 Jan 2025 13:52:53 +0000 (15:52 +0200)
committerAarni Koskela <akx@iki.fi>
Thu, 9 Jan 2025 08:25:16 +0000 (10:25 +0200)
babel/dates.py
babel/lists.py
babel/messages/plurals.py
babel/numbers.py
babel/units.py
tests/test_numbers.py

index 2e481852a86db45c9a1e9aabc7d00596f3be4ff1..8c82d72d4855aecf97c35840d713215672b915f1 100644 (file)
@@ -252,8 +252,11 @@ def get_timezone(zone: str | datetime.tzinfo | None = None) -> datetime.tzinfo:
     raise LookupError(f"Unknown timezone {zone}") from exc
 
 
-def get_period_names(width: Literal['abbreviated', 'narrow', 'wide'] = 'wide',
-                     context: _Context = 'stand-alone', locale: Locale | str | None = LC_TIME) -> LocaleDataDict:
+def get_period_names(
+    width: Literal['abbreviated', 'narrow', 'wide'] = 'wide',
+    context: _Context = 'stand-alone',
+    locale: Locale | str | None = None,
+) -> LocaleDataDict:
     """Return the names for day periods (AM/PM) used by the locale.
 
     >>> get_period_names(locale='en_US')['am']
@@ -261,13 +264,16 @@ def get_period_names(width: Literal['abbreviated', 'narrow', 'wide'] = 'wide',
 
     :param width: the width to use, one of "abbreviated", "narrow", or "wide"
     :param context: the context, either "format" or "stand-alone"
-    :param locale: the `Locale` object, or a locale string
+    :param locale: the `Locale` object, or a locale string. Defaults to the system time locale.
     """
-    return Locale.parse(locale).day_periods[context][width]
+    return Locale.parse(locale or LC_TIME).day_periods[context][width]
 
 
-def get_day_names(width: Literal['abbreviated', 'narrow', 'short', 'wide'] = 'wide',
-                  context: _Context = 'format', locale: Locale | str | None = LC_TIME) -> LocaleDataDict:
+def get_day_names(
+    width: Literal['abbreviated', 'narrow', 'short', 'wide'] = 'wide',
+    context: _Context = 'format',
+    locale: Locale | str | None = None,
+) -> LocaleDataDict:
     """Return the day names used by the locale for the specified format.
 
     >>> get_day_names('wide', locale='en_US')[1]
@@ -281,13 +287,16 @@ def get_day_names(width: Literal['abbreviated', 'narrow', 'short', 'wide'] = 'wi
 
     :param width: the width to use, one of "wide", "abbreviated", "short" or "narrow"
     :param context: the context, either "format" or "stand-alone"
-    :param locale: the `Locale` object, or a locale string
+    :param locale: the `Locale` object, or a locale string. Defaults to the system time locale.
     """
-    return Locale.parse(locale).days[context][width]
+    return Locale.parse(locale or LC_TIME).days[context][width]
 
 
-def get_month_names(width: Literal['abbreviated', 'narrow', 'wide'] = 'wide',
-                    context: _Context = 'format', locale: Locale | str | None = LC_TIME) -> LocaleDataDict:
+def get_month_names(
+    width: Literal['abbreviated', 'narrow', 'wide'] = 'wide',
+    context: _Context = 'format',
+    locale: Locale | str | None = None,
+) -> LocaleDataDict:
     """Return the month names used by the locale for the specified format.
 
     >>> get_month_names('wide', locale='en_US')[1]
@@ -299,13 +308,16 @@ def get_month_names(width: Literal['abbreviated', 'narrow', 'wide'] = 'wide',
 
     :param width: the width to use, one of "wide", "abbreviated", or "narrow"
     :param context: the context, either "format" or "stand-alone"
-    :param locale: the `Locale` object, or a locale string
+    :param locale: the `Locale` object, or a locale string. Defaults to the system time locale.
     """
-    return Locale.parse(locale).months[context][width]
+    return Locale.parse(locale or LC_TIME).months[context][width]
 
 
-def get_quarter_names(width: Literal['abbreviated', 'narrow', 'wide'] = 'wide',
-                      context: _Context = 'format', locale: Locale | str | None = LC_TIME) -> LocaleDataDict:
+def get_quarter_names(
+    width: Literal['abbreviated', 'narrow', 'wide'] = 'wide',
+    context: _Context = 'format',
+    locale: Locale | str | None = None,
+) -> LocaleDataDict:
     """Return the quarter names used by the locale for the specified format.
 
     >>> get_quarter_names('wide', locale='en_US')[1]
@@ -317,13 +329,15 @@ def get_quarter_names(width: Literal['abbreviated', 'narrow', 'wide'] = 'wide',
 
     :param width: the width to use, one of "wide", "abbreviated", or "narrow"
     :param context: the context, either "format" or "stand-alone"
-    :param locale: the `Locale` object, or a locale string
+    :param locale: the `Locale` object, or a locale string. Defaults to the system time locale.
     """
-    return Locale.parse(locale).quarters[context][width]
+    return Locale.parse(locale or LC_TIME).quarters[context][width]
 
 
-def get_era_names(width: Literal['abbreviated', 'narrow', 'wide'] = 'wide',
-                  locale: Locale | str | None = LC_TIME) -> LocaleDataDict:
+def get_era_names(
+    width: Literal['abbreviated', 'narrow', 'wide'] = 'wide',
+    locale: Locale | str | None = None,
+) -> LocaleDataDict:
     """Return the era names used by the locale for the specified format.
 
     >>> get_era_names('wide', locale='en_US')[1]
@@ -332,12 +346,15 @@ def get_era_names(width: Literal['abbreviated', 'narrow', 'wide'] = 'wide',
     u'n. Chr.'
 
     :param width: the width to use, either "wide", "abbreviated", or "narrow"
-    :param locale: the `Locale` object, or a locale string
+    :param locale: the `Locale` object, or a locale string. Defaults to the system time locale.
     """
-    return Locale.parse(locale).eras[width]
+    return Locale.parse(locale or LC_TIME).eras[width]
 
 
-def get_date_format(format: _PredefinedTimeFormat = 'medium', locale: Locale | str | None = LC_TIME) -> DateTimePattern:
+def get_date_format(
+    format: _PredefinedTimeFormat = 'medium',
+    locale: Locale | str | None = None,
+) -> DateTimePattern:
     """Return the date formatting patterns used by the locale for the specified
     format.
 
@@ -348,12 +365,15 @@ def get_date_format(format: _PredefinedTimeFormat = 'medium', locale: Locale | s
 
     :param format: the format to use, one of "full", "long", "medium", or
                    "short"
-    :param locale: the `Locale` object, or a locale string
+    :param locale: the `Locale` object, or a locale string. Defaults to the system time locale.
     """
-    return Locale.parse(locale).date_formats[format]
+    return Locale.parse(locale or LC_TIME).date_formats[format]
 
 
-def get_datetime_format(format: _PredefinedTimeFormat = 'medium', locale: Locale | str | None = LC_TIME) -> DateTimePattern:
+def get_datetime_format(
+    format: _PredefinedTimeFormat = 'medium',
+    locale: Locale | str | None = None,
+) -> DateTimePattern:
     """Return the datetime formatting patterns used by the locale for the
     specified format.
 
@@ -362,15 +382,18 @@ def get_datetime_format(format: _PredefinedTimeFormat = 'medium', locale: Locale
 
     :param format: the format to use, one of "full", "long", "medium", or
                    "short"
-    :param locale: the `Locale` object, or a locale string
+    :param locale: the `Locale` object, or a locale string. Defaults to the system time locale.
     """
-    patterns = Locale.parse(locale).datetime_formats
+    patterns = Locale.parse(locale or LC_TIME).datetime_formats
     if format not in patterns:
         format = None
     return patterns[format]
 
 
-def get_time_format(format: _PredefinedTimeFormat = 'medium', locale: Locale | str | None = LC_TIME) -> DateTimePattern:
+def get_time_format(
+    format: _PredefinedTimeFormat = 'medium',
+    locale: Locale | str | None = None,
+) -> DateTimePattern:
     """Return the time formatting patterns used by the locale for the specified
     format.
 
@@ -381,15 +404,15 @@ def get_time_format(format: _PredefinedTimeFormat = 'medium', locale: Locale | s
 
     :param format: the format to use, one of "full", "long", "medium", or
                    "short"
-    :param locale: the `Locale` object, or a locale string
+    :param locale: the `Locale` object, or a locale string. Defaults to the system time locale.
     """
-    return Locale.parse(locale).time_formats[format]
+    return Locale.parse(locale or LC_TIME).time_formats[format]
 
 
 def get_timezone_gmt(
     datetime: _Instant = None,
     width: Literal['long', 'short', 'iso8601', 'iso8601_short'] = 'long',
-    locale: Locale | str | None = LC_TIME,
+    locale: Locale | str | None = None,
     return_z: bool = False,
 ) -> str:
     """Return the timezone associated with the given `datetime` object formatted
@@ -423,12 +446,12 @@ def get_timezone_gmt(
     :param datetime: the ``datetime`` object; if `None`, the current date and
                      time in UTC is used
     :param width: either "long" or "short" or "iso8601" or "iso8601_short"
-    :param locale: the `Locale` object, or a locale string
+    :param locale: the `Locale` object, or a locale string. Defaults to the system time locale.
     :param return_z: True or False; Function returns indicator "Z"
                      when local time offset is 0
     """
     datetime = _ensure_datetime_tzinfo(_get_datetime(datetime))
-    locale = Locale.parse(locale)
+    locale = Locale.parse(locale or LC_TIME)
 
     offset = datetime.tzinfo.utcoffset(datetime)
     seconds = offset.days * 24 * 60 * 60 + offset.seconds
@@ -448,7 +471,7 @@ def get_timezone_gmt(
 
 def get_timezone_location(
     dt_or_tzinfo: _DtOrTzinfo = None,
-    locale: Locale | str | None = LC_TIME,
+    locale: Locale | str | None = None,
     return_city: bool = False,
 ) -> str:
     """Return a representation of the given timezone using "location format".
@@ -479,13 +502,13 @@ def get_timezone_location(
     :param dt_or_tzinfo: the ``datetime`` or ``tzinfo`` object that determines
                          the timezone; if `None`, the current date and time in
                          UTC is assumed
-    :param locale: the `Locale` object, or a locale string
+    :param locale: the `Locale` object, or a locale string. Defaults to the system time locale.
     :param return_city: True or False, if True then return exemplar city (location)
                         for the time zone
     :return: the localized timezone name using location format
 
     """
-    locale = Locale.parse(locale)
+    locale = Locale.parse(locale or LC_TIME)
 
     zone = _get_tz_name(dt_or_tzinfo)
 
@@ -530,7 +553,7 @@ def get_timezone_name(
     dt_or_tzinfo: _DtOrTzinfo = None,
     width: Literal['long', 'short'] = 'long',
     uncommon: bool = False,
-    locale: Locale | str | None = LC_TIME,
+    locale: Locale | str | None = None,
     zone_variant: Literal['generic', 'daylight', 'standard'] | None = None,
     return_zone: bool = False,
 ) -> str:
@@ -600,12 +623,12 @@ def get_timezone_name(
                            ``'generic'`` variation is assumed.  The following
                            values are valid: ``'generic'``, ``'daylight'`` and
                            ``'standard'``.
-    :param locale: the `Locale` object, or a locale string
+    :param locale: the `Locale` object, or a locale string. Defaults to the system time locale.
     :param return_zone: True or False. If true then function
                         returns long time zone ID
     """
     dt, tzinfo = _get_dt_and_tzinfo(dt_or_tzinfo)
-    locale = Locale.parse(locale)
+    locale = Locale.parse(locale or LC_TIME)
 
     zone = _get_tz_name(dt_or_tzinfo)
 
@@ -651,7 +674,7 @@ def get_timezone_name(
 def format_date(
     date: datetime.date | None = None,
     format: _PredefinedTimeFormat | str = 'medium',
-    locale: Locale | str | None = LC_TIME,
+    locale: Locale | str | None = None,
 ) -> str:
     """Return a date formatted according to the given pattern.
 
@@ -672,14 +695,14 @@ def format_date(
                  date is used
     :param format: one of "full", "long", "medium", or "short", or a custom
                    date/time pattern
-    :param locale: a `Locale` object or a locale identifier
+    :param locale: a `Locale` object or a locale identifier. Defaults to the system time locale.
     """
     if date is None:
         date = datetime.date.today()
     elif isinstance(date, datetime.datetime):
         date = date.date()
 
-    locale = Locale.parse(locale)
+    locale = Locale.parse(locale or LC_TIME)
     if format in ('full', 'long', 'medium', 'short'):
         format = get_date_format(format, locale=locale)
     pattern = parse_pattern(format)
@@ -690,7 +713,7 @@ def format_datetime(
     datetime: _Instant = None,
     format: _PredefinedTimeFormat | str = 'medium',
     tzinfo: datetime.tzinfo | None = None,
-    locale: Locale | str | None = LC_TIME,
+    locale: Locale | str | None = None,
 ) -> str:
     r"""Return a date formatted according to the given pattern.
 
@@ -713,11 +736,11 @@ def format_datetime(
     :param format: one of "full", "long", "medium", or "short", or a custom
                    date/time pattern
     :param tzinfo: the timezone to apply to the time for display
-    :param locale: a `Locale` object or a locale identifier
+    :param locale: a `Locale` object or a locale identifier. Defaults to the system time locale.
     """
     datetime = _ensure_datetime_tzinfo(_get_datetime(datetime), tzinfo)
 
-    locale = Locale.parse(locale)
+    locale = Locale.parse(locale or LC_TIME)
     if format in ('full', 'long', 'medium', 'short'):
         return get_datetime_format(format, locale=locale) \
             .replace("'", "") \
@@ -731,7 +754,8 @@ def format_datetime(
 def format_time(
     time: datetime.time | datetime.datetime | float | None = None,
     format: _PredefinedTimeFormat | str = 'medium',
-    tzinfo: datetime.tzinfo | None = None, locale: Locale | str | None = LC_TIME,
+    tzinfo: datetime.tzinfo | None = None,
+    locale: Locale | str | None = None,
 ) -> str:
     r"""Return a time formatted according to the given pattern.
 
@@ -786,7 +810,7 @@ def format_time(
     :param format: one of "full", "long", "medium", or "short", or a custom
                    date/time pattern
     :param tzinfo: the time-zone to apply to the time for display
-    :param locale: a `Locale` object or a locale identifier
+    :param locale: a `Locale` object or a locale identifier. Defaults to the system time locale.
     """
 
     # get reference date for if we need to find the right timezone variant
@@ -795,7 +819,7 @@ def format_time(
 
     time = _get_time(time, tzinfo)
 
-    locale = Locale.parse(locale)
+    locale = Locale.parse(locale or LC_TIME)
     if format in ('full', 'long', 'medium', 'short'):
         format = get_time_format(format, locale=locale)
     return parse_pattern(format).apply(time, locale, reference_date=ref_date)
@@ -806,7 +830,7 @@ def format_skeleton(
     datetime: _Instant = None,
     tzinfo: datetime.tzinfo | None = None,
     fuzzy: bool = True,
-    locale: Locale | str | None = LC_TIME,
+    locale: Locale | str | None = None,
 ) -> str:
     r"""Return a time and/or date formatted according to the given pattern.
 
@@ -842,9 +866,9 @@ def format_skeleton(
     :param fuzzy: If the skeleton is not found, allow choosing a skeleton that's
                   close enough to it. If there is no close match, a `KeyError`
                   is thrown.
-    :param locale: a `Locale` object or a locale identifier
+    :param locale: a `Locale` object or a locale identifier. Defaults to the system time locale.
     """
-    locale = Locale.parse(locale)
+    locale = Locale.parse(locale or LC_TIME)
     if fuzzy and skeleton not in locale.datetime_skeletons:
         skeleton = match_skeleton(skeleton, locale.datetime_skeletons)
     format = locale.datetime_skeletons[skeleton]
@@ -868,7 +892,7 @@ def format_timedelta(
     threshold: float = .85,
     add_direction: bool = False,
     format: Literal['narrow', 'short', 'medium', 'long'] = 'long',
-    locale: Locale | str | None = LC_TIME,
+    locale: Locale | str | None = None,
 ) -> str:
     """Return a time delta according to the rules of the given locale.
 
@@ -923,7 +947,7 @@ def format_timedelta(
     :param format: the format, can be "narrow", "short" or "long". (
                    "medium" is deprecated, currently converted to "long" to
                    maintain compatibility)
-    :param locale: a `Locale` object or a locale identifier
+    :param locale: a `Locale` object or a locale identifier. Defaults to the system time locale.
     """
     if format not in ('narrow', 'short', 'medium', 'long'):
         raise TypeError('Format must be one of "narrow", "short" or "long"')
@@ -939,7 +963,7 @@ def format_timedelta(
         seconds = int((delta.days * 86400) + delta.seconds)
     else:
         seconds = delta
-    locale = Locale.parse(locale)
+    locale = Locale.parse(locale or LC_TIME)
     date_fields = locale._data["date_fields"]
     unit_patterns = locale._data["unit_patterns"]
 
@@ -988,7 +1012,7 @@ def _format_fallback_interval(
     end: _Instant,
     skeleton: str | None,
     tzinfo: datetime.tzinfo | None,
-    locale: Locale | str | None = LC_TIME,
+    locale: Locale,
 ) -> str:
     if skeleton in locale.datetime_skeletons:  # Use the given skeleton
         format = lambda dt: format_skeleton(skeleton, dt, tzinfo, locale=locale)
@@ -1018,7 +1042,7 @@ def format_interval(
     skeleton: str | None = None,
     tzinfo: datetime.tzinfo | None = None,
     fuzzy: bool = True,
-    locale: Locale | str | None = LC_TIME,
+    locale: Locale | str | None = None,
 ) -> str:
     """
     Format an interval between two instants according to the locale's rules.
@@ -1058,10 +1082,10 @@ def format_interval(
     :param tzinfo: tzinfo to use (if none is already attached)
     :param fuzzy: If the skeleton is not found, allow choosing a skeleton that's
                   close enough to it.
-    :param locale: A locale object or identifier.
+    :param locale: A locale object or identifier. Defaults to the system time locale.
     :return: Formatted interval
     """
-    locale = Locale.parse(locale)
+    locale = Locale.parse(locale or LC_TIME)
 
     # NB: The quote comments below are from the algorithm description in
     #     https://www.unicode.org/reports/tr35/tr35-dates.html#intervalFormats
@@ -1121,7 +1145,7 @@ def get_period_id(
     time: _Instant,
     tzinfo: datetime.tzinfo | None = None,
     type: Literal['selection'] | None = None,
-    locale: Locale | str | None = LC_TIME,
+    locale: Locale | str | None = None,
 ) -> str:
     """
     Get the day period ID for a given time.
@@ -1143,12 +1167,12 @@ def get_period_id(
     :param type: The period type to use. Either "selection" or None.
                  The selection type is used for selecting among phrases such as
                  “Your email arrived yesterday evening” or “Your email arrived last night”.
-    :param locale: the `Locale` object, or a locale string
+    :param locale: the `Locale` object, or a locale string. Defaults to the system time locale.
     :return: period ID. Something is always returned -- even if it's just "am" or "pm".
     """
     time = _get_time(time, tzinfo)
     seconds_past_midnight = int(time.hour * 60 * 60 + time.minute * 60 + time.second)
-    locale = Locale.parse(locale)
+    locale = Locale.parse(locale or LC_TIME)
 
     # The LDML rules state that the rules may not overlap, so iterating in arbitrary
     # order should be alright, though `at` periods should be preferred.
@@ -1199,7 +1223,7 @@ class ParseError(ValueError):
 
 def parse_date(
     string: str,
-    locale: Locale | str | None = LC_TIME,
+    locale: Locale | str | None = None,
     format: _PredefinedTimeFormat | str = 'medium',
 ) -> datetime.date:
     """Parse a date from a string.
@@ -1228,6 +1252,7 @@ def parse_date(
 
     :param string: the string containing the date
     :param locale: a `Locale` object or a locale identifier
+    :param locale: a `Locale` object or a locale identifier. Defaults to the system time locale.
     :param format: the format to use, either an explicit date format,
                    or one of "full", "long", "medium", or "short"
                    (see ``get_time_format``)
@@ -1275,7 +1300,7 @@ def parse_date(
 
 def parse_time(
     string: str,
-    locale: Locale | str | None = LC_TIME,
+    locale: Locale | str | None = None,
     format: _PredefinedTimeFormat | str = 'medium',
 ) -> datetime.time:
     """Parse a time from a string.
@@ -1292,7 +1317,7 @@ def parse_time(
     datetime.time(15, 30)
 
     :param string: the string containing the time
-    :param locale: a `Locale` object or a locale identifier
+    :param locale: a `Locale` object or a locale identifier. Defaults to the system time locale.
     :param format: the format to use, either an explicit time format,
                    or one of "full", "long", "medium", or "short"
                    (see ``get_time_format``)
index 6c34cb0993e25d2eab91c049a13025aae5f60b12..60590d6c9ccb8ebde8cd6d6459bc26536bdd8c73 100644 (file)
@@ -29,7 +29,7 @@ DEFAULT_LOCALE = default_locale()
 def format_list(
     lst: Sequence[str],
     style: Literal['standard', 'standard-short', 'or', 'or-short', 'unit', 'unit-short', 'unit-narrow'] = 'standard',
-    locale: Locale | str | None = DEFAULT_LOCALE,
+    locale: Locale | str | None = None,
 ) -> str:
     """
     Format the items in `lst` as a list.
@@ -74,9 +74,9 @@ def format_list(
 
     :param lst: a sequence of items to format in to a list
     :param style: the style to format the list with. See above for description.
-    :param locale: the locale
+    :param locale: the locale. Defaults to the system locale.
     """
-    locale = Locale.parse(locale)
+    locale = Locale.parse(locale or DEFAULT_LOCALE)
     if not lst:
         return ''
     if len(lst) == 1:
index 01c38f4cd051526935bdf9aa196e5dfd6c213629..27d81d86fac6b84e4f8b403854d49af6699c6044 100644 (file)
@@ -220,11 +220,13 @@ class _PluralTuple(tuple):
         return self.plural_forms
 
 
-def get_plural(locale: str | None = LC_CTYPE) -> _PluralTuple:
+def get_plural(locale: str | None = None) -> _PluralTuple:
     """A tuple with the information catalogs need to perform proper
     pluralization.  The first item of the tuple is the number of plural
     forms, the second the plural expression.
 
+    :param locale: the `Locale` object or locale identifier. Defaults to the system character type locale.
+
     >>> get_plural(locale='en')
     (2, '(n != 1)')
     >>> get_plural(locale='ga')
@@ -246,7 +248,7 @@ def get_plural(locale: str | None = LC_CTYPE) -> _PluralTuple:
     >>> str(tup)
     'nplurals=1; plural=0;'
     """
-    locale = Locale.parse(locale)
+    locale = Locale.parse(locale or LC_CTYPE)
     try:
         tup = PLURALS[str(locale)]
     except KeyError:
index e71e5d8830765c5c6c97f84ef4c9e91023149ec9..aed9aa4c4c60616c2576ae697cf4b5d452319aba 100644 (file)
@@ -108,7 +108,7 @@ def normalize_currency(currency: str, locale: Locale | str | None = None) -> str
 def get_currency_name(
     currency: str,
     count: float | decimal.Decimal | None = None,
-    locale: Locale | str | None = LC_NUMERIC,
+    locale: Locale | str | None = None,
 ) -> str:
     """Return the name used by the locale for the specified currency.
 
@@ -120,9 +120,9 @@ def get_currency_name(
     :param currency: the currency code.
     :param count: the optional count.  If provided the currency name
                   will be pluralized to that number if possible.
-    :param locale: the `Locale` object or locale identifier.
+    :param locale: the `Locale` object or locale identifier. Defaults to the system numeric locale.
     """
-    loc = Locale.parse(locale)
+    loc = Locale.parse(locale or LC_NUMERIC)
     if count is not None:
         try:
             plural_form = loc.plural_form(count)
@@ -138,16 +138,16 @@ def get_currency_name(
     return loc.currencies.get(currency, currency)
 
 
-def get_currency_symbol(currency: str, locale: Locale | str | None = LC_NUMERIC) -> str:
+def get_currency_symbol(currency: str, locale: Locale | str | None = None) -> str:
     """Return the symbol used by the locale for the specified currency.
 
     >>> get_currency_symbol('USD', locale='en_US')
     u'$'
 
     :param currency: the currency code.
-    :param locale: the `Locale` object or locale identifier.
+    :param locale: the `Locale` object or locale identifier. Defaults to the system numeric locale.
     """
-    return Locale.parse(locale).currency_symbols.get(currency, currency)
+    return Locale.parse(locale or LC_NUMERIC).currency_symbols.get(currency, currency)
 
 
 def get_currency_precision(currency: str) -> int:
@@ -167,7 +167,7 @@ def get_currency_precision(currency: str) -> int:
 def get_currency_unit_pattern(
     currency: str,
     count: float | decimal.Decimal | None = None,
-    locale: Locale | str | None = LC_NUMERIC,
+    locale: Locale | str | None = None,
 ) -> str:
     """
     Return the unit pattern used for long display of a currency value
@@ -184,9 +184,9 @@ def get_currency_unit_pattern(
     :param currency: the currency code.
     :param count: the optional count.  If provided the unit
                   pattern for that number will be returned.
-    :param locale: the `Locale` object or locale identifier.
+    :param locale: the `Locale` object or locale identifier. Defaults to the system numeric locale.
     """
-    loc = Locale.parse(locale)
+    loc = Locale.parse(locale or LC_NUMERIC)
     if count is not None:
         plural_form = loc.plural_form(count)
         try:
@@ -325,16 +325,15 @@ def _get_numbering_system(locale: Locale, numbering_system: Literal["default"] |
 
 
 def _get_number_symbols(
-    locale: Locale | str | None,
+    locale: Locale,
     *,
     numbering_system: Literal["default"] | str = "latn",
 ) -> LocaleDataDict:
-    parsed_locale = Locale.parse(locale)
-    numbering_system = _get_numbering_system(parsed_locale, numbering_system)
+    numbering_system = _get_numbering_system(locale, numbering_system)
     try:
-        return parsed_locale.number_symbols[numbering_system]
+        return locale.number_symbols[numbering_system]
     except KeyError as error:
-        raise UnsupportedNumberingSystemError(f"Unknown numbering system {numbering_system} for Locale {parsed_locale}.") from error
+        raise UnsupportedNumberingSystemError(f"Unknown numbering system {numbering_system} for Locale {locale}.") from error
 
 
 class UnsupportedNumberingSystemError(Exception):
@@ -343,7 +342,7 @@ class UnsupportedNumberingSystemError(Exception):
 
 
 def get_decimal_symbol(
-    locale: Locale | str | None = LC_NUMERIC,
+    locale: Locale | str | None = None,
     *,
     numbering_system: Literal["default"] | str = "latn",
 ) -> str:
@@ -356,16 +355,17 @@ def get_decimal_symbol(
     >>> get_decimal_symbol('ar_EG', numbering_system='latn')
     u'.'
 
-    :param locale: the `Locale` object or locale identifier
+    :param locale: the `Locale` object or locale identifier. Defaults to the system numeric locale.
     :param numbering_system: The numbering system used for fetching the symbol. Defaults to "latn".
                              The special value "default" will use the default numbering system of the locale.
     :raise `UnsupportedNumberingSystemError`: If the numbering system is not supported by the locale.
     """
+    locale = Locale.parse(locale or LC_NUMERIC)
     return _get_number_symbols(locale, numbering_system=numbering_system).get('decimal', '.')
 
 
 def get_plus_sign_symbol(
-    locale: Locale | str | None = LC_NUMERIC,
+    locale: Locale | str | None = None,
     *,
     numbering_system: Literal["default"] | str = "latn",
 ) -> str:
@@ -378,16 +378,17 @@ def get_plus_sign_symbol(
     >>> get_plus_sign_symbol('ar_EG', numbering_system='latn')
     u'\u200e+'
 
-    :param locale: the `Locale` object or locale identifier
+    :param locale: the `Locale` object or locale identifier. Defaults to the system numeric locale.
     :param numbering_system: The numbering system used for fetching the symbol. Defaults to "latn".
                              The special value "default" will use the default numbering system of the locale.
     :raise `UnsupportedNumberingSystemError`: if the numbering system is not supported by the locale.
     """
+    locale = Locale.parse(locale or LC_NUMERIC)
     return _get_number_symbols(locale, numbering_system=numbering_system).get('plusSign', '+')
 
 
 def get_minus_sign_symbol(
-    locale: Locale | str | None = LC_NUMERIC,
+    locale: Locale | str | None = None,
     *,
     numbering_system: Literal["default"] | str = "latn",
 ) -> str:
@@ -400,16 +401,17 @@ def get_minus_sign_symbol(
     >>> get_minus_sign_symbol('ar_EG', numbering_system='latn')
     u'\u200e-'
 
-    :param locale: the `Locale` object or locale identifier
+    :param locale: the `Locale` object or locale identifier. Defaults to the system numeric locale.
     :param numbering_system: The numbering system used for fetching the symbol. Defaults to "latn".
                              The special value "default" will use the default numbering system of the locale.
     :raise `UnsupportedNumberingSystemError`: if the numbering system is not supported by the locale.
     """
+    locale = Locale.parse(locale or LC_NUMERIC)
     return _get_number_symbols(locale, numbering_system=numbering_system).get('minusSign', '-')
 
 
 def get_exponential_symbol(
-    locale: Locale | str | None = LC_NUMERIC,
+    locale: Locale | str | None = None,
     *,
     numbering_system: Literal["default"] | str = "latn",
 ) -> str:
@@ -422,16 +424,17 @@ def get_exponential_symbol(
     >>> get_exponential_symbol('ar_EG', numbering_system='latn')
     u'E'
 
-    :param locale: the `Locale` object or locale identifier
+    :param locale: the `Locale` object or locale identifier. Defaults to the system numeric locale.
     :param numbering_system: The numbering system used for fetching the symbol. Defaults to "latn".
                              The special value "default" will use the default numbering system of the locale.
     :raise `UnsupportedNumberingSystemError`: if the numbering system is not supported by the locale.
     """
+    locale = Locale.parse(locale or LC_NUMERIC)
     return _get_number_symbols(locale, numbering_system=numbering_system).get('exponential', 'E')
 
 
 def get_group_symbol(
-    locale: Locale | str | None = LC_NUMERIC,
+    locale: Locale | str | None = None,
     *,
     numbering_system: Literal["default"] | str = "latn",
 ) -> str:
@@ -444,16 +447,17 @@ def get_group_symbol(
     >>> get_group_symbol('ar_EG', numbering_system='latn')
     u','
 
-    :param locale: the `Locale` object or locale identifier
+    :param locale: the `Locale` object or locale identifier. Defaults to the system numeric locale.
     :param numbering_system: The numbering system used for fetching the symbol. Defaults to "latn".
                              The special value "default" will use the default numbering system of the locale.
     :raise `UnsupportedNumberingSystemError`: if the numbering system is not supported by the locale.
     """
+    locale = Locale.parse(locale or LC_NUMERIC)
     return _get_number_symbols(locale, numbering_system=numbering_system).get('group', ',')
 
 
 def get_infinity_symbol(
-    locale: Locale | str | None = LC_NUMERIC,
+    locale: Locale | str | None = None,
     *,
     numbering_system: Literal["default"] | str = "latn",
 ) -> str:
@@ -466,15 +470,16 @@ def get_infinity_symbol(
     >>> get_infinity_symbol('ar_EG', numbering_system='latn')
     u'∞'
 
-    :param locale: the `Locale` object or locale identifier
+    :param locale: the `Locale` object or locale identifier. Defaults to the system numeric locale.
     :param numbering_system: The numbering system used for fetching the symbol. Defaults to "latn".
                              The special value "default" will use the default numbering system of the locale.
     :raise `UnsupportedNumberingSystemError`: if the numbering system is not supported by the locale.
     """
+    locale = Locale.parse(locale or LC_NUMERIC)
     return _get_number_symbols(locale, numbering_system=numbering_system).get('infinity', '∞')
 
 
-def format_number(number: float | decimal.Decimal | str, locale: Locale | str | None = LC_NUMERIC) -> str:
+def format_number(number: float | decimal.Decimal | str, locale: Locale | str | None = None) -> str:
     """Return the given number formatted for a specific locale.
 
     >>> format_number(1099, locale='en_US')  # doctest: +SKIP
@@ -487,7 +492,7 @@ def format_number(number: float | decimal.Decimal | str, locale: Locale | str |
        Use babel.numbers.format_decimal() instead.
 
     :param number: the number to format
-    :param locale: the `Locale` object or locale identifier
+    :param locale: the `Locale` object or locale identifier. Defaults to the system numeric locale.
 
 
     """
@@ -518,7 +523,7 @@ def get_decimal_quantum(precision: int | decimal.Decimal) -> decimal.Decimal:
 def format_decimal(
     number: float | decimal.Decimal | str,
     format: str | NumberPattern | None = None,
-    locale: Locale | str | None = LC_NUMERIC,
+    locale: Locale | str | None = None,
     decimal_quantization: bool = True,
     group_separator: bool = True,
     *,
@@ -562,7 +567,7 @@ def format_decimal(
 
     :param number: the number to format
     :param format:
-    :param locale: the `Locale` object or locale identifier
+    :param locale: the `Locale` object or locale identifier. Defaults to the system numeric locale.
     :param decimal_quantization: Truncate and round high-precision numbers to
                                  the format pattern. Defaults to `True`.
     :param group_separator: Boolean to switch group separator on/off in a locale's
@@ -571,7 +576,7 @@ def format_decimal(
                              The special value "default" will use the default numbering system of the locale.
     :raise `UnsupportedNumberingSystemError`: If the numbering system is not supported by the locale.
     """
-    locale = Locale.parse(locale)
+    locale = Locale.parse(locale or LC_NUMERIC)
     if format is None:
         format = locale.decimal_formats[format]
     pattern = parse_pattern(format)
@@ -583,7 +588,7 @@ def format_compact_decimal(
     number: float | decimal.Decimal | str,
     *,
     format_type: Literal["short", "long"] = "short",
-    locale: Locale | str | None = LC_NUMERIC,
+    locale: Locale | str | None = None,
     fraction_digits: int = 0,
     numbering_system: Literal["default"] | str = "latn",
 ) -> str:
@@ -606,13 +611,13 @@ def format_compact_decimal(
 
     :param number: the number to format
     :param format_type: Compact format to use ("short" or "long")
-    :param locale: the `Locale` object or locale identifier
+    :param locale: the `Locale` object or locale identifier. Defaults to the system numeric locale.
     :param fraction_digits: Number of digits after the decimal point to use. Defaults to `0`.
     :param numbering_system: The numbering system used for formatting number symbols. Defaults to "latn".
                              The special value "default" will use the default numbering system of the locale.
     :raise `UnsupportedNumberingSystemError`: If the numbering system is not supported by the locale.
     """
-    locale = Locale.parse(locale)
+    locale = Locale.parse(locale or LC_NUMERIC)
     compact_format = locale.compact_decimal_formats[format_type]
     number, format = _get_compact_format(number, compact_format, locale, fraction_digits)
     # Did not find a format, fall back.
@@ -670,7 +675,7 @@ def format_currency(
     number: float | decimal.Decimal | str,
     currency: str,
     format: str | NumberPattern | None = None,
-    locale: Locale | str | None = LC_NUMERIC,
+    locale: Locale | str | None = None,
     currency_digits: bool = True,
     format_type: Literal["name", "standard", "accounting"] = "standard",
     decimal_quantization: bool = True,
@@ -758,7 +763,7 @@ def format_currency(
     :param number: the number to format
     :param currency: the currency code
     :param format: the format string to use
-    :param locale: the `Locale` object or locale identifier
+    :param locale: the `Locale` object or locale identifier. Defaults to the system numeric locale.
     :param currency_digits: use the currency's natural number of decimal digits
     :param format_type: the currency format type to use
     :param decimal_quantization: Truncate and round high-precision numbers to
@@ -769,6 +774,8 @@ def format_currency(
                              The special value "default" will use the default numbering system of the locale.
     :raise `UnsupportedNumberingSystemError`: If the numbering system is not supported by the locale.
     """
+    locale = Locale.parse(locale or LC_NUMERIC)
+
     if format_type == 'name':
         return _format_currency_long_name(
             number,
@@ -781,7 +788,6 @@ def format_currency(
             numbering_system=numbering_system,
         )
 
-    locale = Locale.parse(locale)
     if format:
         pattern = parse_pattern(format)
     else:
@@ -799,7 +805,7 @@ def _format_currency_long_name(
     number: float | decimal.Decimal | str,
     currency: str,
     *,
-    locale: Locale | str | None = LC_NUMERIC,
+    locale: Locale,
     format: str | NumberPattern | None,
     currency_digits: bool,
     decimal_quantization: bool,
@@ -808,7 +814,7 @@ def _format_currency_long_name(
 ) -> str:
     # Algorithm described here:
     # https://www.unicode.org/reports/tr35/tr35-numbers.html#Currencies
-    locale = Locale.parse(locale)
+
     # Step 1.
     # There are no examples of items with explicit count (0 or 1) in current
     # locale data. So there is no point implementing that.
@@ -841,7 +847,7 @@ def format_compact_currency(
     currency: str,
     *,
     format_type: Literal["short"] = "short",
-    locale: Locale | str | None = LC_NUMERIC,
+    locale: Locale | str | None = None,
     fraction_digits: int = 0,
     numbering_system: Literal["default"] | str = "latn",
 ) -> str:
@@ -857,13 +863,13 @@ def format_compact_currency(
     :param number: the number to format
     :param currency: the currency code
     :param format_type: the compact format type to use. Defaults to "short".
-    :param locale: the `Locale` object or locale identifier
+    :param locale: the `Locale` object or locale identifier. Defaults to the system numeric locale.
     :param fraction_digits: Number of digits after the decimal point to use. Defaults to `0`.
     :param numbering_system: The numbering system used for formatting number symbols. Defaults to "latn".
                              The special value "default" will use the default numbering system of the locale.
     :raise `UnsupportedNumberingSystemError`: If the numbering system is not supported by the locale.
     """
-    locale = Locale.parse(locale)
+    locale = Locale.parse(locale or LC_NUMERIC)
     try:
         compact_format = locale.compact_currency_formats[format_type]
     except KeyError as error:
@@ -891,7 +897,7 @@ def format_compact_currency(
 def format_percent(
     number: float | decimal.Decimal | str,
     format: str | NumberPattern | None = None,
-    locale: Locale | str | None = LC_NUMERIC,
+    locale: Locale | str | None = None,
     decimal_quantization: bool = True,
     group_separator: bool = True,
     *,
@@ -930,7 +936,7 @@ def format_percent(
 
     :param number: the percent number to format
     :param format:
-    :param locale: the `Locale` object or locale identifier
+    :param locale: the `Locale` object or locale identifier. Defaults to the system numeric locale.
     :param decimal_quantization: Truncate and round high-precision numbers to
                                  the format pattern. Defaults to `True`.
     :param group_separator: Boolean to switch group separator on/off in a locale's
@@ -939,7 +945,7 @@ def format_percent(
                              The special value "default" will use the default numbering system of the locale.
     :raise `UnsupportedNumberingSystemError`: If the numbering system is not supported by the locale.
     """
-    locale = Locale.parse(locale)
+    locale = Locale.parse(locale or LC_NUMERIC)
     if not format:
         format = locale.percent_formats[None]
     pattern = parse_pattern(format)
@@ -950,12 +956,12 @@ def format_percent(
 
 
 def format_scientific(
-        number: float | decimal.Decimal | str,
-        format: str | NumberPattern | None = None,
-        locale: Locale | str | None = LC_NUMERIC,
-        decimal_quantization: bool = True,
-        *,
-        numbering_system: Literal["default"] | str = "latn",
+    number: float | decimal.Decimal | str,
+    format: str | NumberPattern | None = None,
+    locale: Locale | str | None = None,
+    decimal_quantization: bool = True,
+    *,
+    numbering_system: Literal["default"] | str = "latn",
 ) -> str:
     """Return value formatted in scientific notation for a specific locale.
 
@@ -980,14 +986,14 @@ def format_scientific(
 
     :param number: the number to format
     :param format:
-    :param locale: the `Locale` object or locale identifier
+    :param locale: the `Locale` object or locale identifier. Defaults to the system numeric locale.
     :param decimal_quantization: Truncate and round high-precision numbers to
                                  the format pattern. Defaults to `True`.
     :param numbering_system: The numbering system used for formatting number symbols. Defaults to "latn".
                              The special value "default" will use the default numbering system of the locale.
     :raise `UnsupportedNumberingSystemError`: If the numbering system is not supported by the locale.
     """
-    locale = Locale.parse(locale)
+    locale = Locale.parse(locale or LC_NUMERIC)
     if not format:
         format = locale.scientific_formats[None]
     pattern = parse_pattern(format)
@@ -1015,7 +1021,7 @@ SPACE_CHARS_RE = re.compile('|'.join(SPACE_CHARS))
 
 def parse_number(
     string: str,
-    locale: Locale | str | None = LC_NUMERIC,
+    locale: Locale | str | None = None,
     *,
     numbering_system: Literal["default"] | str = "latn",
 ) -> int:
@@ -1034,7 +1040,7 @@ def parse_number(
     NumberFormatError: '1.099,98' is not a valid number
 
     :param string: the string to parse
-    :param locale: the `Locale` object or locale identifier
+    :param locale: the `Locale` object or locale identifier. Defaults to the system numeric locale.
     :param numbering_system: The numbering system used for formatting number symbols. Defaults to "latn".
                              The special value "default" will use the default numbering system of the locale.
     :return: the parsed number
@@ -1059,7 +1065,7 @@ def parse_number(
 
 def parse_decimal(
     string: str,
-    locale: Locale | str | None = LC_NUMERIC,
+    locale: Locale | str | None = None,
     strict: bool = False,
     *,
     numbering_system: Literal["default"] | str = "latn",
@@ -1096,7 +1102,7 @@ def parse_decimal(
     NumberFormatError: '0.00' is not a properly formatted decimal number. Did you mean '0'?
 
     :param string: the string to parse
-    :param locale: the `Locale` object or locale identifier
+    :param locale: the `Locale` object or locale identifier. Defaults to the system numeric locale.
     :param strict: controls whether numbers formatted in a weird way are
                    accepted or rejected
     :param numbering_system: The numbering system used for formatting number symbols. Defaults to "latn".
@@ -1105,7 +1111,7 @@ def parse_decimal(
                               decimal number
     :raise UnsupportedNumberingSystemError: if the numbering system is not supported by the locale.
     """
-    locale = Locale.parse(locale)
+    locale = Locale.parse(locale or LC_NUMERIC)
     group_symbol = get_group_symbol(locale, numbering_system=numbering_system)
     decimal_symbol = get_decimal_symbol(locale, numbering_system=numbering_system)
 
index 8dae6947e79f4468a16b10301e9a0f3db5e4a638..1ff71eb699fa8cc6a3a3678f9a0d09b37f7f6f69 100644 (file)
@@ -18,7 +18,7 @@ class UnknownUnitError(ValueError):
 def get_unit_name(
     measurement_unit: str,
     length: Literal['short', 'long', 'narrow'] = 'long',
-    locale: Locale | str | None = LC_NUMERIC,
+    locale: Locale | str | None = None,
 ) -> str | None:
     """
     Get the display name for a measurement unit in the given locale.
@@ -38,17 +38,17 @@ def get_unit_name(
                              https://unicode.org/repos/cldr/tags/latest/common/validity/unit.xml
 
     :param length: "short", "long" or "narrow"
-    :param locale: the `Locale` object or locale identifier
+    :param locale: the `Locale` object or locale identifier. Defaults to the system numeric locale.
     :return: The unit display name, or None.
     """
-    locale = Locale.parse(locale)
+    locale = Locale.parse(locale or LC_NUMERIC)
     unit = _find_unit_pattern(measurement_unit, locale=locale)
     if not unit:
         raise UnknownUnitError(unit=measurement_unit, locale=locale)
     return locale.unit_display_names.get(unit, {}).get(length)
 
 
-def _find_unit_pattern(unit_id: str, locale: Locale | str | None = LC_NUMERIC) -> str | None:
+def _find_unit_pattern(unit_id: str, locale: Locale | str | None = None) -> str | None:
     """
     Expand a unit into a qualified form.
 
@@ -65,7 +65,7 @@ def _find_unit_pattern(unit_id: str, locale: Locale | str | None = LC_NUMERIC) -
     :param unit_id: the code of a measurement unit.
     :return: A key to the `unit_patterns` mapping, or None.
     """
-    locale = Locale.parse(locale)
+    locale = Locale.parse(locale or LC_NUMERIC)
     unit_patterns = locale._data["unit_patterns"]
     if unit_id in unit_patterns:
         return unit_id
@@ -80,7 +80,7 @@ def format_unit(
     measurement_unit: str,
     length: Literal['short', 'long', 'narrow'] = 'long',
     format: str | None = None,
-    locale: Locale | str | None = LC_NUMERIC,
+    locale: Locale | str | None = None,
     *,
     numbering_system: Literal["default"] | str = "latn",
 ) -> str:
@@ -130,12 +130,12 @@ def format_unit(
                              https://unicode.org/repos/cldr/tags/latest/common/validity/unit.xml
     :param length: "short", "long" or "narrow"
     :param format: An optional format, as accepted by `format_decimal`.
-    :param locale: the `Locale` object or locale identifier
+    :param locale: the `Locale` object or locale identifier. Defaults to the system numeric locale.
     :param numbering_system: The numbering system used for formatting number symbols. Defaults to "latn".
                              The special value "default" will use the default numbering system of the locale.
     :raise `UnsupportedNumberingSystemError`: If the numbering system is not supported by the locale.
     """
-    locale = Locale.parse(locale)
+    locale = Locale.parse(locale or LC_NUMERIC)
 
     q_unit = _find_unit_pattern(measurement_unit, locale=locale)
     if not q_unit:
@@ -161,7 +161,7 @@ def format_unit(
 def _find_compound_unit(
     numerator_unit: str,
     denominator_unit: str,
-    locale: Locale | str | None = LC_NUMERIC,
+    locale: Locale | str | None = None,
 ) -> str | None:
     """
     Find a predefined compound unit pattern.
@@ -182,11 +182,11 @@ def _find_compound_unit(
 
     :param numerator_unit: The numerator unit's identifier
     :param denominator_unit: The denominator unit's identifier
-    :param locale: the `Locale` object or locale identifier
+    :param locale: the `Locale` object or locale identifier. Defaults to the system numeric locale.
     :return: A key to the `unit_patterns` mapping, or None.
     :rtype: str|None
     """
-    locale = Locale.parse(locale)
+    locale = Locale.parse(locale or LC_NUMERIC)
 
     # Qualify the numerator and denominator units.  This will turn possibly partial
     # units like "kilometer" or "hour" into actual units like "length-kilometer" and
@@ -217,7 +217,7 @@ def format_compound_unit(
     denominator_unit: str | None = None,
     length: Literal["short", "long", "narrow"] = "long",
     format: str | None = None,
-    locale: Locale | str | None = LC_NUMERIC,
+    locale: Locale | str | None = None,
     *,
     numbering_system: Literal["default"] | str = "latn",
 ) -> str | None:
@@ -265,13 +265,13 @@ def format_compound_unit(
     :param denominator_unit: The denominator unit. See `format_unit`.
     :param length: The formatting length. "short", "long" or "narrow"
     :param format: An optional format, as accepted by `format_decimal`.
-    :param locale: the `Locale` object or locale identifier
+    :param locale: the `Locale` object or locale identifier. Defaults to the system numeric locale.
     :param numbering_system: The numbering system used for formatting number symbols. Defaults to "latn".
                              The special value "default" will use the default numbering system of the locale.
     :return: A formatted compound value.
     :raise `UnsupportedNumberingSystemError`: If the numbering system is not supported by the locale.
     """
-    locale = Locale.parse(locale)
+    locale = Locale.parse(locale or LC_NUMERIC)
 
     # Look for a specific compound unit first...
 
index 106b83edf1df1cd931b7444db353cb6763bc2671..6828d2434fa413c0826ac4b251b61796c998fe93 100644 (file)
@@ -486,6 +486,12 @@ def test_format_currency():
             == 'US$0,00')          # other
 
 
+def test_format_currency_with_none_locale_with_default(monkeypatch):
+    """Test that the default locale is used when locale is None."""
+    monkeypatch.setattr(numbers, "LC_NUMERIC", "fi_FI")
+    assert numbers.format_currency(0, "USD", locale=None) == "0,00\xa0$"
+
+
 def test_format_currency_format_type():
     assert (numbers.format_currency(1099.98, 'USD', locale='en_US',
                                     format_type="standard")