Skip to content

Commit 4d28eef

Browse files
committed
perf: skip cache and deep_freeze for non-LE templates
When a template has no AWS::LanguageExtensions transform, the work being cached is a single dict lookup on template.get('Transform') — O(1). The previous code still deep_freeze'd the entire template O(n) and stored it in the module-level cache, which was pure overhead for the vast majority of users who don't use language extensions. Now the no-LE path returns a LanguageExtensionResult wrapping the caller's original dict directly — zero copies, zero cache entries. Callers that need to mutate (build_context, package_context) already deep_thaw before mutating, so the aliasing is safe. The LE path still freezes and caches as before.
1 parent d56f52c commit 4d28eef

2 files changed

Lines changed: 17 additions & 30 deletions

File tree

samcli/lib/cfn_language_extensions/sam_integration.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -565,16 +565,12 @@ def expand_language_extensions(
565565
return cached
566566

567567
if not check_using_language_extension(template):
568-
frozen = deep_freeze(template)
569-
result = LanguageExtensionResult(
570-
expanded_template=frozen,
571-
original_template=frozen,
568+
return LanguageExtensionResult(
569+
expanded_template=template,
570+
original_template=template,
572571
dynamic_artifact_properties=(),
573572
had_language_extensions=False,
574573
)
575-
if cache_key is not None:
576-
_cache_put(cache_key, result)
577-
return result
578574

579575
LOG.debug("Expanding CloudFormation Language Extensions (Phase 1)")
580576

tests/unit/lib/cfn_language_extensions/test_sam_integration.py

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -624,16 +624,13 @@ def test_nonexistent_template_path_does_not_error(self):
624624
result = expand_language_extensions(template, template_path="/nonexistent/path/template.yaml")
625625
assert result.had_language_extensions is False
626626

627-
def test_non_language_extension_template_returns_frozen_result(self):
627+
def test_non_language_extension_template_returns_original_dict(self):
628628
template = {"Resources": {}}
629629
result = expand_language_extensions(template)
630630
assert result.had_language_extensions is False
631-
# Both fields point to the same frozen object (no-LE path optimization)
632-
assert result.expanded_template is result.original_template
633-
assert dict(result.expanded_template) == template
634-
# Frozen — mutation raises TypeError
635-
with pytest.raises(TypeError):
636-
result.expanded_template["Resources"] = {}
631+
# No-LE path returns the caller's dict directly (no freeze, no cache)
632+
assert result.expanded_template is template
633+
assert result.original_template is template
637634

638635
def test_mutation_does_not_affect_subsequent_calls(self):
639636
"""Frozen results prevent mutation; callers must deep_thaw first."""
@@ -968,20 +965,16 @@ def test_nonexistent_template_path_skips_cache(self):
968965
assert mock_process.call_count == 2
969966
assert len(_expansion_cache) == 0
970967

971-
def test_non_language_ext_template_cached(self):
972-
"""Templates without language extensions should also be cached when path is given."""
968+
def test_non_language_ext_template_not_cached(self):
969+
"""Templates without language extensions skip the cache entirely."""
973970
with tempfile.TemporaryDirectory() as tmp:
974971
path = self._make_template_file(tmp)
975972
template = {"Resources": {"MyTopic": {"Type": "AWS::SNS::Topic"}}}
976973

977-
result1 = expand_language_extensions(template, template_path=path)
978-
result2 = expand_language_extensions(template, template_path=path)
974+
expand_language_extensions(template, template_path=path)
975+
expand_language_extensions(template, template_path=path)
979976

980-
assert result1.had_language_extensions is False
981-
assert result2.had_language_extensions is False
982-
assert len(_expansion_cache) == 1
983-
# Cache returns the same frozen object
984-
assert result1 is result2
977+
assert len(_expansion_cache) == 0
985978

986979
def test_cache_evicts_oldest_when_full(self):
987980
"""Cache should evict the oldest entry when _MAX_CACHE_SIZE is reached."""
@@ -1041,16 +1034,14 @@ def test_original_template_is_independent_copy(self):
10411034
# original_template should not be affected
10421035
assert "NewResource" not in result.original_template.get("Resources", {})
10431036

1044-
def test_original_template_is_independent_copy_no_extensions(self):
1045-
"""Same independence guarantee when template has no language extensions."""
1037+
def test_no_extensions_result_aliases_input(self):
1038+
"""No-LE path returns the caller's dict directly — no copy overhead."""
10461039
with tempfile.TemporaryDirectory() as tmp:
10471040
path = self._make_template_file(tmp)
10481041
template = {"Resources": {"MyTopic": {"Type": "AWS::SNS::Topic"}}}
10491042

10501043
result = expand_language_extensions(template, template_path=path)
10511044

1052-
# Mutate the caller's template
1053-
template["Resources"]["Injected"] = {"Type": "AWS::SQS::Queue"}
1054-
1055-
# original_template should not be affected
1056-
assert "Injected" not in result.original_template.get("Resources", {})
1045+
# Result aliases the input (no freeze, no copy)
1046+
assert result.original_template is template
1047+
assert result.expanded_template is template

0 commit comments

Comments
 (0)