Skip to content

Add sandbox and snapshot list APIs#14

Merged
steven-passynkov merged 3 commits into
mainfrom
add-list-apis
Apr 14, 2026
Merged

Add sandbox and snapshot list APIs#14
steven-passynkov merged 3 commits into
mainfrom
add-list-apis

Conversation

@steven-passynkov

@steven-passynkov steven-passynkov commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add sync and async sandbox listing support plus helpers to fetch sandbox user home directory and workdir
  • add snapshot listing support and typed paginated response models for sandbox and snapshot list endpoints
  • export the new response types and cover the new client methods with sync, async, and import tests

Testing

  • PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 python3 -m pytest tests/test_import.py tests/_sync/test_sandboxes.py tests/_sync/test_snapshots.py tests/_async/test_sandboxes.py tests/_async/test_snapshots.py

Summary by CodeRabbit

  • New Features
    • Added sandbox listing with pagination support, state-based filtering, and flexible sorting options for improved resource discovery
    • Added snapshot listing with pagination, search filtering, and customizable sorting capabilities
    • New methods to retrieve sandbox system information: user home directory and working directory paths

@coderabbitai

coderabbitai Bot commented Apr 13, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@steven-passynkov has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 1 minutes and 49 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 1 minutes and 49 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0c7cbfc0-0060-4e52-b764-13d537e0e5d9

📥 Commits

Reviewing files that changed from the base of the PR and between 475526a and 073b884.

📒 Files selected for processing (6)
  • leap0/_async/sandbox.py
  • leap0/_sync/sandbox.py
  • leap0/models/snapshot.py
  • tests/_async/test_sandboxes.py
  • tests/_async/test_snapshots.py
  • tests/_sync/test_sandboxes.py
📝 Walkthrough

Walkthrough

This PR adds pagination and listing capabilities for sandboxes and snapshots, along with new methods to retrieve sandbox system information (user home directory and working directory). It includes new response schemas, model classes, client methods with parameter validation, and comprehensive test coverage for both synchronous and asynchronous APIs.

Changes

Cohort / File(s) Summary
Public API Exports
leap0/__init__.py
Adds three new public exports: SandboxListItem, SandboxListResponse, and SnapshotListResponse, with corresponding type-checking imports and _DYNAMIC_IMPORTS mappings for lazy loading.
Response Schemas
leap0/_schemas/sandbox.py, leap0/_schemas/snapshot.py
Introduces paginated response TypedDicts: SandboxListItemResponseDict, ListSandboxesResponseDict, and ListSnapshotsResponseDict to define list API response structures.
Model Classes
leap0/models/sandbox.py, leap0/models/snapshot.py
Adds SandboxListItem, SandboxListResponse, and SnapshotListResponse dataclasses with from_dict constructors for parsing paginated responses and normalizing optional fields.
Async Client Methods
leap0/_async/sandbox.py, leap0/_async/snapshots.py
Implements AsyncSandboxesClient.list() and AsyncSnapshotsClient.list() with parameter validation, plus sandbox system info methods get_user_home_dir() and get_workdir(). Adds corresponding convenience methods on AsyncSandbox.
Sync Client Methods
leap0/_sync/sandbox.py, leap0/_sync/snapshots.py
Mirrors async implementation with synchronous SandboxesClient.list() and SnapshotsClient.list(), along with get_user_home_dir() and get_workdir() client and handle methods for Sandbox.
Test Coverage
tests/_async/test_sandboxes.py, tests/_async/test_snapshots.py, tests/_sync/test_sandboxes.py, tests/_sync/test_snapshots.py, tests/test_import.py
Comprehensive tests for new list methods, system info retrieval, parameter validation, HTTP request/response mapping, and public API import assertions.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • tighten sandbox handles and template creds #10 — Modifies sandbox models and client bindings including sandbox references and handle typing patterns used in the new system info methods.
  • refactor async #7 — Updates leap0/__init__.py exports and adds/modifies sandbox/snapshot model symbols; overlaps directly with public API changes in this PR.
  • Refactor #12 — Modifies leap0/models/snapshot.py and leap0/models/sandbox.py, indicating code-level overlap in model implementations.

Poem

🐰 Lists hop through endpoints, pages turning fast,
Snapshots and sandboxes gathered, queries vast!
Home directories whispered, workdirs laid bare,
Validation guards the way with utmost care.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 29.31% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add sandbox and snapshot list APIs' clearly and concisely summarizes the main change: introducing list endpoints for both sandboxes and snapshots.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch add-list-apis

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
leap0/_sync/sandbox.py (1)

262-310: Consider conditionally including state in params.

When state is None, the params dict includes "state": None. While httpx typically omits None values from query strings, explicitly building the params dict without None entries is more defensive and clearer.

♻️ Optional: Build params dict conditionally
+        params: dict[str, str | int] = {
+            "sort": sort,
+            "order-by": order_by,
+            "page": page,
+            "page-size": page_size,
+        }
+        if state is not None:
+            params["state"] = state
+
         data = cast(ListSandboxesResponseDict, self._transport.request_json(
             "GET",
             "/v1/sandboxes",
-            params={
-                "state": state,
-                "sort": sort,
-                "order-by": order_by,
-                "page": page,
-                "page-size": page_size,
-            },
+            params=params,
             timeout=http_timeout,
         ))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@leap0/_sync/sandbox.py` around lines 262 - 310, The list method currently
always includes "state": state in the params passed to
self._transport.request_json causing a None value to be sent when state is None;
change the params construction in list (and the call to
self._transport.request_json) to build the params dict conditionally (e.g.,
start with params = {"sort": sort, "order-by": order_by, "page": page,
"page-size": page_size} and only add "state": state if state is not None) so the
request omits the state key when state is None; keep existing validation
(valid_states) and preserve the same param keys used by _transport.request_json.
leap0/_async/sandbox.py (1)

257-305: Same optional improvement as the sync version: consider conditionally including state in params.

The validation logic and param handling correctly mirror the sync implementation.

♻️ Optional: Build params dict conditionally
+        params: dict[str, str | int] = {
+            "sort": sort,
+            "order-by": order_by,
+            "page": page,
+            "page-size": page_size,
+        }
+        if state is not None:
+            params["state"] = state
+
         data = cast(ListSandboxesResponseDict, await self._transport.request_json(
             "GET",
             "/v1/sandboxes",
-            params={
-                "state": state,
-                "sort": sort,
-                "order-by": order_by,
-                "page": page,
-                "page-size": page_size,
-            },
+            params=params,
             timeout=http_timeout,
         ))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@leap0/_async/sandbox.py` around lines 257 - 305, The list method currently
always includes "state" in the params dict even when state is None; update async
sandbox.List (the async method named list) to only include the "state" key when
state is not None (keep the existing validation via valid_states), e.g. build
params dict conditionally before calling self._transport.request_json so you
only send "state" when provided while leaving "sort", "order-by", "page", and
"page-size" unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@leap0/models/snapshot.py`:
- Around line 110-116: The from_dict classmethod on SnapshotListResponse
currently silently defaults missing "items" and "total_items" to [] and 0;
change it to treat those keys as required: check that "items" exists in the
input dict and is a list (and that each element can be passed to
Snapshot.from_dict), and that "total_items" exists and can be coerced to int; if
validation fails, raise a clear exception (ValueError/TypeError) rather than
using the defaults so malformed wire responses aren't silently accepted.
Reference the from_dict method, the "items" and "total_items" keys, and
Snapshot.from_dict when adding these checks.

In `@tests/_async/test_snapshots.py`:
- Around line 59-62: The test test_list_validates_page_size is currently
catching a broad Exception; update it to assert the specific Leap0Error thrown
by AsyncSnapshotsClient.list when page_size is invalid by replacing
pytest.raises(Exception, match="page_size") with pytest.raises(Leap0Error,
match="page_size") and ensure Leap0Error is imported into the test module so the
test mirrors the sync variant.

---

Nitpick comments:
In `@leap0/_async/sandbox.py`:
- Around line 257-305: The list method currently always includes "state" in the
params dict even when state is None; update async sandbox.List (the async method
named list) to only include the "state" key when state is not None (keep the
existing validation via valid_states), e.g. build params dict conditionally
before calling self._transport.request_json so you only send "state" when
provided while leaving "sort", "order-by", "page", and "page-size" unchanged.

In `@leap0/_sync/sandbox.py`:
- Around line 262-310: The list method currently always includes "state": state
in the params passed to self._transport.request_json causing a None value to be
sent when state is None; change the params construction in list (and the call to
self._transport.request_json) to build the params dict conditionally (e.g.,
start with params = {"sort": sort, "order-by": order_by, "page": page,
"page-size": page_size} and only add "state": state if state is not None) so the
request omits the state key when state is None; keep existing validation
(valid_states) and preserve the same param keys used by _transport.request_json.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5096f7e7-dcc8-4d28-accc-6bcb2cf1c6c6

📥 Commits

Reviewing files that changed from the base of the PR and between 44d40ad and 475526a.

📒 Files selected for processing (14)
  • leap0/__init__.py
  • leap0/_async/sandbox.py
  • leap0/_async/snapshots.py
  • leap0/_schemas/sandbox.py
  • leap0/_schemas/snapshot.py
  • leap0/_sync/sandbox.py
  • leap0/_sync/snapshots.py
  • leap0/models/sandbox.py
  • leap0/models/snapshot.py
  • tests/_async/test_sandboxes.py
  • tests/_async/test_snapshots.py
  • tests/_sync/test_sandboxes.py
  • tests/_sync/test_snapshots.py
  • tests/test_import.py

Comment thread leap0/models/snapshot.py
Comment thread tests/_async/test_snapshots.py
@steven-passynkov

Copy link
Copy Markdown
Contributor Author

@CodeRabbit review?

@coderabbitai

coderabbitai Bot commented Apr 13, 2026

Copy link
Copy Markdown

@steven-passynkov Sure, I'll review the changes in this PR now!

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@steven-passynkov steven-passynkov merged commit 2ed8618 into main Apr 14, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant