diff --git a/foxglove/client/api.py b/foxglove/client/api.py index ca72466..7373bc4 100644 --- a/foxglove/client/api.py +++ b/foxglove/client/api.py @@ -1210,13 +1210,12 @@ def get_datasets( sort_by: Optional[str] = None, sort_order: Optional[str] = None, limit: Optional[int] = None, - offset: Optional[int] = None, cursor: Optional[str] = None, ): """Return a Page of datasets; name is a case-insensitive substring filter. - ``limit`` is the page size. Use ``cursor`` for continuation; ``offset`` is - deprecated. Use ``page.auto_paging_iter()`` to traverse all matching items. + Use ``cursor`` to advance pages and ``limit`` to set the size of the page. + Use ``page.auto_paging_iter()`` to traverse all matching items. """ return self._get_page( "/v1/datasets", @@ -1228,7 +1227,6 @@ def get_datasets( "sortBy": camelize(sort_by), "sortOrder": sort_order, "limit": limit, - "offset": offset, "cursor": cursor, } ), @@ -1242,8 +1240,6 @@ def _get_page( params: Dict[str, Any], collection: Optional[str] = None, ) -> Page[T]: - if params.get("cursor") is not None and params.get("offset", 0) != 0: - raise ValueError("cursor cannot be combined with a nonzero offset") response = self.__session.get(self.__url__(path), params=params) result = json_or_raise(response) items = result[collection] if collection else result @@ -1258,7 +1254,6 @@ def fetch_page(cursor: str) -> Page[T]: next_cursor=response.headers.get("fg-pagination-next-cursor"), previous_cursor=response.headers.get("fg-pagination-previous-cursor"), fetch_page=fetch_page, - legacy_offset=params.get("offset", 0) != 0, ) def get_dataset(self, *, dataset_id: str): @@ -1296,7 +1291,6 @@ def get_dataset_episodes( sort_by: Optional[str] = None, sort_order: Optional[str] = None, limit: Optional[int] = None, - offset: Optional[int] = None, cursor: Optional[str] = None, start: Optional[datetime.datetime] = None, end: Optional[datetime.datetime] = None, @@ -1307,7 +1301,7 @@ def get_dataset_episodes( """Return a Page of the latest committed or initial editable membership. Supply ``start`` and ``end`` together to filter overlapping episode windows. - ``limit`` is page size; ``cursor`` continues a page and ``offset`` is deprecated. + Use ``cursor`` to advance pages and ``limit`` to set the size of the page. Use ``page.auto_paging_iter()`` to traverse all matching episodes. """ return self._get_dataset_episodes( @@ -1316,7 +1310,6 @@ def get_dataset_episodes( sort_by=sort_by, sort_order=sort_order, limit=limit, - offset=offset, cursor=cursor, start=start, end=end, @@ -1351,11 +1344,10 @@ def get_dataset_versions( sort_order: Optional[str] = None, limit: Optional[int] = None, cursor: Optional[str] = None, - offset: Optional[int] = None, ): """Return a Page of committed versions and the current editable version. - ``limit`` is page size; ``cursor`` continues a page and ``offset`` is deprecated. + Use ``cursor`` to advance pages and ``limit`` to set the size of the page. Use ``page.auto_paging_iter()`` to traverse all versions. """ return self._get_page( @@ -1367,7 +1359,6 @@ def get_dataset_versions( "sortOrder": sort_order, "limit": limit, "cursor": cursor, - "offset": offset, } ), ) @@ -1387,7 +1378,6 @@ def get_dataset_version_episodes( sort_by: Optional[str] = None, sort_order: Optional[str] = None, limit: Optional[int] = None, - offset: Optional[int] = None, cursor: Optional[str] = None, start: Optional[datetime.datetime] = None, end: Optional[datetime.datetime] = None, @@ -1398,7 +1388,7 @@ def get_dataset_version_episodes( """Return a Page of episode membership in a specific version. Supply ``start`` and ``end`` together to filter overlapping episode windows. - ``limit`` is page size; ``cursor`` continues a page and ``offset`` is deprecated. + Use ``cursor`` to advance pages and ``limit`` to set the size of the page. Use ``page.auto_paging_iter()`` to traverse all matching episodes. """ return self._get_dataset_episodes( @@ -1407,7 +1397,6 @@ def get_dataset_version_episodes( sort_by=sort_by, sort_order=sort_order, limit=limit, - offset=offset, cursor=cursor, start=start, end=end, @@ -1424,7 +1413,6 @@ def _get_dataset_episodes( sort_by: Optional[str], sort_order: Optional[str], limit: Optional[int], - offset: Optional[int], cursor: Optional[str], start: Optional[datetime.datetime], end: Optional[datetime.datetime], @@ -1446,7 +1434,6 @@ def _get_dataset_episodes( "sortBy": camelize(sort_by), "sortOrder": sort_order, "limit": limit, - "offset": offset, "cursor": cursor, "start": start.astimezone().isoformat() if start else None, "end": end.astimezone().isoformat() if end else None, @@ -1576,14 +1563,13 @@ def get_episodes( sort_by: Optional[str] = None, sort_order: Optional[str] = None, limit: Optional[int] = None, - offset: Optional[int] = None, cursor: Optional[str] = None, include_recordings: bool = False, ): """Return a Page of episodes, optionally with recording details. Supply ``start`` and ``end`` together to filter overlapping episode windows. - ``limit`` is page size; ``cursor`` continues a page and ``offset`` is deprecated. + Use ``cursor`` to advance pages and ``limit`` to set the size of the page. Use ``page.auto_paging_iter()`` to traverse all matching episodes. """ _validate_episode_range(start, end) @@ -1605,7 +1591,6 @@ def get_episodes( "sortBy": camelize(sort_by), "sortOrder": sort_order, "limit": limit, - "offset": offset, "cursor": cursor, "include": "recordings" if include_recordings else None, } diff --git a/foxglove/client/pagination.py b/foxglove/client/pagination.py index 0c87d7e..a700858 100644 --- a/foxglove/client/pagination.py +++ b/foxglove/client/pagination.py @@ -18,25 +18,17 @@ def __init__( next_cursor: Optional[str] = None, previous_cursor: Optional[str] = None, fetch_page: Callable[[str], "Page[T]"], - legacy_offset: bool = False, ): self.items = items self.next_cursor = next_cursor self.previous_cursor = previous_cursor self._fetch_page = fetch_page - self._legacy_offset = legacy_offset def auto_paging_iter(self) -> Iterator[T]: """Yield this page, then fetch further pages on demand in display order. - Starting from a nonzero deprecated offset is unsupported: those responses - do not contain continuation cursors. Request the first page or a cursor - page instead. Errors fetching subsequent pages propagate to the caller. + Errors fetching subsequent pages propagate to the caller. """ - if self._legacy_offset: - raise ValueError( - "Automatic pagination requires cursor pagination, not a nonzero offset" - ) page = self while True: yield from page.items diff --git a/pyproject.toml b/pyproject.toml index 7efef40..b70fdd6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta" [project] name = "foxglove-client" -version = "0.20.0" +version = "0.21.0" description = "Client library for the Foxglove API." readme = "README.md" requires-python = ">=3.7" diff --git a/tests/test_datasets.py b/tests/test_datasets.py index 289a1fe..26c2539 100644 --- a/tests/test_datasets.py +++ b/tests/test_datasets.py @@ -96,7 +96,7 @@ def test_dataset_metadata_methods(): episode_ids=["ep_1"], ) datasets = client.get_datasets( - project_id="prj_1", sort_by="updated_at", limit=10, offset=2 + project_id="prj_1", sort_by="updated_at", limit=10, cursor="next" ) fetched = client.get_dataset(dataset_id="ds_1") updated = client.update_dataset(dataset_id="ds_1", description=None) diff --git a/tests/test_episodes.py b/tests/test_episodes.py index 36ff513..bb0abff 100644 --- a/tests/test_episodes.py +++ b/tests/test_episodes.py @@ -87,7 +87,7 @@ def test_get_episodes_maps_response_and_filters(): sort_by="start_time", sort_order="asc", limit=10, - offset=20, + cursor="next", include_recordings=True, ) @@ -105,7 +105,7 @@ def test_get_episodes_maps_response_and_filters(): "sortBy": "startTime", "sortOrder": "asc", "limit": "10", - "offset": "20", + "cursor": "next", "include": "recordings", } diff --git a/tests/test_pagination.py b/tests/test_pagination.py index ef36d50..67d35c0 100644 --- a/tests/test_pagination.py +++ b/tests/test_pagination.py @@ -117,21 +117,10 @@ def test_pages_are_lazy_and_preserve_query( @pytest.mark.parametrize("method,kwargs,path,collection,factory,filters", LIST_CASES) -@responses.activate -def test_offset_rules(method, kwargs, path, collection, factory, filters): +def test_offset_is_not_accepted(method, kwargs, path, collection, factory, filters): get_page = getattr(Client("test"), method) - with pytest.raises(ValueError, match="nonzero offset"): - get_page(**kwargs, cursor="cursor", offset=1) - assert not responses.calls - responses.add( - responses.GET, api_url(path), json={collection: []} if collection else [] - ) - page = get_page(**kwargs, offset=1) - with pytest.raises(ValueError, match="nonzero offset"): - list(page.auto_paging_iter()) - page = get_page(**kwargs, offset=0, cursor="cursor", limit=0) - assert list(page.auto_paging_iter()) == [] - assert page.next_cursor is None + with pytest.raises(TypeError, match="unexpected keyword argument 'offset'"): + get_page(**kwargs, offset=1) @responses.activate diff --git a/uv.lock b/uv.lock index 3b20949..c85aad1 100644 --- a/uv.lock +++ b/uv.lock @@ -877,7 +877,7 @@ wheels = [ [[package]] name = "foxglove-client" -version = "0.20.0" +version = "0.21.0" source = { editable = "." } dependencies = [ { name = "arrow", version = "1.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.8'" },