Skip to content

Commit f064914

Browse files
committed
Try to hold _cache_lock less in load()
1 parent dea267e commit f064914

1 file changed

Lines changed: 47 additions & 29 deletions

File tree

babel/localedata.py

Lines changed: 47 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
120143
def 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

176194
def merge(dict1: MutableMapping[Any, Any], dict2: Mapping[Any, Any]) -> None:

0 commit comments

Comments
 (0)