1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
|
#
# Copyright (C) 2007-2011 Edgewall Software, 2013-2025 the Babel team
# All rights reserved.
#
# This software is licensed as described in the file LICENSE, which
# you should have received as part of this distribution. The terms
# are also available at https://github.com/python-babel/babel/blob/master/LICENSE.
#
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at https://github.com/python-babel/babel/commits/master/.
import os
import pickle
import random
import sys
import tempfile
import unittest
from operator import methodcaller
import pytest
from babel import Locale, UnknownLocaleError, localedata
class MergeResolveTestCase(unittest.TestCase):
def test_merge_items(self):
d = {1: 'foo', 3: 'baz'}
localedata.merge(d, {1: 'Foo', 2: 'Bar'})
assert d == {1: 'Foo', 2: 'Bar', 3: 'baz'}
def test_merge_nested_dict(self):
d1 = {'x': {'a': 1, 'b': 2, 'c': 3}}
d2 = {'x': {'a': 1, 'b': 12, 'd': 14}}
localedata.merge(d1, d2)
assert d1 == {'x': {'a': 1, 'b': 12, 'c': 3, 'd': 14}}
def test_merge_nested_dict_no_overlap(self):
d1 = {'x': {'a': 1, 'b': 2}}
d2 = {'y': {'a': 11, 'b': 12}}
localedata.merge(d1, d2)
assert d1 == {'x': {'a': 1, 'b': 2}, 'y': {'a': 11, 'b': 12}}
def test_merge_with_alias_and_resolve(self):
alias = localedata.Alias('x')
d1 = {
'x': {'a': 1, 'b': 2, 'c': 3},
'y': alias,
}
d2 = {
'x': {'a': 1, 'b': 12, 'd': 14},
'y': {'b': 22, 'e': 25},
}
localedata.merge(d1, d2)
assert d1 == {'x': {'a': 1, 'b': 12, 'c': 3, 'd': 14}, 'y': (alias, {'b': 22, 'e': 25})}
d = localedata.LocaleDataDict(d1)
assert dict(d.items()) == {'x': {'a': 1, 'b': 12, 'c': 3, 'd': 14}, 'y': {'a': 1, 'b': 22, 'c': 3, 'd': 14, 'e': 25}}
def test_load():
assert localedata.load('en_US')['languages']['sv'] == 'Swedish'
assert localedata.load('en_US') is localedata.load('en_US')
def test_load_inheritance(monkeypatch):
from babel.localedata import _cache
_cache.clear()
localedata.load('hi_Latn')
# Must not be ['root', 'hi_Latn'] even though 'hi_Latn' matches the 'lang_Script'
# form used by 'nonLikelyScripts'. This is because 'hi_Latn' has an explicit parent locale 'en_IN'.
assert list(_cache.keys()) == ['root', 'en', 'en_001', 'en_IN', 'hi_Latn']
_cache.clear()
localedata.load('az_Arab')
# Must not include 'az' as 'Arab' is not a likely script for 'az'.
assert list(_cache.keys()) == ['root', 'az_Arab']
def test_merge():
d = {1: 'foo', 3: 'baz'}
localedata.merge(d, {1: 'Foo', 2: 'Bar'})
assert d == {1: 'Foo', 2: 'Bar', 3: 'baz'}
def test_locale_identification():
for locale in localedata.locale_identifiers():
assert localedata.exists(locale)
def test_unique_ids():
# Check all locale IDs are uniques.
all_ids = localedata.locale_identifiers()
assert len(all_ids) == len(set(all_ids))
# Check locale IDs don't collide after lower-case normalization.
lower_case_ids = list(map(methodcaller('lower'), all_ids))
assert len(lower_case_ids) == len(set(lower_case_ids))
def test_mixedcased_locale():
for locale in localedata.locale_identifiers():
locale_id = ''.join([
methodcaller(random.choice(['lower', 'upper']))(c) for c in locale])
assert localedata.exists(locale_id)
def test_locale_argument_acceptance():
# Testing None input.
normalized_locale = localedata.normalize_locale(None)
assert normalized_locale is None
assert not localedata.exists(None)
# Testing list input.
normalized_locale = localedata.normalize_locale(['en_us', None])
assert normalized_locale is None
assert not localedata.exists(['en_us', None])
def test_locale_identifiers_cache(monkeypatch):
original_listdir = localedata.os.listdir
listdir_calls = []
def listdir_spy(*args):
rv = original_listdir(*args)
listdir_calls.append((args, rv))
return rv
monkeypatch.setattr(localedata.os, 'listdir', listdir_spy)
localedata.locale_identifiers.cache_clear()
assert not listdir_calls
l = localedata.locale_identifiers()
assert len(listdir_calls) == 1
assert localedata.locale_identifiers() is l
assert len(listdir_calls) == 1
localedata.locale_identifiers.cache_clear()
assert localedata.locale_identifiers()
assert len(listdir_calls) == 2
def test_locale_name_cleanup():
"""
Test that locale identifiers are cleaned up to avoid directory traversal.
"""
no_exist_name = os.path.join(tempfile.gettempdir(), "babel%d.dat" % random.randint(1, 99999))
with open(no_exist_name, "wb") as f:
pickle.dump({}, f)
try:
name = os.path.splitext(os.path.relpath(no_exist_name, localedata._dirname))[0]
except ValueError:
if sys.platform == "win32":
pytest.skip("unable to form relpath")
raise
assert not localedata.exists(name)
with pytest.raises(IOError):
localedata.load(name)
with pytest.raises(UnknownLocaleError):
Locale(name)
@pytest.mark.skipif(sys.platform != "win32", reason="windows-only test")
def test_reserved_locale_names():
for name in ("con", "aux", "nul", "prn", "com8", "lpt5"):
with pytest.raises(ValueError):
localedata.load(name)
with pytest.raises(ValueError):
Locale(name)
|