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
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""Tests for the CLI entrypoint module."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from render_sdk.workflows import cli
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TestCLIMain:
|
|
9
|
+
"""Tests for the main() CLI entrypoint."""
|
|
10
|
+
|
|
11
|
+
def test_no_arguments_shows_usage_and_exits(self, mocker, capsys):
|
|
12
|
+
"""Test that running with no arguments shows usage and exits with code 1."""
|
|
13
|
+
mocker.patch("sys.argv", ["render-workflows"])
|
|
14
|
+
|
|
15
|
+
with pytest.raises(SystemExit) as exc_info:
|
|
16
|
+
cli.main()
|
|
17
|
+
|
|
18
|
+
assert exc_info.value.code == 1
|
|
19
|
+
captured = capsys.readouterr()
|
|
20
|
+
assert "Usage: render-workflows <module:app>" in captured.err
|
|
21
|
+
assert "Example: render-workflows myapp:app" in captured.err
|
|
22
|
+
|
|
23
|
+
def test_invalid_format_no_colon_shows_error(self, mocker, capsys):
|
|
24
|
+
"""Test that an argument without a colon shows an error."""
|
|
25
|
+
mocker.patch("sys.argv", ["render-workflows", "myapp"])
|
|
26
|
+
|
|
27
|
+
with pytest.raises(SystemExit) as exc_info:
|
|
28
|
+
cli.main()
|
|
29
|
+
|
|
30
|
+
assert exc_info.value.code == 1
|
|
31
|
+
captured = capsys.readouterr()
|
|
32
|
+
assert "Error: Invalid app path 'myapp'" in captured.err
|
|
33
|
+
assert "Expected format: <module>:<app_variable>" in captured.err
|
|
34
|
+
|
|
35
|
+
def test_module_not_found_shows_error(self, mocker, capsys):
|
|
36
|
+
"""Test that a non-existent module shows an import error."""
|
|
37
|
+
mocker.patch("sys.argv", ["render-workflows", "nonexistent_module:app"])
|
|
38
|
+
|
|
39
|
+
with pytest.raises(SystemExit) as exc_info:
|
|
40
|
+
cli.main()
|
|
41
|
+
|
|
42
|
+
assert exc_info.value.code == 1
|
|
43
|
+
captured = capsys.readouterr()
|
|
44
|
+
assert "Error: Could not import module 'nonexistent_module'" in captured.err
|
|
45
|
+
|
|
46
|
+
def test_attribute_not_found_shows_error(self, mocker, capsys):
|
|
47
|
+
"""Test that a missing attribute shows an error."""
|
|
48
|
+
mocker.patch("sys.argv", ["render-workflows", "os:nonexistent_attr"])
|
|
49
|
+
|
|
50
|
+
with pytest.raises(SystemExit) as exc_info:
|
|
51
|
+
cli.main()
|
|
52
|
+
|
|
53
|
+
assert exc_info.value.code == 1
|
|
54
|
+
captured = capsys.readouterr()
|
|
55
|
+
assert "Error: Module 'os' has no attribute 'nonexistent_attr'" in captured.err
|
|
56
|
+
|
|
57
|
+
def test_object_without_start_method_shows_error(self, mocker, capsys):
|
|
58
|
+
"""Test that an object without a start() method shows an error."""
|
|
59
|
+
# os.path is a module, not a Workflows instance
|
|
60
|
+
mocker.patch("sys.argv", ["render-workflows", "os:path"])
|
|
61
|
+
|
|
62
|
+
with pytest.raises(SystemExit) as exc_info:
|
|
63
|
+
cli.main()
|
|
64
|
+
|
|
65
|
+
assert exc_info.value.code == 1
|
|
66
|
+
captured = capsys.readouterr()
|
|
67
|
+
assert "Error: 'path' does not have a start() method" in captured.err
|
|
68
|
+
assert "Expected a Workflows instance" in captured.err
|
|
69
|
+
|
|
70
|
+
def test_valid_app_calls_start(self, mocker):
|
|
71
|
+
"""Test that a valid app path calls the app's start() method."""
|
|
72
|
+
# Create a mock app with a start method
|
|
73
|
+
mock_app = mocker.Mock()
|
|
74
|
+
mock_app.start = mocker.Mock()
|
|
75
|
+
|
|
76
|
+
# Create a mock module containing the app
|
|
77
|
+
mock_module = mocker.Mock()
|
|
78
|
+
mock_module.app = mock_app
|
|
79
|
+
|
|
80
|
+
# Patch sys.argv and importlib
|
|
81
|
+
mocker.patch("sys.argv", ["render-workflows", "mymodule:app"])
|
|
82
|
+
mocker.patch("importlib.import_module", return_value=mock_module)
|
|
83
|
+
|
|
84
|
+
# Should not raise
|
|
85
|
+
cli.main()
|
|
86
|
+
|
|
87
|
+
# Verify start() was called
|
|
88
|
+
mock_app.start.assert_called_once()
|
|
89
|
+
|
|
90
|
+
def test_nested_module_path(self, mocker):
|
|
91
|
+
"""Test that nested module paths (e.g., 'myproject.tasks:app') work."""
|
|
92
|
+
mock_app = mocker.Mock()
|
|
93
|
+
mock_app.start = mocker.Mock()
|
|
94
|
+
|
|
95
|
+
mock_module = mocker.Mock()
|
|
96
|
+
mock_module.app = mock_app
|
|
97
|
+
|
|
98
|
+
mocker.patch("sys.argv", ["render-workflows", "myproject.tasks.workers:app"])
|
|
99
|
+
mock_import = mocker.patch("importlib.import_module", return_value=mock_module)
|
|
100
|
+
|
|
101
|
+
cli.main()
|
|
102
|
+
|
|
103
|
+
# Verify the full module path was used
|
|
104
|
+
mock_import.assert_called_once_with("myproject.tasks.workers")
|
|
105
|
+
mock_app.start.assert_called_once()
|
|
106
|
+
|
|
107
|
+
def test_colon_in_module_path_uses_last_colon(self, mocker, capsys):
|
|
108
|
+
"""Test that multiple colons use rsplit to get the last segment."""
|
|
109
|
+
# This is an edge case: "weird:module:app" should split as
|
|
110
|
+
# module="weird:module", app="app"
|
|
111
|
+
mocker.patch("sys.argv", ["render-workflows", "weird:module:app"])
|
|
112
|
+
|
|
113
|
+
with pytest.raises(SystemExit) as exc_info:
|
|
114
|
+
cli.main()
|
|
115
|
+
|
|
116
|
+
# Should fail on import (module "weird:module" doesn't exist)
|
|
117
|
+
assert exc_info.value.code == 1
|
|
118
|
+
captured = capsys.readouterr()
|
|
119
|
+
assert "Could not import module 'weird:module'" in captured.err
|
|
120
|
+
|
|
121
|
+
def test_import_error_includes_original_message(self, mocker, capsys):
|
|
122
|
+
"""Test that import errors include the original exception message."""
|
|
123
|
+
mocker.patch("sys.argv", ["render-workflows", "mymodule:app"])
|
|
124
|
+
mocker.patch(
|
|
125
|
+
"importlib.import_module",
|
|
126
|
+
side_effect=ImportError("No module named 'dependency'"),
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
with pytest.raises(SystemExit) as exc_info:
|
|
130
|
+
cli.main()
|
|
131
|
+
|
|
132
|
+
assert exc_info.value.code == 1
|
|
133
|
+
captured = capsys.readouterr()
|
|
134
|
+
assert "No module named 'dependency'" in captured.err
|
|
@@ -70,7 +70,9 @@ def test_task_registration_network_payload(task_registry, task_decorator, mocker
|
|
|
70
70
|
return f"Hello {msg}"
|
|
71
71
|
|
|
72
72
|
@task_decorator(
|
|
73
|
-
options=Options(
|
|
73
|
+
options=Options(
|
|
74
|
+
retry=Retry(max_retries=3, wait_duration_ms=1000, backoff_scaling=1.5)
|
|
75
|
+
),
|
|
74
76
|
)
|
|
75
77
|
def retry_task(data: str) -> str:
|
|
76
78
|
return data.upper()
|
|
@@ -120,6 +122,74 @@ def test_task_registration_network_payload(task_registry, task_decorator, mocker
|
|
|
120
122
|
assert retry_options.factor == 1.5
|
|
121
123
|
|
|
122
124
|
|
|
125
|
+
def test_task_registration_with_timeout_seconds(task_registry, task_decorator, mocker):
|
|
126
|
+
"""
|
|
127
|
+
Test that task registration correctly serializes timeout_seconds
|
|
128
|
+
in the network payload.
|
|
129
|
+
"""
|
|
130
|
+
from render_sdk.workflows.callback_api.types import UNSET
|
|
131
|
+
|
|
132
|
+
# Mock the UDSClient class
|
|
133
|
+
mock_uds_client_class = mocker.patch("render_sdk.workflows.runner.UDSClient")
|
|
134
|
+
|
|
135
|
+
# Set up the mock instance
|
|
136
|
+
mock_client_instance = mocker.Mock()
|
|
137
|
+
mock_uds_client_class.return_value = mock_client_instance
|
|
138
|
+
mock_client_instance.register_tasks = mocker.AsyncMock(
|
|
139
|
+
return_value={"status": "success"},
|
|
140
|
+
)
|
|
141
|
+
mock_client_instance.disconnect = mocker.AsyncMock()
|
|
142
|
+
|
|
143
|
+
# Define a task with timeout_seconds
|
|
144
|
+
@task_decorator(options=Options(timeout_seconds=120))
|
|
145
|
+
def timeout_task(x: int) -> int:
|
|
146
|
+
return x * 2
|
|
147
|
+
|
|
148
|
+
# Define a task without timeout_seconds
|
|
149
|
+
@task_decorator
|
|
150
|
+
def no_timeout_task(x: int) -> int:
|
|
151
|
+
return x * 3
|
|
152
|
+
|
|
153
|
+
# Define a task with both timeout_seconds and retry
|
|
154
|
+
@task_decorator(
|
|
155
|
+
options=Options(
|
|
156
|
+
timeout_seconds=300,
|
|
157
|
+
retry=Retry(max_retries=2, wait_duration_ms=500, backoff_scaling=1.5),
|
|
158
|
+
)
|
|
159
|
+
)
|
|
160
|
+
def timeout_and_retry_task(x: int) -> int:
|
|
161
|
+
return x * 4
|
|
162
|
+
|
|
163
|
+
# Mock get_task_registry to return our test registry
|
|
164
|
+
mock_get_registry = mocker.patch("render_sdk.workflows.runner.get_task_registry")
|
|
165
|
+
mock_get_registry.return_value = task_registry
|
|
166
|
+
|
|
167
|
+
register("/tmp/test.sock") # noqa:S108
|
|
168
|
+
|
|
169
|
+
# Get the actual payload that was sent
|
|
170
|
+
sent_tasks = mock_client_instance.register_tasks.call_args[0][0]
|
|
171
|
+
|
|
172
|
+
# Find tasks by name
|
|
173
|
+
task_by_name = {task.name: task for task in sent_tasks.tasks}
|
|
174
|
+
|
|
175
|
+
# Verify task with timeout_seconds
|
|
176
|
+
assert "timeout_task" in task_by_name
|
|
177
|
+
timeout_task_payload = task_by_name["timeout_task"]
|
|
178
|
+
assert timeout_task_payload.options.timeout_seconds == 120
|
|
179
|
+
|
|
180
|
+
# Verify task without timeout_seconds has UNSET timeout
|
|
181
|
+
assert "no_timeout_task" in task_by_name
|
|
182
|
+
no_timeout_payload = task_by_name["no_timeout_task"]
|
|
183
|
+
assert no_timeout_payload.options.timeout_seconds is UNSET
|
|
184
|
+
|
|
185
|
+
# Verify task with both timeout and retry
|
|
186
|
+
assert "timeout_and_retry_task" in task_by_name
|
|
187
|
+
combined_payload = task_by_name["timeout_and_retry_task"]
|
|
188
|
+
assert combined_payload.options.timeout_seconds == 300
|
|
189
|
+
assert combined_payload.options.retry is not UNSET
|
|
190
|
+
assert combined_payload.options.retry.max_retries == 2
|
|
191
|
+
|
|
192
|
+
|
|
123
193
|
@pytest.mark.asyncio
|
|
124
194
|
async def test_callback_payloads_with_mocked_client(
|
|
125
195
|
task_registry,
|
|
@@ -93,7 +93,9 @@ def test_task_registration_with_options_object():
|
|
|
93
93
|
|
|
94
94
|
# Task with only retry options
|
|
95
95
|
@task_decorator(
|
|
96
|
-
options=Options(
|
|
96
|
+
options=Options(
|
|
97
|
+
retry=Retry(max_retries=1, wait_duration_ms=500, backoff_scaling=1.0)
|
|
98
|
+
),
|
|
97
99
|
)
|
|
98
100
|
def task_with_retry_only(x: int) -> int:
|
|
99
101
|
return x
|
|
@@ -128,3 +130,58 @@ def test_task_registration_preserves_function_attributes(task_registry, task_dec
|
|
|
128
130
|
# Verify the original function attributes are preserved
|
|
129
131
|
assert documented_task.__name__ == "documented_task"
|
|
130
132
|
assert documented_task.__doc__ == "This is a documented function."
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def test_task_registration_with_timeout_seconds():
|
|
136
|
+
"""Test task registration with timeout_seconds option."""
|
|
137
|
+
registry = TaskRegistry()
|
|
138
|
+
task_decorator = create_task_decorator(registry)
|
|
139
|
+
|
|
140
|
+
# Task with timeout_seconds
|
|
141
|
+
@task_decorator(options=Options(timeout_seconds=120))
|
|
142
|
+
def task_with_timeout(x: int) -> int:
|
|
143
|
+
return x
|
|
144
|
+
|
|
145
|
+
# Verify task registered correctly
|
|
146
|
+
task_names = registry.get_task_names()
|
|
147
|
+
assert "task_with_timeout" in task_names
|
|
148
|
+
|
|
149
|
+
# Verify timeout_seconds is set
|
|
150
|
+
task_info = registry.get_task("task_with_timeout")
|
|
151
|
+
assert task_info.options is not None
|
|
152
|
+
assert task_info.options.timeout_seconds == 120
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def test_task_registration_without_timeout_seconds():
|
|
156
|
+
"""Test task registration without timeout_seconds defaults to None."""
|
|
157
|
+
registry = TaskRegistry()
|
|
158
|
+
task_decorator = create_task_decorator(registry)
|
|
159
|
+
|
|
160
|
+
@task_decorator
|
|
161
|
+
def task_without_timeout(x: int) -> int:
|
|
162
|
+
return x
|
|
163
|
+
|
|
164
|
+
task_info = registry.get_task("task_without_timeout")
|
|
165
|
+
assert task_info.options is not None
|
|
166
|
+
assert task_info.options.timeout_seconds is None
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def test_task_registration_with_timeout_and_retry():
|
|
170
|
+
"""Test task registration with both timeout_seconds and retry options."""
|
|
171
|
+
registry = TaskRegistry()
|
|
172
|
+
task_decorator = create_task_decorator(registry)
|
|
173
|
+
|
|
174
|
+
@task_decorator(
|
|
175
|
+
options=Options(
|
|
176
|
+
timeout_seconds=300,
|
|
177
|
+
retry=Retry(max_retries=3, wait_duration_ms=1000, backoff_scaling=2.0),
|
|
178
|
+
)
|
|
179
|
+
)
|
|
180
|
+
def task_with_both(x: int) -> int:
|
|
181
|
+
return x
|
|
182
|
+
|
|
183
|
+
task_info = registry.get_task("task_with_both")
|
|
184
|
+
assert task_info.options is not None
|
|
185
|
+
assert task_info.options.timeout_seconds == 300
|
|
186
|
+
assert task_info.options.retry is not None
|
|
187
|
+
assert task_info.options.retry.max_retries == 3
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: render_sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Python SDK for Render Workflows
|
|
5
|
+
License-File: LICENSE
|
|
5
6
|
Author: Render
|
|
6
7
|
Author-email: support@render.com
|
|
7
8
|
Requires-Python: >=3.10,<4.0
|
|
@@ -15,7 +16,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
15
16
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
17
|
Classifier: Programming Language :: Python :: 3.13
|
|
17
18
|
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
-
Requires-Dist: aiohttp (>=3.
|
|
19
|
+
Requires-Dist: aiohttp (>=3.12.14,<4.0.0)
|
|
19
20
|
Requires-Dist: httpx (>=0.28.1,<0.29.0)
|
|
20
21
|
Requires-Dist: openapi-python-client (>=0.26.1,<0.27.0)
|
|
21
22
|
Description-Content-Type: text/markdown
|