Skip to content

Commit d5abdd1

Browse files
authored
Merge pull request #125 from lautat/fix/thread-safe-force-locale
Fix locale selection in multi-threaded Flask app when force_locale is used
2 parents 33b6ffe + d75182c commit d5abdd1

2 files changed

Lines changed: 50 additions & 7 deletions

File tree

flask_babel/__init__.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,9 @@ def refresh():
305305
if hasattr(ctx, key):
306306
delattr(ctx, key)
307307

308+
if hasattr(ctx, 'forced_babel_locale'):
309+
ctx.babel_locale = ctx.forced_babel_locale
310+
308311

309312
@contextmanager
310313
def force_locale(locale):
@@ -325,20 +328,19 @@ def force_locale(locale):
325328
yield
326329
return
327330

328-
babel = current_app.extensions['babel']
329-
330-
orig_locale_selector_func = babel.locale_selector_func
331331
orig_attrs = {}
332332
for key in ('babel_translations', 'babel_locale'):
333333
orig_attrs[key] = getattr(ctx, key, None)
334334

335335
try:
336-
babel.locale_selector_func = lambda: locale
337-
for key in orig_attrs:
338-
setattr(ctx, key, None)
336+
ctx.babel_locale = Locale.parse(locale)
337+
ctx.forced_babel_locale = ctx.babel_locale
338+
ctx.babel_translations = None
339339
yield
340340
finally:
341-
babel.locale_selector_func = orig_locale_selector_func
341+
if hasattr(ctx, 'forced_babel_locale'):
342+
del ctx.forced_babel_locale
343+
342344
for key, value in orig_attrs.items():
343345
setattr(ctx, key, value)
344346

tests/tests.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
77

88
import pickle
9+
from threading import Thread, Semaphore
910

1011
import unittest
1112
from decimal import Decimal
@@ -204,6 +205,46 @@ def select_locale():
204205
assert str(babel.get_locale()) == 'en_US'
205206
assert str(babel.get_locale()) == 'de_DE'
206207

208+
def test_force_locale_with_threading(self):
209+
app = flask.Flask(__name__)
210+
b = babel.Babel(app)
211+
212+
@b.localeselector
213+
def select_locale():
214+
return 'de_DE'
215+
216+
semaphore = Semaphore(value=0)
217+
218+
def first_request():
219+
with app.test_request_context():
220+
with babel.force_locale('en_US'):
221+
assert str(babel.get_locale()) == 'en_US'
222+
semaphore.acquire()
223+
224+
thread = Thread(target=first_request)
225+
thread.start()
226+
227+
try:
228+
with app.test_request_context():
229+
assert str(babel.get_locale()) == 'de_DE'
230+
finally:
231+
semaphore.release()
232+
thread.join()
233+
234+
def test_refresh_during_force_locale(self):
235+
app = flask.Flask(__name__)
236+
b = babel.Babel(app)
237+
238+
@b.localeselector
239+
def select_locale():
240+
return 'de_DE'
241+
242+
with app.test_request_context():
243+
with babel.force_locale('en_US'):
244+
assert str(babel.get_locale()) == 'en_US'
245+
babel.refresh()
246+
assert str(babel.get_locale()) == 'en_US'
247+
207248

208249
class NumberFormattingTestCase(unittest.TestCase):
209250

0 commit comments

Comments
 (0)