Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion babel/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1372,7 +1372,7 @@ def get_cldr_version() -> str:
function is a string representing a version number, e.g. '47'.

>>> get_cldr_version()
'47'
'48'

.. versionadded:: 2.18

Expand Down
2 changes: 1 addition & 1 deletion babel/numbers.py
Original file line number Diff line number Diff line change
Expand Up @@ -741,7 +741,7 @@ def format_currency(
>>> format_currency(1099.98, 'JPY', locale='en_US')
'\\xa51,100'
>>> format_currency(1099.98, 'COP', '#,##0.00', locale='es_ES')
'1.099,98'
'1.100'
However, the number of decimal digits can be overridden from the currency
information, by setting the last parameter to ``False``:
Expand Down
24 changes: 24 additions & 0 deletions babel/units.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
from __future__ import annotations

import decimal
import warnings
from typing import Literal

from babel.core import Locale
from babel.numbers import LC_NUMERIC, format_decimal

_DEPRECATED_UNIT_IDS: dict[str, str] = {
# Unit IDs deprecated in CLDR 48
"concentr-permillion": "concentr-part-per-1e6",
"concentr-portion": "concentr-part",
"concentr-portion-per-1e9": "concentr-part-per-1e9",
"permillion": "part-per-1e6",
"portion": "part",
"portion-per-1e9": "part-per-1e9",
}


class UnknownUnitError(ValueError):
def __init__(self, unit: str, locale: Locale) -> None:
Expand Down Expand Up @@ -66,6 +77,19 @@ def _find_unit_pattern(unit_id: str, locale: Locale | str | None = None) -> str
unit_patterns: dict[str, str] = locale._data["unit_patterns"]
if unit_id in unit_patterns:
return unit_id

# Only if a direct hit didn't work, try deprecated units...
new_id = _DEPRECATED_UNIT_IDS.get(unit_id)
if new_id is not None:
warnings.warn(
f"Unit identifier {unit_id!r} is deprecated; use {new_id!r} instead.",
DeprecationWarning,
stacklevel=4,
)
if new_id in unit_patterns:
return new_id

# ... and finally fuzzy matching.
for unit_pattern in sorted(unit_patterns, key=len):
if unit_pattern.endswith(unit_id):
return unit_pattern
Expand Down
8 changes: 4 additions & 4 deletions scripts/download_import_cldr.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
import zipfile
from urllib.request import Request, urlopen

URL = 'https://unicode.org/Public/cldr/47/cldr-common-47.zip'
FILENAME = 'cldr-common-47.0.zip'
# Via https://unicode.org/Public/cldr/45/hashes/SHASUM512.txt
FILESUM = '3b1eb2a046dae23cf16f611f452833e2a95affb1aa2ae3fa599753d229d152577114c2ff44ca98a7f369fa41dc6f45b0d7a6647653ca79694aacfd3f3be59801'
URL = 'https://unicode.org/Public/cldr/48.2/cldr-common-48.2.zip'
FILENAME = 'cldr-common-48.2.zip'
# Via https://unicode.org/Public/cldr/48.2/hashes/SHASUM512.txt (core.zip; see https://unicode-org.atlassian.net/browse/CLDR-19388)
FILESUM = 'de8660f5371e0fcfd03a42e3b4fc4c686ec6cd602b402f1e3d227844005a54eb7952873894443523837d5828c42874a1a267a19f91ded207a2d166144791fa62'


def reporthook(bytes_transmitted, total_size):
Expand Down
39 changes: 11 additions & 28 deletions scripts/import_cldr.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import pickle
import re
import sys
import warnings
from optparse import OptionParser
from xml.etree import ElementTree

Expand Down Expand Up @@ -61,23 +62,6 @@ def _text(elem):
log = logging.getLogger("import_cldr")


def need_conversion(dst_filename, data_dict, source_filename):
with open(source_filename, 'rb') as f:
blob = f.read(4096)
version_match = re.search(b'version number="\\$Revision: (\\d+)', blob)
if not version_match: # CLDR 36.0 was shipped without proper revision numbers
return True
version = int(version_match.group(1))

data_dict['_version'] = version
if not os.path.isfile(dst_filename):
return True

with open(dst_filename, 'rb') as f:
data = pickle.load(f)
return data.get('_version') != version


def _translate_alias(ctxt, path):
parts = path.split('/')
keys = ctxt[:]
Expand Down Expand Up @@ -164,7 +148,7 @@ def main():
parser = OptionParser(usage='%prog path/to/cldr')
parser.add_option(
'-f', '--force', dest='force', action='store_true', default=False,
help='force import even if destination file seems up to date',
help='legacy option; conversion is always done',
)
parser.add_option(
'-j', '--json', dest='dump_json', action='store_true', default=False,
Expand All @@ -186,22 +170,21 @@ def main():
return process_data(
srcdir=args[0],
destdir=BABEL_PACKAGE_ROOT,
force=bool(options.force),
dump_json=bool(options.dump_json),
)


def process_data(srcdir, destdir, force=False, dump_json=False):
def process_data(srcdir, destdir, force=True, dump_json=False):
if not force:
warnings.warn("Setting `force=False` has no effect.", DeprecationWarning, stacklevel=2)
sup_filename = os.path.join(srcdir, 'supplemental', 'supplementalData.xml')
sup = parse(sup_filename)

# Import global data from the supplemental files
global_path = os.path.join(destdir, 'global.dat')
global_data = {}
if force or need_conversion(global_path, global_data, sup_filename):
global_data.update(parse_global(srcdir, sup))
write_datafile(global_path, global_data, dump_json=dump_json)
_process_local_datas(sup, srcdir, destdir, force=force, dump_json=dump_json)
global_data = parse_global(srcdir, sup)
write_datafile(global_path, global_data, dump_json=dump_json)
_process_local_datas(sup, srcdir, destdir, dump_json=dump_json)


def parse_global(srcdir, sup):
Expand Down Expand Up @@ -366,7 +349,9 @@ def parse_global(srcdir, sup):
return global_data


def _process_local_datas(sup, srcdir, destdir, force=False, dump_json=False):
def _process_local_datas(sup, srcdir, destdir, force=True, dump_json=False):
if not force:
warnings.warn("Setting `force=False` has no effect.", DeprecationWarning, stacklevel=2)
day_period_rules = parse_day_period_rules(parse(os.path.join(srcdir, 'supplemental', 'dayPeriods.xml')))
# build a territory containment mapping for inheritance
regions = {}
Expand Down Expand Up @@ -401,8 +386,6 @@ def _process_local_datas(sup, srcdir, destdir, force=False, dump_json=False):
data_filename = os.path.join(destdir, "locale-data", f"{stem}.dat")

data = {}
if not (force or need_conversion(data_filename, data, full_filename)):
continue

tree = parse(full_filename)

Expand Down
2 changes: 1 addition & 1 deletion tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -413,4 +413,4 @@ def test_locale_parse_empty():


def test_get_cldr_version():
assert core.get_cldr_version() == "47"
assert core.get_cldr_version() == "48"
8 changes: 4 additions & 4 deletions tests/test_dates.py
Original file line number Diff line number Diff line change
Expand Up @@ -613,10 +613,10 @@ def test_format_time(timezone_getter):
def test_format_skeleton(timezone_getter):
dt = datetime(2007, 4, 1, 15, 30)
assert (dates.format_skeleton('yMEd', dt, locale='en_US') == 'Sun, 4/1/2007')
assert (dates.format_skeleton('yMEd', dt, locale='th') == 'อา. 1/4/2007')
assert (dates.format_skeleton('yMEd', dt, locale='th') == 'อาทิตย์ 1/4/2007')

assert (dates.format_skeleton('EHm', dt, locale='en') == 'Sun 15:30')
assert (dates.format_skeleton('EHm', dt, tzinfo=timezone_getter('Asia/Bangkok'), locale='th') == 'อา. 22:30 น.')
assert (dates.format_skeleton('EHm', dt, tzinfo=timezone_getter('Asia/Bangkok'), locale='th') == 'อาทิตย์ 22:30 น.')


@pytest.mark.parametrize(('skeleton', 'expected'), [
Expand Down Expand Up @@ -1194,8 +1194,8 @@ def test_issue_1192():
# The actual returned value here is not actually strictly specified ("get_timezone_name"
# is not an operation specified as such). Issue #1192 concerned this invocation returning
# the invalid "no inheritance marker" value; _that_ should never be returned here.
# IOW, if the below "Hawaii-Aleutian Time" changes with e.g. CLDR updates, that's fine.
assert dates.get_timezone_name('Pacific/Honolulu', 'short', locale='en_GB') == "Hawaii-Aleutian Time"
# IOW, if the below "United States (Honolulu) Time" changes with e.g. CLDR updates, that's fine.
assert dates.get_timezone_name('Pacific/Honolulu', 'short', locale='en_GB') == "United States (Honolulu) Time"


@pytest.mark.xfail
Expand Down
2 changes: 1 addition & 1 deletion tests/test_lists.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
(['string1', 'string2'], 'en', 'string1 and string2'),
(['string1', 'string2', 'string3'], 'en', 'string1, string2, and string3'),
(['string1', 'string2', 'string3'], 'zh', 'string1、string2和string3'),
(['string1', 'string2', 'string3', 'string4'], 'ne', 'string1,string2, string3 र string4'),
(['string1', 'string2', 'string3', 'string4'], 'ne', 'string1, string2, string3 र string4'),
])
def test_format_list(list, locale, expected):
assert lists.format_list(list, locale=locale) == expected
Expand Down
6 changes: 3 additions & 3 deletions tests/test_numbers.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def test_list_currencies():

assert list_currencies(locale='pa_Arab') == {'PKR', 'INR', 'EUR'}

assert len(list_currencies()) == 307
assert len(list_currencies()) == 308


def test_validate_currency():
Expand Down Expand Up @@ -297,7 +297,7 @@ def test_format_currency_format_type():
assert (numbers.format_currency(1099.98, 'JPY', locale='en_US')
== '\xa51,100')
assert (numbers.format_currency(1099.98, 'COP', '#,##0.00', locale='es_ES')
== '1.099,98')
== '1.100')
assert (numbers.format_currency(1099.98, 'JPY', locale='en_US',
currency_digits=False)
== '\xa51,099.98')
Expand All @@ -320,7 +320,7 @@ def test_format_compact_currency():
assert numbers.format_compact_currency(123, 'EUR', locale='yav', format_type="short") == '€\xa0123'
assert numbers.format_compact_currency(12345, 'EUR', locale='yav', format_type="short") == '€\xa012K'
assert numbers.format_compact_currency(123456789, 'EUR', locale='de_DE', fraction_digits=1) == '123,5\xa0Mio.\xa0€'
assert numbers.format_compact_currency(123456789, 'USD', locale='ar_EG', fraction_digits=2, format_type="short", numbering_system="default") == '123٫46\xa0مليون\xa0US$'
assert numbers.format_compact_currency(123456789, 'USD', locale='ar_EG', fraction_digits=2, format_type="short", numbering_system="default") == '\u200f123٫46\xa0مليون\xa0US$'


def test_format_compact_currency_invalid_format_type():
Expand Down
2 changes: 1 addition & 1 deletion tests/test_support_format.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def test_format_currency(ar_eg_format, en_us_format):
def test_format_compact_currency(ar_eg_format, en_us_format):
assert en_us_format.compact_currency(1099.98, 'USD') == '$1K'
assert en_us_format.compact_currency(Decimal("1099.98"), 'USD') == '$1K'
assert ar_eg_format.compact_currency(1099.98, 'EGP') == '1\xa0ألف\xa0ج.م.\u200f'
assert ar_eg_format.compact_currency(1099.98, 'EGP') == '\u200f1\xa0ألف\xa0ج.م.\u200f'


def test_format_percent(ar_eg_format, en_us_format):
Expand Down
12 changes: 9 additions & 3 deletions tests/test_units.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
('speed-light-speed', 1, '1 světlo'),
('speed-light-speed', 2, '2 světla'),
('speed-light-speed', 5, '5 světel'),
('concentr-portion-per-1e9', 1, '1 částice na miliardu'),
('concentr-portion-per-1e9', 2, '2 částice na miliardu'),
('concentr-portion-per-1e9', 5, '5 částic na miliardu'),
('concentr-part-per-1e9', 1, '1 částice na miliardu'),
('concentr-part-per-1e9', 2, '2 částice na miliardu'),
('concentr-part-per-1e9', 5, '5 částic na miliardu'),
('duration-night', 1, '1 noc'),
('duration-night', 2, '2 noci'),
('duration-night', 5, '5 nocí'),
Expand All @@ -29,3 +29,9 @@ def test_new_cldr46_units(unit, count, expected):
])
def test_issue_1217(count, unit, locale, length, expected):
assert format_unit(count, unit, length, locale=locale) == expected


def test_deprecated_unit_ids():
for id in ("concentr-permillion", "concentr-portion", "concentr-portion-per-1e9"):
with pytest.warns(DeprecationWarning, match=id):
format_unit(1, id, locale='en')
Comment thread
akx marked this conversation as resolved.
Loading