render 1.0.1__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/__init__.py +98 -0
- render/client/__init__.py +119 -0
- render/client/client.py +110 -0
- render/client/errors.py +39 -0
- render/client/sse.py +90 -0
- render/client/sse_sync.py +20 -0
- render/client/tests/__init__.py +0 -0
- render/client/tests/test_client.py +379 -0
- render/client/tests/test_method_parity.py +118 -0
- render/client/tests/test_render.py +52 -0
- render/client/tests/test_sse.py +67 -0
- render/client/tests/test_util.py +303 -0
- render/client/tests/test_util_sync.py +73 -0
- render/client/tests/test_workflows_sync.py +313 -0
- render/client/types.py +52 -0
- render/client/util.py +285 -0
- render/client/util_sync.py +83 -0
- render/client/workflows.py +363 -0
- render/client/workflows_sync.py +298 -0
- render/experimental/__init__.py +37 -0
- render/experimental/experimental.py +93 -0
- render/experimental/experimental_sync.py +78 -0
- render/experimental/key_value/__init__.py +33 -0
- render/experimental/key_value/api.py +243 -0
- render/experimental/key_value/compare.py +83 -0
- render/experimental/key_value/provider.py +285 -0
- render/experimental/key_value/tests/__init__.py +0 -0
- render/experimental/key_value/tests/test_api.py +371 -0
- render/experimental/key_value/tests/test_compare.py +136 -0
- render/experimental/key_value/tests/test_provider.py +1016 -0
- render/experimental/key_value/types.py +130 -0
- render/experimental/key_value/utils.py +11 -0
- render/experimental/object/__init__.py +38 -0
- render/experimental/object/api.py +250 -0
- render/experimental/object/api_sync.py +250 -0
- render/experimental/object/client.py +545 -0
- render/experimental/object/client_sync.py +547 -0
- render/experimental/object/e2e_helpers.py +78 -0
- render/experimental/object/tests/__init__.py +0 -0
- render/experimental/object/tests/test_client.py +228 -0
- render/experimental/object/tests/test_e2e.py +125 -0
- render/experimental/object/types.py +87 -0
- render/experimental/sandbox/__init__.py +31 -0
- render/experimental/sandbox/_tar.py +278 -0
- render/experimental/sandbox/api.py +518 -0
- render/experimental/sandbox/api_sync.py +514 -0
- render/experimental/sandbox/client.py +168 -0
- render/experimental/sandbox/client_sync.py +170 -0
- render/experimental/sandbox/errors.py +37 -0
- render/experimental/sandbox/files.py +378 -0
- render/experimental/sandbox/tests/__init__.py +0 -0
- render/experimental/sandbox/tests/test_client.py +802 -0
- render/experimental/sandbox/tests/test_download.py +728 -0
- render/experimental/sandbox/tests/test_e2e.py +252 -0
- render/experimental/sandbox/tests/test_tar.py +153 -0
- render/experimental/sandbox/tests/test_types.py +57 -0
- render/experimental/sandbox/types.py +46 -0
- render/public_api/__init__.py +8 -0
- render/public_api/api/__init__.py +1 -0
- render/public_api/api/artifact_sources/__init__.py +1 -0
- render/public_api/api/artifact_sources/add_or_update_artifact_source_secret_file.py +252 -0
- render/public_api/api/artifact_sources/create_artifact_source.py +243 -0
- render/public_api/api/artifact_sources/delete_artifact_source.py +188 -0
- render/public_api/api/artifact_sources/delete_artifact_source_env_var.py +223 -0
- render/public_api/api/artifact_sources/delete_artifact_source_secret_file.py +223 -0
- render/public_api/api/artifact_sources/get_artifact_source.py +185 -0
- render/public_api/api/artifact_sources/get_env_vars_for_artifact_source.py +241 -0
- render/public_api/api/artifact_sources/link_artifact_source_to_env_group.py +215 -0
- render/public_api/api/artifact_sources/list_artifact_sources.py +338 -0
- render/public_api/api/artifact_sources/list_artifacts_in_artifact_source.py +241 -0
- render/public_api/api/artifact_sources/list_secret_files_for_artifact_source.py +241 -0
- render/public_api/api/artifact_sources/retrieve_artifact_source_env_var.py +225 -0
- render/public_api/api/artifact_sources/retrieve_artifact_source_secret_file.py +225 -0
- render/public_api/api/artifact_sources/unlink_artifact_source_from_env_group.py +209 -0
- render/public_api/api/artifact_sources/update_artifact_source.py +244 -0
- render/public_api/api/artifact_sources/update_artifact_source_env_var.py +257 -0
- render/public_api/api/artifact_sources/update_env_vars_for_artifact_source.py +245 -0
- render/public_api/api/artifact_sources/update_secret_files_for_artifact_source.py +255 -0
- render/public_api/api/audit_logs/__init__.py +1 -0
- render/public_api/api/audit_logs/list_organization_audit_logs.py +303 -0
- render/public_api/api/audit_logs/list_owner_audit_logs.py +303 -0
- render/public_api/api/blueprints/__init__.py +1 -0
- render/public_api/api/blueprints/disconnect_blueprint.py +210 -0
- render/public_api/api/blueprints/list_blueprint_syncs.py +241 -0
- render/public_api/api/blueprints/list_blueprints.py +251 -0
- render/public_api/api/blueprints/retrieve_blueprint.py +200 -0
- render/public_api/api/blueprints/update_blueprint.py +222 -0
- render/public_api/api/blueprints/validate_blueprint.py +217 -0
- render/public_api/api/custom_domains/__init__.py +1 -0
- render/public_api/api/custom_domains/create_custom_domain.py +242 -0
- render/public_api/api/custom_domains/delete_custom_domain.py +216 -0
- render/public_api/api/custom_domains/list_custom_domains.py +346 -0
- render/public_api/api/custom_domains/refresh_custom_domain.py +216 -0
- render/public_api/api/custom_domains/retrieve_custom_domain.py +218 -0
- render/public_api/api/dedicated_i_ps/__init__.py +1 -0
- render/public_api/api/dedicated_i_ps/create_dedicated_ip.py +234 -0
- render/public_api/api/dedicated_i_ps/delete_dedicated_ip.py +198 -0
- render/public_api/api/dedicated_i_ps/list_dedicated_ips.py +223 -0
- render/public_api/api/dedicated_i_ps/retrieve_dedicated_ip.py +200 -0
- render/public_api/api/dedicated_i_ps/update_dedicated_ip.py +244 -0
- render/public_api/api/deploys/__init__.py +1 -0
- render/public_api/api/deploys/cancel_deploy.py +216 -0
- render/public_api/api/deploys/create_deploy.py +231 -0
- render/public_api/api/deploys/list_deploys.py +377 -0
- render/public_api/api/deploys/retrieve_deploy.py +213 -0
- render/public_api/api/deploys/rollback_deploy.py +241 -0
- render/public_api/api/disks/__init__.py +1 -0
- render/public_api/api/disks/add_disk.py +222 -0
- render/public_api/api/disks/delete_disk.py +210 -0
- render/public_api/api/disks/list_disks.py +381 -0
- render/public_api/api/disks/list_snapshots.py +214 -0
- render/public_api/api/disks/restore_snapshot.py +255 -0
- render/public_api/api/disks/retrieve_disk.py +200 -0
- render/public_api/api/disks/update_disk.py +243 -0
- render/public_api/api/environment_groups/__init__.py +1 -0
- render/public_api/api/environment_groups/create_env_group.py +199 -0
- render/public_api/api/environment_groups/delete_env_group.py +192 -0
- render/public_api/api/environment_groups/delete_env_group_env_var.py +201 -0
- render/public_api/api/environment_groups/delete_env_group_secret_file.py +201 -0
- render/public_api/api/environment_groups/link_service_to_env_group.py +211 -0
- render/public_api/api/environment_groups/list_env_groups.py +352 -0
- render/public_api/api/environment_groups/retrieve_env_group.py +190 -0
- render/public_api/api/environment_groups/retrieve_env_group_env_var.py +203 -0
- render/public_api/api/environment_groups/retrieve_env_group_secret_file.py +203 -0
- render/public_api/api/environment_groups/unlink_service_from_env_group.py +209 -0
- render/public_api/api/environment_groups/update_env_group.py +212 -0
- render/public_api/api/environment_groups/update_env_group_env_var.py +230 -0
- render/public_api/api/environment_groups/update_env_group_secret_file.py +225 -0
- render/public_api/api/events/__init__.py +1 -0
- render/public_api/api/events/retrieve_event.py +205 -0
- render/public_api/api/key_value/__init__.py +1 -0
- render/public_api/api/key_value/create_key_value.py +199 -0
- render/public_api/api/key_value/delete_key_value.py +188 -0
- render/public_api/api/key_value/list_key_value.py +380 -0
- render/public_api/api/key_value/resume_key_value.py +203 -0
- render/public_api/api/key_value/retrieve_key_value.py +190 -0
- render/public_api/api/key_value/retrieve_key_value_connection_info.py +194 -0
- render/public_api/api/key_value/suspend_key_value.py +203 -0
- render/public_api/api/key_value/update_key_value.py +221 -0
- render/public_api/api/logs/__init__.py +1 -0
- render/public_api/api/logs/delete_owner_log_stream.py +188 -0
- render/public_api/api/logs/delete_resource_log_stream.py +192 -0
- render/public_api/api/logs/get_owner_log_stream.py +205 -0
- render/public_api/api/logs/get_resource_log_stream.py +209 -0
- render/public_api/api/logs/list_logs.py +535 -0
- render/public_api/api/logs/list_logs_values.py +527 -0
- render/public_api/api/logs/list_resource_log_streams.py +317 -0
- render/public_api/api/logs/subscribe_logs.py +527 -0
- render/public_api/api/logs/update_owner_log_stream.py +231 -0
- render/public_api/api/logs/update_resource_log_stream.py +231 -0
- render/public_api/api/maintenance/__init__.py +1 -0
- render/public_api/api/maintenance/list_maintenance.py +249 -0
- render/public_api/api/maintenance/retrieve_maintenance.py +190 -0
- render/public_api/api/maintenance/trigger_maintenance.py +212 -0
- render/public_api/api/maintenance/update_maintenance.py +222 -0
- render/public_api/api/metrics/__init__.py +1 -0
- render/public_api/api/metrics/delete_owner_metrics_stream.py +172 -0
- render/public_api/api/metrics/get_active_connections.py +237 -0
- render/public_api/api/metrics/get_bandwidth.py +237 -0
- render/public_api/api/metrics/get_bandwidth_sources.py +250 -0
- render/public_api/api/metrics/get_cpu.py +287 -0
- render/public_api/api/metrics/get_cpu_limit.py +267 -0
- render/public_api/api/metrics/get_cpu_target.py +267 -0
- render/public_api/api/metrics/get_disk_capacity.py +252 -0
- render/public_api/api/metrics/get_disk_usage.py +252 -0
- render/public_api/api/metrics/get_http_latency.py +297 -0
- render/public_api/api/metrics/get_http_requests.py +302 -0
- render/public_api/api/metrics/get_instance_count.py +252 -0
- render/public_api/api/metrics/get_memory.py +267 -0
- render/public_api/api/metrics/get_memory_limit.py +267 -0
- render/public_api/api/metrics/get_memory_target.py +267 -0
- render/public_api/api/metrics/get_owner_metrics_stream.py +174 -0
- render/public_api/api/metrics/get_replication_lag.py +237 -0
- render/public_api/api/metrics/get_task_runs_completed.py +281 -0
- render/public_api/api/metrics/get_task_runs_queued.py +237 -0
- render/public_api/api/metrics/list_application_filter_values.py +254 -0
- render/public_api/api/metrics/list_http_filter_values.py +282 -0
- render/public_api/api/metrics/list_path_filter_values.py +289 -0
- render/public_api/api/metrics/upsert_owner_metrics_stream.py +192 -0
- render/public_api/api/notification_settings/__init__.py +1 -0
- render/public_api/api/notification_settings/list_notification_overrides.py +255 -0
- render/public_api/api/notification_settings/patch_owner_notification_settings.py +207 -0
- render/public_api/api/notification_settings/patch_service_notification_overrides.py +207 -0
- render/public_api/api/notification_settings/retrieve_owner_notification_settings.py +193 -0
- render/public_api/api/notification_settings/retrieve_service_notification_overrides.py +193 -0
- render/public_api/api/object_storage/__init__.py +1 -0
- render/public_api/api/object_storage/delete_object.py +215 -0
- render/public_api/api/object_storage/get_object.py +221 -0
- render/public_api/api/object_storage/list_objects.py +240 -0
- render/public_api/api/object_storage/put_object.py +248 -0
- render/public_api/api/one_off_jobs/__init__.py +1 -0
- render/public_api/api/one_off_jobs/cancel_job.py +203 -0
- render/public_api/api/one_off_jobs/list_job.py +367 -0
- render/public_api/api/one_off_jobs/post_job.py +216 -0
- render/public_api/api/one_off_jobs/retrieve_job.py +203 -0
- render/public_api/api/postgres/__init__.py +1 -0
- render/public_api/api/postgres/create_postgres.py +199 -0
- render/public_api/api/postgres/create_postgres_export.py +207 -0
- render/public_api/api/postgres/create_postgres_user.py +229 -0
- render/public_api/api/postgres/delete_postgres.py +192 -0
- render/public_api/api/postgres/delete_postgres_user.py +201 -0
- render/public_api/api/postgres/failover_postgres.py +207 -0
- render/public_api/api/postgres/list_available_postgres_parameter_overrides.py +190 -0
- render/public_api/api/postgres/list_postgres.py +418 -0
- render/public_api/api/postgres/list_postgres_export.py +199 -0
- render/public_api/api/postgres/list_postgres_parameter_overrides.py +190 -0
- render/public_api/api/postgres/list_postgres_processes.py +190 -0
- render/public_api/api/postgres/list_postgres_sizes.py +190 -0
- render/public_api/api/postgres/list_postgres_table_scans.py +190 -0
- render/public_api/api/postgres/list_postgres_top_queries.py +194 -0
- render/public_api/api/postgres/list_postgres_users.py +195 -0
- render/public_api/api/postgres/promote_postgres.py +203 -0
- render/public_api/api/postgres/recover_postgres.py +231 -0
- render/public_api/api/postgres/restart_postgres.py +203 -0
- render/public_api/api/postgres/resume_postgres.py +203 -0
- render/public_api/api/postgres/retrieve_postgres.py +190 -0
- render/public_api/api/postgres/retrieve_postgres_connection_info.py +194 -0
- render/public_api/api/postgres/retrieve_postgres_recovery_info.py +194 -0
- render/public_api/api/postgres/setup_postgres_replication.py +231 -0
- render/public_api/api/postgres/suspend_postgres.py +203 -0
- render/public_api/api/postgres/update_postgres.py +217 -0
- render/public_api/api/postgres/update_postgres_parameter_overrides.py +217 -0
- render/public_api/api/projects_environments/__init__.py +1 -0
- render/public_api/api/projects_environments/add_resources_to_environment.py +212 -0
- render/public_api/api/projects_environments/create_environment.py +199 -0
- render/public_api/api/projects_environments/create_project.py +199 -0
- render/public_api/api/projects_environments/delete_environment.py +216 -0
- render/public_api/api/projects_environments/delete_project.py +216 -0
- render/public_api/api/projects_environments/list_environments.py +369 -0
- render/public_api/api/projects_environments/list_projects.py +329 -0
- render/public_api/api/projects_environments/remove_resources_from_environment.py +211 -0
- render/public_api/api/projects_environments/retrieve_environment.py +190 -0
- render/public_api/api/projects_environments/retrieve_project.py +190 -0
- render/public_api/api/projects_environments/update_environment.py +212 -0
- render/public_api/api/projects_environments/update_project.py +224 -0
- render/public_api/api/redis_deprecated/__init__.py +1 -0
- render/public_api/api/redis_deprecated/create_redis.py +199 -0
- render/public_api/api/redis_deprecated/delete_redis.py +188 -0
- render/public_api/api/redis_deprecated/list_redis.py +384 -0
- render/public_api/api/redis_deprecated/resume_redis.py +203 -0
- render/public_api/api/redis_deprecated/retrieve_redis.py +190 -0
- render/public_api/api/redis_deprecated/retrieve_redis_connection_info.py +194 -0
- render/public_api/api/redis_deprecated/suspend_redis.py +203 -0
- render/public_api/api/redis_deprecated/update_redis.py +217 -0
- render/public_api/api/registry_credentials/__init__.py +1 -0
- render/public_api/api/registry_credentials/create_registry_credential.py +209 -0
- render/public_api/api/registry_credentials/delete_registry_credential.py +198 -0
- render/public_api/api/registry_credentials/list_registry_credentials.py +370 -0
- render/public_api/api/registry_credentials/retrieve_registry_credential.py +200 -0
- render/public_api/api/registry_credentials/update_registry_credential.py +241 -0
- render/public_api/api/sandboxes/__init__.py +1 -0
- render/public_api/api/sandboxes/connect_sandbox_files.py +281 -0
- render/public_api/api/sandboxes/connect_sandbox_run.py +271 -0
- render/public_api/api/sandboxes/create_sandbox.py +199 -0
- render/public_api/api/sandboxes/list_sandbox_files.py +244 -0
- render/public_api/api/sandboxes/list_sandbox_groups.py +216 -0
- render/public_api/api/sandboxes/list_sandboxes.py +269 -0
- render/public_api/api/sandboxes/retrieve_sandbox.py +211 -0
- render/public_api/api/sandboxes/retrieve_sandbox_execution.py +224 -0
- render/public_api/api/sandboxes/stream_sandbox_logs.py +274 -0
- render/public_api/api/sandboxes/terminate_sandbox.py +217 -0
- render/public_api/api/sandboxes/update_sandbox_exec.py +258 -0
- render/public_api/api/services/__init__.py +1 -0
- render/public_api/api/services/add_or_update_secret_file.py +252 -0
- render/public_api/api/services/autoscale_service.py +230 -0
- render/public_api/api/services/create_ephemeral_shell.py +217 -0
- render/public_api/api/services/create_service.py +204 -0
- render/public_api/api/services/delete_autoscaling_config.py +198 -0
- render/public_api/api/services/delete_env_var.py +223 -0
- render/public_api/api/services/delete_secret_file.py +223 -0
- render/public_api/api/services/delete_service.py +198 -0
- render/public_api/api/services/get_env_vars_for_service.py +241 -0
- render/public_api/api/services/list_events.py +288 -0
- render/public_api/api/services/list_instances.py +190 -0
- render/public_api/api/services/list_secret_files_for_service.py +241 -0
- render/public_api/api/services/list_services.py +439 -0
- render/public_api/api/services/preview_service.py +227 -0
- render/public_api/api/services/purge_cache.py +203 -0
- render/public_api/api/services/restart_service.py +211 -0
- render/public_api/api/services/resume_service.py +203 -0
- render/public_api/api/services/retrieve_env_var.py +225 -0
- render/public_api/api/services/retrieve_secret_file.py +225 -0
- render/public_api/api/services/retrieve_service.py +190 -0
- render/public_api/api/services/retrieve_service_outbound_ips.py +207 -0
- render/public_api/api/services/scale_service.py +237 -0
- render/public_api/api/services/suspend_service.py +203 -0
- render/public_api/api/services/update_env_var.py +257 -0
- render/public_api/api/services/update_env_vars_for_service.py +241 -0
- render/public_api/api/services/update_secret_files_for_service.py +255 -0
- render/public_api/api/services/update_service.py +227 -0
- render/public_api/api/services_cron_jobs/__init__.py +1 -0
- render/public_api/api/services_cron_jobs/cancel_cron_job_run.py +183 -0
- render/public_api/api/services_cron_jobs/run_cron_job.py +185 -0
- render/public_api/api/services_headers/__init__.py +1 -0
- render/public_api/api/services_headers/add_headers.py +222 -0
- render/public_api/api/services_headers/delete_header.py +211 -0
- render/public_api/api/services_headers/list_headers.py +302 -0
- render/public_api/api/services_headers/update_headers.py +243 -0
- render/public_api/api/services_routes/__init__.py +1 -0
- render/public_api/api/services_routes/add_route.py +227 -0
- render/public_api/api/services_routes/delete_route.py +211 -0
- render/public_api/api/services_routes/list_routes.py +306 -0
- render/public_api/api/services_routes/patch_route.py +259 -0
- render/public_api/api/services_routes/put_routes.py +259 -0
- render/public_api/api/users/__init__.py +1 -0
- render/public_api/api/users/get_user.py +161 -0
- render/public_api/api/webhooks/__init__.py +1 -0
- render/public_api/api/webhooks/create_webhook.py +199 -0
- render/public_api/api/webhooks/delete_webhook.py +183 -0
- render/public_api/api/webhooks/list_webhook_events.py +268 -0
- render/public_api/api/webhooks/list_webhooks.py +237 -0
- render/public_api/api/webhooks/retrieve_webhook.py +185 -0
- render/public_api/api/webhooks/update_webhook.py +212 -0
- render/public_api/api/workflow_tasks_beta/__init__.py +1 -0
- render/public_api/api/workflow_tasks_beta/cancel_task_run.py +188 -0
- render/public_api/api/workflow_tasks_beta/create_task.py +204 -0
- render/public_api/api/workflow_tasks_beta/get_task.py +190 -0
- render/public_api/api/workflow_tasks_beta/get_task_run.py +190 -0
- render/public_api/api/workflow_tasks_beta/list_task_runs.py +317 -0
- render/public_api/api/workflow_tasks_beta/list_tasks.py +298 -0
- render/public_api/api/workflow_tasks_beta/stream_task_runs_events.py +230 -0
- render/public_api/api/workflows_beta/__init__.py +1 -0
- render/public_api/api/workflows_beta/create_workflow.py +199 -0
- render/public_api/api/workflows_beta/create_workflow_version.py +197 -0
- render/public_api/api/workflows_beta/delete_workflow.py +188 -0
- render/public_api/api/workflows_beta/get_workflow.py +190 -0
- render/public_api/api/workflows_beta/get_workflow_version.py +190 -0
- render/public_api/api/workflows_beta/list_workflow_versions.py +275 -0
- render/public_api/api/workflows_beta/list_workflows.py +298 -0
- render/public_api/api/workflows_beta/update_workflow.py +212 -0
- render/public_api/api/workspaces/__init__.py +1 -0
- render/public_api/api/workspaces/list_owners.py +255 -0
- render/public_api/api/workspaces/remove_workspace_member.py +206 -0
- render/public_api/api/workspaces/retrieve_owner.py +207 -0
- render/public_api/api/workspaces/retrieve_owner_members.py +200 -0
- render/public_api/api/workspaces/update_workspace_member.py +235 -0
- render/public_api/client.py +268 -0
- render/public_api/errors.py +16 -0
- render/public_api/models/__init__.py +861 -0
- render/public_api/models/add_headers_response_201.py +72 -0
- render/public_api/models/add_or_update_artifact_source_secret_file_body.py +59 -0
- render/public_api/models/add_or_update_secret_file_body.py +59 -0
- render/public_api/models/artifact.py +145 -0
- render/public_api/models/artifact_fetch_failed.py +67 -0
- render/public_api/models/artifact_source.py +162 -0
- render/public_api/models/artifact_source_changed.py +70 -0
- render/public_api/models/artifact_source_git.py +167 -0
- render/public_api/models/artifact_source_git_region.py +12 -0
- render/public_api/models/artifact_source_git_runtime.py +14 -0
- render/public_api/models/artifact_source_image.py +78 -0
- render/public_api/models/artifact_source_patch_git.py +175 -0
- render/public_api/models/artifact_source_patch_git_region.py +12 -0
- render/public_api/models/artifact_source_patch_git_runtime.py +14 -0
- render/public_api/models/artifact_source_patch_image.py +71 -0
- render/public_api/models/artifact_source_patch_input.py +101 -0
- render/public_api/models/artifact_source_post_input.py +213 -0
- render/public_api/models/artifact_source_service_link.py +67 -0
- render/public_api/models/artifact_source_with_cursor.py +73 -0
- render/public_api/models/audit_log.py +113 -0
- render/public_api/models/audit_log_actor.py +80 -0
- render/public_api/models/audit_log_actor_type.py +10 -0
- render/public_api/models/audit_log_event.py +87 -0
- render/public_api/models/audit_log_metadata.py +49 -0
- render/public_api/models/audit_log_status.py +9 -0
- render/public_api/models/audit_log_with_cursor.py +73 -0
- render/public_api/models/auto_deploy.py +9 -0
- render/public_api/models/auto_deploy_disabled.py +79 -0
- render/public_api/models/auto_deploy_enabled.py +68 -0
- render/public_api/models/auto_deploy_trigger.py +10 -0
- render/public_api/models/autoscaling_config.py +89 -0
- render/public_api/models/autoscaling_config_changed.py +83 -0
- render/public_api/models/autoscaling_criteria.py +73 -0
- render/public_api/models/autoscaling_criteria_percentage.py +68 -0
- render/public_api/models/autoscaling_ended.py +67 -0
- render/public_api/models/autoscaling_started.py +105 -0
- render/public_api/models/background_worker_details.py +256 -0
- render/public_api/models/background_worker_details_patch.py +184 -0
- render/public_api/models/background_worker_details_post.py +266 -0
- render/public_api/models/blueprint.py +128 -0
- render/public_api/models/blueprint_detail.py +150 -0
- render/public_api/models/blueprint_patch.py +77 -0
- render/public_api/models/blueprint_with_cursor.py +74 -0
- render/public_api/models/branch_deleted.py +67 -0
- render/public_api/models/build.py +148 -0
- render/public_api/models/build_config.py +96 -0
- render/public_api/models/build_deploy_end_reason.py +122 -0
- render/public_api/models/build_deploy_end_reason_id.py +59 -0
- render/public_api/models/build_deploy_trigger.py +150 -0
- render/public_api/models/build_ended.py +91 -0
- render/public_api/models/build_filter.py +67 -0
- render/public_api/models/build_plan.py +9 -0
- render/public_api/models/build_runtime.py +14 -0
- render/public_api/models/build_started.py +73 -0
- render/public_api/models/build_status.py +13 -0
- render/public_api/models/cache.py +61 -0
- render/public_api/models/cache_profile.py +10 -0
- render/public_api/models/cidr_block_and_description.py +67 -0
- render/public_api/models/commit_ignored.py +67 -0
- render/public_api/models/commit_ref.py +59 -0
- render/public_api/models/connect_sandbox_files_operation.py +9 -0
- render/public_api/models/connect_sandbox_run_operation.py +9 -0
- render/public_api/models/create_custom_domain_body.py +59 -0
- render/public_api/models/create_deploy_body.py +130 -0
- render/public_api/models/create_deploy_body_clear_cache.py +9 -0
- render/public_api/models/create_ephemeral_shell_body.py +69 -0
- render/public_api/models/create_ephemeral_shell_response_201.py +59 -0
- render/public_api/models/create_registry_credential_body.py +93 -0
- render/public_api/models/create_version.py +70 -0
- render/public_api/models/credential_create_input.py +59 -0
- render/public_api/models/cron_job_details.py +163 -0
- render/public_api/models/cron_job_details_patch.py +138 -0
- render/public_api/models/cron_job_details_post.py +172 -0
- render/public_api/models/cron_job_run.py +123 -0
- render/public_api/models/cron_job_run_ended.py +110 -0
- render/public_api/models/cron_job_run_started.py +59 -0
- render/public_api/models/cron_job_run_status.py +11 -0
- render/public_api/models/custom_domain.py +135 -0
- render/public_api/models/custom_domain_domain_type.py +9 -0
- render/public_api/models/custom_domain_server.py +68 -0
- render/public_api/models/custom_domain_verification_status.py +9 -0
- render/public_api/models/custom_domain_with_cursor.py +73 -0
- render/public_api/models/database_role.py +9 -0
- render/public_api/models/database_status.py +18 -0
- render/public_api/models/dedicated_ip.py +146 -0
- render/public_api/models/dedicated_ip_status.py +14 -0
- render/public_api/models/dedicated_ippatch.py +81 -0
- render/public_api/models/dedicated_ippost.py +100 -0
- render/public_api/models/deploy.py +211 -0
- render/public_api/models/deploy_commit.py +86 -0
- render/public_api/models/deploy_ended.py +102 -0
- render/public_api/models/deploy_image.py +78 -0
- render/public_api/models/deploy_mode.py +9 -0
- render/public_api/models/deploy_started.py +85 -0
- render/public_api/models/deploy_status.py +18 -0
- render/public_api/models/deploy_trigger.py +17 -0
- render/public_api/models/deploy_with_cursor.py +81 -0
- render/public_api/models/disk.py +83 -0
- render/public_api/models/disk_created.py +67 -0
- render/public_api/models/disk_deleted.py +59 -0
- render/public_api/models/disk_details.py +112 -0
- render/public_api/models/disk_patch.py +77 -0
- render/public_api/models/disk_post.py +83 -0
- render/public_api/models/disk_snapshot.py +88 -0
- render/public_api/models/disk_updated.py +75 -0
- render/public_api/models/disk_with_cursor.py +73 -0
- render/public_api/models/docker_details.py +108 -0
- render/public_api/models/docker_details_patch.py +86 -0
- render/public_api/models/docker_details_post.py +86 -0
- render/public_api/models/edge_cache_disabled.py +65 -0
- render/public_api/models/edge_cache_enabled.py +65 -0
- render/public_api/models/edge_cache_purged.py +65 -0
- render/public_api/models/edge_cache_trigger.py +91 -0
- render/public_api/models/env_group.py +162 -0
- render/public_api/models/env_group_link.py +77 -0
- render/public_api/models/env_group_meta.py +126 -0
- render/public_api/models/env_group_patch_input.py +59 -0
- render/public_api/models/env_group_post_input.py +161 -0
- render/public_api/models/env_var.py +67 -0
- render/public_api/models/env_var_generate_value.py +59 -0
- render/public_api/models/env_var_key_generate_value.py +68 -0
- render/public_api/models/env_var_key_value.py +67 -0
- render/public_api/models/env_var_value.py +59 -0
- render/public_api/models/env_var_with_cursor.py +73 -0
- render/public_api/models/environment.py +152 -0
- render/public_api/models/environment_patch_input.py +112 -0
- render/public_api/models/environment_post_input.py +122 -0
- render/public_api/models/environment_resources_post_input.py +59 -0
- render/public_api/models/environment_with_cursor.py +74 -0
- render/public_api/models/error.py +79 -0
- render/public_api/models/error_code.py +10 -0
- render/public_api/models/event.py +1122 -0
- render/public_api/models/event_status.py +10 -0
- render/public_api/models/event_type.py +74 -0
- render/public_api/models/execution.py +144 -0
- render/public_api/models/execution_operation.py +11 -0
- render/public_api/models/execution_type.py +9 -0
- render/public_api/models/failure_reason.py +138 -0
- render/public_api/models/filter_application_values_collection_item.py +78 -0
- render/public_api/models/filter_application_values_collection_item_filter.py +8 -0
- render/public_api/models/filter_http_values_collection_item.py +78 -0
- render/public_api/models/filter_http_values_collection_item_filter.py +9 -0
- render/public_api/models/get_bandwidth_sources_response_200.py +75 -0
- render/public_api/models/get_bandwidth_sources_response_200_data_item.py +101 -0
- render/public_api/models/get_bandwidth_sources_response_200_data_item_labels.py +78 -0
- render/public_api/models/get_bandwidth_sources_response_200_data_item_labels_traffic_source.py +12 -0
- render/public_api/models/get_bandwidth_sources_response_200_data_item_values_item.py +68 -0
- render/public_api/models/get_cpu_aggregation_method.py +10 -0
- render/public_api/models/get_http_requests_aggregate_by.py +9 -0
- render/public_api/models/get_object_output.py +71 -0
- render/public_api/models/get_task_runs_completed_aggregate_by.py +8 -0
- render/public_api/models/get_task_runs_completed_state.py +9 -0
- render/public_api/models/header.py +83 -0
- render/public_api/models/header_input.py +76 -0
- render/public_api/models/header_with_cursor.py +73 -0
- render/public_api/models/image.py +80 -0
- render/public_api/models/image_pull_failed.py +67 -0
- render/public_api/models/initial_deploy_hook_ended.py +59 -0
- render/public_api/models/initial_deploy_hook_started.py +59 -0
- render/public_api/models/instance_count_changed.py +67 -0
- render/public_api/models/instance_type_changed.py +67 -0
- render/public_api/models/internal_routing.py +8 -0
- render/public_api/models/job.py +144 -0
- render/public_api/models/job_run_ended.py +92 -0
- render/public_api/models/job_status.py +12 -0
- render/public_api/models/job_with_cursor.py +73 -0
- render/public_api/models/key_value.py +182 -0
- render/public_api/models/key_value_available.py +44 -0
- render/public_api/models/key_value_config_restart.py +67 -0
- render/public_api/models/key_value_connection_info.py +76 -0
- render/public_api/models/key_value_detail.py +192 -0
- render/public_api/models/key_value_options.py +79 -0
- render/public_api/models/key_value_patch_input.py +138 -0
- render/public_api/models/key_value_plan.py +13 -0
- render/public_api/models/key_value_post_input.py +166 -0
- render/public_api/models/key_value_unhealthy.py +44 -0
- render/public_api/models/key_value_with_cursor.py +73 -0
- render/public_api/models/label.py +68 -0
- render/public_api/models/list_custom_domains_domain_type.py +9 -0
- render/public_api/models/list_custom_domains_verification_status.py +9 -0
- render/public_api/models/list_logs_response_200.py +102 -0
- render/public_api/models/list_logs_values_label.py +13 -0
- render/public_api/models/list_objects_response.py +92 -0
- render/public_api/models/list_postgres_suspended_item.py +9 -0
- render/public_api/models/list_postgres_users_response_200_item.py +86 -0
- render/public_api/models/list_routes_type_item.py +9 -0
- render/public_api/models/list_services_suspended_item.py +9 -0
- render/public_api/models/log.py +100 -0
- render/public_api/models/log_direction.py +9 -0
- render/public_api/models/log_label.py +70 -0
- render/public_api/models/log_label_name.py +22 -0
- render/public_api/models/log_stream_owner_update.py +80 -0
- render/public_api/models/log_stream_preview_setting.py +9 -0
- render/public_api/models/log_stream_resource_update.py +80 -0
- render/public_api/models/log_stream_setting.py +9 -0
- render/public_api/models/maintenance_ended.py +44 -0
- render/public_api/models/maintenance_mode.py +68 -0
- render/public_api/models/maintenance_mode_enabled.py +59 -0
- render/public_api/models/maintenance_mode_uri_updated.py +67 -0
- render/public_api/models/maintenance_run.py +105 -0
- render/public_api/models/maintenance_run_patch.py +69 -0
- render/public_api/models/maintenance_run_with_resource.py +114 -0
- render/public_api/models/maintenance_started.py +65 -0
- render/public_api/models/maintenance_state.py +13 -0
- render/public_api/models/maintenance_trigger.py +91 -0
- render/public_api/models/maxmemory_policy.py +15 -0
- render/public_api/models/metrics_stream.py +77 -0
- render/public_api/models/metrics_stream_input.py +86 -0
- render/public_api/models/native_environment_details.py +78 -0
- render/public_api/models/native_environment_details_patch.py +68 -0
- render/public_api/models/native_environment_details_post.py +68 -0
- render/public_api/models/notification_override.py +78 -0
- render/public_api/models/notification_override_with_cursor.py +73 -0
- render/public_api/models/notification_service_override.py +78 -0
- render/public_api/models/notification_service_override_patch.py +84 -0
- render/public_api/models/notification_setting.py +93 -0
- render/public_api/models/notification_setting_patch.py +85 -0
- render/public_api/models/notify_override.py +11 -0
- render/public_api/models/notify_preview_override.py +10 -0
- render/public_api/models/notify_setting.py +10 -0
- render/public_api/models/notify_setting_v2.py +10 -0
- render/public_api/models/object_metadata.py +77 -0
- render/public_api/models/object_with_cursor.py +73 -0
- render/public_api/models/oom_killed.py +59 -0
- render/public_api/models/otel_provider_type.py +16 -0
- render/public_api/models/outbound_ips.py +81 -0
- render/public_api/models/outbound_ips_type.py +9 -0
- render/public_api/models/owner.py +121 -0
- render/public_api/models/owner_log_stream_setting.py +86 -0
- render/public_api/models/owner_type.py +9 -0
- render/public_api/models/owner_with_cursor.py +81 -0
- render/public_api/models/paid_plan.py +13 -0
- render/public_api/models/patch_route_response_200.py +72 -0
- render/public_api/models/persistence_mode.py +10 -0
- render/public_api/models/pipeline_minutes_exhausted.py +73 -0
- render/public_api/models/plan.py +22 -0
- render/public_api/models/post_job_body.py +70 -0
- render/public_api/models/postgres.py +300 -0
- render/public_api/models/postgres_available.py +44 -0
- render/public_api/models/postgres_available_parameter_override.py +106 -0
- render/public_api/models/postgres_available_parameter_overrides_result.py +75 -0
- render/public_api/models/postgres_backup_completed.py +44 -0
- render/public_api/models/postgres_backup_failed.py +44 -0
- render/public_api/models/postgres_backup_started.py +44 -0
- render/public_api/models/postgres_cluster_leader_changed.py +59 -0
- render/public_api/models/postgres_connection_info.py +103 -0
- render/public_api/models/postgres_connection_pool_changed.py +91 -0
- render/public_api/models/postgres_connection_pool_enabled_changed.py +83 -0
- render/public_api/models/postgres_created.py +72 -0
- render/public_api/models/postgres_detail.py +336 -0
- render/public_api/models/postgres_detail_suspended.py +9 -0
- render/public_api/models/postgres_disk_size_changed.py +91 -0
- render/public_api/models/postgres_export.py +80 -0
- render/public_api/models/postgres_ha_status_changed.py +67 -0
- render/public_api/models/postgres_parameter_overrides.py +44 -0
- render/public_api/models/postgres_parameter_overrides_wrapper.py +65 -0
- render/public_api/models/postgres_patch_input.py +199 -0
- render/public_api/models/postgres_pitr_checkpoint_completed.py +44 -0
- render/public_api/models/postgres_pitr_checkpoint_failed.py +44 -0
- render/public_api/models/postgres_pitr_checkpoint_started.py +44 -0
- render/public_api/models/postgres_plans.py +35 -0
- render/public_api/models/postgres_post_input.py +254 -0
- render/public_api/models/postgres_process.py +185 -0
- render/public_api/models/postgres_processes_result.py +73 -0
- render/public_api/models/postgres_put_parameter_overrides_result.py +82 -0
- render/public_api/models/postgres_query_statistic.py +222 -0
- render/public_api/models/postgres_read_replica_stale.py +44 -0
- render/public_api/models/postgres_read_replicas_changed.py +67 -0
- render/public_api/models/postgres_replication_setup_input.py +96 -0
- render/public_api/models/postgres_restarted.py +72 -0
- render/public_api/models/postgres_size.py +96 -0
- render/public_api/models/postgres_sizes_result.py +73 -0
- render/public_api/models/postgres_suspended.py +9 -0
- render/public_api/models/postgres_table_scan.py +87 -0
- render/public_api/models/postgres_table_scans_result.py +73 -0
- render/public_api/models/postgres_top_queries_result.py +73 -0
- render/public_api/models/postgres_unavailable.py +44 -0
- render/public_api/models/postgres_upgrade_failed.py +67 -0
- render/public_api/models/postgres_upgrade_started.py +91 -0
- render/public_api/models/postgres_upgrade_succeeded.py +67 -0
- render/public_api/models/postgres_version.py +15 -0
- render/public_api/models/postgres_with_cursor.py +73 -0
- render/public_api/models/pre_deploy_ended.py +99 -0
- render/public_api/models/pre_deploy_started.py +67 -0
- render/public_api/models/preview_input.py +94 -0
- render/public_api/models/previews.py +67 -0
- render/public_api/models/previews_generation.py +10 -0
- render/public_api/models/private_service_details.py +282 -0
- render/public_api/models/private_service_details_patch.py +184 -0
- render/public_api/models/private_service_details_post.py +266 -0
- render/public_api/models/project.py +108 -0
- render/public_api/models/project_patch_input.py +60 -0
- render/public_api/models/project_post_environment_input.py +114 -0
- render/public_api/models/project_post_input.py +89 -0
- render/public_api/models/project_with_cursor.py +73 -0
- render/public_api/models/protected_status.py +9 -0
- render/public_api/models/pull_request_previews_enabled.py +9 -0
- render/public_api/models/put_object_input.py +59 -0
- render/public_api/models/put_object_output.py +79 -0
- render/public_api/models/read_replica.py +91 -0
- render/public_api/models/read_replica_input.py +83 -0
- render/public_api/models/recovery_info.py +80 -0
- render/public_api/models/recovery_info_recovery_status.py +10 -0
- render/public_api/models/recovery_input.py +114 -0
- render/public_api/models/redis.py +182 -0
- render/public_api/models/redis_connection_info.py +76 -0
- render/public_api/models/redis_detail.py +192 -0
- render/public_api/models/redis_options.py +79 -0
- render/public_api/models/redis_patch_input.py +138 -0
- render/public_api/models/redis_plan.py +13 -0
- render/public_api/models/redis_post_input.py +166 -0
- render/public_api/models/redis_with_cursor.py +73 -0
- render/public_api/models/region.py +12 -0
- render/public_api/models/registry_credential.py +95 -0
- render/public_api/models/registry_credential_registry.py +12 -0
- render/public_api/models/registry_credential_summary.py +67 -0
- render/public_api/models/render_subdomain_policy.py +9 -0
- render/public_api/models/resource.py +67 -0
- render/public_api/models/resource_log_stream_setting.py +87 -0
- render/public_api/models/resource_ref.py +77 -0
- render/public_api/models/resource_ref_type.py +17 -0
- render/public_api/models/rollback_deploy_body.py +59 -0
- render/public_api/models/route.py +93 -0
- render/public_api/models/route_patch.py +60 -0
- render/public_api/models/route_post.py +88 -0
- render/public_api/models/route_put.py +77 -0
- render/public_api/models/route_type.py +9 -0
- render/public_api/models/route_with_cursor.py +73 -0
- render/public_api/models/run_task.py +96 -0
- render/public_api/models/runtime.py +12 -0
- render/public_api/models/sandbox.py +149 -0
- render/public_api/models/sandbox_connect_request.py +62 -0
- render/public_api/models/sandbox_connect_response.py +98 -0
- render/public_api/models/sandbox_directory_listing.py +82 -0
- render/public_api/models/sandbox_exec_update_request.py +60 -0
- render/public_api/models/sandbox_exec_update_response.py +130 -0
- render/public_api/models/sandbox_file_entry.py +98 -0
- render/public_api/models/sandbox_file_entry_type.py +10 -0
- render/public_api/models/sandbox_group.py +141 -0
- render/public_api/models/sandbox_group_with_cursor.py +74 -0
- render/public_api/models/sandbox_network_policy.py +61 -0
- render/public_api/models/sandbox_network_policy_default.py +9 -0
- render/public_api/models/sandbox_plan.py +10 -0
- render/public_api/models/sandbox_post.py +138 -0
- render/public_api/models/sandbox_post_env.py +44 -0
- render/public_api/models/sandbox_status.py +13 -0
- render/public_api/models/sandbox_with_cursor.py +74 -0
- render/public_api/models/scale_service_body.py +59 -0
- render/public_api/models/schemas_build_filter.py +70 -0
- render/public_api/models/schemas_image.py +96 -0
- render/public_api/models/schemas_user.py +68 -0
- render/public_api/models/secret_file.py +67 -0
- render/public_api/models/secret_file_input.py +67 -0
- render/public_api/models/secret_file_with_cursor.py +73 -0
- render/public_api/models/server_available.py +44 -0
- render/public_api/models/server_failed.py +81 -0
- render/public_api/models/server_hardware_failure.py +44 -0
- render/public_api/models/server_port.py +69 -0
- render/public_api/models/server_port_protocol.py +9 -0
- render/public_api/models/server_restarted.py +66 -0
- render/public_api/models/service_disk.py +78 -0
- render/public_api/models/service_env.py +15 -0
- render/public_api/models/service_event.py +770 -0
- render/public_api/models/service_event_type.py +48 -0
- render/public_api/models/service_event_with_cursor.py +66 -0
- render/public_api/models/service_instance.py +69 -0
- render/public_api/models/service_patch.py +273 -0
- render/public_api/models/service_post.py +357 -0
- render/public_api/models/service_resumed.py +44 -0
- render/public_api/models/service_runtime.py +15 -0
- render/public_api/models/service_suspended.py +44 -0
- render/public_api/models/service_type.py +12 -0
- render/public_api/models/service_type_short.py +12 -0
- render/public_api/models/setup_postgres_replication_response_201.py +44 -0
- render/public_api/models/snapshot_restore_post.py +72 -0
- render/public_api/models/static_site_details.py +183 -0
- render/public_api/models/static_site_details_patch.py +147 -0
- render/public_api/models/static_site_details_post.py +189 -0
- render/public_api/models/status.py +12 -0
- render/public_api/models/stream_sandbox_logs_accept.py +8 -0
- render/public_api/models/stream_task_runs_events_accept.py +8 -0
- render/public_api/models/suspender_added.py +83 -0
- render/public_api/models/suspender_removed.py +83 -0
- render/public_api/models/suspender_type.py +14 -0
- render/public_api/models/sync.py +118 -0
- render/public_api/models/sync_state.py +12 -0
- render/public_api/models/sync_with_cursor.py +74 -0
- render/public_api/models/task.py +97 -0
- render/public_api/models/task_attempt.py +121 -0
- render/public_api/models/task_attempt_details.py +141 -0
- render/public_api/models/task_data_type_1.py +44 -0
- render/public_api/models/task_run.py +168 -0
- render/public_api/models/task_run_details.py +216 -0
- render/public_api/models/task_run_status.py +14 -0
- render/public_api/models/task_run_with_cursor.py +73 -0
- render/public_api/models/task_with_cursor.py +73 -0
- render/public_api/models/team_member.py +102 -0
- render/public_api/models/team_member_role.py +12 -0
- render/public_api/models/team_member_status.py +9 -0
- render/public_api/models/time_series.py +100 -0
- render/public_api/models/time_series_value.py +70 -0
- render/public_api/models/update_env_group_secret_file_body.py +59 -0
- render/public_api/models/update_registry_credential_body.py +85 -0
- render/public_api/models/update_workspace_member_body.py +61 -0
- render/public_api/models/user.py +67 -0
- render/public_api/models/validate_blueprint_request.py +84 -0
- render/public_api/models/validate_blueprint_response.py +105 -0
- render/public_api/models/validation_error.py +88 -0
- render/public_api/models/validation_plan_summary.py +107 -0
- render/public_api/models/web_service_details.py +385 -0
- render/public_api/models/web_service_details_patch.py +270 -0
- render/public_api/models/web_service_details_post.py +354 -0
- render/public_api/models/webhook.py +110 -0
- render/public_api/models/webhook_event.py +115 -0
- render/public_api/models/webhook_event_with_cursor.py +73 -0
- render/public_api/models/webhook_patch_input.py +98 -0
- render/public_api/models/webhook_post_input.py +102 -0
- render/public_api/models/webhook_with_cursor.py +73 -0
- render/public_api/models/workflow.py +163 -0
- render/public_api/models/workflow_create.py +169 -0
- render/public_api/models/workflow_update.py +109 -0
- render/public_api/models/workflow_version.py +95 -0
- render/public_api/models/workflow_version_status.py +13 -0
- render/public_api/models/workflow_version_with_cursor.py +73 -0
- render/public_api/models/workflow_with_cursor.py +73 -0
- render/public_api/models/zero_downtime_redeploy_ended.py +44 -0
- render/public_api/models/zero_downtime_redeploy_started.py +59 -0
- render/public_api/py.typed +1 -0
- render/public_api/types.py +54 -0
- render/py.typed +0 -0
- render/render.py +81 -0
- render/render_async.py +80 -0
- render/version.py +27 -0
- render/workflows/__init__.py +25 -0
- render/workflows/_callback_models.py +194 -0
- render/workflows/_uds_http.py +128 -0
- render/workflows/app.py +263 -0
- render/workflows/cli.py +62 -0
- render/workflows/client.py +354 -0
- render/workflows/context.py +75 -0
- render/workflows/executor.py +86 -0
- render/workflows/runner.py +174 -0
- render/workflows/task.py +348 -0
- render/workflows/tests/__init__.py +0 -0
- render/workflows/tests/test_app.py +343 -0
- render/workflows/tests/test_cli.py +134 -0
- render/workflows/tests/test_client.py +59 -0
- render/workflows/tests/test_context.py +118 -0
- render/workflows/tests/test_end_to_end.py +307 -0
- render/workflows/tests/test_executor.py +221 -0
- render/workflows/tests/test_lazy_imports.py +100 -0
- render/workflows/tests/test_registration.py +293 -0
- render/workflows/tests/test_translate_errors.py +126 -0
- render/workflows/tests/test_uds_http.py +199 -0
- render/workflows/tests/test_version.py +15 -0
- render-1.0.1.dist-info/METADATA +382 -0
- render-1.0.1.dist-info/RECORD +799 -0
- render-1.0.1.dist-info/WHEEL +4 -0
- render-1.0.1.dist-info/entry_points.txt +2 -0
- render-1.0.1.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""End-to-end tests that simulate the full workflow."""
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from render.workflows._callback_models import TaskOptions
|
|
7
|
+
from render.workflows.client import Status, UDSClient
|
|
8
|
+
from render.workflows.executor import TaskExecutor
|
|
9
|
+
from render.workflows.runner import register
|
|
10
|
+
from render.workflows.task import (
|
|
11
|
+
Options,
|
|
12
|
+
Retry,
|
|
13
|
+
TaskRegistry,
|
|
14
|
+
create_task_decorator,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# Fixtures
|
|
19
|
+
@pytest.fixture
|
|
20
|
+
def task_registry():
|
|
21
|
+
"""Create a fresh task registry for each test."""
|
|
22
|
+
return TaskRegistry()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@pytest.fixture
|
|
26
|
+
def task_decorator(task_registry):
|
|
27
|
+
"""Create a task decorator bound to the test registry."""
|
|
28
|
+
return create_task_decorator(task_registry)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@pytest.fixture
|
|
32
|
+
def mock_client(mocker):
|
|
33
|
+
"""Create a mock UDS client."""
|
|
34
|
+
mock = mocker.create_autospec(UDSClient, spec_set=True)
|
|
35
|
+
mock.post_callback = mocker.AsyncMock()
|
|
36
|
+
return mock
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@pytest.fixture
|
|
40
|
+
def task_executor(task_registry, mock_client):
|
|
41
|
+
"""Create a task executor with mocked client."""
|
|
42
|
+
return TaskExecutor(task_registry, mock_client)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# End-to-end tests
|
|
46
|
+
def test_task_registration_network_payload(task_registry, task_decorator, mocker):
|
|
47
|
+
"""
|
|
48
|
+
Test that task registration actually sends the
|
|
49
|
+
correct payload over the network.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
# Mock the UDSClient class
|
|
53
|
+
mock_uds_client_class = mocker.patch("render.workflows.runner.UDSClient")
|
|
54
|
+
|
|
55
|
+
# Set up the mock instance
|
|
56
|
+
mock_client_instance = mocker.Mock()
|
|
57
|
+
mock_uds_client_class.return_value = mock_client_instance
|
|
58
|
+
mock_client_instance.register_tasks = mocker.AsyncMock(
|
|
59
|
+
return_value={"status": "success"},
|
|
60
|
+
)
|
|
61
|
+
mock_client_instance.disconnect = mocker.AsyncMock()
|
|
62
|
+
|
|
63
|
+
# Define tasks with various configurations
|
|
64
|
+
@task_decorator
|
|
65
|
+
def simple_task(ctx, x: int) -> int:
|
|
66
|
+
return x * 2
|
|
67
|
+
|
|
68
|
+
@task_decorator(name="custom_name")
|
|
69
|
+
def renamed_task(ctx, msg: str) -> str:
|
|
70
|
+
return f"Hello {msg}"
|
|
71
|
+
|
|
72
|
+
@task_decorator(
|
|
73
|
+
options=Options(
|
|
74
|
+
retry=Retry(max_retries=3, wait_duration_ms=1000, backoff_scaling=1.5)
|
|
75
|
+
),
|
|
76
|
+
)
|
|
77
|
+
def retry_task(ctx, data: str) -> str:
|
|
78
|
+
return data.upper()
|
|
79
|
+
|
|
80
|
+
# Mock get_task_registry to return our test registry
|
|
81
|
+
mock_get_registry = mocker.patch("render.workflows.runner.get_task_registry")
|
|
82
|
+
mock_get_registry.return_value = task_registry
|
|
83
|
+
|
|
84
|
+
register("/tmp/test.sock") # noqa:S108
|
|
85
|
+
|
|
86
|
+
# Verify that UDSClient was instantiated with correct socket path
|
|
87
|
+
mock_uds_client_class.assert_called_once_with("/tmp/test.sock") # noqa:S108
|
|
88
|
+
|
|
89
|
+
# Verify that register_tasks was called exactly once
|
|
90
|
+
mock_client_instance.register_tasks.assert_called_once()
|
|
91
|
+
|
|
92
|
+
# Get the actual payload that was sent
|
|
93
|
+
sent_tasks = mock_client_instance.register_tasks.call_args[0][0]
|
|
94
|
+
|
|
95
|
+
# Verify we have the expected number of tasks
|
|
96
|
+
assert len(sent_tasks.tasks) == 3
|
|
97
|
+
|
|
98
|
+
# Find tasks by name to verify their structure
|
|
99
|
+
task_by_name = {task.name: task for task in sent_tasks.tasks}
|
|
100
|
+
|
|
101
|
+
# Verify simple task
|
|
102
|
+
assert "simple_task" in task_by_name
|
|
103
|
+
simple_task_payload = task_by_name["simple_task"]
|
|
104
|
+
assert simple_task_payload.name == "simple_task"
|
|
105
|
+
assert isinstance(simple_task_payload.options, TaskOptions)
|
|
106
|
+
|
|
107
|
+
# The context is supplied by the runtime, so it is not a declared input
|
|
108
|
+
assert [p.name for p in simple_task_payload.parameters] == ["x"]
|
|
109
|
+
|
|
110
|
+
# Verify renamed task
|
|
111
|
+
assert "custom_name" in task_by_name
|
|
112
|
+
renamed_task_payload = task_by_name["custom_name"]
|
|
113
|
+
assert renamed_task_payload.name == "custom_name"
|
|
114
|
+
assert isinstance(renamed_task_payload.options, TaskOptions)
|
|
115
|
+
|
|
116
|
+
# Verify retry task with options
|
|
117
|
+
assert "retry_task" in task_by_name
|
|
118
|
+
retry_task_payload = task_by_name["retry_task"]
|
|
119
|
+
assert retry_task_payload.name == "retry_task"
|
|
120
|
+
|
|
121
|
+
# Verify retry options structure
|
|
122
|
+
retry_options = retry_task_payload.options.retry
|
|
123
|
+
assert retry_options.max_retries == 3
|
|
124
|
+
assert retry_options.wait_duration_ms == 1000
|
|
125
|
+
assert retry_options.factor == 1.5
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def test_task_registration_with_timeout_seconds(task_registry, task_decorator, mocker):
|
|
129
|
+
"""
|
|
130
|
+
Test that task registration correctly serializes timeout_seconds
|
|
131
|
+
in the network payload.
|
|
132
|
+
"""
|
|
133
|
+
from render.workflows._callback_models import UNSET
|
|
134
|
+
|
|
135
|
+
# Mock the UDSClient class
|
|
136
|
+
mock_uds_client_class = mocker.patch("render.workflows.runner.UDSClient")
|
|
137
|
+
|
|
138
|
+
# Set up the mock instance
|
|
139
|
+
mock_client_instance = mocker.Mock()
|
|
140
|
+
mock_uds_client_class.return_value = mock_client_instance
|
|
141
|
+
mock_client_instance.register_tasks = mocker.AsyncMock(
|
|
142
|
+
return_value={"status": "success"},
|
|
143
|
+
)
|
|
144
|
+
mock_client_instance.disconnect = mocker.AsyncMock()
|
|
145
|
+
|
|
146
|
+
# Define a task with timeout_seconds
|
|
147
|
+
@task_decorator(options=Options(timeout_seconds=120))
|
|
148
|
+
def timeout_task(ctx, x: int) -> int:
|
|
149
|
+
return x * 2
|
|
150
|
+
|
|
151
|
+
# Define a task without timeout_seconds
|
|
152
|
+
@task_decorator
|
|
153
|
+
def no_timeout_task(ctx, x: int) -> int:
|
|
154
|
+
return x * 3
|
|
155
|
+
|
|
156
|
+
# Define a task with both timeout_seconds and retry
|
|
157
|
+
@task_decorator(
|
|
158
|
+
options=Options(
|
|
159
|
+
timeout_seconds=300,
|
|
160
|
+
retry=Retry(max_retries=2, wait_duration_ms=500, backoff_scaling=1.5),
|
|
161
|
+
)
|
|
162
|
+
)
|
|
163
|
+
def timeout_and_retry_task(ctx, x: int) -> int:
|
|
164
|
+
return x * 4
|
|
165
|
+
|
|
166
|
+
# Mock get_task_registry to return our test registry
|
|
167
|
+
mock_get_registry = mocker.patch("render.workflows.runner.get_task_registry")
|
|
168
|
+
mock_get_registry.return_value = task_registry
|
|
169
|
+
|
|
170
|
+
register("/tmp/test.sock") # noqa:S108
|
|
171
|
+
|
|
172
|
+
# Get the actual payload that was sent
|
|
173
|
+
sent_tasks = mock_client_instance.register_tasks.call_args[0][0]
|
|
174
|
+
|
|
175
|
+
# Find tasks by name
|
|
176
|
+
task_by_name = {task.name: task for task in sent_tasks.tasks}
|
|
177
|
+
|
|
178
|
+
# Verify task with timeout_seconds
|
|
179
|
+
assert "timeout_task" in task_by_name
|
|
180
|
+
timeout_task_payload = task_by_name["timeout_task"]
|
|
181
|
+
assert timeout_task_payload.options.timeout_seconds == 120
|
|
182
|
+
|
|
183
|
+
# Verify task without timeout_seconds has UNSET timeout
|
|
184
|
+
assert "no_timeout_task" in task_by_name
|
|
185
|
+
no_timeout_payload = task_by_name["no_timeout_task"]
|
|
186
|
+
assert no_timeout_payload.options.timeout_seconds is UNSET
|
|
187
|
+
|
|
188
|
+
# Verify task with both timeout and retry
|
|
189
|
+
assert "timeout_and_retry_task" in task_by_name
|
|
190
|
+
combined_payload = task_by_name["timeout_and_retry_task"]
|
|
191
|
+
assert combined_payload.options.timeout_seconds == 300
|
|
192
|
+
assert combined_payload.options.retry is not UNSET
|
|
193
|
+
assert combined_payload.options.retry.max_retries == 2
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def test_task_registration_with_dict_retry_config(
|
|
197
|
+
task_registry, task_decorator, mocker
|
|
198
|
+
):
|
|
199
|
+
"""
|
|
200
|
+
Test that registration handles dict retry configs passed to Options().
|
|
201
|
+
A caller may construct Options(retry={...}) with a plain dict instead
|
|
202
|
+
of a Retry instance. Options.__post_init__ should coerce it.
|
|
203
|
+
"""
|
|
204
|
+
mock_uds_client_class = mocker.patch("render.workflows.runner.UDSClient")
|
|
205
|
+
mock_client_instance = mocker.Mock()
|
|
206
|
+
mock_uds_client_class.return_value = mock_client_instance
|
|
207
|
+
mock_client_instance.register_tasks = mocker.AsyncMock(
|
|
208
|
+
return_value={"status": "success"},
|
|
209
|
+
)
|
|
210
|
+
mock_client_instance.disconnect = mocker.AsyncMock()
|
|
211
|
+
|
|
212
|
+
# Options receives a plain dict instead of a Retry instance
|
|
213
|
+
dict_retry = {
|
|
214
|
+
"max_retries": 5,
|
|
215
|
+
"wait_duration_ms": 2000,
|
|
216
|
+
"backoff_scaling": 2.0,
|
|
217
|
+
}
|
|
218
|
+
options = Options(retry=dict_retry)
|
|
219
|
+
|
|
220
|
+
@task_decorator(options=options)
|
|
221
|
+
def my_task(ctx, x: int) -> int:
|
|
222
|
+
return x
|
|
223
|
+
|
|
224
|
+
mock_get_registry = mocker.patch("render.workflows.runner.get_task_registry")
|
|
225
|
+
mock_get_registry.return_value = task_registry
|
|
226
|
+
|
|
227
|
+
register("/tmp/test.sock") # noqa:S108
|
|
228
|
+
|
|
229
|
+
sent_tasks = mock_client_instance.register_tasks.call_args[0][0]
|
|
230
|
+
task_payload = sent_tasks.tasks[0]
|
|
231
|
+
|
|
232
|
+
assert task_payload.options.retry.max_retries == 5
|
|
233
|
+
assert task_payload.options.retry.wait_duration_ms == 2000
|
|
234
|
+
assert task_payload.options.retry.factor == 2.0
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def test_task_registration_without_retry_config(task_registry, task_decorator, mocker):
|
|
238
|
+
"""
|
|
239
|
+
Test that tasks with no retry config register successfully. This is the
|
|
240
|
+
common case that was unaffected by the dict retry bug.
|
|
241
|
+
"""
|
|
242
|
+
from render.workflows._callback_models import UNSET
|
|
243
|
+
|
|
244
|
+
mock_uds_client_class = mocker.patch("render.workflows.runner.UDSClient")
|
|
245
|
+
mock_client_instance = mocker.Mock()
|
|
246
|
+
mock_uds_client_class.return_value = mock_client_instance
|
|
247
|
+
mock_client_instance.register_tasks = mocker.AsyncMock(
|
|
248
|
+
return_value={"status": "success"},
|
|
249
|
+
)
|
|
250
|
+
mock_client_instance.disconnect = mocker.AsyncMock()
|
|
251
|
+
|
|
252
|
+
@task_decorator
|
|
253
|
+
def my_task(ctx, x: int) -> int:
|
|
254
|
+
return x
|
|
255
|
+
|
|
256
|
+
mock_get_registry = mocker.patch("render.workflows.runner.get_task_registry")
|
|
257
|
+
mock_get_registry.return_value = task_registry
|
|
258
|
+
|
|
259
|
+
register("/tmp/test.sock") # noqa:S108
|
|
260
|
+
|
|
261
|
+
sent_tasks = mock_client_instance.register_tasks.call_args[0][0]
|
|
262
|
+
task_payload = sent_tasks.tasks[0]
|
|
263
|
+
|
|
264
|
+
assert task_payload.options.retry is UNSET
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
@pytest.mark.asyncio
|
|
268
|
+
async def test_callback_payloads_with_mocked_client(
|
|
269
|
+
task_registry,
|
|
270
|
+
task_decorator,
|
|
271
|
+
mock_client,
|
|
272
|
+
):
|
|
273
|
+
"""Test that callback payloads are correctly formatted and sent."""
|
|
274
|
+
|
|
275
|
+
@task_decorator
|
|
276
|
+
def test_task(ctx, value: int) -> int:
|
|
277
|
+
return value * 10
|
|
278
|
+
|
|
279
|
+
@task_decorator
|
|
280
|
+
def failing_task(ctx, should_fail: bool) -> str:
|
|
281
|
+
if should_fail:
|
|
282
|
+
raise ValueError("Test failure")
|
|
283
|
+
return "success"
|
|
284
|
+
|
|
285
|
+
# Test success callback
|
|
286
|
+
executor = TaskExecutor(task_registry, mock_client)
|
|
287
|
+
|
|
288
|
+
result = await executor.execute("test_task", [5])
|
|
289
|
+
assert result == 50
|
|
290
|
+
|
|
291
|
+
# Verify success callback was sent with correct payload
|
|
292
|
+
mock_client.post_callback.assert_called_once()
|
|
293
|
+
success_payload = mock_client.post_callback.call_args[0][0]
|
|
294
|
+
assert success_payload.status == Status.SUCCESS
|
|
295
|
+
assert success_payload.result == 50
|
|
296
|
+
|
|
297
|
+
# Reset mock and test error callback
|
|
298
|
+
mock_client.reset_mock()
|
|
299
|
+
|
|
300
|
+
with pytest.raises(ValueError):
|
|
301
|
+
await executor.execute("failing_task", [True])
|
|
302
|
+
|
|
303
|
+
# Verify error callback was sent with correct payload
|
|
304
|
+
mock_client.post_callback.assert_called_once()
|
|
305
|
+
error_payload = mock_client.post_callback.call_args[0][0]
|
|
306
|
+
assert error_payload.status == Status.ERROR
|
|
307
|
+
assert "Test failure" in error_payload.error
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Tests for the task executor functionality."""
|
|
3
|
+
|
|
4
|
+
from unittest.mock import AsyncMock
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
from render.client.errors import ClientError, ServerError, TimeoutError
|
|
9
|
+
from render.workflows.client import Status, UDSClient, _retry_transient_errors
|
|
10
|
+
from render.workflows.executor import TaskExecutor
|
|
11
|
+
from render.workflows.task import TaskRegistry, create_task_decorator
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# Fixtures
|
|
15
|
+
@pytest.fixture
|
|
16
|
+
def task_registry():
|
|
17
|
+
"""Create a fresh task registry for each test."""
|
|
18
|
+
return TaskRegistry()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@pytest.fixture
|
|
22
|
+
def task_decorator(task_registry):
|
|
23
|
+
"""Create a task decorator bound to the test registry."""
|
|
24
|
+
return create_task_decorator(task_registry)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@pytest.fixture
|
|
28
|
+
def mock_client(mocker):
|
|
29
|
+
"""Create a mock UDS client."""
|
|
30
|
+
mock = mocker.create_autospec(UDSClient, spec_set=True)
|
|
31
|
+
mock.post_callback = mocker.AsyncMock()
|
|
32
|
+
return mock
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@pytest.fixture
|
|
36
|
+
def task_executor(task_registry, mock_client):
|
|
37
|
+
"""Create a task executor with mocked client."""
|
|
38
|
+
return TaskExecutor(task_registry, mock_client)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# Basic Task Execution Tests
|
|
42
|
+
@pytest.mark.asyncio
|
|
43
|
+
async def test_simple_task_execution(task_decorator, task_executor, mock_client):
|
|
44
|
+
"""Test executing a simple task without subtasks."""
|
|
45
|
+
|
|
46
|
+
@task_decorator
|
|
47
|
+
def add_numbers(ctx, a: int, b: int) -> int:
|
|
48
|
+
return a + b
|
|
49
|
+
|
|
50
|
+
result = await task_executor.execute("add_numbers", [5, 3])
|
|
51
|
+
|
|
52
|
+
assert result == 8
|
|
53
|
+
mock_client.post_callback.assert_called_once()
|
|
54
|
+
call_args = mock_client.post_callback.call_args[0][0]
|
|
55
|
+
assert call_args.status == Status.SUCCESS
|
|
56
|
+
assert call_args.result == 8
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@pytest.mark.asyncio
|
|
60
|
+
async def test_task_with_string_result(task_decorator, task_executor, mock_client):
|
|
61
|
+
"""Test task that returns a string."""
|
|
62
|
+
|
|
63
|
+
@task_decorator
|
|
64
|
+
def greet(ctx, name: str) -> str:
|
|
65
|
+
return f"Hello, {name}!"
|
|
66
|
+
|
|
67
|
+
result = await task_executor.execute("greet", ["World"])
|
|
68
|
+
|
|
69
|
+
assert result == "Hello, World!"
|
|
70
|
+
mock_client.post_callback.assert_called_once()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@pytest.mark.asyncio
|
|
74
|
+
async def test_task_execution_error(task_decorator, task_executor, mock_client):
|
|
75
|
+
"""Test task execution that raises an error."""
|
|
76
|
+
|
|
77
|
+
@task_decorator
|
|
78
|
+
def failing_task(ctx, x: int) -> int:
|
|
79
|
+
if x < 0:
|
|
80
|
+
raise ValueError("Negative numbers not allowed")
|
|
81
|
+
return x * 2
|
|
82
|
+
|
|
83
|
+
with pytest.raises(ValueError):
|
|
84
|
+
await task_executor.execute("failing_task", [-5])
|
|
85
|
+
|
|
86
|
+
mock_client.post_callback.assert_called_once()
|
|
87
|
+
call_args = mock_client.post_callback.call_args[0][0]
|
|
88
|
+
assert call_args.status == Status.ERROR
|
|
89
|
+
assert "Negative numbers not allowed" in call_args.error
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@pytest.mark.asyncio
|
|
93
|
+
async def test_nonexistent_task(task_executor, mock_client):
|
|
94
|
+
"""Test executing a task that doesn't exist."""
|
|
95
|
+
with pytest.raises(ValueError):
|
|
96
|
+
await task_executor.execute("nonexistent_task", [])
|
|
97
|
+
|
|
98
|
+
mock_client.post_callback.assert_called_once()
|
|
99
|
+
call_args = mock_client.post_callback.call_args[0][0]
|
|
100
|
+
assert call_args.status == Status.ERROR
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# Integration Tests
|
|
104
|
+
@pytest.mark.asyncio
|
|
105
|
+
async def test_complex_task_chain(task_registry, task_decorator, mock_client, mocker):
|
|
106
|
+
"""Test a complex chain of task executions."""
|
|
107
|
+
|
|
108
|
+
@task_decorator
|
|
109
|
+
def increment(ctx, x: int) -> int:
|
|
110
|
+
return x + 1
|
|
111
|
+
|
|
112
|
+
@task_decorator
|
|
113
|
+
def double(ctx, x: int) -> int:
|
|
114
|
+
return x * 2
|
|
115
|
+
|
|
116
|
+
@task_decorator
|
|
117
|
+
async def complex_calculation(ctx, start: int) -> int:
|
|
118
|
+
# Reach subtasks through the context
|
|
119
|
+
step1 = await ctx.run(increment, start)
|
|
120
|
+
step2 = await ctx.run(double, step1)
|
|
121
|
+
step3 = await ctx.run(increment, step2)
|
|
122
|
+
return step3
|
|
123
|
+
|
|
124
|
+
# Configure mock to simulate subtask execution
|
|
125
|
+
def mock_run_subtask(task_name, args):
|
|
126
|
+
if task_name == "increment":
|
|
127
|
+
return args[0] + 1
|
|
128
|
+
elif task_name == "double":
|
|
129
|
+
return args[0] * 2
|
|
130
|
+
else:
|
|
131
|
+
raise ValueError(f"Unknown task: {task_name}")
|
|
132
|
+
|
|
133
|
+
mock_client.run_subtask = mocker.AsyncMock(side_effect=mock_run_subtask)
|
|
134
|
+
|
|
135
|
+
executor = TaskExecutor(task_registry, mock_client)
|
|
136
|
+
result = await executor.execute("complex_calculation", [5])
|
|
137
|
+
|
|
138
|
+
# Should compute: ((5 + 1) * 2) + 1 = (6 * 2) + 1 = 12 + 1 = 13
|
|
139
|
+
assert result == 13
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@pytest.mark.asyncio
|
|
143
|
+
async def test_callback_format(task_registry, task_decorator, mock_client):
|
|
144
|
+
"""Test that callbacks are formatted correctly."""
|
|
145
|
+
|
|
146
|
+
@task_decorator
|
|
147
|
+
def simple_task(ctx, value: str) -> str:
|
|
148
|
+
return f"processed: {value}"
|
|
149
|
+
|
|
150
|
+
executor = TaskExecutor(task_registry, mock_client)
|
|
151
|
+
await executor.execute("simple_task", ["test"])
|
|
152
|
+
|
|
153
|
+
# Check that callback was called with correct format
|
|
154
|
+
mock_client.post_callback.assert_called_once()
|
|
155
|
+
call_args = mock_client.post_callback.call_args[0][0]
|
|
156
|
+
assert call_args.status == Status.SUCCESS
|
|
157
|
+
assert call_args.result == "processed: test"
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
# UDS Retry Tests
|
|
161
|
+
@pytest.fixture(autouse=False)
|
|
162
|
+
def _no_sleep(mocker):
|
|
163
|
+
"""Patch asyncio.sleep in the client module to avoid real delays."""
|
|
164
|
+
mocker.patch("render.workflows.client.asyncio.sleep", new_callable=AsyncMock)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
@pytest.mark.asyncio
|
|
168
|
+
@pytest.mark.usefixtures("_no_sleep")
|
|
169
|
+
async def test_retry_on_server_error_then_succeed():
|
|
170
|
+
"""Test that transient ServerError triggers retries and eventually succeeds."""
|
|
171
|
+
mock_fn = AsyncMock(
|
|
172
|
+
side_effect=[
|
|
173
|
+
ServerError("connection refused"),
|
|
174
|
+
ServerError("connection refused"),
|
|
175
|
+
"ok",
|
|
176
|
+
]
|
|
177
|
+
)
|
|
178
|
+
decorated = _retry_transient_errors(mock_fn)
|
|
179
|
+
|
|
180
|
+
result = await decorated()
|
|
181
|
+
|
|
182
|
+
assert result == "ok"
|
|
183
|
+
assert mock_fn.call_count == 3
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
@pytest.mark.asyncio
|
|
187
|
+
@pytest.mark.usefixtures("_no_sleep")
|
|
188
|
+
async def test_retry_on_timeout_error_then_succeed():
|
|
189
|
+
"""Test that TimeoutError triggers retries and eventually succeeds."""
|
|
190
|
+
mock_fn = AsyncMock(side_effect=[TimeoutError("timed out"), "ok"])
|
|
191
|
+
decorated = _retry_transient_errors(mock_fn)
|
|
192
|
+
|
|
193
|
+
result = await decorated()
|
|
194
|
+
|
|
195
|
+
assert result == "ok"
|
|
196
|
+
assert mock_fn.call_count == 2
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@pytest.mark.asyncio
|
|
200
|
+
async def test_no_retry_on_client_error():
|
|
201
|
+
"""Test that ClientError (4xx) is raised immediately without retry."""
|
|
202
|
+
mock_fn = AsyncMock(side_effect=ClientError("bad request"))
|
|
203
|
+
decorated = _retry_transient_errors(mock_fn)
|
|
204
|
+
|
|
205
|
+
with pytest.raises(ClientError):
|
|
206
|
+
await decorated()
|
|
207
|
+
|
|
208
|
+
assert mock_fn.call_count == 1
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
@pytest.mark.asyncio
|
|
212
|
+
@pytest.mark.usefixtures("_no_sleep")
|
|
213
|
+
async def test_retry_exhausted_raises_last_error():
|
|
214
|
+
"""Test that after exhausting all retries, the last error is raised."""
|
|
215
|
+
mock_fn = AsyncMock(side_effect=ServerError("connection refused"))
|
|
216
|
+
decorated = _retry_transient_errors(mock_fn)
|
|
217
|
+
|
|
218
|
+
with pytest.raises(ServerError):
|
|
219
|
+
await decorated()
|
|
220
|
+
|
|
221
|
+
assert mock_fn.call_count == 25 # _UDS_MAX_RETRIES
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""Regression guard for the worker import graph.
|
|
2
|
+
|
|
3
|
+
The worker path (``from render import Workflows``) deliberately avoids
|
|
4
|
+
loading the REST API client, the public_api models, the auto-generated
|
|
5
|
+
callback API, the experimental object storage code, ``httpx``, and
|
|
6
|
+
``attrs`` — none of those are needed to define or run a task. Keeping
|
|
7
|
+
the import graph slim is what keeps the workflow path fast.
|
|
8
|
+
|
|
9
|
+
If you find yourself adding an eager top-level ``from render.<heavy>
|
|
10
|
+
import ...`` to a module on the worker path, lazy-load it instead — see
|
|
11
|
+
``render/__init__.py`` and ``render/client/__init__.py`` for the
|
|
12
|
+
pattern. If a fresh module truly belongs on the worker path, add it here
|
|
13
|
+
and document why.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
import subprocess
|
|
20
|
+
import sys
|
|
21
|
+
|
|
22
|
+
# Modules that must NOT be loaded by `from render import Workflows`.
|
|
23
|
+
# Each entry matches the module itself and any submodule under it.
|
|
24
|
+
_FORBIDDEN_ON_WORKER_PATH: tuple[str, ...] = (
|
|
25
|
+
# The REST API surface (Render / RenderAsync).
|
|
26
|
+
"render.render",
|
|
27
|
+
"render.render_async",
|
|
28
|
+
# The auto-generated REST client + every model in it.
|
|
29
|
+
"render.public_api",
|
|
30
|
+
# The hand-written REST client wrappers (Client, WorkflowsService, etc.)
|
|
31
|
+
# plus client.types and client.sse. Worker-path leaf modules are listed
|
|
32
|
+
# in _ALLOWED_ON_WORKER_PATH below.
|
|
33
|
+
"render.client",
|
|
34
|
+
# Object storage / experimental APIs.
|
|
35
|
+
"render.experimental",
|
|
36
|
+
# Third-party HTTP/attribute libraries — only the REST/experimental
|
|
37
|
+
# paths should pull these in, never the worker path.
|
|
38
|
+
"httpx",
|
|
39
|
+
"httpcore",
|
|
40
|
+
"attr",
|
|
41
|
+
"attrs",
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
# Submodules of forbidden packages that ARE allowed on the worker path.
|
|
45
|
+
_ALLOWED_ON_WORKER_PATH: frozenset[str] = frozenset(
|
|
46
|
+
{
|
|
47
|
+
# The errors module is shared between the REST client and the
|
|
48
|
+
# hand-rolled UDS HTTP transport. The package init itself is
|
|
49
|
+
# lazy, so loading it carries no other cost.
|
|
50
|
+
"render.client",
|
|
51
|
+
"render.client.errors",
|
|
52
|
+
}
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _modules_loaded_by(import_statement: str) -> set[str]:
|
|
57
|
+
"""Run the given import in a fresh interpreter and return sys.modules."""
|
|
58
|
+
code = f"import sys\n{import_statement}\nimport json\nprint(json.dumps(sorted(sys.modules.keys())))\n"
|
|
59
|
+
result = subprocess.run( # noqa: S603 — code is a constructed literal, not user input
|
|
60
|
+
[sys.executable, "-c", code],
|
|
61
|
+
check=True,
|
|
62
|
+
capture_output=True,
|
|
63
|
+
text=True,
|
|
64
|
+
)
|
|
65
|
+
return set(json.loads(result.stdout))
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_worker_path_does_not_load_rest_or_experimental_modules() -> None:
|
|
69
|
+
loaded = _modules_loaded_by("from render import Workflows")
|
|
70
|
+
|
|
71
|
+
leaked: list[str] = []
|
|
72
|
+
for module in sorted(loaded):
|
|
73
|
+
if module in _ALLOWED_ON_WORKER_PATH:
|
|
74
|
+
continue
|
|
75
|
+
for forbidden in _FORBIDDEN_ON_WORKER_PATH:
|
|
76
|
+
if module == forbidden or module.startswith(forbidden + "."):
|
|
77
|
+
leaked.append(module)
|
|
78
|
+
break
|
|
79
|
+
|
|
80
|
+
assert not leaked, (
|
|
81
|
+
"Importing `render.Workflows` eagerly loaded modules that are "
|
|
82
|
+
"off the worker hot path:\n - "
|
|
83
|
+
+ "\n - ".join(leaked)
|
|
84
|
+
+ "\n\nLazy-load the offending imports or move them inside the "
|
|
85
|
+
"function bodies where they're used. See the module docstring of "
|
|
86
|
+
"test_lazy_imports.py for the contract."
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def test_lazy_attrs_still_resolve_when_explicitly_accessed() -> None:
|
|
91
|
+
"""Smoke check: the lazy machinery must keep `from render import
|
|
92
|
+
Render` working — otherwise the leak guard above could be satisfied by
|
|
93
|
+
simply breaking the symbol."""
|
|
94
|
+
loaded = _modules_loaded_by(
|
|
95
|
+
"from render import Render, RenderAsync, Workflows; "
|
|
96
|
+
"assert Render is not None and RenderAsync is not None "
|
|
97
|
+
"and Workflows is not None"
|
|
98
|
+
)
|
|
99
|
+
assert "render.render" in loaded
|
|
100
|
+
assert "render.render_async" in loaded
|