Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 6 additions & 21 deletions foxglove/client/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -1228,7 +1227,6 @@ def get_datasets(
"sortBy": camelize(sort_by),
"sortOrder": sort_order,
"limit": limit,
"offset": offset,
"cursor": cursor,
}
),
Expand All @@ -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
Expand All @@ -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):
Expand Down Expand Up @@ -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,
Expand All @@ -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(
Expand All @@ -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,
Expand Down Expand Up @@ -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(
Expand All @@ -1367,7 +1359,6 @@ def get_dataset_versions(
"sortOrder": sort_order,
"limit": limit,
"cursor": cursor,
"offset": offset,
}
),
)
Expand All @@ -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,
Expand All @@ -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(
Expand All @@ -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,
Expand All @@ -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],
Expand All @@ -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,
Expand Down Expand Up @@ -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)
Expand All @@ -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,
}
Expand Down
10 changes: 1 addition & 9 deletions foxglove/client/pagination.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion tests/test_datasets.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions tests/test_episodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)

Expand All @@ -105,7 +105,7 @@ def test_get_episodes_maps_response_and_filters():
"sortBy": "startTime",
"sortOrder": "asc",
"limit": "10",
"offset": "20",
"cursor": "next",
"include": "recordings",
}

Expand Down
17 changes: 3 additions & 14 deletions tests/test_pagination.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading