Skip to content

Commit 7e7f6ae

Browse files
committed
Use functools.cache for real_momoize
1 parent ca6ab4e commit 7e7f6ae

2 files changed

Lines changed: 43 additions & 23 deletions

File tree

salt/utils/platform.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212

1313
import distro
1414

15-
# Use functools.lru_cache rather than importing from salt.utils.decorators.
15+
16+
# Use a local wraps-based memoize rather than importing from salt.utils.decorators.
1617
# This module is synced to the remote's extmods/utils/platform.py, and in
1718
# Python 3.14+ (forkserver default start method) it can be accidentally
1819
# imported as the stdlib ``platform`` module when extmods/utils/ sits at
@@ -21,7 +22,26 @@
2122
# salt.utils.decorators → salt.utils.versions → salt.version
2223
# → import platform (ourselves!) → salt.utils.decorators (cycle)
2324
# functools is part of the stdlib and has no such dependency.
24-
real_memoize = functools.cache
25+
#
26+
# We cannot use functools.cache/lru_cache directly as the decorator because
27+
# those produce functools._lru_cache_wrapper objects which fail
28+
# inspect.isfunction(), causing the Salt loader to skip them when loading
29+
# salt.utils.platform as a utils module (salt/loader/lazy.py line ~1109).
30+
def real_memoize(func):
31+
"""Cache the result of a zero-or-more-argument function (stdlib-only, loader-safe)."""
32+
cache = {}
33+
_sentinel = object()
34+
35+
@functools.wraps(func)
36+
def _wrapper(*args, **kwargs):
37+
key = (args, tuple(sorted(kwargs.items())))
38+
result = cache.get(key, _sentinel)
39+
if result is _sentinel:
40+
result = func(*args, **kwargs)
41+
cache[key] = result
42+
return result
43+
44+
return _wrapper
2545

2646

2747
def linux_distribution(full_distribution_name=True):

tests/pytests/unit/loader/test_grains_cleanup.py

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -266,41 +266,41 @@ def test_clean_modules_removes_from_sys_modules(minion_opts):
266266
f"{loaded_base_name}.ext.{tag}",
267267
}
268268

269+
# Prefixes for modules that belong specifically to this loader's tag.
270+
# clean_modules() only removes modules under these prefixes, so we only
271+
# check these prefixes — not ALL salt.loaded.* modules. Checking the
272+
# broader namespace would make the test sensitive to modules loaded by
273+
# other tests that ran in the same process (e.g. salt.loaded.int.modules.*
274+
# from execution-module unit tests).
275+
tag_prefixes = (
276+
f"{loaded_base_name}.int.{tag}.",
277+
f"{loaded_base_name}.ext.{tag}.",
278+
)
279+
269280
# Load some modules
270281
for key in list(loader.keys())[:5]:
271282
try:
272283
_ = loader[key]
273284
except Exception: # pylint: disable=broad-except
274285
pass
275286

276-
# Find modules that were loaded
277-
loaded_before = [m for m in sys.modules if m.startswith(loaded_base_name)]
287+
# Find tag-specific modules that were loaded
288+
loaded_before = [
289+
m for m in sys.modules if any(m.startswith(p) for p in tag_prefixes)
290+
]
278291
assert len(loaded_before) > 0, "No modules were loaded for testing"
279292

280293
# Clean modules
281294
loader.clean_modules()
282295

283-
# Verify actual loaded modules are removed but base stubs remain
284-
remaining = [m for m in sys.modules if m.startswith(loaded_base_name)]
285-
286-
# All remaining modules should be base stubs or utils modules (shared infrastructure)
287-
# Filter out both base stubs and utils modules
288-
unexpected = []
289-
for m in remaining:
290-
# Skip base stubs
291-
if m in expected_base_stubs:
292-
continue
293-
# Skip utils modules (shared infrastructure)
294-
parts = m.split(".")
295-
# Utils modules: salt.loaded.int.utils, salt.loaded.int.utils.*, etc.
296-
if len(parts) >= 4 and parts[3] in ("utils", "wrapper"):
297-
continue
298-
# Anything else is unexpected
299-
unexpected.append(m)
296+
# All tag-specific modules should have been removed
297+
remaining_tag = [
298+
m for m in sys.modules if any(m.startswith(p) for p in tag_prefixes)
299+
]
300300

301301
assert (
302-
len(unexpected) == 0
303-
), f"clean_modules() failed to remove {len(unexpected)} modules: {unexpected}"
302+
len(remaining_tag) == 0
303+
), f"clean_modules() failed to remove {len(remaining_tag)} modules: {remaining_tag}"
304304

305305
# Base stubs should still be present
306306
for stub in expected_base_stubs:

0 commit comments

Comments
 (0)