2121from collections .abc import Iterator , Mapping , MutableMapping
2222from functools import lru_cache
2323from itertools import chain
24- from typing import Any , MutableMapping , TypeVar
24+ from typing import TYPE_CHECKING , Any , MutableMapping , TypeVar
2525
2626_Key = TypeVar ('_Key' ,)
2727_Value = TypeVar ('_Value' )
2828
29+ if TYPE_CHECKING :
30+ from abc import abstractmethod
31+ from typing import runtime_checkable
32+
33+ @runtime_checkable
34+ class CopyableMutableMapping (MutableMapping [_Key , _Value ]):
35+ """An abstract base class for copyable mutable mappings."""
36+
37+ @abstractmethod
38+ def copy (self ) -> CopyableMutableMapping [_Key , _Value ]:
39+ ...
40+
2941_cache : dict [str , Any ] = {}
3042_cache_lock = threading .RLock ()
3143_dirname = os .path .join (os .path .dirname (__file__ ), 'locale-data' )
@@ -164,7 +176,7 @@ def merge(dict1: MutableMapping[Any, Any], dict2: Mapping[Any, Any]) -> None:
164176 for key , val2 in dict2 .items ():
165177 if val2 is not None :
166178 val1 = dict1 .get (key )
167- if isinstance (val2 , dict ):
179+ if isinstance (val2 , MutableMapping ):
168180 if val1 is None :
169181 val1 = {}
170182 if isinstance (val1 , Alias ):
@@ -195,7 +207,7 @@ def __init__(self, keys: tuple[str, ...]) -> None:
195207 def __repr__ (self ) -> str :
196208 return f"<{ type (self ).__name__ } { self .keys !r} >"
197209
198- def resolve (self , data : Mapping [_Key , _Value ]) -> dict [_Key , _Value ]:
210+ def resolve (self , data : CopyableMutableMapping [_Key , _Value ]) -> CopyableMutableMapping [_Key , _Value ]:
199211 """Resolve the alias based on the given data.
200212
201213 This is done recursively, so if one alias resolves to a second alias,
@@ -212,18 +224,18 @@ def resolve(self, data: Mapping[_Key, _Value]) -> dict[_Key, _Value]:
212224 elif isinstance (data , tuple ):
213225 alias , others = data
214226 data = alias .resolve (base )
215- return dict ( data )
227+ return data
216228
217229class LocaleDataDict (MutableMapping [_Key , _Value ]):
218230 """Dictionary wrapper that automatically resolves aliases to the actual
219231 values.
220232 """
221233
222- def __init__ (self , data : MutableMapping [_Key , _Value ], base : Mapping [_Key , _Value ] | None = None ):
223- self ._data = dict ( data ) # Storing as a dict allows copy() to work
234+ def __init__ (self , data : CopyableMutableMapping [_Key , _Value ], base : CopyableMutableMapping [_Key , _Value ] | None = None ):
235+ self ._data = data
224236 if base is None :
225237 base = data
226- self .base = dict ( base )
238+ self .base = base
227239
228240 def __len__ (self ) -> int :
229241 return len (self ._data )
@@ -239,8 +251,9 @@ def __getitem__(self, key: _Key) -> _Value:
239251 alias , others = val
240252 val = alias .resolve (self .base ).copy ()
241253 merge (val , others )
242- if isinstance (val , dict ): # Return a nested alias-resolving dict
243- val = LocaleDataDict (val , base = self .base )
254+ if isinstance (val , MutableMapping ) and not isinstance (val , LocaleDataDict ):
255+ # Return a nested alias-resolving dict
256+ val = LocaleDataDict (val , base = self .base ) # type: ignore # assume copyable
244257 if val is not orig :
245258 self ._data [key ] = val # type: ignore # Cache the resolved value
246259 return val # type: ignore
0 commit comments