import urllib.parse from typing import Optional, List from typing_extensions import Unpack from e2b.api import handle_api_exception from e2b.api.client.api.sandboxes import get_v2_sandboxes from e2b.api.client.api.snapshots import get_snapshots from e2b.api.client.models.error import Error from e2b.api.client.types import UNSET from e2b.connection_config import ApiParams, ConnectionConfig from e2b.exceptions import SandboxException from e2b.sandbox.sandbox_api import ( SandboxPaginatorBase, SandboxInfo, SnapshotPaginatorBase, SnapshotInfo, ) from e2b.api.client_sync import get_api_client class SandboxPaginator(SandboxPaginatorBase): """ Paginator for listing sandboxes. Example: ```python paginator = Sandbox.list() while paginator.has_next: sandboxes = paginator.next_items() print(sandboxes) ``` """ def next_items(self, **opts: Unpack[ApiParams]) -> List[SandboxInfo]: """ Returns the next page of sandboxes. Call this method only if `has_next` is `True`, otherwise it will raise an exception. :param opts: Per-call connection options (e.g. `api_key`, `domain`, `headers`, `request_timeout`). When provided, this call uses these options instead of the ones the paginator was constructed with. :returns: List of sandboxes """ if not self.has_next: raise Exception("No more items to fetch") # Convert filters to the format expected by the API metadata: Optional[str] = None if self.query and self.query.metadata: quoted_metadata = { urllib.parse.quote(k): urllib.parse.quote(v) for k, v in self.query.metadata.items() } metadata = urllib.parse.urlencode(quoted_metadata) config = ConnectionConfig(**{**self._opts, **opts}) api_client = get_api_client(config) res = get_v2_sandboxes.sync_detailed( client=api_client, metadata=metadata if metadata else UNSET, state=self.query.state if self.query and self.query.state else UNSET, limit=self.limit if self.limit else UNSET, next_token=self._next_token if self._next_token else UNSET, ) if res.status_code >= 300: raise handle_api_exception(res) self._update_pagination(res.headers) if res.parsed is None: return [] # Check if res.parsed is Error if isinstance(res.parsed, Error): raise SandboxException(f"{res.parsed.message}: Request failed") return [SandboxInfo._from_listed_sandbox(sandbox) for sandbox in res.parsed] class SnapshotPaginator(SnapshotPaginatorBase): """ Paginator for listing snapshots. Example: ```python paginator = Sandbox.list_snapshots() while paginator.has_next: snapshots = paginator.next_items() print(snapshots) ``` """ def next_items(self, **opts: Unpack[ApiParams]) -> List[SnapshotInfo]: """ Returns the next page of snapshots. Call this method only if `has_next` is `True`, otherwise it will raise an exception. :param opts: Per-call connection options (e.g. `api_key`, `domain`, `headers`, `request_timeout`). When provided, this call uses these options instead of the ones the paginator was constructed with. :returns: List of snapshots """ if not self.has_next: raise Exception("No more items to fetch") config = ConnectionConfig(**{**self._opts, **opts}) api_client = get_api_client(config) res = get_snapshots.sync_detailed( client=api_client, sandbox_id=self.sandbox_id if self.sandbox_id else UNSET, limit=self.limit if self.limit else UNSET, next_token=self._next_token if self._next_token else UNSET, ) if res.status_code >= 300: raise handle_api_exception(res) self._update_pagination(res.headers) if res.parsed is None: return [] if isinstance(res.parsed, Error): raise SandboxException(f"{res.parsed.message}: Request failed") return [ SnapshotInfo( snapshot_id=snapshot.snapshot_id, names=list(snapshot.names) if snapshot.names else [], ) for snapshot in res.parsed ]