2020import threading
2121from collections .abc import Iterator , Mapping
2222from itertools import chain
23- from typing import Any , MutableMapping , TypeVar
23+ from typing import TYPE_CHECKING , Any , MutableMapping , TypeVar
2424
2525_Key = TypeVar ('_Key' ,)
2626_Value = TypeVar ('_Value' )
2727
28+ if TYPE_CHECKING :
29+ from abc import abstractmethod
30+ from typing import runtime_checkable
31+
32+ @runtime_checkable
33+ class CopyableMutableMapping (MutableMapping [_Key , _Value ]):
34+ """An abstract base class for copyable mutable mappings."""
35+
36+ @abstractmethod
37+ def copy (self ) -> CopyableMutableMapping [_Key , _Value ]:
38+ ...
39+
2840_cache : dict [str , Any ] = {}
2941_cache_lock = threading .RLock ()
3042_dirname = os .path .join (os .path .dirname (__file__ ), 'locale-data' )
@@ -167,7 +179,7 @@ def merge(dict1: MutableMapping[Any, Any], dict2: Mapping[Any, Any]) -> None:
167179 for key , val2 in dict2 .items ():
168180 if val2 is not None :
169181 val1 = dict1 .get (key )
170- if isinstance (val2 , dict ):
182+ if isinstance (val2 , MutableMapping ):
171183 if val1 is None :
172184 val1 = {}
173185 if isinstance (val1 , Alias ):
@@ -198,7 +210,7 @@ def __init__(self, keys: tuple[str, ...]) -> None:
198210 def __repr__ (self ) -> str :
199211 return f"<{ type (self ).__name__ } { self .keys !r} >"
200212
201- def resolve (self , data : Mapping [_Key , _Value ]) -> dict [_Key , _Value ]:
213+ def resolve (self , data : CopyableMutableMapping [_Key , _Value ]) -> CopyableMutableMapping [_Key , _Value ]:
202214 """Resolve the alias based on the given data.
203215
204216 This is done recursively, so if one alias resolves to a second alias,
@@ -215,18 +227,18 @@ def resolve(self, data: Mapping[_Key, _Value]) -> dict[_Key, _Value]:
215227 elif isinstance (data , tuple ):
216228 alias , others = data
217229 data = alias .resolve (base )
218- return dict ( data )
230+ return data
219231
220232class LocaleDataDict (MutableMapping [_Key , _Value ]):
221233 """Dictionary wrapper that automatically resolves aliases to the actual
222234 values.
223235 """
224236
225- def __init__ (self , data : MutableMapping [_Key , _Value ], base : Mapping [_Key , _Value ] | None = None ):
226- self ._data = dict ( data ) # Storing as a dict allows copy() to work
237+ def __init__ (self , data : CopyableMutableMapping [_Key , _Value ], base : CopyableMutableMapping [_Key , _Value ] | None = None ):
238+ self ._data = data
227239 if base is None :
228240 base = data
229- self .base = dict ( base )
241+ self .base = base
230242
231243 def __len__ (self ) -> int :
232244 return len (self ._data )
@@ -242,8 +254,9 @@ def __getitem__(self, key: _Key) -> _Value:
242254 alias , others = val
243255 val = alias .resolve (self .base ).copy ()
244256 merge (val , others )
245- if isinstance (val , dict ): # Return a nested alias-resolving dict
246- val = LocaleDataDict (val , base = self .base )
257+ if isinstance (val , MutableMapping ) and not isinstance (val , LocaleDataDict ):
258+ # Return a nested alias-resolving dict
259+ val = LocaleDataDict (val , base = self .base ) # type: ignore # assume copyable
247260 if val is not orig :
248261 self ._data [key ] = val # type: ignore # Cache the resolved value
249262 return val # type: ignore
0 commit comments