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
5 changes: 3 additions & 2 deletions api.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@ from runloop_api_client.types import (
Methods:

- <code title="post /v1/blueprints">client.blueprints.<a href="./src/runloop_api_client/resources/blueprints.py">create</a>(\*\*<a href="src/runloop_api_client/types/blueprint_create_params.py">params</a>) -> <a href="./src/runloop_api_client/types/blueprint_view.py">BlueprintView</a></code>
- <code title="create_and_await_build_complete">client.blueprints.<a href="./src/runloop_api_client/resources/blueprints.py">create_and_await_build_complete</a>(\*\*<a href="src/runloop_api_client/types/blueprint_create_params.py">params) -> <a href="./src/runloop_api_client/types/blueprint_view.py">BlueprintView</a></code>
- <code title="get /v1/blueprints/{id}">client.blueprints.<a href="./src/runloop_api_client/resources/blueprints.py">retrieve</a>(id) -> <a href="./src/runloop_api_client/types/blueprint_view.py">BlueprintView</a></code>
- <code title="get /v1/blueprints">client.blueprints.<a href="./src/runloop_api_client/resources/blueprints.py">list</a>(\*\*<a href="src/runloop_api_client/types/blueprint_list_params.py">params</a>) -> <a href="./src/runloop_api_client/types/blueprint_view.py">SyncBlueprintsCursorIDPage[BlueprintView]</a></code>
- <code title="post /v1/blueprints/{id}/delete">client.blueprints.<a href="./src/runloop_api_client/resources/blueprints.py">delete</a>(id) -> object</code>
- <code title="get /v1/blueprints/list_public">client.blueprints.<a href="./src/runloop_api_client/resources/blueprints.py">list_public</a>(\*\*<a href="src/runloop_api_client/types/blueprint_list_public_params.py">params</a>) -> <a href="./src/runloop_api_client/types/blueprint_view.py">SyncBlueprintsCursorIDPage[BlueprintView]</a></code>
- <code title="get /v1/blueprints/{id}/logs">client.blueprints.<a href="./src/runloop_api_client/resources/blueprints.py">logs</a>(id) -> <a href="./src/runloop_api_client/types/blueprint_build_logs_list_view.py">BlueprintBuildLogsListView</a></code>
- <code title="post /v1/blueprints/preview">client.blueprints.<a href="./src/runloop_api_client/resources/blueprints.py">preview</a>(\*\*<a href="src/runloop_api_client/types/blueprint_preview_params.py">params</a>) -> <a href="./src/runloop_api_client/types/blueprint_preview_view.py">BlueprintPreviewView</a></code>
- <code title="create_and_await_build_complete">client.blueprints.<a href="./src/runloop_api_client/resources/blueprints.py">create_and_await_build_complete</a>(<a href="src/runloop_api_client/types/blueprint_create_params.py">create_args</a>, <a href="src/runloop_api_client/resources/blueprints.py">request_args</a>=None) -> <a href="./src/runloop_api_client/types/blueprint_view.py">BlueprintView</a></code>

# Devboxes

Expand All @@ -86,6 +86,7 @@ from runloop_api_client.types import (
Methods:

- <code title="post /v1/devboxes">client.devboxes.<a href="./src/runloop_api_client/resources/devboxes/devboxes.py">create</a>(\*\*<a href="src/runloop_api_client/types/devbox_create_params.py">params</a>) -> <a href="./src/runloop_api_client/types/devbox_view.py">DevboxView</a></code>
- <code title="create_and_await_running">client.devboxes.<a href="./src/runloop_api_client/resources/devboxes/devboxes.py">create_and_await_running</a>(\*\*<a href="src/runloop_api_client/types/devbox_create_params.py">params</a>) -> <a href="./src/runloop_api_client/types/devbox_view.py">DevboxView</a></code>
- <code title="get /v1/devboxes/{id}">client.devboxes.<a href="./src/runloop_api_client/resources/devboxes/devboxes.py">retrieve</a>(id) -> <a href="./src/runloop_api_client/types/devbox_view.py">DevboxView</a></code>
- <code title="post /v1/devboxes/{id}">client.devboxes.<a href="./src/runloop_api_client/resources/devboxes/devboxes.py">update</a>(id, \*\*<a href="src/runloop_api_client/types/devbox_update_params.py">params</a>) -> <a href="./src/runloop_api_client/types/devbox_view.py">DevboxView</a></code>
- <code title="get /v1/devboxes">client.devboxes.<a href="./src/runloop_api_client/resources/devboxes/devboxes.py">list</a>(\*\*<a href="src/runloop_api_client/types/devbox_list_params.py">params</a>) -> <a href="./src/runloop_api_client/types/devbox_view.py">SyncDevboxesCursorIDPage[DevboxView]</a></code>
Expand All @@ -106,7 +107,7 @@ Methods:
- <code title="post /v1/devboxes/{id}/suspend">client.devboxes.<a href="./src/runloop_api_client/resources/devboxes/devboxes.py">suspend</a>(id) -> <a href="./src/runloop_api_client/types/devbox_view.py">DevboxView</a></code>
- <code title="post /v1/devboxes/{id}/upload_file">client.devboxes.<a href="./src/runloop_api_client/resources/devboxes/devboxes.py">upload_file</a>(id, \*\*<a href="src/runloop_api_client/types/devbox_upload_file_params.py">params</a>) -> object</code>
- <code title="post /v1/devboxes/{id}/write_file_contents">client.devboxes.<a href="./src/runloop_api_client/resources/devboxes/devboxes.py">write_file_contents</a>(id, \*\*<a href="src/runloop_api_client/types/devbox_write_file_contents_params.py">params</a>) -> <a href="./src/runloop_api_client/types/devbox_execution_detail_view.py">DevboxExecutionDetailView</a></code>
- <code title="create_and_await_running">client.devboxes.<a href="./src/runloop_api_client/resources/devboxes/devboxes.py">create_and_await_running</a>(<a href="src/runloop_api_client/types/devbox_create_params.py">create_args</a>, <a href="src/runloop_api_client/resources/devboxes/devboxes.py">request_args</a>=None) -> <a href="./src/runloop_api_client/types/devbox_view.py">DevboxView</a></code>


## DiskSnapshots

Expand Down
31 changes: 13 additions & 18 deletions src/runloop_api_client/lib/polling_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

from .polling import PollingConfig, PollingTimeout

T = TypeVar('T')
T = TypeVar("T")


async def async_poll_until(
retriever: Callable[[], Awaitable[T]],
Expand All @@ -14,27 +15,27 @@ async def async_poll_until(
) -> T:
"""
Poll until a condition is met or timeout/max attempts are reached.

Args:
retriever: Async or sync callable that returns the object to check
is_terminal: Callable that returns True when polling should stop
config: Optional polling configuration
on_error: Optional error handler that can return a value to continue polling
or re-raise the exception to stop polling

Returns:
The final state of the polled object

Raises:
PollingTimeout: When max attempts or timeout is reached
"""
if config is None:
config = PollingConfig()

attempts = 0
start_time = time.time()
last_result: Union[T, None] = None

while True:
try:
last_result = await retriever()
Expand All @@ -43,23 +44,17 @@ async def async_poll_until(
last_result = on_error(e)
else:
raise

if is_terminal(last_result):
return last_result

attempts += 1
if attempts >= config.max_attempts:
raise PollingTimeout(
f"Exceeded maximum attempts ({config.max_attempts})",
last_result
)

raise PollingTimeout(f"Exceeded maximum attempts ({config.max_attempts})", last_result)

if config.timeout_seconds is not None:
elapsed = time.time() - start_time
if elapsed >= config.timeout_seconds:
raise PollingTimeout(
f"Exceeded timeout of {config.timeout_seconds} seconds",
last_result
)

raise PollingTimeout(f"Exceeded timeout of {config.timeout_seconds} seconds", last_result)

await asyncio.sleep(config.interval_seconds)
103 changes: 76 additions & 27 deletions src/runloop_api_client/resources/blueprints.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,16 +230,30 @@ def is_done_building(blueprint: BlueprintView) -> bool:
def create_and_await_build_complete(
self,
*,
create_args: blueprint_create_params.BlueprintCreateParams,
request_args: BlueprintRequestArgs | None = None,
name: str,
base_blueprint_id: Optional[str] | NotGiven = NOT_GIVEN,
code_mounts: Optional[Iterable[CodeMountParameters]] | NotGiven = NOT_GIVEN,
dockerfile: Optional[str] | NotGiven = NOT_GIVEN,
file_mounts: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN,
launch_parameters: Optional[LaunchParameters] | NotGiven = NOT_GIVEN,
polling_config: PollingConfig | None = None,
services: Optional[Iterable[blueprint_create_params.Service]] | NotGiven = NOT_GIVEN,
system_setup_commands: Optional[List[str]] | NotGiven = NOT_GIVEN,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
idempotency_key: str | None = None,
) -> BlueprintView:
"""Create a new Blueprint and wait for it to finish building.

This is a wrapper around the `create` method that waits for the blueprint to finish building.

Args:
create_args: Arguments to pass to the `create` method. See the `create` method for detailed documentation.
request_args: Optional request arguments including polling configuration and additional request options
See the `create` method for detailed documentation.
polling_config: Optional polling configuration

Returns:
The built blueprint
Expand All @@ -249,18 +263,29 @@ def create_and_await_build_complete(
RunloopError: If blueprint enters a non-built terminal state
"""
# Pass all create_args to the underlying create method
blueprint = self.create(**create_args)

if request_args is None:
request_args = {}
blueprint = self.create(
name=name,
base_blueprint_id=base_blueprint_id,
code_mounts=code_mounts,
dockerfile=dockerfile,
file_mounts=file_mounts,
launch_parameters=launch_parameters,
services=services,
system_setup_commands=system_setup_commands,
extra_headers=extra_headers,
extra_query=extra_query,
extra_body=extra_body,
timeout=timeout,
idempotency_key=idempotency_key,
)

return self.await_build_complete(
blueprint.id,
polling_config=request_args.get("polling_config", None),
extra_headers=request_args.get("extra_headers", None),
extra_query=request_args.get("extra_query", None),
extra_body=request_args.get("extra_body", None),
timeout=request_args.get("timeout", None),
polling_config=polling_config,
extra_headers=extra_headers,
extra_query=extra_query,
extra_body=extra_body,
timeout=timeout,
)

def list(
Expand Down Expand Up @@ -700,16 +725,30 @@ def is_done_building(blueprint: BlueprintView) -> bool:
async def create_and_await_build_complete(
self,
*,
create_args: blueprint_create_params.BlueprintCreateParams,
request_args: BlueprintRequestArgs | None = None,
name: str,
base_blueprint_id: Optional[str] | NotGiven = NOT_GIVEN,
code_mounts: Optional[Iterable[CodeMountParameters]] | NotGiven = NOT_GIVEN,
dockerfile: Optional[str] | NotGiven = NOT_GIVEN,
file_mounts: Optional[Dict[str, str]] | NotGiven = NOT_GIVEN,
launch_parameters: Optional[LaunchParameters] | NotGiven = NOT_GIVEN,
polling_config: PollingConfig | None = None,
services: Optional[Iterable[blueprint_create_params.Service]] | NotGiven = NOT_GIVEN,
system_setup_commands: Optional[List[str]] | NotGiven = NOT_GIVEN,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
# The extra values given here take precedence over values defined on the client or passed to this method.
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
idempotency_key: str | None = None,
) -> BlueprintView:
"""Create a new Blueprint and wait for it to finish building.

This is a wrapper around the `create` method that waits for the blueprint to finish building.

Args:
create_args: Arguments to pass to the `create` method. See the `create` method for detailed documentation.
request_args: Optional request arguments including polling configuration and additional request options
See the `create` method for detailed documentation.
polling_config: Optional polling configuration

Returns:
The built blueprint
Expand All @@ -719,19 +758,29 @@ async def create_and_await_build_complete(
RunloopError: If blueprint enters a non-built terminal state
"""
# Pass all create_args to the underlying create method
blueprint = await self.create(**create_args)

# Extract polling config and other request args
if request_args is None:
request_args = {}
blueprint = await self.create(
name=name,
base_blueprint_id=base_blueprint_id,
code_mounts=code_mounts,
dockerfile=dockerfile,
file_mounts=file_mounts,
launch_parameters=launch_parameters,
services=services,
system_setup_commands=system_setup_commands,
extra_headers=extra_headers,
extra_query=extra_query,
extra_body=extra_body,
timeout=timeout,
idempotency_key=idempotency_key,
)

return await self.await_build_complete(
blueprint.id,
polling_config=request_args.get("polling_config", None),
extra_headers=request_args.get("extra_headers", None),
extra_query=request_args.get("extra_query", None),
extra_body=request_args.get("extra_body", None),
timeout=request_args.get("timeout", None),
polling_config=polling_config,
extra_headers=extra_headers,
extra_query=extra_query,
extra_body=extra_body,
timeout=timeout,
)

def list(
Expand Down
Loading