Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
localedata.py255 linesDownload Raw Back to babel
1"""2    babel.localedata3    ~~~~~~~~~~~~~~~~4 5    Low-level locale data access.6 7    :note: The `Locale` class, which uses this module under the hood, provides a8           more convenient interface for accessing the locale data.9 10    :copyright: (c) 2013-2024 by the Babel Team.11    :license: BSD, see LICENSE for more details.12"""13 14from __future__ import annotations15 16import os17import pickle18import re19import sys20import threading21from collections import abc22from collections.abc import Iterator, Mapping, MutableMapping23from functools import lru_cache24from itertools import chain25from typing import Any26 27_cache: dict[str, Any] = {}28_cache_lock = threading.RLock()29_dirname = os.path.join(os.path.dirname(__file__), 'locale-data')30_windows_reserved_name_re = re.compile("^(con|prn|aux|nul|com[0-9]|lpt[0-9])$", re.I)31 32 33def normalize_locale(name: str) -> str | None:34    """Normalize a locale ID by stripping spaces and apply proper casing.35 36    Returns the normalized locale ID string or `None` if the ID is not37    recognized.38    """39    if not name or not isinstance(name, str):40        return None41    name = name.strip().lower()42    for locale_id in chain.from_iterable([_cache, locale_identifiers()]):43        if name == locale_id.lower():44            return locale_id45 46 47def resolve_locale_filename(name: os.PathLike[str] | str) -> str:48    """49    Resolve a locale identifier to a `.dat` path on disk.50    """51 52    # Clean up any possible relative paths.53    name = os.path.basename(name)54 55    # Ensure we're not left with one of the Windows reserved names.56    if sys.platform == "win32" and _windows_reserved_name_re.match(os.path.splitext(name)[0]):57        raise ValueError(f"Name {name} is invalid on Windows")58 59    # Build the path.60    return os.path.join(_dirname, f"{name}.dat")61 62 63def exists(name: str) -> bool:64    """Check whether locale data is available for the given locale.65 66    Returns `True` if it exists, `False` otherwise.67 68    :param name: the locale identifier string69    """70    if not name or not isinstance(name, str):71        return False72    if name in _cache:73        return True74    file_found = os.path.exists(resolve_locale_filename(name))75    return True if file_found else bool(normalize_locale(name))76 77 78@lru_cache(maxsize=None)79def locale_identifiers() -> list[str]:80    """Return a list of all locale identifiers for which locale data is81    available.82 83    This data is cached after the first invocation.84    You can clear the cache by calling `locale_identifiers.cache_clear()`.85 86    .. versionadded:: 0.8.187 88    :return: a list of locale identifiers (strings)89    """90    return [91        stem92        for stem, extension in93        (os.path.splitext(filename) for filename in os.listdir(_dirname))94        if extension == '.dat' and stem != 'root'95    ]96 97 98def load(name: os.PathLike[str] | str, merge_inherited: bool = True) -> dict[str, Any]:99    """Load the locale data for the given locale.100 101    The locale data is a dictionary that contains much of the data defined by102    the Common Locale Data Repository (CLDR). This data is stored as a103    collection of pickle files inside the ``babel`` package.104 105    >>> d = load('en_US')106    >>> d['languages']['sv']107    u'Swedish'108 109    Note that the results are cached, and subsequent requests for the same110    locale return the same dictionary:111 112    >>> d1 = load('en_US')113    >>> d2 = load('en_US')114    >>> d1 is d2115    True116 117    :param name: the locale identifier string (or "root")118    :param merge_inherited: whether the inherited data should be merged into119                            the data of the requested locale120    :raise `IOError`: if no locale data file is found for the given locale121                      identifier, or one of the locales it inherits from122    """123    name = os.path.basename(name)124    _cache_lock.acquire()125    try:126        data = _cache.get(name)127        if not data:128            # Load inherited data129            if name == 'root' or not merge_inherited:130                data = {}131            else:132                from babel.core import get_global133                parent = get_global('parent_exceptions').get(name)134                if not parent:135                    parts = name.split('_')136                    parent = "root" if len(parts) == 1 else "_".join(parts[:-1])137                data = load(parent).copy()138            filename = resolve_locale_filename(name)139            with open(filename, 'rb') as fileobj:140                if name != 'root' and merge_inherited:141                    merge(data, pickle.load(fileobj))142                else:143                    data = pickle.load(fileobj)144            _cache[name] = data145        return data146    finally:147        _cache_lock.release()148 149 150def merge(dict1: MutableMapping[Any, Any], dict2: Mapping[Any, Any]) -> None:151    """Merge the data from `dict2` into the `dict1` dictionary, making copies152    of nested dictionaries.153 154    >>> d = {1: 'foo', 3: 'baz'}155    >>> merge(d, {1: 'Foo', 2: 'Bar'})156    >>> sorted(d.items())157    [(1, 'Foo'), (2, 'Bar'), (3, 'baz')]158 159    :param dict1: the dictionary to merge into160    :param dict2: the dictionary containing the data that should be merged161    """162    for key, val2 in dict2.items():163        if val2 is not None:164            val1 = dict1.get(key)165            if isinstance(val2, dict):166                if val1 is None:167                    val1 = {}168                if isinstance(val1, Alias):169                    val1 = (val1, val2)170                elif isinstance(val1, tuple):171                    alias, others = val1172                    others = others.copy()173                    merge(others, val2)174                    val1 = (alias, others)175                else:176                    val1 = val1.copy()177                    merge(val1, val2)178            else:179                val1 = val2180            dict1[key] = val1181 182 183class Alias:184    """Representation of an alias in the locale data.185 186    An alias is a value that refers to some other part of the locale data,187    as specified by the `keys`.188    """189 190    def __init__(self, keys: tuple[str, ...]) -> None:191        self.keys = tuple(keys)192 193    def __repr__(self) -> str:194        return f"<{type(self).__name__} {self.keys!r}>"195 196    def resolve(self, data: Mapping[str | int | None, Any]) -> Mapping[str | int | None, Any]:197        """Resolve the alias based on the given data.198 199        This is done recursively, so if one alias resolves to a second alias,200        that second alias will also be resolved.201 202        :param data: the locale data203        :type data: `dict`204        """205        base = data206        for key in self.keys:207            data = data[key]208        if isinstance(data, Alias):209            data = data.resolve(base)210        elif isinstance(data, tuple):211            alias, others = data212            data = alias.resolve(base)213        return data214 215 216class LocaleDataDict(abc.MutableMapping):217    """Dictionary wrapper that automatically resolves aliases to the actual218    values.219    """220 221    def __init__(self, data: MutableMapping[str | int | None, Any], base: Mapping[str | int | None, Any] | None = None):222        self._data = data223        if base is None:224            base = data225        self.base = base226 227    def __len__(self) -> int:228        return len(self._data)229 230    def __iter__(self) -> Iterator[str | int | None]:231        return iter(self._data)232 233    def __getitem__(self, key: str | int | None) -> Any:234        orig = val = self._data[key]235        if isinstance(val, Alias):  # resolve an alias236            val = val.resolve(self.base)237        if isinstance(val, tuple):  # Merge a partial dict with an alias238            alias, others = val239            val = alias.resolve(self.base).copy()240            merge(val, others)241        if isinstance(val, dict):  # Return a nested alias-resolving dict242            val = LocaleDataDict(val, base=self.base)243        if val is not orig:244            self._data[key] = val245        return val246 247    def __setitem__(self, key: str | int | None, value: Any) -> None:248        self._data[key] = value249 250    def __delitem__(self, key: str | int | None) -> None:251        del self._data[key]252 253    def copy(self) -> LocaleDataDict:254        return LocaleDataDict(self._data.copy(), base=self.base)255 
codekingpro/portable-devtools · Team Ai