From ce2dd148ebc7254bdcc0680dfb34b10b1c8ff6ec Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 22 Aug 2026 17:26:12 +0000 Subject: [PATCH 1/2] Support searching by multiple platforms for collections and granules - Update platform() method in GranuleCollectionBaseQuery to accept Union[str, Sequence[str]] - Backward compatible: single string works as before - Multiple platforms passed as list for proper URL formatting (platform[]=...) - Add ValueError for empty/falsy platform values - Add tests for single, multiple, and empty platform cases in both CollectionQuery and GranuleQuery - Update CHANGELOG.md with new feature Closes #80 Co-authored-by: Suhas --- CHANGELOG.md | 1 + cmr/queries.py | 15 ++++++++++++--- tests/test_collection.py | 14 ++++++++++++++ tests/test_granule.py | 14 ++++++++++++++ 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 127b621..8db9430 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### Added - Add method `Query.results` for returning results as an iterator instead of sequence ([#37](https://github.com/nasa/python_cmr/issues/37)) +- Support searching by multiple platforms for collections and granules ([#80](https://github.com/nasa/python_cmr/issues/80)) ### Changed diff --git a/cmr/queries.py b/cmr/queries.py index 1cf0f69..bd7dced 100644 --- a/cmr/queries.py +++ b/cmr/queries.py @@ -781,18 +781,27 @@ def entry_title(self, entry_title: str) -> Self: return self - def platform(self, platform: str) -> Self: + def platform(self, platform: Union[str, Sequence[str]]) -> Self: """ Filter by the satellite platform the granule came from. - :param platform: name of the satellite + :param platform: name of the satellite (single string) or sequence of satellite names :returns: self """ if not platform: raise ValueError("Please provide a value for platform") - self.params['platform'] = platform + # Handle string vs sequence of strings + if isinstance(platform, str): + self.params['platform'] = platform + else: + # Convert sequence to list for proper URL formatting + platform_list = list(platform) + if not platform_list: + raise ValueError("Please provide a value for platform") + self.params['platform'] = platform_list + return self diff --git a/tests/test_collection.py b/tests/test_collection.py index 2c2d866..2edeb4f 100644 --- a/tests/test_collection.py +++ b/tests/test_collection.py @@ -144,12 +144,26 @@ def test_platform(self): self.assertIn("platform", query.params) self.assertEqual(query.params["platform"], "1B") + def test_multiple_platforms(self): + query = CollectionQuery() + + query.platform(["Terra", "Aqua"]) + + self.assertIn("platform", query.params) + self.assertEqual(query.params["platform"], ["Terra", "Aqua"]) + def test_empty_platform(self): query = CollectionQuery() with self.assertRaises(ValueError): query.platform(None) # type: ignore[arg-type] + def test_empty_platform_list(self): + query = CollectionQuery() + + with self.assertRaises(ValueError): + query.platform([]) + def test_revision_date(self): query = CollectionQuery() collections = query.short_name("SWOT_L2_HR_RiverSP_reach_2.0").revision_date("2022-05-16", "2024-06-30").get_all() diff --git a/tests/test_granule.py b/tests/test_granule.py index fbf188f..d14b319 100644 --- a/tests/test_granule.py +++ b/tests/test_granule.py @@ -344,6 +344,14 @@ def test_platform(self): self.assertIn(self.platform, query.params) self.assertEqual(query.params[self.platform], "1B") + def test_multiple_platforms(self): + query = GranuleQuery() + + query.platform(["Terra", "Aqua"]) + + self.assertIn(self.platform, query.params) + self.assertEqual(query.params[self.platform], ["Terra", "Aqua"]) + def test_sort_key(self): query = GranuleQuery() # Various sort keys using this as an example @@ -363,6 +371,12 @@ def test_empty_platform(self): with self.assertRaises(ValueError): query.platform(None) # type: ignore[arg-type] + def test_empty_platform_list(self): + query = GranuleQuery() + + with self.assertRaises(ValueError): + query.platform([]) + def test_granule_ur(self): query = GranuleQuery() From 3796f680fefd3ad1e1ed0b3f9e33c8f4895e5316 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 22 Aug 2026 20:18:48 +0000 Subject: [PATCH 2/2] Simplify platform() method per review feedback Apply Chuck Daniels' suggestion to simplify the string-vs-sequence handling. The new implementation keeps the string case as-is and converts any sequence type to list in a single expression. This ensures other sequence types (tuples, etc.) become lists since _build_url specifically looks for list instances when formatting multi-valued parameters. Co-authored-by: Suhas --- cmr/queries.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/cmr/queries.py b/cmr/queries.py index bd7dced..3e4a1f9 100644 --- a/cmr/queries.py +++ b/cmr/queries.py @@ -792,15 +792,9 @@ def platform(self, platform: Union[str, Sequence[str]]) -> Self: if not platform: raise ValueError("Please provide a value for platform") - # Handle string vs sequence of strings - if isinstance(platform, str): - self.params['platform'] = platform - else: - # Convert sequence to list for proper URL formatting - platform_list = list(platform) - if not platform_list: - raise ValueError("Please provide a value for platform") - self.params['platform'] = platform_list + self.params['platform'] = ( + platform if isinstance(platform, str) else list(platform) + ) return self