runpod 2.0.0rc2__tar.gz → 2.0.1.dev1__tar.gz
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.
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/.github/workflows/cd.yml +6 -6
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/.github/workflows/ci.yml +7 -7
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/.github/workflows/vhs.yml +1 -1
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/CHANGELOG.md +25 -0
- {runpod-2.0.0rc2/runpod.egg-info → runpod-2.0.1.dev1}/PKG-INFO +7 -5
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/docs/serverless/volume_cache.md +22 -4
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/pytest.ini +1 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/requirements.txt +6 -4
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/_health/system.py +33 -1
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/_version.py +3 -3
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/api.py +5 -0
- runpod-2.0.1.dev1/runpod/apps/auth.py +185 -0
- runpod-2.0.1.dev1/runpod/apps/build.py +722 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/deploy.py +8 -2
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/dev.py +51 -8
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/gpu.py +160 -11
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/logs.py +46 -16
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/volume.py +27 -1
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/cli/groups/ssh/functions.py +2 -7
- runpod-2.0.1.dev1/runpod/rp_cli/api.py +30 -0
- runpod-2.0.1.dev1/runpod/rp_cli/catalog.py +308 -0
- runpod-2.0.1.dev1/runpod/rp_cli/compute.py +258 -0
- runpod-2.0.1.dev1/runpod/rp_cli/endpoint.py +1215 -0
- runpod-2.0.1.dev1/runpod/rp_cli/endpoint_config.py +292 -0
- runpod-2.0.1.dev1/runpod/rp_cli/logs.py +157 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/rp_cli/main.py +140 -16
- runpod-2.0.1.dev1/runpod/rp_cli/options.py +64 -0
- runpod-2.0.1.dev1/runpod/rp_cli/output.py +115 -0
- runpod-2.0.1.dev1/runpod/rp_cli/pod.py +783 -0
- runpod-2.0.1.dev1/runpod/rp_cli/volume.py +298 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/serverless/modules/rp_scale.py +35 -2
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/serverless/utils/rp_volume_cache.py +278 -36
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1/runpod.egg-info}/PKG-INFO +7 -5
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod.egg-info/SOURCES.txt +23 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod.egg-info/requires.txt +6 -4
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod.egg-info/scm_file_list.json +23 -0
- runpod-2.0.1.dev1/runpod.egg-info/scm_version.json +8 -0
- runpod-2.0.1.dev1/tests/test_apps/conftest.py +15 -0
- runpod-2.0.1.dev1/tests/test_apps/test_agent_auth.py +242 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_apps/test_app.py +74 -0
- runpod-2.0.1.dev1/tests/test_apps/test_build.py +646 -0
- runpod-2.0.1.dev1/tests/test_apps/test_gpu_catalog.py +246 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_apps/test_logs.py +143 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_apps/test_volume.py +38 -0
- runpod-2.0.1.dev1/tests/test_cli/test_agent_login.py +185 -0
- runpod-2.0.1.dev1/tests/test_cli/test_catalog_command.py +318 -0
- runpod-2.0.1.dev1/tests/test_cli/test_cli_api.py +42 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_cli/test_cli_groups/test_ssh_functions.py +40 -13
- runpod-2.0.1.dev1/tests/test_cli/test_cli_logs.py +288 -0
- runpod-2.0.1.dev1/tests/test_cli/test_cli_options.py +98 -0
- runpod-2.0.1.dev1/tests/test_cli/test_cli_output.py +120 -0
- runpod-2.0.1.dev1/tests/test_cli/test_compute.py +199 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_cli/test_deploy_command.py +187 -0
- runpod-2.0.1.dev1/tests/test_cli/test_endpoint_command.py +1356 -0
- runpod-2.0.1.dev1/tests/test_cli/test_pod_command.py +946 -0
- runpod-2.0.1.dev1/tests/test_cli/test_volume_command.py +339 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_modules/test_fitness/test_system_checks.py +38 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_rp_scale.py +118 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_utils/test_rp_volume_cache.py +393 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_worker.py +8 -3
- runpod-2.0.0rc2/runpod/apps/auth.py +0 -77
- runpod-2.0.0rc2/runpod/apps/build.py +0 -378
- runpod-2.0.0rc2/runpod.egg-info/scm_version.json +0 -8
- runpod-2.0.0rc2/tests/test_apps/test_build.py +0 -255
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/.dockerignore +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/.github/dependabot.yml +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/.github/tape_collection/cli_config.tape +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/.github/tape_collection/cli_help.tape +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/.github/tape_collection/cli_ssh.tape +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/.github/tests.json +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/.gitignore +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/ARCHITECTURE.md +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/CONTRIBUTING.md +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/LICENSE +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/MANIFEST.in +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/Makefile +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/README.md +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/SECURITY.md +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/build_tools/compile_gpu_test.sh +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/build_tools/gpu_test.c +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/docs/api/handling_errors.md +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/docs/api/queries.md +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/docs/cli/demos/config.gif +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/docs/cli/demos/help.gif +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/docs/cli/demos/ssh.gif +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/docs/cli/references/command_line_interface.md +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/docs/cli/references/projects.md +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/docs/cli/references/runpod.toml.md +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/docs/cli/start_here.md +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/docs/getting_Started.md +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/docs/images/env_var_location.png +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/docs/serverless/functions/error_handling.md +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/docs/serverless/functions/logging.md +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/docs/serverless/gpu_binary_compilation.md +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/docs/serverless/local_testing.md +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/docs/serverless/utils/rp_cleanup.md +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/docs/serverless/utils/rp_debugger.md +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/docs/serverless/utils/rp_download.md +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/docs/serverless/utils/rp_upload.md +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/docs/serverless/utils/rp_validator.md +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/docs/serverless/worker.md +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/docs/serverless/worker_fitness_checks.md +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/docs/serverless/worker_realtime.md +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/examples/api/create_container_registry_auth.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/examples/api/create_endpoint.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/examples/api/create_template.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/examples/api/get_endpoints.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/examples/apps/README.md +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/examples/apps/gpu_inference.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/examples/apps/hello_world.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/examples/apps/streaming.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/examples/apps/train_and_eval.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/examples/apps/web_service.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/examples/endpoints/asyncio_job_request.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/examples/endpoints/health.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/examples/endpoints/run.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/examples/endpoints/run_sync.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/examples/endpoints/streaming.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/examples/rest_wrapper.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/examples/serverless/concurrent_handler.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/examples/serverless/logger.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/examples/serverless/simple_handler.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/examples/serverless/volume_cache_handler.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/pyproject.toml +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/_binary_helpers.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/_health/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/_health/cuda.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/_health/fitness.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/_health/gpu.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/_logger.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/_startup.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/agent.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/api/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/api/ctl_commands.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/api/graphql.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/api/mutations/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/api/mutations/apps.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/api/mutations/container_register_auth.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/api/queries/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/api/queries/apps.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/api/queries/user.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/api/rest.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/app.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/context.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/datacenter.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/discovery.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/discovery_state.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/entrypoint.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/errors.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/handles.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/images.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/init.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/invoker.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/job.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/manage.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/markers.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/model.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/monitor.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/placement.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/protocol.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/registry.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/schedule.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/secret.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/serialization.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/shim.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/spec.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/stubs.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/targets.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/tasks.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/utils/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/utils/client.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/utils/events.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/utils/lookup.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/utils/names.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/utils/network.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/apps/watch.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/cli/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/cli/entry.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/cli/groups/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/cli/groups/config/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/cli/groups/config/functions.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/cli/groups/pod/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/cli/groups/pod/commands.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/cli/groups/ssh/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/cli/groups/ssh/commands.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/cli/utils/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/cli/utils/rp_info.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/cli/utils/rp_runpodignore.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/cli/utils/rp_userspace.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/cli/utils/ssh_cmd.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/endpoint/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/endpoint/asyncio/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/endpoint/asyncio/asyncio_runner.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/endpoint/helpers.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/endpoint/runner.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/error.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/http_client.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/rp_cli/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/rp_cli/console.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/rp_cli/update.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/serverless/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/serverless/binaries/README.md +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/serverless/binaries/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/serverless/binaries/gpu_test +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/serverless/modules/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/serverless/modules/rp_capture.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/serverless/modules/rp_fastapi.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/serverless/modules/rp_fitness.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/serverless/modules/rp_gpu_fitness.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/serverless/modules/rp_handler.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/serverless/modules/rp_http.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/serverless/modules/rp_job.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/serverless/modules/rp_local.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/serverless/modules/rp_logger.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/serverless/modules/rp_ping.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/serverless/modules/rp_prestart.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/serverless/modules/rp_progress.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/serverless/modules/rp_system_fitness.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/serverless/modules/rp_tips.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/serverless/modules/worker_state.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/serverless/utils/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/serverless/utils/rp_cleanup.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/serverless/utils/rp_cuda.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/serverless/utils/rp_debugger.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/serverless/utils/rp_download.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/serverless/utils/rp_model_cache.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/serverless/utils/rp_ssrf.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/serverless/utils/rp_upload.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/serverless/utils/rp_validator.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/serverless/worker.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/user_agent.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod/version.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod.egg-info/dependency_links.txt +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod.egg-info/entry_points.txt +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/runpod.egg-info/top_level.txt +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/scripts/README.md +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/scripts/benchmark_cold_start.sh +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/scripts/compare_benchmarks.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/setup.cfg +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/setup.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/e2e/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/e2e/apps_live_smoke.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/e2e/conftest.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/e2e/e2e_provisioner.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/e2e/examples/01_hello_world.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/e2e/examples/02_invocation_styles.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/e2e/examples/03_gpu.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/e2e/examples/04_dependencies.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/e2e/examples/05_api_service.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/e2e/examples/06_tasks.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/e2e/examples/07_pipelines.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/e2e/examples/08_volumes.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/e2e/examples/09_secrets.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/e2e/examples/10_cached_models.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/e2e/examples/11_train_eval.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/e2e/examples/12_custom_image.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/e2e/examples/13_streaming.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/e2e/examples/README.md +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/e2e/examples/run_all.sh +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/e2e/fixtures/cold_start/handler.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/e2e/fixtures/cold_start/pyproject.toml +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/e2e/matrix/main.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/e2e/matrix/run.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/e2e/task_pod_smoke.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/e2e/test_cold_start.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/e2e/test_mock_worker.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/e2e/tests.json +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/fixtures/mock_gpu_test +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_agent.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_api/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_api/test_ctl_commands.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_api/test_rest.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_apps/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_apps/test_api_client.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_apps/test_auth.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_apps/test_compat_lock.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_apps/test_config_params.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_apps/test_deploy.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_apps/test_dev.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_apps/test_discovery.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_apps/test_dispatch.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_apps/test_images.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_apps/test_job.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_apps/test_manage.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_apps/test_markers.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_apps/test_model.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_apps/test_monitor.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_apps/test_placement.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_apps/test_registry.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_apps/test_retry.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_apps/test_secret.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_apps/test_shim.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_apps/test_targets.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_apps/test_tasks.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_apps/test_watch.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_cli/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_cli/test_cli_groups/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_cli/test_cli_groups/test_config_functions.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_cli/test_cli_groups/test_pod_commands.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_cli/test_cli_groups/test_ssh_commands.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_cli/test_cli_groups/test_ssh_dispatch.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_cli/test_cli_utils/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_cli/test_cli_utils/test_info.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_cli/test_cli_utils/test_runpodignore.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_cli/test_cli_utils/test_ssh_cmd.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_cli/test_cli_utils/test_userspace.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_cli/test_console.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_cli/test_init_command.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_cli/test_management_commands.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_cli/test_update_command.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_endpoint/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_endpoint/test_asyncio_init.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_endpoint/test_asyncio_runner.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_endpoint/test_init.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_endpoint/test_runner.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_error.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_init.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_performance/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_performance/test_cold_start.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/local_sim/.env_example +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/local_sim/Makefile +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/local_sim/localhost.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/local_sim/worker.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_capture.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_init.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_integration_worker_state.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_modules/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_modules/test_fastapi.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_modules/test_fitness/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_modules/test_fitness/conftest.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_modules/test_fitness/test_force_kill.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_modules/test_fitness/test_gpu_checks.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_modules/test_fitness/test_gpu_integration.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_modules/test_fitness/test_registration.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_modules/test_fitness/test_safe_startup.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_modules/test_fitness/test_startup.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_modules/test_handler.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_modules/test_http.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_modules/test_job.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_modules/test_local.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_modules/test_logger.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_modules/test_ping.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_modules/test_progress.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_modules/test_scale.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_modules/test_state.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_modules/test_tips.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_prestart.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_prestart_lifecycle.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_scale_mirror.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_utils/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_utils/test_cleanup.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_utils/test_cuda.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_utils/test_debugger.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_utils/test_download.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_utils/test_model_cache.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_utils/test_ssrf.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_utils/test_upload.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_utils/test_validate.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_serverless/test_utils_init.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_shared/__init__.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_shared/test_auth.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_user_agent.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_version.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/test_whatever.py +0 -0
- {runpod-2.0.0rc2 → runpod-2.0.1.dev1}/tests/whatever.py +0 -0
|
@@ -28,7 +28,7 @@ jobs:
|
|
|
28
28
|
|
|
29
29
|
test-publish:
|
|
30
30
|
if: github.ref == 'refs/heads/staging'
|
|
31
|
-
runs-on: ubuntu-
|
|
31
|
+
runs-on: runpod-4vCPU-ubuntu-2404 # GitHub-hosted (trusted publishing needs that) with allow-listed static IPs
|
|
32
32
|
environment: test-release
|
|
33
33
|
permissions:
|
|
34
34
|
contents: read
|
|
@@ -58,7 +58,7 @@ jobs:
|
|
|
58
58
|
|
|
59
59
|
release-please:
|
|
60
60
|
if: github.ref == 'refs/heads/main'
|
|
61
|
-
runs-on: ubuntu-
|
|
61
|
+
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
62
62
|
outputs:
|
|
63
63
|
release_created: ${{ steps.release.outputs.release_created }}
|
|
64
64
|
tag_name: ${{ steps.release.outputs.tag_name }}
|
|
@@ -74,7 +74,7 @@ jobs:
|
|
|
74
74
|
name: PyPI Publish
|
|
75
75
|
needs: release-please
|
|
76
76
|
if: ${{ always() && (needs.release-please.outputs.release_created || (github.event_name == 'workflow_dispatch' && inputs.force_publish == 'true')) }}
|
|
77
|
-
runs-on: ubuntu-
|
|
77
|
+
runs-on: runpod-4vCPU-ubuntu-2404 # GitHub-hosted (trusted publishing needs that) with allow-listed static IPs
|
|
78
78
|
environment:
|
|
79
79
|
name: pypi-production
|
|
80
80
|
url: https://pypi.org/project/runpod/
|
|
@@ -109,7 +109,7 @@ jobs:
|
|
|
109
109
|
linear-sync:
|
|
110
110
|
name: Linear Release Sync
|
|
111
111
|
needs: release-please
|
|
112
|
-
runs-on: ubuntu-
|
|
112
|
+
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
113
113
|
permissions:
|
|
114
114
|
contents: read
|
|
115
115
|
steps:
|
|
@@ -128,7 +128,7 @@ jobs:
|
|
|
128
128
|
name: Linear Release Complete
|
|
129
129
|
if: ${{ needs.release-please.outputs.release_created }}
|
|
130
130
|
needs: [release-please, linear-sync]
|
|
131
|
-
runs-on: ubuntu-
|
|
131
|
+
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
132
132
|
permissions:
|
|
133
133
|
contents: read
|
|
134
134
|
steps:
|
|
@@ -148,7 +148,7 @@ jobs:
|
|
|
148
148
|
# name: Notify workers
|
|
149
149
|
# needs: [release-please, pypi-publish]
|
|
150
150
|
# if: ${{ needs.release-please.outputs.release_created || (github.event_name == 'workflow_dispatch' && inputs.force_publish == 'true') }}
|
|
151
|
-
# runs-on: ubuntu-
|
|
151
|
+
# runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
152
152
|
# strategy:
|
|
153
153
|
# matrix:
|
|
154
154
|
# repo:
|
|
@@ -19,7 +19,7 @@ jobs:
|
|
|
19
19
|
|
|
20
20
|
test:
|
|
21
21
|
if: github.event_name != 'schedule'
|
|
22
|
-
runs-on: ubuntu-
|
|
22
|
+
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
23
23
|
strategy:
|
|
24
24
|
matrix:
|
|
25
25
|
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
@@ -57,7 +57,7 @@ jobs:
|
|
|
57
57
|
|
|
58
58
|
e2e:
|
|
59
59
|
if: github.event_name != 'schedule' && github.repository == 'runpod/runpod-python'
|
|
60
|
-
runs-on: ubuntu-
|
|
60
|
+
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
61
61
|
timeout-minutes: 15
|
|
62
62
|
steps:
|
|
63
63
|
- uses: actions/checkout@v7
|
|
@@ -90,7 +90,7 @@ jobs:
|
|
|
90
90
|
|
|
91
91
|
codeql:
|
|
92
92
|
if: github.event_name == 'schedule' || github.event_name == 'pull_request'
|
|
93
|
-
runs-on: ubuntu-
|
|
93
|
+
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
94
94
|
permissions:
|
|
95
95
|
actions: read
|
|
96
96
|
contents: read
|
|
@@ -99,16 +99,16 @@ jobs:
|
|
|
99
99
|
- uses: actions/checkout@v7
|
|
100
100
|
|
|
101
101
|
- name: Initialize CodeQL
|
|
102
|
-
uses: github/codeql-action/init@v4.39.
|
|
102
|
+
uses: github/codeql-action/init@v4.39.1
|
|
103
103
|
with:
|
|
104
104
|
languages: python
|
|
105
105
|
queries: security-extended,security-and-quality
|
|
106
106
|
|
|
107
107
|
- name: Autobuild
|
|
108
|
-
uses: github/codeql-action/autobuild@v4.39.
|
|
108
|
+
uses: github/codeql-action/autobuild@v4.39.1
|
|
109
109
|
|
|
110
110
|
- name: Perform CodeQL Analysis
|
|
111
|
-
uses: github/codeql-action/analyze@v4.39.
|
|
111
|
+
uses: github/codeql-action/analyze@v4.39.1
|
|
112
112
|
with:
|
|
113
113
|
category: "/language:python"
|
|
114
114
|
|
|
@@ -117,7 +117,7 @@ jobs:
|
|
|
117
117
|
# ──────────────────────────────────────────────
|
|
118
118
|
|
|
119
119
|
validation:
|
|
120
|
-
runs-on: ubuntu-
|
|
120
|
+
runs-on: blacksmith-2vcpu-ubuntu-2404
|
|
121
121
|
needs: [test, e2e, codeql]
|
|
122
122
|
if: always()
|
|
123
123
|
steps:
|
|
@@ -14,6 +14,31 @@
|
|
|
14
14
|
* Deployment artifacts apply project ignore rules and mandatory secret-file exclusions while preserving vendored dependency assets.
|
|
15
15
|
* Synchronous calls wait across polling timeouts on Python 3.10 and propagate operation timeouts.
|
|
16
16
|
|
|
17
|
+
## [2.1.0](https://github.com/runpod/runpod-python/compare/v2.0.0...v2.1.0) (2026-10-28)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Features
|
|
21
|
+
|
|
22
|
+
* add rp catalog for gpu and cpu pricing and stock ([#637](https://github.com/runpod/runpod-python/issues/637)) ([9540b8a](https://github.com/runpod/runpod-python/commit/9540b8afff98b83e33146c4c0c03d6fa07157f62))
|
|
23
|
+
* add rp endpoint command group ([#640](https://github.com/runpod/runpod-python/issues/640)) ([be3f76b](https://github.com/runpod/runpod-python/commit/be3f76b4ade25eb62fcce41ffcd0d50088f8ef31))
|
|
24
|
+
* add rp pod lifecycle commands ([#639](https://github.com/runpod/runpod-python/issues/639)) ([eeffe2a](https://github.com/runpod/runpod-python/commit/eeffe2ae4651eab0fb4250580457d002882cfe7f))
|
|
25
|
+
* add rp volume commands for network and global volumes ([#638](https://github.com/runpod/runpod-python/issues/638)) ([598cb88](https://github.com/runpod/runpod-python/commit/598cb88b2eb00497916a8fa6117064c898d3d884))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### Bug Fixes
|
|
29
|
+
|
|
30
|
+
* **apps:** bind volume mounts from RUNPOD_MOUNTS when the runtime did not ([#626](https://github.com/runpod/runpod-python/issues/626)) ([fc4a6c7](https://github.com/runpod/runpod-python/commit/fc4a6c73c3915625d65df63416db835d0b927b8f))
|
|
31
|
+
* **apps:** keep transitive torch out of GPU artifacts ([#623](https://github.com/runpod/runpod-python/issues/623)) ([25515f0](https://github.com/runpod/runpod-python/commit/25515f02a2e3ea36035c856e564106ccb8760b7f))
|
|
32
|
+
* **apps:** pin requested gpu devices instead of their whole pool ([#624](https://github.com/runpod/runpod-python/issues/624)) ([432c55b](https://github.com/runpod/runpod-python/commit/432c55b8a24b5bccbe37955392f9e1a72bad475e))
|
|
33
|
+
* **cli:** delete dev endpoints when rp flash dev is stopped by a signal ([#625](https://github.com/runpod/runpod-python/issues/625)) ([f866ec3](https://github.com/runpod/runpod-python/commit/f866ec3d2117b697a2534286509674657f37511f))
|
|
34
|
+
* **deps:** pin certifi to >=2026.10.19 for emSign bundle remediation (GHSA-6q48-6c7w-9gw2) ([#659](https://github.com/runpod/runpod-python/issues/659)) ([714a565](https://github.com/runpod/runpod-python/commit/714a565514bae7dd2b6ca355b52e8e76d70d1102))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
### Performance Improvements
|
|
38
|
+
|
|
39
|
+
* **serverless:** range-parallel VolumeCache hydrate ([#621](https://github.com/runpod/runpod-python/issues/621)) ([29704a4](https://github.com/runpod/runpod-python/commit/29704a49ab591eaca9a83d7d2eef935d9d787eb2))
|
|
40
|
+
* **serverless:** take a full batch once drained ([#646](https://github.com/runpod/runpod-python/issues/646)) ([397c37d](https://github.com/runpod/runpod-python/commit/397c37d3d2a04aaa2eb3c8a83cdc359e230bcb6e))
|
|
41
|
+
|
|
17
42
|
## [1.12.0](https://github.com/runpod/runpod-python/compare/v1.11.0...v1.12.0) (2026-08-10)
|
|
18
43
|
|
|
19
44
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: runpod
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.1.dev1
|
|
4
4
|
Summary: 🐍 | Python library for Runpod API and serverless worker SDK.
|
|
5
5
|
Home-page: https://runpod.io
|
|
6
6
|
Author: Runpod
|
|
@@ -28,16 +28,18 @@ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
|
28
28
|
Requires-Python: >=3.10
|
|
29
29
|
Description-Content-Type: text/markdown
|
|
30
30
|
License-File: LICENSE
|
|
31
|
-
Requires-Dist: aiohttp[speedups]>=3.14.
|
|
31
|
+
Requires-Dist: aiohttp[speedups]>=3.14.5
|
|
32
32
|
Requires-Dist: aiohttp-retry>=2.9.1
|
|
33
33
|
Requires-Dist: backoff>=2.2.1
|
|
34
|
-
Requires-Dist: boto3>=1.43.
|
|
34
|
+
Requires-Dist: boto3>=1.43.118
|
|
35
|
+
Requires-Dist: certifi>=2026.10.19
|
|
35
36
|
Requires-Dist: click>=8.5.0
|
|
36
37
|
Requires-Dist: cloudpickle>=3.1.2
|
|
37
38
|
Requires-Dist: colorama<0.4.7,>=0.4.6
|
|
38
39
|
Requires-Dist: cryptography>=50.0.2
|
|
39
|
-
Requires-Dist: fastapi[all]>=0.
|
|
40
|
-
Requires-Dist:
|
|
40
|
+
Requires-Dist: fastapi[all]>=0.162.0
|
|
41
|
+
Requires-Dist: packaging>=26.3
|
|
42
|
+
Requires-Dist: paramiko>=5.1.0
|
|
41
43
|
Requires-Dist: pathspec>=1.1.1
|
|
42
44
|
Requires-Dist: prettytable>=3.18.0
|
|
43
45
|
Requires-Dist: psutil>=7.2.2
|
|
@@ -59,7 +59,9 @@ vc.sync(background=False) # persist new files back to the volume, inli
|
|
|
59
59
|
| `namespace` | `RUNPOD_ENDPOINT_ID` | Isolation key for the on-volume mirror. Must be a single safe path component. |
|
|
60
60
|
| `volume_path` | `/runpod-volume` | Network-volume mount point. |
|
|
61
61
|
| `best_effort` | `True` | Swallow and log errors instead of raising. Set `False` while debugging. |
|
|
62
|
-
| `max_workers` | `min(32, (os.cpu_count() or 4) * 4)` | Thread count for parallel copy of large files (I/O-bound). |
|
|
62
|
+
| `max_workers` | `min(32, (os.cpu_count() or 4) * 4)` | Thread count for parallel copy of large files on sync (I/O-bound). |
|
|
63
|
+
| `hydrate_workers` | `max_workers` if set, else `64` | Concurrent range reads when hydrating large files. |
|
|
64
|
+
| `hydrate_chunk_size` | `8 MiB` | Bytes per hydrate range. Peak hydrate memory is about `hydrate_workers * hydrate_chunk_size` (512 MiB by default). |
|
|
63
65
|
|
|
64
66
|
## How it works
|
|
65
67
|
|
|
@@ -77,9 +79,25 @@ vc.sync(background=False) # persist new files back to the volume, inli
|
|
|
77
79
|
files are skipped. The `small.tar` archive is re-packed as a whole whenever
|
|
78
80
|
any small file has changed, since unpacking and re-diffing many tiny files
|
|
79
81
|
individually is slower than the network volume's per-file overhead.
|
|
80
|
-
- **Parallel copy.**
|
|
81
|
-
`max_workers` (default `min(32, (os.cpu_count() or 4) * 4)`),
|
|
82
|
-
work is I/O-bound.
|
|
82
|
+
- **Parallel copy.** On sync, large files are copied whole across a thread
|
|
83
|
+
pool sized by `max_workers` (default `min(32, (os.cpu_count() or 4) * 4)`),
|
|
84
|
+
since the work is I/O-bound.
|
|
85
|
+
- **Range-parallel hydrate.** On hydrate, every large file is split into
|
|
86
|
+
`hydrate_chunk_size` ranges, and one pool of `hydrate_workers` threads reads
|
|
87
|
+
the ranges of all files at once, each on its own file descriptor.
|
|
88
|
+
Object-store mounts such as Global Store serve one sequential stream at
|
|
89
|
+
roughly 140 MiB/s, and reading several whole files at once doesn't add up,
|
|
90
|
+
but independent range reads scale to the host's network (~1.1 GiB/s
|
|
91
|
+
measured). Transient `ENOMEM`/`EIO`/`EAGAIN` errors, which such mounts
|
|
92
|
+
return under load, are retried with jittered exponential backoff. A file is assembled in a temp
|
|
93
|
+
file and renamed into place only when every range has copied, so a failed
|
|
94
|
+
file leaves any existing local copy untouched and is simply not restored.
|
|
95
|
+
A mirror file whose size differs from the manifest, or whose size or mtime
|
|
96
|
+
changes between ranges because a concurrent `sync()` replaced it, is
|
|
97
|
+
likewise not restored. This is a best-effort check: an object-store mount
|
|
98
|
+
can serve a replacement that lands in the middle of a single range read
|
|
99
|
+
without it being visible. Keep `hydrate_workers * hydrate_chunk_size` near the default: past ~1 GiB in
|
|
100
|
+
flight, Global Store starts failing reads with `ENOMEM`.
|
|
83
101
|
- **Packing/extraction.** Packing the small-file archive uses the `tar`
|
|
84
102
|
binary when available (falling back to the stdlib `tarfile` module
|
|
85
103
|
otherwise). Extraction always goes through `tarfile`, validating every
|
|
@@ -13,6 +13,7 @@ filterwarnings =
|
|
|
13
13
|
python_files = tests.py test_*.py *_test.py
|
|
14
14
|
norecursedirs = venv *.egg-info .git build tests/e2e
|
|
15
15
|
asyncio_mode = auto
|
|
16
|
+
asyncio_default_fixture_loop_scope = function
|
|
16
17
|
markers =
|
|
17
18
|
qb: Queue-based tests (local execution, fast)
|
|
18
19
|
lb: Load-balanced tests (remote provisioning, slow)
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
aiohttp[speedups] >= 3.14.
|
|
1
|
+
aiohttp[speedups] >= 3.14.5
|
|
2
2
|
aiohttp-retry >= 2.9.1
|
|
3
3
|
|
|
4
4
|
backoff >= 2.2.1
|
|
5
|
-
boto3 >= 1.43.
|
|
5
|
+
boto3 >= 1.43.118
|
|
6
|
+
certifi >= 2026.10.19
|
|
6
7
|
click >= 8.5.0
|
|
7
8
|
cloudpickle >= 3.1.2
|
|
8
9
|
colorama >= 0.4.6, < 0.4.7
|
|
9
10
|
cryptography >= 50.0.2
|
|
10
|
-
fastapi[all] >= 0.
|
|
11
|
-
|
|
11
|
+
fastapi[all] >= 0.162.0
|
|
12
|
+
packaging >= 26.3
|
|
13
|
+
paramiko >= 5.1.0
|
|
12
14
|
pathspec >= 1.1.1
|
|
13
15
|
prettytable >= 3.18.0
|
|
14
16
|
psutil >= 7.2.2
|
|
@@ -65,6 +65,22 @@ def _handler_loaded_gpu_framework() -> bool:
|
|
|
65
65
|
return any(sys.modules.get(name) is not None for name in GPU_FRAMEWORKS)
|
|
66
66
|
|
|
67
67
|
|
|
68
|
+
def _torch_without_cuda_context(torch) -> bool:
|
|
69
|
+
"""
|
|
70
|
+
True if the handler imported torch but has not created a CUDA context.
|
|
71
|
+
|
|
72
|
+
The torch checks must never be the first to initialize CUDA in the handler
|
|
73
|
+
process. An engine built in this process afterwards (vLLM's in-process
|
|
74
|
+
AsyncLLM/LLM) sees "CUDA is initialized" and spawns its workers instead of
|
|
75
|
+
forking, re-importing the engine and torch in every child (10.6-11.5 s per
|
|
76
|
+
cold start measured with vLLM v0.30.0). This gate runs before
|
|
77
|
+
torch.cuda.is_available(), whose default cudaGetDeviceCount path also
|
|
78
|
+
initializes the driver and poisons later forks. When the handler has
|
|
79
|
+
already created a context the checks cost nothing extra.
|
|
80
|
+
"""
|
|
81
|
+
return not torch.cuda.is_initialized()
|
|
82
|
+
|
|
83
|
+
|
|
68
84
|
def configure() -> None:
|
|
69
85
|
"""Read current fitness settings; setup errors are handled by the runner."""
|
|
70
86
|
global \
|
|
@@ -395,6 +411,14 @@ async def _check_cuda_initialization() -> None:
|
|
|
395
411
|
try:
|
|
396
412
|
torch = _loaded_module("torch")
|
|
397
413
|
|
|
414
|
+
if _torch_without_cuda_context(torch):
|
|
415
|
+
log.debug(
|
|
416
|
+
"Handler has not created a CUDA context, skipping CUDA initialization "
|
|
417
|
+
"check so the engine can still fork (the gpu_test binary, where it "
|
|
418
|
+
"runs, covers device allocation)"
|
|
419
|
+
)
|
|
420
|
+
return
|
|
421
|
+
|
|
398
422
|
if not torch.cuda.is_available():
|
|
399
423
|
log.debug("CUDA not available in PyTorch, skipping initialization check")
|
|
400
424
|
return
|
|
@@ -496,6 +520,13 @@ async def _check_gpu_compute_benchmark() -> None:
|
|
|
496
520
|
try:
|
|
497
521
|
torch = _loaded_module("torch")
|
|
498
522
|
|
|
523
|
+
if _torch_without_cuda_context(torch):
|
|
524
|
+
log.debug(
|
|
525
|
+
"Handler has not created a CUDA context, skipping GPU compute "
|
|
526
|
+
"benchmark so the engine can still fork"
|
|
527
|
+
)
|
|
528
|
+
return
|
|
529
|
+
|
|
499
530
|
if not torch.cuda.is_available():
|
|
500
531
|
log.debug("CUDA not available in PyTorch, skipping benchmark")
|
|
501
532
|
return
|
|
@@ -580,7 +611,8 @@ def auto_register_system_checks() -> None:
|
|
|
580
611
|
The two checks that use torch and allocate on the device are marked
|
|
581
612
|
@defer_to_worker_start so the import-time pass cannot create a CUDA context
|
|
582
613
|
before the handler module runs. They only use a framework the handler has
|
|
583
|
-
already imported,
|
|
614
|
+
already imported, and only run when the handler has already created a CUDA
|
|
615
|
+
context, so they never add a torch import or a CUDA context to cold start.
|
|
584
616
|
"""
|
|
585
617
|
configure()
|
|
586
618
|
log.debug("Registering system resource fitness checks")
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '2.0.
|
|
22
|
-
__version_tuple__ = version_tuple = (2, 0,
|
|
21
|
+
__version__ = version = '2.0.1.dev1'
|
|
22
|
+
__version_tuple__ = version_tuple = (2, 0, 1, 'dev1')
|
|
23
23
|
|
|
24
|
-
__commit_id__ = commit_id = '
|
|
24
|
+
__commit_id__ = commit_id = 'ga4ba07898'
|
|
@@ -164,6 +164,11 @@ class AppsApiClient:
|
|
|
164
164
|
def __init__(self, api_key: Optional[str] = None):
|
|
165
165
|
self._api_key = api_key
|
|
166
166
|
|
|
167
|
+
async def gpu_pool_members(self) -> Optional[Dict[str, Set[str]]]:
|
|
168
|
+
from .gpu import live_pool_members_async
|
|
169
|
+
|
|
170
|
+
return await live_pool_members_async(self._api_key)
|
|
171
|
+
|
|
167
172
|
async def _execute(
|
|
168
173
|
self,
|
|
169
174
|
query: str,
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"""browser-approval and email-verification login flows."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import base64
|
|
5
|
+
import datetime as dt
|
|
6
|
+
import hashlib
|
|
7
|
+
import os
|
|
8
|
+
import secrets
|
|
9
|
+
from typing import Any, Callable, Dict, Optional
|
|
10
|
+
from urllib.parse import quote
|
|
11
|
+
from uuid import uuid4
|
|
12
|
+
|
|
13
|
+
import aiohttp
|
|
14
|
+
|
|
15
|
+
from runpod.api.rest import _build_url
|
|
16
|
+
from runpod.user_agent import USER_AGENT
|
|
17
|
+
|
|
18
|
+
from .api import AppsApiClient
|
|
19
|
+
from .errors import AppError
|
|
20
|
+
|
|
21
|
+
CONSOLE_URL = os.environ.get("RUNPOD_CONSOLE_URL", "https://console.runpod.io")
|
|
22
|
+
|
|
23
|
+
POLL_INTERVAL_SECONDS = 2.0
|
|
24
|
+
DEFAULT_TIMEOUT_SECONDS = 600.0
|
|
25
|
+
|
|
26
|
+
_APPROVED_STATUSES = frozenset({"APPROVED", "CONSUMED"})
|
|
27
|
+
_FAILED_STATUSES = frozenset({"DENIED", "EXPIRED"})
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class LoginError(AppError):
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class _VerificationCodeRejected(LoginError):
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
async def _post_authentication_attempt(
|
|
39
|
+
session: aiohttp.ClientSession,
|
|
40
|
+
path: str,
|
|
41
|
+
body: dict,
|
|
42
|
+
*,
|
|
43
|
+
creating: bool = False,
|
|
44
|
+
) -> dict:
|
|
45
|
+
try:
|
|
46
|
+
async with session.post(
|
|
47
|
+
_build_url(path),
|
|
48
|
+
json=body,
|
|
49
|
+
headers={"Idempotency-Key": str(uuid4())} if creating else {},
|
|
50
|
+
allow_redirects=False,
|
|
51
|
+
) as response:
|
|
52
|
+
if response.status != (201 if creating else 200):
|
|
53
|
+
if response.status == 403:
|
|
54
|
+
raise LoginError(
|
|
55
|
+
"email sign-in is not allowed for this account; sign in "
|
|
56
|
+
"through the console and use runpod login --api-key"
|
|
57
|
+
)
|
|
58
|
+
if response.status == 404:
|
|
59
|
+
if creating:
|
|
60
|
+
raise LoginError("email sign-in is unavailable on this API")
|
|
61
|
+
raise LoginError(
|
|
62
|
+
"login attempt expired, was already used, or is locked; "
|
|
63
|
+
"start login again"
|
|
64
|
+
)
|
|
65
|
+
if response.status == 422:
|
|
66
|
+
if creating:
|
|
67
|
+
raise LoginError(
|
|
68
|
+
"email sign-in request rejected; check --email"
|
|
69
|
+
)
|
|
70
|
+
raise _VerificationCodeRejected("verification code rejected")
|
|
71
|
+
if response.status == 429:
|
|
72
|
+
raise LoginError(
|
|
73
|
+
"too many login attempts; wait before trying again"
|
|
74
|
+
)
|
|
75
|
+
raise LoginError(f"email sign-in failed (HTTP {response.status})")
|
|
76
|
+
payload = await response.json()
|
|
77
|
+
except (aiohttp.ClientError, asyncio.TimeoutError):
|
|
78
|
+
raise LoginError(
|
|
79
|
+
"could not complete email sign-in; check your connection"
|
|
80
|
+
) from None
|
|
81
|
+
except ValueError:
|
|
82
|
+
raise LoginError("invalid response from the email sign-in API") from None
|
|
83
|
+
|
|
84
|
+
if not isinstance(payload, dict):
|
|
85
|
+
raise LoginError("invalid response from the email sign-in API")
|
|
86
|
+
return payload
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
async def agent_login(email: str, *, read_code: Callable[[], str]) -> str:
|
|
90
|
+
"""exchange an emailed code for an api key bound to this login's verifier."""
|
|
91
|
+
verifier = secrets.token_urlsafe(32)
|
|
92
|
+
challenge = (
|
|
93
|
+
base64.urlsafe_b64encode(hashlib.sha256(verifier.encode("ascii")).digest())
|
|
94
|
+
.decode("ascii")
|
|
95
|
+
.rstrip("=")
|
|
96
|
+
)
|
|
97
|
+
path = "/v2/signup/authentication-attempts"
|
|
98
|
+
|
|
99
|
+
async with aiohttp.ClientSession(
|
|
100
|
+
timeout=aiohttp.ClientTimeout(total=30),
|
|
101
|
+
headers={"Accept": "application/json", "User-Agent": USER_AGENT},
|
|
102
|
+
cookie_jar=aiohttp.DummyCookieJar(),
|
|
103
|
+
) as session:
|
|
104
|
+
attempt = await _post_authentication_attempt(
|
|
105
|
+
session,
|
|
106
|
+
path,
|
|
107
|
+
{"email": email, "keyName": "runpod-cli-agent", "codeChallenge": challenge},
|
|
108
|
+
creating=True,
|
|
109
|
+
)
|
|
110
|
+
attempt_id = attempt.get("attemptId")
|
|
111
|
+
if not isinstance(attempt_id, str) or not attempt_id:
|
|
112
|
+
raise LoginError("auth attempt failed to initialize")
|
|
113
|
+
|
|
114
|
+
while True:
|
|
115
|
+
code = read_code()
|
|
116
|
+
try:
|
|
117
|
+
result = await _post_authentication_attempt(
|
|
118
|
+
session,
|
|
119
|
+
f"{path}/{quote(attempt_id, safe='')}/verify",
|
|
120
|
+
{"code": code, "codeVerifier": verifier},
|
|
121
|
+
)
|
|
122
|
+
except _VerificationCodeRejected:
|
|
123
|
+
continue
|
|
124
|
+
break
|
|
125
|
+
credential = result.get("credential")
|
|
126
|
+
api_key = credential.get("apiKey") if isinstance(credential, dict) else None
|
|
127
|
+
if not isinstance(api_key, str) or not api_key.strip():
|
|
128
|
+
raise LoginError("email sign-in response did not contain an API key")
|
|
129
|
+
return api_key
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _parse_expires_at(value: Optional[str]) -> Optional[dt.datetime]:
|
|
133
|
+
if not value:
|
|
134
|
+
return None
|
|
135
|
+
try:
|
|
136
|
+
return dt.datetime.fromisoformat(value.replace("Z", "+00:00"))
|
|
137
|
+
except ValueError:
|
|
138
|
+
return None
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def auth_url(request_id: str) -> str:
|
|
142
|
+
return f"{CONSOLE_URL}/flash/login?request={request_id}"
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
async def browser_login(
|
|
146
|
+
*,
|
|
147
|
+
timeout_seconds: float = DEFAULT_TIMEOUT_SECONDS,
|
|
148
|
+
on_url: Optional[Callable[[str], None]] = None,
|
|
149
|
+
api: Optional[AppsApiClient] = None,
|
|
150
|
+
) -> str:
|
|
151
|
+
"""run the browser-approval flow and return the granted api key.
|
|
152
|
+
|
|
153
|
+
on_url is called with the approval url as soon as the request is
|
|
154
|
+
created (the caller renders/opens it).
|
|
155
|
+
"""
|
|
156
|
+
client = api or AppsApiClient()
|
|
157
|
+
request: Dict[str, Any] = await client.create_auth_request()
|
|
158
|
+
request_id = request.get("id")
|
|
159
|
+
|
|
160
|
+
if not request_id:
|
|
161
|
+
raise LoginError("auth request failed to initialize")
|
|
162
|
+
|
|
163
|
+
if on_url is not None:
|
|
164
|
+
on_url(auth_url(request_id))
|
|
165
|
+
|
|
166
|
+
deadline = dt.datetime.now(dt.timezone.utc) + dt.timedelta(seconds=timeout_seconds)
|
|
167
|
+
expires_at = _parse_expires_at(request.get("expiresAt"))
|
|
168
|
+
if expires_at and expires_at < deadline:
|
|
169
|
+
deadline = expires_at
|
|
170
|
+
|
|
171
|
+
while True:
|
|
172
|
+
status_payload = await client.get_auth_request_status(request_id)
|
|
173
|
+
status = status_payload.get("status")
|
|
174
|
+
api_key = status_payload.get("apiKey")
|
|
175
|
+
|
|
176
|
+
if api_key and status in _APPROVED_STATUSES:
|
|
177
|
+
return api_key
|
|
178
|
+
if status in _FAILED_STATUSES:
|
|
179
|
+
raise LoginError(f"login {status.lower()}")
|
|
180
|
+
if status == "CONSUMED":
|
|
181
|
+
raise LoginError("login request was already used")
|
|
182
|
+
if dt.datetime.now(dt.timezone.utc) >= deadline:
|
|
183
|
+
raise LoginError("login timed out")
|
|
184
|
+
|
|
185
|
+
await asyncio.sleep(POLL_INTERVAL_SECONDS)
|