@@ -117,6 +117,29 @@ def _is_non_likely_script(name: str) -> bool:
117117 return False
118118
119119
120+ def _read_locale_file (name : str ) -> dict [str , Any ]:
121+ """Unpickle a single locale data file."""
122+ with open (resolve_locale_filename (name ), 'rb' ) as fileobj :
123+ return pickle .load (fileobj )
124+
125+
126+ def _read_locale_merging (name : str ) -> dict [str , Any ]:
127+ """Read a locale's data file, merging parent data."""
128+
129+ from babel .core import get_global
130+
131+ parent = get_global ('parent_exceptions' ).get (name )
132+ if not parent :
133+ if _is_non_likely_script (name ):
134+ parent = 'root'
135+ else :
136+ parts = name .split ('_' )
137+ parent = "root" if len (parts ) == 1 else "_" .join (parts [:- 1 ])
138+ data = load (parent ).copy ()
139+ merge (data , _read_locale_file (name ))
140+ return data
141+
142+
120143def load (name : os .PathLike [str ] | str , merge_inherited : bool = True ) -> dict [str , Any ]:
121144 """Load the locale data for the given locale.
122145
@@ -128,8 +151,8 @@ def load(name: os.PathLike[str] | str, merge_inherited: bool = True) -> dict[str
128151 >>> d['languages']['sv']
129152 'Swedish'
130153
131- Note that the results are cached, and subsequent requests for the same
132- locale return the same dictionary:
154+ Note that the results are cached (when ``merge_inherited`` is True; the default).
155+ Subsequent requests for the same locale return the same dictionary.
133156
134157 >>> d1 = load('en_US')
135158 >>> d2 = load('en_US')
@@ -138,39 +161,34 @@ def load(name: os.PathLike[str] | str, merge_inherited: bool = True) -> dict[str
138161
139162 :param name: the locale identifier string (or "root")
140163 :param merge_inherited: whether the inherited data should be merged into
141- the data of the requested locale
164+ the data of the requested locale. Setting this to
165+ ``False`` will disable caching for the locale data.
142166 :raise `IOError`: if no locale data file is found for the given locale
143167 identifier, or one of the locales it inherits from
144168 """
145169 name = os .path .basename (name )
146- _cache_lock .acquire ()
170+ if not merge_inherited :
171+ return _read_locale_file (name )
172+
173+ # Fast path: cache reads are atomic under the GIL, and `load()` is the
174+ # only writer to `_cache`, so a hit here is safe without the lock.
147175 try :
148- data = _cache .get (name )
149- if not data :
150- # Load inherited data
151- if name == 'root' or not merge_inherited :
152- data = {}
153- else :
154- from babel .core import get_global
155-
156- parent = get_global ('parent_exceptions' ).get (name )
157- if not parent :
158- if _is_non_likely_script (name ):
159- parent = 'root'
160- else :
161- parts = name .split ('_' )
162- parent = "root" if len (parts ) == 1 else "_" .join (parts [:- 1 ])
163- data = load (parent ).copy ()
164- filename = resolve_locale_filename (name )
165- with open (filename , 'rb' ) as fileobj :
166- if name != 'root' and merge_inherited :
167- merge (data , pickle .load (fileobj ))
168- else :
169- data = pickle .load (fileobj )
170- _cache [name ] = data
176+ return _cache [name ]
177+ except KeyError :
178+ pass
179+
180+ with _cache_lock :
181+ # Re-check under the lock in case another thread populated it meanwhile.
182+ try :
183+ return _cache [name ]
184+ except KeyError :
185+ pass
186+ if name == 'root' :
187+ data = _read_locale_file ('root' )
188+ else :
189+ data = _read_locale_merging (name )
190+ _cache [name ] = data
171191 return data
172- finally :
173- _cache_lock .release ()
174192
175193
176194def merge (dict1 : MutableMapping [Any , Any ], dict2 : Mapping [Any , Any ]) -> None :
0 commit comments