gh-146054: Limit the growth of `encodings.search_function` cache (GH-146055)
diff --git a/Lib/encodings/__init__.py b/Lib/encodings/__init__.py
index e205ec3..169c483 100644
--- a/Lib/encodings/__init__.py
+++ b/Lib/encodings/__init__.py
@@ -34,6 +34,7 @@
from . import aliases
_cache = {}
+_MAXCACHE = 500
_unknown = '--unknown--'
_import_tail = ['*']
_aliases = aliases.aliases
@@ -111,6 +112,8 @@ def search_function(encoding):
if mod is None:
# Cache misses
+ if len(_cache) >= _MAXCACHE:
+ _cache.clear()
_cache[encoding] = None
return None
@@ -132,6 +135,8 @@ def search_function(encoding):
entry = codecs.CodecInfo(*entry)
# Cache the codec registry entry
+ if len(_cache) >= _MAXCACHE:
+ _cache.clear()
_cache[encoding] = entry
# Register its aliases (without overwriting previously registered
diff --git a/Lib/test/test_codecs.py b/Lib/test/test_codecs.py
index c31faec..79c8a7e 100644
--- a/Lib/test/test_codecs.py
+++ b/Lib/test/test_codecs.py
@@ -3908,5 +3908,16 @@ def test_encodings_normalize_encoding(self):
self.assertEqual(normalize('utf\xE9\u20AC\U0010ffff-8'), 'utf_8')
+class CodecCacheTest(unittest.TestCase):
+ def test_cache_bounded(self):
+ for i in range(encodings._MAXCACHE + 1000):
+ try:
+ b'x'.decode(f'nonexist_{i}')
+ except LookupError:
+ pass
+
+ self.assertLessEqual(len(encodings._cache), encodings._MAXCACHE)
+
+
if __name__ == "__main__":
unittest.main()
diff --git a/Misc/NEWS.d/next/Library/2026-03-17-11-46-20.gh-issue-146054.udYcqn.rst b/Misc/NEWS.d/next/Library/2026-03-17-11-46-20.gh-issue-146054.udYcqn.rst
new file mode 100644
index 0000000..8692c7f
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-03-17-11-46-20.gh-issue-146054.udYcqn.rst
@@ -0,0 +1,2 @@
+Limit the size of :func:`encodings.search_function` cache.
+Found by OSS Fuzz in :oss-fuzz:`493449985`.