210 lines
6.1 KiB
Python
210 lines
6.1 KiB
Python
|
# -*- coding: utf-8 -*-
|
||
|
"""
|
||
|
babel.localedata
|
||
|
~~~~~~~~~~~~~~~~
|
||
|
|
||
|
Low-level locale data access.
|
||
|
|
||
|
:note: The `Locale` class, which uses this module under the hood, provides a
|
||
|
more convenient interface for accessing the locale data.
|
||
|
|
||
|
:copyright: (c) 2013 by the Babel Team.
|
||
|
:license: BSD, see LICENSE for more details.
|
||
|
"""
|
||
|
|
||
|
import os
|
||
|
import threading
|
||
|
from collections import MutableMapping
|
||
|
|
||
|
from babel._compat import pickle
|
||
|
|
||
|
|
||
|
_cache = {}
|
||
|
_cache_lock = threading.RLock()
|
||
|
_dirname = os.path.join(os.path.dirname(__file__), 'localedata')
|
||
|
|
||
|
|
||
|
def exists(name):
|
||
|
"""Check whether locale data is available for the given locale. Ther
|
||
|
return value is `True` if it exists, `False` otherwise.
|
||
|
|
||
|
:param name: the locale identifier string
|
||
|
"""
|
||
|
if name in _cache:
|
||
|
return True
|
||
|
return os.path.exists(os.path.join(_dirname, '%s.dat' % name))
|
||
|
|
||
|
|
||
|
def locale_identifiers():
|
||
|
"""Return a list of all locale identifiers for which locale data is
|
||
|
available.
|
||
|
|
||
|
.. versionadded:: 0.8.1
|
||
|
|
||
|
:return: a list of locale identifiers (strings)
|
||
|
"""
|
||
|
return [stem for stem, extension in [
|
||
|
os.path.splitext(filename) for filename in os.listdir(_dirname)
|
||
|
] if extension == '.dat' and stem != 'root']
|
||
|
|
||
|
|
||
|
def load(name, merge_inherited=True):
|
||
|
"""Load the locale data for the given locale.
|
||
|
|
||
|
The locale data is a dictionary that contains much of the data defined by
|
||
|
the Common Locale Data Repository (CLDR). This data is stored as a
|
||
|
collection of pickle files inside the ``babel`` package.
|
||
|
|
||
|
>>> d = load('en_US')
|
||
|
>>> d['languages']['sv']
|
||
|
u'Swedish'
|
||
|
|
||
|
Note that the results are cached, and subsequent requests for the same
|
||
|
locale return the same dictionary:
|
||
|
|
||
|
>>> d1 = load('en_US')
|
||
|
>>> d2 = load('en_US')
|
||
|
>>> d1 is d2
|
||
|
True
|
||
|
|
||
|
:param name: the locale identifier string (or "root")
|
||
|
:param merge_inherited: whether the inherited data should be merged into
|
||
|
the data of the requested locale
|
||
|
:raise `IOError`: if no locale data file is found for the given locale
|
||
|
identifer, or one of the locales it inherits from
|
||
|
"""
|
||
|
_cache_lock.acquire()
|
||
|
try:
|
||
|
data = _cache.get(name)
|
||
|
if not data:
|
||
|
# Load inherited data
|
||
|
if name == 'root' or not merge_inherited:
|
||
|
data = {}
|
||
|
else:
|
||
|
parts = name.split('_')
|
||
|
if len(parts) == 1:
|
||
|
parent = 'root'
|
||
|
else:
|
||
|
parent = '_'.join(parts[:-1])
|
||
|
data = load(parent).copy()
|
||
|
filename = os.path.join(_dirname, '%s.dat' % name)
|
||
|
fileobj = open(filename, 'rb')
|
||
|
try:
|
||
|
if name != 'root' and merge_inherited:
|
||
|
merge(data, pickle.load(fileobj))
|
||
|
else:
|
||
|
data = pickle.load(fileobj)
|
||
|
_cache[name] = data
|
||
|
finally:
|
||
|
fileobj.close()
|
||
|
return data
|
||
|
finally:
|
||
|
_cache_lock.release()
|
||
|
|
||
|
|
||
|
def merge(dict1, dict2):
|
||
|
"""Merge the data from `dict2` into the `dict1` dictionary, making copies
|
||
|
of nested dictionaries.
|
||
|
|
||
|
>>> d = {1: 'foo', 3: 'baz'}
|
||
|
>>> merge(d, {1: 'Foo', 2: 'Bar'})
|
||
|
>>> items = d.items(); items.sort(); items
|
||
|
[(1, 'Foo'), (2, 'Bar'), (3, 'baz')]
|
||
|
|
||
|
:param dict1: the dictionary to merge into
|
||
|
:param dict2: the dictionary containing the data that should be merged
|
||
|
"""
|
||
|
for key, val2 in dict2.items():
|
||
|
if val2 is not None:
|
||
|
val1 = dict1.get(key)
|
||
|
if isinstance(val2, dict):
|
||
|
if val1 is None:
|
||
|
val1 = {}
|
||
|
if isinstance(val1, Alias):
|
||
|
val1 = (val1, val2)
|
||
|
elif isinstance(val1, tuple):
|
||
|
alias, others = val1
|
||
|
others = others.copy()
|
||
|
merge(others, val2)
|
||
|
val1 = (alias, others)
|
||
|
else:
|
||
|
val1 = val1.copy()
|
||
|
merge(val1, val2)
|
||
|
else:
|
||
|
val1 = val2
|
||
|
dict1[key] = val1
|
||
|
|
||
|
|
||
|
class Alias(object):
|
||
|
"""Representation of an alias in the locale data.
|
||
|
|
||
|
An alias is a value that refers to some other part of the locale data,
|
||
|
as specified by the `keys`.
|
||
|
"""
|
||
|
|
||
|
def __init__(self, keys):
|
||
|
self.keys = tuple(keys)
|
||
|
|
||
|
def __repr__(self):
|
||
|
return '<%s %r>' % (type(self).__name__, self.keys)
|
||
|
|
||
|
def resolve(self, data):
|
||
|
"""Resolve the alias based on the given data.
|
||
|
|
||
|
This is done recursively, so if one alias resolves to a second alias,
|
||
|
that second alias will also be resolved.
|
||
|
|
||
|
:param data: the locale data
|
||
|
:type data: `dict`
|
||
|
"""
|
||
|
base = data
|
||
|
for key in self.keys:
|
||
|
data = data[key]
|
||
|
if isinstance(data, Alias):
|
||
|
data = data.resolve(base)
|
||
|
elif isinstance(data, tuple):
|
||
|
alias, others = data
|
||
|
data = alias.resolve(base)
|
||
|
return data
|
||
|
|
||
|
|
||
|
class LocaleDataDict(MutableMapping):
|
||
|
"""Dictionary wrapper that automatically resolves aliases to the actual
|
||
|
values.
|
||
|
"""
|
||
|
|
||
|
def __init__(self, data, base=None):
|
||
|
self._data = data
|
||
|
if base is None:
|
||
|
base = data
|
||
|
self.base = base
|
||
|
|
||
|
def __len__(self):
|
||
|
return len(self._data)
|
||
|
|
||
|
def __iter__(self):
|
||
|
return iter(self._data)
|
||
|
|
||
|
def __getitem__(self, key):
|
||
|
orig = val = self._data[key]
|
||
|
if isinstance(val, Alias): # resolve an alias
|
||
|
val = val.resolve(self.base)
|
||
|
if isinstance(val, tuple): # Merge a partial dict with an alias
|
||
|
alias, others = val
|
||
|
val = alias.resolve(self.base).copy()
|
||
|
merge(val, others)
|
||
|
if type(val) is dict: # Return a nested alias-resolving dict
|
||
|
val = LocaleDataDict(val, base=self.base)
|
||
|
if val is not orig:
|
||
|
self._data[key] = val
|
||
|
return val
|
||
|
|
||
|
def __setitem__(self, key, value):
|
||
|
self._data[key] = value
|
||
|
|
||
|
def __delitem__(self, key):
|
||
|
del self._data[key]
|
||
|
|
||
|
def copy(self):
|
||
|
return LocaleDataDict(self._data.copy(), base=self.base)
|