From 32e6561bb33198eeb963b7dbc48e241d89433818 Mon Sep 17 00:00:00 2001 From: xmo-odoo Date: Thu, 12 Feb 2026 15:21:03 +0100 Subject: [PATCH] Cache validity of a locale name Currently `_cache` is only populated on `load`, this is an issue with code which do a lot of locale parsing / instantiation but for one reason or an other rarely end up needing to actually load the locale data (e.g. date formatting with non-locale-dependent patterns) because every cache miss is an `os.path.exists`. This change takes advantage of semantics differences between `exists` and `load`: `exists` just needs the entry to exist in the cache, while `load` needs the entry to be truthy. Thus `exists` can cache its lookup by setting an empty dict (or even `None` but that seems a bit too dodgy). Alternatively we could remove the `os.path.exists` "slow path" and call `normalize_locale` every time, as `locale_identifiers` gets cached on first call, the initial call would be a lot more expensive but then no further `exists` would need to touch the filesystem. For a third alternative, `exists` could be decorated with `functools.cache`, in order to have its own cache completely independent of `load`. `load` could also be migrated to `functools.cache` as, as far as I can tell, the `localedata._cache` is never invalidated anyway, with the drawback that a locale's data might be resolved multiple times if concurrent calls are tight enough (`functools.cache` synchronises the cache but not the resolution of the value). --- babel/localedata.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/babel/localedata.py b/babel/localedata.py index 2b225a142..f66655d53 100644 --- a/babel/localedata.py +++ b/babel/localedata.py @@ -72,7 +72,10 @@ def exists(name: str) -> bool: if name in _cache: return True file_found = os.path.exists(resolve_locale_filename(name)) - return True if file_found else bool(normalize_locale(name)) + if file_found or normalize_locale(name): + _cache.setdefault(name, {}) + return True + return False @lru_cache(maxsize=None)