From 1efee0adde4f4c929fae79534c3266efae829087 Mon Sep 17 00:00:00 2001 From: Kevin Turcios Date: Fri, 31 Jul 2026 02:47:21 -0500 Subject: [PATCH] Cache generated method compilation --- src/attr/_make.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/attr/_make.py b/src/attr/_make.py index 6794464e9..e2dcccbbe 100644 --- a/src/attr/_make.py +++ b/src/attr/_make.py @@ -223,10 +223,36 @@ def _compile_and_eval( Evaluate the script with the given global (globs) and local (locs) variables. """ - bytecode = compile(script, filename, "exec") + bytecode = _compiled_script_cache.pop(script, None) + if bytecode is None: + bytecode = compile(script, filename, "exec") + else: + bytecode = replace_code_filename(bytecode, filename) + + _compiled_script_cache[script] = bytecode + if len(_compiled_script_cache) > _COMPILED_SCRIPT_CACHE_SIZE: + del _compiled_script_cache[next(iter(_compiled_script_cache))] + eval(bytecode, globs, locs) +_COMPILED_SCRIPT_CACHE_SIZE = 128 +_compiled_script_cache: dict[str, types.CodeType] = {} + + +def replace_code_filename( + code: types.CodeType, filename: str +) -> types.CodeType: + """Return *code* with its filename updated recursively.""" + constants = tuple( + replace_code_filename(constant, filename) + if isinstance(constant, types.CodeType) + else constant + for constant in code.co_consts + ) + return code.replace(co_filename=filename, co_consts=constants) + + def _linecache_and_compile( script: str, filename: str,