From 6001bb09c9699bead2a8417218853a68c372fbb0 Mon Sep 17 00:00:00 2001 From: Cedric Conday <277679649+CedricConday@users.noreply.github.com> Date: Sat, 18 Jul 2026 16:26:40 +0000 Subject: [PATCH] fix: guard get_next/get_previous against unset _more ResultSet._more is a declared Pydantic private attribute defaulting to None, so hasattr(self, "_more") is always True and the existing guard never triggers. When _more is not set (e.g. a ResultSet not produced by the @returns decorator), get_next()/get_previous() called None(**params) and raised "'NoneType' object is not callable". Guard on `self._more is None` instead so these methods return None as intended. Adds a regression test. --- predicthq/endpoints/schemas.py | 4 ++-- tests/endpoints/test_schemas.py | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/predicthq/endpoints/schemas.py b/predicthq/endpoints/schemas.py index a3066eb..de87aa8 100644 --- a/predicthq/endpoints/schemas.py +++ b/predicthq/endpoints/schemas.py @@ -29,13 +29,13 @@ def has_next(self): return self.next is not None def get_next(self): - if not self.has_next() or not hasattr(self, "_more"): + if not self.has_next() or self._more is None: return params = self._parse_params(self.next) return self._more(**params) def get_previous(self): - if not self.has_previous() or not hasattr(self, "_more"): + if not self.has_previous() or self._more is None: return params = self._parse_params(self.previous) return self._more(**params) diff --git a/tests/endpoints/test_schemas.py b/tests/endpoints/test_schemas.py index b95b5dc..48efa0b 100644 --- a/tests/endpoints/test_schemas.py +++ b/tests/endpoints/test_schemas.py @@ -135,3 +135,19 @@ def load_page(self, page): endpoint.load_page(page=3).model_dump(), ] assert list(p1.iter_all()) == list(p1) + list(p2) + list(p3) + + +def test_resultset_without_more_returns_none(): + # A ResultSet not produced by the @returns decorator has no _more callable set. + # _more is a declared private attr defaulting to None, so hasattr() is always + # True; get_next()/get_previous() must guard on `_more is None` and return None + # rather than raising "'NoneType' object is not callable". + result_set = schemas.ResultSet( + count=5, + next="https://example.org/?page=2", + previous="https://example.org/?page=1", + ) + assert result_set.has_next() is True + assert result_set.has_previous() is True + assert result_set.get_next() is None + assert result_set.get_previous() is None