From 861f376c718b223eae2561e87fa0c98bf31bf44a Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Tue, 31 Mar 2026 08:06:55 +0000 Subject: [PATCH 1/2] bump version --- tftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tftest.py b/tftest.py index 3ada96b..f8c8f77 100644 --- a/tftest.py +++ b/tftest.py @@ -36,7 +36,7 @@ from pathlib import Path from typing import Any, Dict, List, Optional, Union -__version__ = '1.8.6' +__version__ = '1.8.7' _LOGGER = logging.getLogger('tftest') From 3b6fdbbe383e5e817a8874f5f1663f4b4a66c098 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 29 Jun 2026 11:26:15 +0000 Subject: [PATCH 2/2] Fix TypeError when calling decorated methods with positional arguments The _cache decorator's wrapper was defined as cache(self, **kwargs), which failed with a TypeError when decorated methods (like output()) were called with positional arguments. We now use inspect.signature to bind the arguments, allowing both positional and keyword arguments to be handled correctly while preserving cache key generation. Also added a regression test to test_cache.py and bumped the version to 1.8.8. --- test/test_cache.py | 11 +++++++++++ tftest.py | 21 +++++++++++++-------- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/test/test_cache.py b/test/test_cache.py index 8fea6ae..124e3d2 100644 --- a/test/test_cache.py +++ b/test/test_cache.py @@ -198,3 +198,14 @@ def test_use_cache_with_new_tf_content(tf, dummy_tf_filepath): f.write(str(uuid.uuid4())) assert mock_execute_command.call_count == expected_call_count + + +@pytest.mark.parametrize("tf", [True], indirect=True) +def test_output_positional_arg(tf): + """ + Ensures output() can be called with positional arguments when caching is enabled + """ + with patch.object(tf, 'execute_command') as mock_execute: + mock_execute.return_value.out = "{}" + tf.output("my_output", use_cache=True) + assert mock_execute.call_count == 1 diff --git a/tftest.py b/tftest.py index f8c8f77..e34d738 100644 --- a/tftest.py +++ b/tftest.py @@ -36,7 +36,7 @@ from pathlib import Path from typing import Any, Dict, List, Optional, Union -__version__ = '1.8.7' +__version__ = '1.8.8' _LOGGER = logging.getLogger('tftest') @@ -411,8 +411,9 @@ def generate_cache_hash(self, method_kwargs): default=str).encode("cp037")).hexdigest() + ".pickle" def _cache(func): + sig = inspect.signature(func) - def cache(self, **kwargs): + def cache(self, *args, **kwargs): """ Runs the tftest instance method or retreives the cache value if it exists @@ -424,16 +425,20 @@ def cache(self, **kwargs): _LOGGER.info("Cache decorated method: %s", func.__name__) if not self.enable_cache: - return func(self, **kwargs) - elif not kwargs.get("use_cache", False): - return func(self, **kwargs) + return func(self, *args, **kwargs) + + method_args = sig.bind(self, *args, **kwargs).arguments + method_args.pop('self', None) + + if not method_args.get("use_cache", False): + return func(self, *args, **kwargs) cache_dir = self.cache_dir / \ Path(sha1(str(self.tfdir).encode("cp037")).hexdigest()) / \ Path(func.__name__) cache_dir.mkdir(parents=True, exist_ok=True) - hash_filename = self.generate_cache_hash(kwargs) + hash_filename = self.generate_cache_hash(method_args) cache_key = cache_dir / hash_filename _LOGGER.debug("Cache key: %s", cache_key) @@ -446,12 +451,12 @@ def cache(self, **kwargs): return pickle.load(f) _LOGGER.info("Running command") - out = func(self, **kwargs) + out = func(self, *args, **kwargs) if out: # the hash value will now include any changes # to the tfdir directory - hash_filename = self.generate_cache_hash(kwargs) + hash_filename = self.generate_cache_hash(method_args) cache_key = cache_dir / hash_filename _LOGGER.debug("Cache key: %s", cache_key)