From 60281a9b97b3f115554bf4bebb537abd8f512b5e Mon Sep 17 00:00:00 2001 From: Richard Tibbles Date: Tue, 31 Mar 2026 18:14:53 -0700 Subject: [PATCH 1/2] fix: remove functools.cache from methods receiving Launchpad Entry objects Launchpad Entry objects are not hashable, so functools.cache raises TypeError when copy-to-series actually finds packages to copy (which it now does since the dynamic series detection fix). These methods are only called once per workflow run, so caching isn't needed. Co-Authored-By: Claude Opus 4.6 (1M context) --- scripts/launchpad_copy.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/scripts/launchpad_copy.py b/scripts/launchpad_copy.py index 4fa2b1f..bb33fdc 100644 --- a/scripts/launchpad_copy.py +++ b/scripts/launchpad_copy.py @@ -158,7 +158,6 @@ def get_series(self, name): log.debug("Locating the series: %s...", name) return ppa.distribution.getSeries(name_or_version=name) - @functools.cache def get_published_sources(self, ppa, series_name=None, status=None): kwargs = {} if series_name: @@ -169,7 +168,6 @@ def get_published_sources(self, ppa, series_name=None, status=None): log.debug("Listing source packages...") return ppa.getPublishedSources(**kwargs) - @functools.cache def get_builds_for_source(self, source): log.debug( "Listing %s builds for %s %s...", @@ -179,7 +177,6 @@ def get_builds_for_source(self, source): ) return source.getBuilds() - @functools.cache def get_source_packages(self, ppa, series_name, package_names=None): """Return {package_name: {version: source, ...}, ...}""" res = defaultdict(dict) @@ -207,7 +204,6 @@ def has_published_binaries(self, ppa, name, version, series_name): builds = self.get_builds_for(ppa, name, version, series_name) return not builds or builds[0].buildstate == "Successfully built" - @functools.cache def get_usable_sources(self, ppa, package_names, series_name): res = [] for source in self.get_published_sources(ppa, series_name): From 267167858fce517272a572780baa2779f001c54b Mon Sep 17 00:00:00 2001 From: Richard Tibbles Date: Tue, 31 Mar 2026 18:25:52 -0700 Subject: [PATCH 2/2] fix: update tests to call get_usable_sources directly Tests used __wrapped__ to bypass functools.cache, which no longer exists after removing the decorator. Co-Authored-By: Claude Opus 4.6 (1M context) --- tests/test_launchpad_copy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_launchpad_copy.py b/tests/test_launchpad_copy.py index 47419ab..7810e75 100644 --- a/tests/test_launchpad_copy.py +++ b/tests/test_launchpad_copy.py @@ -240,7 +240,7 @@ def test_get_usable_sources_filters_by_whitelist(self): src_bad.status = "Published" with patch.object(wrapper, "get_published_sources", return_value=[src_good, src_bad]): - result = LaunchpadWrapper.get_usable_sources.__wrapped__(wrapper, mock_ppa, ("kolibri-server",), "jammy") + result = wrapper.get_usable_sources(mock_ppa, ("kolibri-server",), "jammy") assert len(result) == 1 assert result[0] == ("kolibri-server", "1.0") @@ -255,7 +255,7 @@ def test_get_usable_sources_skips_superseded(self): src.status = "Superseded" with patch.object(wrapper, "get_published_sources", return_value=[src]): - result = LaunchpadWrapper.get_usable_sources.__wrapped__(wrapper, mock_ppa, ("kolibri-server",), "jammy") + result = wrapper.get_usable_sources(mock_ppa, ("kolibri-server",), "jammy") assert len(result) == 0