render_sdk 0.1.2__py3-none-any.whl → 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- render_sdk/__init__.py +41 -4
- render_sdk/client/__init__.py +25 -0
- render_sdk/client/client.py +5 -0
- render_sdk/client/sse.py +5 -1
- render_sdk/client/tests/test_client.py +6 -4
- render_sdk/client/tests/test_sse.py +1 -0
- render_sdk/client/types.py +2 -1
- render_sdk/client/workflows.py +13 -3
- render_sdk/experimental/__init__.py +31 -0
- render_sdk/experimental/experimental.py +71 -0
- render_sdk/experimental/object/__init__.py +30 -0
- render_sdk/experimental/object/api.py +260 -0
- render_sdk/experimental/object/client.py +475 -0
- render_sdk/experimental/object/types.py +87 -0
- render_sdk/public_api/api/audit_logs/list_organization_audit_logs.py +303 -0
- render_sdk/public_api/api/audit_logs/list_owner_audit_logs.py +303 -0
- render_sdk/public_api/api/blob_storage/delete_blob.py +215 -0
- render_sdk/public_api/api/blob_storage/get_blob.py +221 -0
- render_sdk/public_api/api/{workflows/list_workflow_versions.py → blob_storage/list_blobs.py} +52 -30
- render_sdk/public_api/api/blob_storage/put_blob.py +248 -0
- render_sdk/public_api/api/blueprints/validate_blueprint.py +212 -0
- render_sdk/public_api/api/key_value/resume_key_value.py +203 -0
- render_sdk/public_api/api/key_value/suspend_key_value.py +203 -0
- render_sdk/public_api/api/metrics/get_bandwidth_sources.py +251 -0
- render_sdk/public_api/api/postgres/create_postgres_user.py +229 -0
- render_sdk/public_api/api/postgres/delete_postgres_user.py +201 -0
- render_sdk/public_api/api/postgres/list_postgres_users.py +195 -0
- render_sdk/public_api/api/redis_deprecated/__init__.py +1 -0
- render_sdk/public_api/api/{redis → redis_deprecated}/create_redis.py +4 -4
- render_sdk/public_api/api/{redis → redis_deprecated}/delete_redis.py +4 -4
- render_sdk/public_api/api/{redis → redis_deprecated}/list_redis.py +4 -0
- render_sdk/public_api/api/{redis → redis_deprecated}/retrieve_redis.py +4 -4
- render_sdk/public_api/api/{redis → redis_deprecated}/retrieve_redis_connection_info.py +4 -0
- render_sdk/public_api/api/{redis → redis_deprecated}/update_redis.py +4 -4
- render_sdk/public_api/api/services/create_service.py +4 -4
- render_sdk/public_api/api/workflow_tasks_ea/__init__.py +1 -0
- render_sdk/public_api/api/{workflows → workflow_tasks_ea}/cancel_task_run.py +12 -4
- render_sdk/public_api/api/{workflows → workflow_tasks_ea}/create_task.py +12 -4
- render_sdk/public_api/api/{workflows → workflow_tasks_ea}/get_task.py +12 -4
- render_sdk/public_api/api/{workflows → workflow_tasks_ea}/get_task_run.py +12 -4
- render_sdk/public_api/api/{workflows → workflow_tasks_ea}/list_task_runs.py +12 -0
- render_sdk/public_api/api/{workflows → workflow_tasks_ea}/list_tasks.py +24 -12
- render_sdk/public_api/api/workflows_ea/__init__.py +1 -0
- render_sdk/public_api/api/workflows_ea/create_workflow.py +199 -0
- render_sdk/public_api/api/{workflows/deploy_workflow.py → workflows_ea/create_workflow_version.py} +31 -14
- render_sdk/public_api/api/{workflows → workflows_ea}/delete_workflow.py +12 -4
- render_sdk/public_api/api/{workflows → workflows_ea}/get_workflow.py +32 -14
- render_sdk/public_api/api/{workflows → workflows_ea}/get_workflow_version.py +12 -4
- render_sdk/public_api/api/workflows_ea/list_workflow_versions.py +275 -0
- render_sdk/public_api/api/{workflows → workflows_ea}/list_workflows.py +41 -14
- render_sdk/public_api/api/workflows_ea/update_workflow.py +212 -0
- render_sdk/public_api/api/workspaces/remove_workspace_member.py +206 -0
- render_sdk/public_api/api/workspaces/update_workspace_member.py +235 -0
- render_sdk/public_api/models/__init__.py +82 -4
- render_sdk/public_api/models/audit_log.py +113 -0
- render_sdk/public_api/models/audit_log_actor.py +80 -0
- render_sdk/public_api/models/audit_log_actor_type.py +10 -0
- render_sdk/public_api/models/audit_log_event.py +80 -0
- render_sdk/public_api/models/audit_log_metadata.py +49 -0
- render_sdk/public_api/models/audit_log_status.py +9 -0
- render_sdk/public_api/models/audit_log_with_cursor.py +73 -0
- render_sdk/public_api/models/background_worker_details.py +2 -2
- render_sdk/public_api/models/background_worker_details_patch.py +1 -1
- render_sdk/public_api/models/background_worker_details_post.py +1 -1
- render_sdk/public_api/models/blob_metadata.py +85 -0
- render_sdk/public_api/models/blob_with_cursor.py +73 -0
- render_sdk/public_api/models/cache.py +6 -4
- render_sdk/public_api/models/cache_profile.py +10 -0
- render_sdk/public_api/models/create_deploy_body.py +23 -0
- render_sdk/public_api/models/create_version.py +70 -0
- render_sdk/public_api/models/credential_create_input.py +59 -0
- render_sdk/public_api/models/cron_job_details.py +2 -2
- render_sdk/public_api/models/cron_job_details_patch.py +1 -1
- render_sdk/public_api/models/cron_job_details_post.py +1 -1
- render_sdk/public_api/models/deploy_mode.py +9 -0
- render_sdk/public_api/models/event.py +11 -27
- render_sdk/public_api/models/event_type.py +1 -1
- render_sdk/public_api/models/get_bandwidth_sources_response_200.py +75 -0
- render_sdk/public_api/models/get_bandwidth_sources_response_200_data_item.py +101 -0
- render_sdk/public_api/models/get_bandwidth_sources_response_200_data_item_labels.py +78 -0
- render_sdk/public_api/models/get_bandwidth_sources_response_200_data_item_labels_traffic_source.py +12 -0
- render_sdk/public_api/models/get_bandwidth_sources_response_200_data_item_values_item.py +68 -0
- render_sdk/public_api/models/{server_unhealthy.py → get_bandwidth_sources_response_400.py} +12 -12
- render_sdk/public_api/models/get_blob_output.py +71 -0
- render_sdk/public_api/models/list_postgres_users_response_200_item.py +86 -0
- render_sdk/public_api/models/otel_provider_type.py +2 -0
- render_sdk/public_api/models/postgres.py +8 -0
- render_sdk/public_api/models/postgres_detail.py +26 -0
- render_sdk/public_api/models/postgres_parameter_overrides.py +44 -0
- render_sdk/public_api/models/postgres_patch_input.py +27 -0
- render_sdk/public_api/models/postgres_post_input.py +27 -0
- render_sdk/public_api/models/postgres_version.py +1 -0
- render_sdk/public_api/models/preview_input.py +2 -2
- render_sdk/public_api/models/private_service_details.py +2 -2
- render_sdk/public_api/models/private_service_details_patch.py +1 -1
- render_sdk/public_api/models/private_service_details_post.py +1 -1
- render_sdk/public_api/models/project_post_environment_input.py +26 -1
- render_sdk/public_api/models/put_blob_input.py +59 -0
- render_sdk/public_api/models/put_blob_output.py +79 -0
- render_sdk/public_api/models/read_replica.py +25 -1
- render_sdk/public_api/models/read_replica_input.py +25 -1
- render_sdk/public_api/models/run_task.py +35 -7
- render_sdk/public_api/models/service_event.py +12 -27
- render_sdk/public_api/models/service_event_type.py +0 -1
- render_sdk/public_api/models/service_post.py +9 -6
- render_sdk/public_api/models/task_attempt.py +88 -0
- render_sdk/public_api/models/task_attempt_details.py +108 -0
- render_sdk/public_api/models/task_data_type_1.py +44 -0
- render_sdk/public_api/models/task_run.py +23 -1
- render_sdk/public_api/models/task_run_details.py +50 -5
- render_sdk/public_api/models/task_run_status.py +1 -0
- render_sdk/public_api/models/task_with_cursor.py +73 -0
- render_sdk/public_api/models/team_member.py +5 -4
- render_sdk/public_api/models/team_member_role.py +12 -0
- render_sdk/public_api/models/update_workspace_member_body.py +61 -0
- render_sdk/public_api/models/validate_blueprint_request.py +84 -0
- render_sdk/public_api/models/validate_blueprint_response.py +105 -0
- render_sdk/public_api/models/validation_error.py +88 -0
- render_sdk/public_api/models/validation_plan_summary.py +107 -0
- render_sdk/public_api/models/web_service_details.py +2 -2
- render_sdk/public_api/models/web_service_details_patch.py +6 -5
- render_sdk/public_api/models/web_service_details_post.py +6 -5
- render_sdk/public_api/models/workflow.py +144 -0
- render_sdk/public_api/models/workflow_create.py +99 -0
- render_sdk/public_api/models/workflow_update.py +90 -0
- render_sdk/public_api/models/workflow_version.py +10 -14
- render_sdk/public_api/models/workflow_version_status.py +13 -0
- render_sdk/public_api/models/workflow_version_with_cursor.py +73 -0
- render_sdk/public_api/models/workflow_with_cursor.py +73 -0
- render_sdk/render.py +65 -0
- render_sdk/version.py +27 -0
- render_sdk/workflows/__init__.py +5 -1
- render_sdk/workflows/app.py +262 -0
- render_sdk/workflows/callback_api/models/__init__.py +2 -0
- render_sdk/workflows/callback_api/models/task.py +21 -0
- render_sdk/workflows/callback_api/models/task_options.py +18 -0
- render_sdk/workflows/callback_api/models/task_parameter.py +88 -0
- render_sdk/workflows/callback_api/py.typed +1 -1
- render_sdk/workflows/cli.py +58 -0
- render_sdk/workflows/client.py +8 -9
- render_sdk/workflows/executor.py +19 -7
- render_sdk/workflows/runner.py +43 -10
- render_sdk/workflows/task.py +84 -5
- render_sdk/workflows/tests/test_app.py +412 -0
- render_sdk/workflows/tests/test_cli.py +134 -0
- render_sdk/workflows/tests/test_end_to_end.py +71 -1
- render_sdk/workflows/tests/test_registration.py +58 -1
- {render_sdk-0.1.2.dist-info → render_sdk-0.2.0.dist-info}/METADATA +4 -3
- {render_sdk-0.1.2.dist-info → render_sdk-0.2.0.dist-info}/RECORD +155 -83
- {render_sdk-0.1.2.dist-info → render_sdk-0.2.0.dist-info}/WHEEL +1 -1
- render_sdk-0.2.0.dist-info/entry_points.txt +3 -0
- render_sdk/public_api/models/image_version.py +0 -79
- /render_sdk/public_api/api/{redis → audit_logs}/__init__.py +0 -0
- /render_sdk/public_api/api/{workflows → blob_storage}/__init__.py +0 -0
- /render_sdk/public_api/api/{workflows → workflow_tasks_ea}/stream_task_runs_events.py +0 -0
- {render_sdk-0.1.2.dist-info → render_sdk-0.2.0.dist-info/licenses}/LICENSE +0 -0
render_sdk/__init__.py
CHANGED
|
@@ -1,8 +1,45 @@
|
|
|
1
1
|
"""Render Python SDK
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Task definition (for workers):
|
|
4
|
+
|
|
5
|
+
from render_sdk import Workflows, Retry
|
|
6
|
+
|
|
7
|
+
app = Workflows(auto_start=True)
|
|
8
|
+
|
|
9
|
+
@app.task
|
|
10
|
+
def my_task(x: int) -> int:
|
|
11
|
+
return x * 2
|
|
12
|
+
|
|
13
|
+
REST API access (for clients):
|
|
14
|
+
|
|
15
|
+
from render_sdk import Render
|
|
16
|
+
|
|
17
|
+
render = Render()
|
|
18
|
+
result = await render.workflows.run_task("my-workflow/my_task", [5])
|
|
6
19
|
"""
|
|
7
20
|
|
|
8
|
-
__version__ = "0.
|
|
21
|
+
__version__ = "0.2.0"
|
|
22
|
+
|
|
23
|
+
# Primary user-facing APIs
|
|
24
|
+
from render_sdk.render import Render
|
|
25
|
+
from render_sdk.workflows import Options, Retry, Workflows, start, task
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
"__version__",
|
|
29
|
+
# Primary APIs
|
|
30
|
+
"Render", # REST API client
|
|
31
|
+
"Workflows", # Task definition
|
|
32
|
+
# Configuration
|
|
33
|
+
"Options",
|
|
34
|
+
"Retry",
|
|
35
|
+
# Deprecated: use Workflows.task and Workflows.start() instead
|
|
36
|
+
"start",
|
|
37
|
+
"task",
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
# Direct client access available via:
|
|
41
|
+
# render.client # Access from existing Render instance
|
|
42
|
+
# from render_sdk.client import Client # Or import directly
|
|
43
|
+
#
|
|
44
|
+
# Raw API access available via:
|
|
45
|
+
# from render_sdk.public_api import AuthenticatedClient
|
render_sdk/client/__init__.py
CHANGED
|
@@ -16,6 +16,19 @@ from render_sdk.client.types import (
|
|
|
16
16
|
TaskRunStatus,
|
|
17
17
|
)
|
|
18
18
|
from render_sdk.client.workflows import WorkflowsService
|
|
19
|
+
from render_sdk.experimental import (
|
|
20
|
+
DownloadResponse,
|
|
21
|
+
ExperimentalService,
|
|
22
|
+
ObjectApi,
|
|
23
|
+
ObjectClient,
|
|
24
|
+
ObjectData,
|
|
25
|
+
OwnerID,
|
|
26
|
+
PutObjectResult,
|
|
27
|
+
Region,
|
|
28
|
+
ScopedObjectClient,
|
|
29
|
+
StorageService,
|
|
30
|
+
UploadResponse,
|
|
31
|
+
)
|
|
19
32
|
|
|
20
33
|
__all__ = [
|
|
21
34
|
"Client",
|
|
@@ -29,4 +42,16 @@ __all__ = [
|
|
|
29
42
|
"LimitParam",
|
|
30
43
|
"CursorParam",
|
|
31
44
|
"OwnerIDParam",
|
|
45
|
+
# Experimental exports
|
|
46
|
+
"ExperimentalService",
|
|
47
|
+
"StorageService",
|
|
48
|
+
"ObjectApi",
|
|
49
|
+
"ObjectClient",
|
|
50
|
+
"ScopedObjectClient",
|
|
51
|
+
"DownloadResponse",
|
|
52
|
+
"ObjectData",
|
|
53
|
+
"OwnerID",
|
|
54
|
+
"PutObjectResult",
|
|
55
|
+
"Region",
|
|
56
|
+
"UploadResponse",
|
|
32
57
|
]
|
render_sdk/client/client.py
CHANGED
|
@@ -7,7 +7,9 @@ import os
|
|
|
7
7
|
|
|
8
8
|
from render_sdk.client.sse import SSEClient
|
|
9
9
|
from render_sdk.client.workflows import WorkflowsService
|
|
10
|
+
from render_sdk.experimental.experimental import ExperimentalService
|
|
10
11
|
from render_sdk.public_api.client import AuthenticatedClient
|
|
12
|
+
from render_sdk.version import get_user_agent
|
|
11
13
|
|
|
12
14
|
|
|
13
15
|
class Client:
|
|
@@ -21,6 +23,7 @@ class Client:
|
|
|
21
23
|
token: The authentication token
|
|
22
24
|
base_url: The API base URL
|
|
23
25
|
workflows: Service client for workflow operations
|
|
26
|
+
experimental: Service client for experimental features
|
|
24
27
|
"""
|
|
25
28
|
|
|
26
29
|
token: str
|
|
@@ -71,8 +74,10 @@ class Client:
|
|
|
71
74
|
self.internal = AuthenticatedClient(
|
|
72
75
|
base_url=api_base,
|
|
73
76
|
token=self.token,
|
|
77
|
+
headers={"User-Agent": get_user_agent()},
|
|
74
78
|
)
|
|
75
79
|
|
|
76
80
|
# Initialize service clients
|
|
77
81
|
self.workflows = WorkflowsService(self)
|
|
78
82
|
self.sse = SSEClient(self)
|
|
83
|
+
self.experimental = ExperimentalService(self)
|
render_sdk/client/sse.py
CHANGED
|
@@ -12,7 +12,10 @@ import httpx
|
|
|
12
12
|
|
|
13
13
|
from render_sdk.client.types import TaskRunDetails
|
|
14
14
|
from render_sdk.client.util import handle_http_error, handle_httpx_exception
|
|
15
|
-
from render_sdk.public_api.api.
|
|
15
|
+
from render_sdk.public_api.api.workflow_tasks_ea.stream_task_runs_events import (
|
|
16
|
+
_get_kwargs,
|
|
17
|
+
)
|
|
18
|
+
from render_sdk.version import get_user_agent
|
|
16
19
|
|
|
17
20
|
if TYPE_CHECKING:
|
|
18
21
|
from render_sdk.client.client import Client
|
|
@@ -58,6 +61,7 @@ class SSEClient:
|
|
|
58
61
|
"Accept": "text/event-stream",
|
|
59
62
|
"Cache-Control": "no-cache",
|
|
60
63
|
"Authorization": f"Bearer {self.client.token}",
|
|
64
|
+
"User-Agent": get_user_agent(),
|
|
61
65
|
}
|
|
62
66
|
)
|
|
63
67
|
|
|
@@ -25,6 +25,7 @@ def mock_task_run(mocker):
|
|
|
25
25
|
parent_task_run_id=None,
|
|
26
26
|
root_task_run_id=None,
|
|
27
27
|
retries=0,
|
|
28
|
+
attempts=[],
|
|
28
29
|
started_at=None,
|
|
29
30
|
task_id=None,
|
|
30
31
|
)
|
|
@@ -42,6 +43,7 @@ def mock_task_run_details(mocker):
|
|
|
42
43
|
parent_task_run_id=None,
|
|
43
44
|
root_task_run_id=None,
|
|
44
45
|
retries=0,
|
|
46
|
+
attempts=[],
|
|
45
47
|
started_at=None,
|
|
46
48
|
task_id=None,
|
|
47
49
|
)
|
|
@@ -74,28 +76,28 @@ def workflows_service(client):
|
|
|
74
76
|
@pytest.fixture
|
|
75
77
|
def mock_cancel_task_run_asyncio(mocker):
|
|
76
78
|
return mocker.patch(
|
|
77
|
-
"render_sdk.public_api.api.
|
|
79
|
+
"render_sdk.public_api.api.workflow_tasks_ea.cancel_task_run.asyncio_detailed"
|
|
78
80
|
)
|
|
79
81
|
|
|
80
82
|
|
|
81
83
|
@pytest.fixture
|
|
82
84
|
def mock_list_task_runs_asyncio(mocker):
|
|
83
85
|
return mocker.patch(
|
|
84
|
-
"render_sdk.public_api.api.
|
|
86
|
+
"render_sdk.public_api.api.workflow_tasks_ea.list_task_runs.asyncio_detailed"
|
|
85
87
|
)
|
|
86
88
|
|
|
87
89
|
|
|
88
90
|
@pytest.fixture
|
|
89
91
|
def mock_create_task_asyncio(mocker):
|
|
90
92
|
return mocker.patch(
|
|
91
|
-
"render_sdk.public_api.api.
|
|
93
|
+
"render_sdk.public_api.api.workflow_tasks_ea.create_task.asyncio_detailed"
|
|
92
94
|
)
|
|
93
95
|
|
|
94
96
|
|
|
95
97
|
@pytest.fixture
|
|
96
98
|
def mock_get_task_run_asyncio(mocker):
|
|
97
99
|
return mocker.patch(
|
|
98
|
-
"render_sdk.public_api.api.
|
|
100
|
+
"render_sdk.public_api.api.workflow_tasks_ea.get_task_run.asyncio_detailed"
|
|
99
101
|
)
|
|
100
102
|
|
|
101
103
|
|
render_sdk/client/types.py
CHANGED
|
@@ -11,7 +11,8 @@ from render_sdk.public_api.models.task_run_status import TaskRunStatus as _TaskR
|
|
|
11
11
|
|
|
12
12
|
# Type aliases to match Go client interface
|
|
13
13
|
TaskIdentifier = str
|
|
14
|
-
TaskData
|
|
14
|
+
# TaskData can be either positional (list) or named (dict) parameters
|
|
15
|
+
TaskData = list[Any] | dict[str, Any]
|
|
15
16
|
|
|
16
17
|
# Re-export model classes with cleaner names
|
|
17
18
|
TaskRun = _TaskRun
|
render_sdk/client/workflows.py
CHANGED
|
@@ -15,7 +15,7 @@ from render_sdk.client.types import (
|
|
|
15
15
|
TaskRunStatusValues,
|
|
16
16
|
)
|
|
17
17
|
from render_sdk.client.util import handle_http_errors, retry_with_backoff
|
|
18
|
-
from render_sdk.public_api.api.
|
|
18
|
+
from render_sdk.public_api.api.workflow_tasks_ea import (
|
|
19
19
|
cancel_task_run,
|
|
20
20
|
create_task,
|
|
21
21
|
get_task_run,
|
|
@@ -23,6 +23,7 @@ from render_sdk.public_api.api.workflows import (
|
|
|
23
23
|
)
|
|
24
24
|
from render_sdk.public_api.models.error import Error
|
|
25
25
|
from render_sdk.public_api.models.run_task import RunTask
|
|
26
|
+
from render_sdk.public_api.models.task_data_type_1 import TaskDataType1
|
|
26
27
|
from render_sdk.public_api.types import UNSET, Response
|
|
27
28
|
|
|
28
29
|
if TYPE_CHECKING:
|
|
@@ -125,7 +126,9 @@ class WorkflowsService:
|
|
|
125
126
|
|
|
126
127
|
Args:
|
|
127
128
|
task_identifier: The identifier of the task to run
|
|
128
|
-
input_data: The input data for the task
|
|
129
|
+
input_data: The input data for the task. Can be either:
|
|
130
|
+
- A list for positional arguments: [arg1, arg2, arg3]
|
|
131
|
+
- A dict for named parameters: {"param1": value1, "param2": value2}
|
|
129
132
|
|
|
130
133
|
Returns:
|
|
131
134
|
AwaitableTaskRun: An awaitable task run object
|
|
@@ -147,10 +150,17 @@ class WorkflowsService:
|
|
|
147
150
|
self, task_identifier: TaskIdentifier, input_data: TaskData
|
|
148
151
|
) -> Response[Error | TaskRun]:
|
|
149
152
|
"""Internal method to make the create task API call."""
|
|
153
|
+
# Convert dict to TaskDataType1 for named parameters
|
|
154
|
+
task_data_input: TaskDataType1 | list[Any]
|
|
155
|
+
if isinstance(input_data, dict):
|
|
156
|
+
task_data_input = TaskDataType1.from_dict(input_data)
|
|
157
|
+
else:
|
|
158
|
+
task_data_input = input_data
|
|
159
|
+
|
|
150
160
|
# Create the request body
|
|
151
161
|
run_task = RunTask(
|
|
152
162
|
task=task_identifier,
|
|
153
|
-
input_=
|
|
163
|
+
input_=task_data_input,
|
|
154
164
|
)
|
|
155
165
|
|
|
156
166
|
# Make the API call
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Experimental API exports."""
|
|
2
|
+
|
|
3
|
+
from render_sdk.experimental.experimental import ExperimentalService, StorageService
|
|
4
|
+
from render_sdk.experimental.object import (
|
|
5
|
+
DownloadResponse,
|
|
6
|
+
ObjectApi,
|
|
7
|
+
ObjectClient,
|
|
8
|
+
ObjectData,
|
|
9
|
+
OwnerID,
|
|
10
|
+
PutObjectResult,
|
|
11
|
+
Region,
|
|
12
|
+
ScopedObjectClient,
|
|
13
|
+
UploadResponse,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
# Experimental Service
|
|
18
|
+
"ExperimentalService",
|
|
19
|
+
"StorageService",
|
|
20
|
+
# Object API classes
|
|
21
|
+
"ObjectApi",
|
|
22
|
+
"ObjectClient",
|
|
23
|
+
"ScopedObjectClient",
|
|
24
|
+
# Object types
|
|
25
|
+
"DownloadResponse",
|
|
26
|
+
"ObjectData",
|
|
27
|
+
"OwnerID",
|
|
28
|
+
"PutObjectResult",
|
|
29
|
+
"Region",
|
|
30
|
+
"UploadResponse",
|
|
31
|
+
]
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""Experimental Service
|
|
2
|
+
|
|
3
|
+
This module provides the ExperimentalService class that exposes experimental features
|
|
4
|
+
from the Render SDK.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
8
|
+
|
|
9
|
+
from render_sdk.experimental.object.client import ObjectClient
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from render_sdk.client.client import Client
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class StorageService:
|
|
16
|
+
"""Storage Service
|
|
17
|
+
|
|
18
|
+
Provides access to experimental storage features.
|
|
19
|
+
|
|
20
|
+
Example:
|
|
21
|
+
```python
|
|
22
|
+
# Access object storage
|
|
23
|
+
await client.experimental.storage.objects.put(
|
|
24
|
+
owner_id="tea-xxxxx",
|
|
25
|
+
region="oregon",
|
|
26
|
+
key="file.png",
|
|
27
|
+
data=b"content"
|
|
28
|
+
)
|
|
29
|
+
```
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def __init__(self, client: "Client"):
|
|
33
|
+
"""Initialize the storage service.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
client: The Render client instance
|
|
37
|
+
"""
|
|
38
|
+
self.objects = ObjectClient(client.internal)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class ExperimentalService:
|
|
42
|
+
"""Experimental Service
|
|
43
|
+
|
|
44
|
+
Provides access to experimental Render SDK features.
|
|
45
|
+
|
|
46
|
+
Features in this namespace may change or be removed without a migration plan.
|
|
47
|
+
When a feature stabilizes, it will be promoted to the main SDK namespace.
|
|
48
|
+
|
|
49
|
+
Example:
|
|
50
|
+
```python
|
|
51
|
+
from render_sdk.client import Client
|
|
52
|
+
|
|
53
|
+
client = Client()
|
|
54
|
+
|
|
55
|
+
# Access experimental object storage
|
|
56
|
+
await client.experimental.storage.objects.put(
|
|
57
|
+
owner_id="tea-xxxxx",
|
|
58
|
+
region="oregon",
|
|
59
|
+
key="file.png",
|
|
60
|
+
data=b"content"
|
|
61
|
+
)
|
|
62
|
+
```
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
def __init__(self, client: "Client"):
|
|
66
|
+
"""Initialize the experimental service.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
client: The Render client instance
|
|
70
|
+
"""
|
|
71
|
+
self.storage = StorageService(client)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Object storage API exports."""
|
|
2
|
+
|
|
3
|
+
from render_sdk.experimental.object.api import ObjectApi
|
|
4
|
+
from render_sdk.experimental.object.client import ObjectClient, ScopedObjectClient
|
|
5
|
+
from render_sdk.experimental.object.types import (
|
|
6
|
+
DownloadResponse,
|
|
7
|
+
ListObjectsResponse,
|
|
8
|
+
ObjectData,
|
|
9
|
+
ObjectMetadata,
|
|
10
|
+
OwnerID,
|
|
11
|
+
PutObjectResult,
|
|
12
|
+
Region,
|
|
13
|
+
UploadResponse,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
# API classes
|
|
18
|
+
"ObjectApi",
|
|
19
|
+
"ObjectClient",
|
|
20
|
+
"ScopedObjectClient",
|
|
21
|
+
# Types
|
|
22
|
+
"DownloadResponse",
|
|
23
|
+
"ListObjectsResponse",
|
|
24
|
+
"ObjectData",
|
|
25
|
+
"ObjectMetadata",
|
|
26
|
+
"OwnerID",
|
|
27
|
+
"PutObjectResult",
|
|
28
|
+
"Region",
|
|
29
|
+
"UploadResponse",
|
|
30
|
+
]
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"""Layer 2: Typed Object API Client
|
|
2
|
+
|
|
3
|
+
Provides idiomatic Python wrapper around the raw OpenAPI client.
|
|
4
|
+
Handles presigned URL flow but still exposes the two-step nature
|
|
5
|
+
(get URL, then upload/download). Useful for advanced use cases
|
|
6
|
+
requiring fine-grained control.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import builtins
|
|
10
|
+
from typing import TYPE_CHECKING
|
|
11
|
+
|
|
12
|
+
from render_sdk.client.errors import RenderError
|
|
13
|
+
from render_sdk.client.util import handle_http_errors
|
|
14
|
+
from render_sdk.experimental.object.types import (
|
|
15
|
+
DownloadResponse,
|
|
16
|
+
ListObjectsResponse,
|
|
17
|
+
ObjectMetadata,
|
|
18
|
+
UploadResponse,
|
|
19
|
+
)
|
|
20
|
+
from render_sdk.public_api.api.blob_storage import (
|
|
21
|
+
delete_blob,
|
|
22
|
+
get_blob,
|
|
23
|
+
list_blobs,
|
|
24
|
+
put_blob,
|
|
25
|
+
)
|
|
26
|
+
from render_sdk.public_api.models.blob_with_cursor import BlobWithCursor
|
|
27
|
+
from render_sdk.public_api.models.error import Error
|
|
28
|
+
from render_sdk.public_api.models.get_blob_output import GetBlobOutput
|
|
29
|
+
from render_sdk.public_api.models.put_blob_input import (
|
|
30
|
+
PutBlobInput as PutBlobInputModel,
|
|
31
|
+
)
|
|
32
|
+
from render_sdk.public_api.models.put_blob_output import PutBlobOutput
|
|
33
|
+
from render_sdk.public_api.models.region import Region
|
|
34
|
+
from render_sdk.public_api.types import UNSET, Response
|
|
35
|
+
|
|
36
|
+
if TYPE_CHECKING:
|
|
37
|
+
from render_sdk.public_api.client import AuthenticatedClient
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class ObjectApi:
|
|
41
|
+
"""Layer 2: Typed Object API Client
|
|
42
|
+
|
|
43
|
+
Provides idiomatic Python wrapper around the raw OpenAPI client.
|
|
44
|
+
Handles presigned URL flow but still exposes the two-step nature.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def __init__(self, client: "AuthenticatedClient"):
|
|
48
|
+
self.client = client
|
|
49
|
+
|
|
50
|
+
async def get_upload_url(
|
|
51
|
+
self,
|
|
52
|
+
owner_id: str,
|
|
53
|
+
region: Region | str,
|
|
54
|
+
key: str,
|
|
55
|
+
size_bytes: int,
|
|
56
|
+
) -> UploadResponse:
|
|
57
|
+
"""Get a presigned URL for uploading an object.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
owner_id: Owner ID (workspace team ID)
|
|
61
|
+
region: Storage region
|
|
62
|
+
key: Object key (path)
|
|
63
|
+
size_bytes: Size of the object in bytes
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
UploadResponse: Upload URL with expiration and size limit
|
|
67
|
+
|
|
68
|
+
Raises:
|
|
69
|
+
ClientError: For 4xx client errors
|
|
70
|
+
ServerError: For 5xx server errors
|
|
71
|
+
TimeoutError: If the request times out
|
|
72
|
+
"""
|
|
73
|
+
response = await self._get_upload_url_api_call(
|
|
74
|
+
owner_id, region, key, size_bytes
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
if not isinstance(response.parsed, PutBlobOutput):
|
|
78
|
+
raise RenderError("Failed to get upload URL: unexpected response type")
|
|
79
|
+
|
|
80
|
+
return UploadResponse(
|
|
81
|
+
url=response.parsed.url,
|
|
82
|
+
expires_at=response.parsed.expires_at,
|
|
83
|
+
max_size_bytes=response.parsed.max_size_bytes,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
@handle_http_errors("get upload URL")
|
|
87
|
+
async def _get_upload_url_api_call(
|
|
88
|
+
self,
|
|
89
|
+
owner_id: str,
|
|
90
|
+
region: Region | str,
|
|
91
|
+
key: str,
|
|
92
|
+
size_bytes: int,
|
|
93
|
+
) -> Response[Error | PutBlobOutput]:
|
|
94
|
+
"""Internal method to make the get upload URL API call."""
|
|
95
|
+
# Convert region to Region enum if it's a string
|
|
96
|
+
if isinstance(region, str):
|
|
97
|
+
region = Region(region)
|
|
98
|
+
|
|
99
|
+
body = PutBlobInputModel(size_bytes=size_bytes)
|
|
100
|
+
|
|
101
|
+
return await put_blob.asyncio_detailed(
|
|
102
|
+
owner_id=owner_id,
|
|
103
|
+
region=region,
|
|
104
|
+
key=key,
|
|
105
|
+
client=self.client,
|
|
106
|
+
body=body,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
async def get_download_url(
|
|
110
|
+
self,
|
|
111
|
+
owner_id: str,
|
|
112
|
+
region: Region | str,
|
|
113
|
+
key: str,
|
|
114
|
+
) -> DownloadResponse:
|
|
115
|
+
"""Get a presigned URL for downloading an object.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
owner_id: Owner ID (workspace team ID)
|
|
119
|
+
region: Storage region
|
|
120
|
+
key: Object key (path)
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
DownloadResponse: Download URL with expiration
|
|
124
|
+
|
|
125
|
+
Raises:
|
|
126
|
+
ClientError: For 4xx client errors
|
|
127
|
+
ServerError: For 5xx server errors
|
|
128
|
+
TimeoutError: If the request times out
|
|
129
|
+
"""
|
|
130
|
+
response = await self._get_download_url_api_call(owner_id, region, key)
|
|
131
|
+
|
|
132
|
+
if not isinstance(response.parsed, GetBlobOutput):
|
|
133
|
+
raise RenderError("Failed to get download URL: unexpected response type")
|
|
134
|
+
|
|
135
|
+
return DownloadResponse(
|
|
136
|
+
url=response.parsed.url,
|
|
137
|
+
expires_at=response.parsed.expires_at,
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
@handle_http_errors("get download URL")
|
|
141
|
+
async def _get_download_url_api_call(
|
|
142
|
+
self,
|
|
143
|
+
owner_id: str,
|
|
144
|
+
region: Region | str,
|
|
145
|
+
key: str,
|
|
146
|
+
) -> Response[Error | GetBlobOutput]:
|
|
147
|
+
"""Internal method to make the get download URL API call."""
|
|
148
|
+
# Convert region to Region enum if it's a string
|
|
149
|
+
if isinstance(region, str):
|
|
150
|
+
region = Region(region)
|
|
151
|
+
|
|
152
|
+
return await get_blob.asyncio_detailed(
|
|
153
|
+
owner_id=owner_id,
|
|
154
|
+
region=region,
|
|
155
|
+
key=key,
|
|
156
|
+
client=self.client,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
async def delete(
|
|
160
|
+
self,
|
|
161
|
+
owner_id: str,
|
|
162
|
+
region: Region | str,
|
|
163
|
+
key: str,
|
|
164
|
+
) -> None:
|
|
165
|
+
"""Delete an object.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
owner_id: Owner ID (workspace team ID)
|
|
169
|
+
region: Storage region
|
|
170
|
+
key: Object key (path)
|
|
171
|
+
|
|
172
|
+
Raises:
|
|
173
|
+
ClientError: For 4xx client errors
|
|
174
|
+
ServerError: For 5xx server errors
|
|
175
|
+
TimeoutError: If the request times out
|
|
176
|
+
"""
|
|
177
|
+
await self._delete_api_call(owner_id, region, key)
|
|
178
|
+
|
|
179
|
+
@handle_http_errors("delete object")
|
|
180
|
+
async def _delete_api_call(
|
|
181
|
+
self,
|
|
182
|
+
owner_id: str,
|
|
183
|
+
region: Region | str,
|
|
184
|
+
key: str,
|
|
185
|
+
) -> Response[Error | None]:
|
|
186
|
+
"""Internal method to make the delete object API call."""
|
|
187
|
+
# Convert region to Region enum if it's a string
|
|
188
|
+
if isinstance(region, str):
|
|
189
|
+
region = Region(region)
|
|
190
|
+
|
|
191
|
+
return await delete_blob.asyncio_detailed(
|
|
192
|
+
owner_id=owner_id,
|
|
193
|
+
region=region,
|
|
194
|
+
key=key,
|
|
195
|
+
client=self.client,
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
async def list_objects(
|
|
199
|
+
self,
|
|
200
|
+
owner_id: str,
|
|
201
|
+
region: Region | str,
|
|
202
|
+
cursor: str | None = None,
|
|
203
|
+
limit: int | None = None,
|
|
204
|
+
) -> ListObjectsResponse:
|
|
205
|
+
"""List objects in storage.
|
|
206
|
+
|
|
207
|
+
Args:
|
|
208
|
+
owner_id: Owner ID (workspace team ID)
|
|
209
|
+
region: Storage region
|
|
210
|
+
cursor: Pagination cursor from previous response
|
|
211
|
+
limit: Maximum number of objects to return (default 20)
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
ListObjectsResponse: List of object metadata with optional next cursor
|
|
215
|
+
|
|
216
|
+
Raises:
|
|
217
|
+
ClientError: For 4xx client errors
|
|
218
|
+
ServerError: For 5xx server errors
|
|
219
|
+
TimeoutError: If the request times out
|
|
220
|
+
"""
|
|
221
|
+
response = await self._list_objects_api_call(owner_id, region, cursor, limit)
|
|
222
|
+
|
|
223
|
+
if not isinstance(response.parsed, builtins.list):
|
|
224
|
+
raise RenderError("Failed to list objects: unexpected response type")
|
|
225
|
+
|
|
226
|
+
objects = [
|
|
227
|
+
ObjectMetadata(
|
|
228
|
+
key=item.blob.key,
|
|
229
|
+
size=item.blob.size_bytes,
|
|
230
|
+
last_modified=item.blob.last_modified,
|
|
231
|
+
content_type=item.blob.content_type,
|
|
232
|
+
)
|
|
233
|
+
for item in response.parsed
|
|
234
|
+
]
|
|
235
|
+
|
|
236
|
+
# The cursor for the next page is the cursor of the last item
|
|
237
|
+
next_cursor = response.parsed[-1].cursor if response.parsed else None
|
|
238
|
+
|
|
239
|
+
return ListObjectsResponse(objects=objects, next_cursor=next_cursor)
|
|
240
|
+
|
|
241
|
+
@handle_http_errors("list objects")
|
|
242
|
+
async def _list_objects_api_call(
|
|
243
|
+
self,
|
|
244
|
+
owner_id: str,
|
|
245
|
+
region: Region | str,
|
|
246
|
+
cursor: str | None,
|
|
247
|
+
limit: int | None,
|
|
248
|
+
) -> Response[Error | builtins.list[BlobWithCursor]]:
|
|
249
|
+
"""Internal method to make the list objects API call."""
|
|
250
|
+
# Convert region to Region enum if it's a string
|
|
251
|
+
if isinstance(region, str):
|
|
252
|
+
region = Region(region)
|
|
253
|
+
|
|
254
|
+
return await list_blobs.asyncio_detailed(
|
|
255
|
+
owner_id=owner_id,
|
|
256
|
+
region=region,
|
|
257
|
+
cursor=cursor if cursor is not None else UNSET,
|
|
258
|
+
limit=limit if limit is not None else UNSET,
|
|
259
|
+
client=self.client,
|
|
260
|
+
)
|