runpod 2.0.1.dev1__tar.gz → 2.1.0__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.
Files changed (373) hide show
  1. {runpod-2.0.1.dev1 → runpod-2.1.0}/CHANGELOG.md +14 -0
  2. {runpod-2.0.1.dev1/runpod.egg-info → runpod-2.1.0}/PKG-INFO +1 -1
  3. runpod-2.1.0/docs/api/handling_errors.md +69 -0
  4. {runpod-2.0.1.dev1 → runpod-2.1.0}/docs/cli/references/command_line_interface.md +4 -1
  5. {runpod-2.0.1.dev1 → runpod-2.1.0}/docs/cli/start_here.md +1 -1
  6. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/_version.py +3 -3
  7. runpod-2.1.0/runpod/api/_rate_limit.py +259 -0
  8. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/api/graphql.py +2 -3
  9. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/api/queries/apps.py +1 -2
  10. runpod-2.1.0/runpod/api/rest.py +279 -0
  11. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/api.py +14 -5
  12. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/auth.py +39 -26
  13. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/error.py +14 -0
  14. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/rp_cli/main.py +34 -7
  15. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/serverless/modules/rp_fastapi.py +1 -1
  16. {runpod-2.0.1.dev1 → runpod-2.1.0/runpod.egg-info}/PKG-INFO +1 -1
  17. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod.egg-info/SOURCES.txt +2 -0
  18. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod.egg-info/scm_file_list.json +2 -0
  19. runpod-2.1.0/runpod.egg-info/scm_version.json +8 -0
  20. runpod-2.1.0/tests/test_api/test_rate_limit.py +811 -0
  21. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_apps/test_api_client.py +37 -4
  22. runpod-2.1.0/tests/test_apps/test_auth.py +122 -0
  23. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_cli/test_management_commands.py +38 -4
  24. runpod-2.0.1.dev1/docs/api/handling_errors.md +0 -28
  25. runpod-2.0.1.dev1/runpod/api/rest.py +0 -185
  26. runpod-2.0.1.dev1/runpod.egg-info/scm_version.json +0 -8
  27. runpod-2.0.1.dev1/tests/test_apps/test_auth.py +0 -60
  28. {runpod-2.0.1.dev1 → runpod-2.1.0}/.dockerignore +0 -0
  29. {runpod-2.0.1.dev1 → runpod-2.1.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  30. {runpod-2.0.1.dev1 → runpod-2.1.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  31. {runpod-2.0.1.dev1 → runpod-2.1.0}/.github/dependabot.yml +0 -0
  32. {runpod-2.0.1.dev1 → runpod-2.1.0}/.github/tape_collection/cli_config.tape +0 -0
  33. {runpod-2.0.1.dev1 → runpod-2.1.0}/.github/tape_collection/cli_help.tape +0 -0
  34. {runpod-2.0.1.dev1 → runpod-2.1.0}/.github/tape_collection/cli_ssh.tape +0 -0
  35. {runpod-2.0.1.dev1 → runpod-2.1.0}/.github/tests.json +0 -0
  36. {runpod-2.0.1.dev1 → runpod-2.1.0}/.github/workflows/cd.yml +0 -0
  37. {runpod-2.0.1.dev1 → runpod-2.1.0}/.github/workflows/ci.yml +0 -0
  38. {runpod-2.0.1.dev1 → runpod-2.1.0}/.github/workflows/vhs.yml +0 -0
  39. {runpod-2.0.1.dev1 → runpod-2.1.0}/.gitignore +0 -0
  40. {runpod-2.0.1.dev1 → runpod-2.1.0}/ARCHITECTURE.md +0 -0
  41. {runpod-2.0.1.dev1 → runpod-2.1.0}/CONTRIBUTING.md +0 -0
  42. {runpod-2.0.1.dev1 → runpod-2.1.0}/LICENSE +0 -0
  43. {runpod-2.0.1.dev1 → runpod-2.1.0}/MANIFEST.in +0 -0
  44. {runpod-2.0.1.dev1 → runpod-2.1.0}/Makefile +0 -0
  45. {runpod-2.0.1.dev1 → runpod-2.1.0}/README.md +0 -0
  46. {runpod-2.0.1.dev1 → runpod-2.1.0}/SECURITY.md +0 -0
  47. {runpod-2.0.1.dev1 → runpod-2.1.0}/build_tools/compile_gpu_test.sh +0 -0
  48. {runpod-2.0.1.dev1 → runpod-2.1.0}/build_tools/gpu_test.c +0 -0
  49. {runpod-2.0.1.dev1 → runpod-2.1.0}/docs/api/queries.md +0 -0
  50. {runpod-2.0.1.dev1 → runpod-2.1.0}/docs/cli/demos/config.gif +0 -0
  51. {runpod-2.0.1.dev1 → runpod-2.1.0}/docs/cli/demos/help.gif +0 -0
  52. {runpod-2.0.1.dev1 → runpod-2.1.0}/docs/cli/demos/ssh.gif +0 -0
  53. {runpod-2.0.1.dev1 → runpod-2.1.0}/docs/cli/references/projects.md +0 -0
  54. {runpod-2.0.1.dev1 → runpod-2.1.0}/docs/cli/references/runpod.toml.md +0 -0
  55. {runpod-2.0.1.dev1 → runpod-2.1.0}/docs/getting_Started.md +0 -0
  56. {runpod-2.0.1.dev1 → runpod-2.1.0}/docs/images/env_var_location.png +0 -0
  57. {runpod-2.0.1.dev1 → runpod-2.1.0}/docs/serverless/functions/error_handling.md +0 -0
  58. {runpod-2.0.1.dev1 → runpod-2.1.0}/docs/serverless/functions/logging.md +0 -0
  59. {runpod-2.0.1.dev1 → runpod-2.1.0}/docs/serverless/gpu_binary_compilation.md +0 -0
  60. {runpod-2.0.1.dev1 → runpod-2.1.0}/docs/serverless/local_testing.md +0 -0
  61. {runpod-2.0.1.dev1 → runpod-2.1.0}/docs/serverless/utils/rp_cleanup.md +0 -0
  62. {runpod-2.0.1.dev1 → runpod-2.1.0}/docs/serverless/utils/rp_debugger.md +0 -0
  63. {runpod-2.0.1.dev1 → runpod-2.1.0}/docs/serverless/utils/rp_download.md +0 -0
  64. {runpod-2.0.1.dev1 → runpod-2.1.0}/docs/serverless/utils/rp_upload.md +0 -0
  65. {runpod-2.0.1.dev1 → runpod-2.1.0}/docs/serverless/utils/rp_validator.md +0 -0
  66. {runpod-2.0.1.dev1 → runpod-2.1.0}/docs/serverless/volume_cache.md +0 -0
  67. {runpod-2.0.1.dev1 → runpod-2.1.0}/docs/serverless/worker.md +0 -0
  68. {runpod-2.0.1.dev1 → runpod-2.1.0}/docs/serverless/worker_fitness_checks.md +0 -0
  69. {runpod-2.0.1.dev1 → runpod-2.1.0}/docs/serverless/worker_realtime.md +0 -0
  70. {runpod-2.0.1.dev1 → runpod-2.1.0}/examples/api/create_container_registry_auth.py +0 -0
  71. {runpod-2.0.1.dev1 → runpod-2.1.0}/examples/api/create_endpoint.py +0 -0
  72. {runpod-2.0.1.dev1 → runpod-2.1.0}/examples/api/create_template.py +0 -0
  73. {runpod-2.0.1.dev1 → runpod-2.1.0}/examples/api/get_endpoints.py +0 -0
  74. {runpod-2.0.1.dev1 → runpod-2.1.0}/examples/apps/README.md +0 -0
  75. {runpod-2.0.1.dev1 → runpod-2.1.0}/examples/apps/gpu_inference.py +0 -0
  76. {runpod-2.0.1.dev1 → runpod-2.1.0}/examples/apps/hello_world.py +0 -0
  77. {runpod-2.0.1.dev1 → runpod-2.1.0}/examples/apps/streaming.py +0 -0
  78. {runpod-2.0.1.dev1 → runpod-2.1.0}/examples/apps/train_and_eval.py +0 -0
  79. {runpod-2.0.1.dev1 → runpod-2.1.0}/examples/apps/web_service.py +0 -0
  80. {runpod-2.0.1.dev1 → runpod-2.1.0}/examples/endpoints/asyncio_job_request.py +0 -0
  81. {runpod-2.0.1.dev1 → runpod-2.1.0}/examples/endpoints/health.py +0 -0
  82. {runpod-2.0.1.dev1 → runpod-2.1.0}/examples/endpoints/run.py +0 -0
  83. {runpod-2.0.1.dev1 → runpod-2.1.0}/examples/endpoints/run_sync.py +0 -0
  84. {runpod-2.0.1.dev1 → runpod-2.1.0}/examples/endpoints/streaming.py +0 -0
  85. {runpod-2.0.1.dev1 → runpod-2.1.0}/examples/rest_wrapper.py +0 -0
  86. {runpod-2.0.1.dev1 → runpod-2.1.0}/examples/serverless/concurrent_handler.py +0 -0
  87. {runpod-2.0.1.dev1 → runpod-2.1.0}/examples/serverless/logger.py +0 -0
  88. {runpod-2.0.1.dev1 → runpod-2.1.0}/examples/serverless/simple_handler.py +0 -0
  89. {runpod-2.0.1.dev1 → runpod-2.1.0}/examples/serverless/volume_cache_handler.py +0 -0
  90. {runpod-2.0.1.dev1 → runpod-2.1.0}/pyproject.toml +0 -0
  91. {runpod-2.0.1.dev1 → runpod-2.1.0}/pytest.ini +0 -0
  92. {runpod-2.0.1.dev1 → runpod-2.1.0}/requirements.txt +0 -0
  93. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/__init__.py +0 -0
  94. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/_binary_helpers.py +0 -0
  95. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/_health/__init__.py +0 -0
  96. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/_health/cuda.py +0 -0
  97. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/_health/fitness.py +0 -0
  98. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/_health/gpu.py +0 -0
  99. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/_health/system.py +0 -0
  100. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/_logger.py +0 -0
  101. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/_startup.py +0 -0
  102. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/agent.py +0 -0
  103. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/api/__init__.py +0 -0
  104. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/api/ctl_commands.py +0 -0
  105. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/api/mutations/__init__.py +0 -0
  106. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/api/mutations/apps.py +0 -0
  107. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/api/mutations/container_register_auth.py +0 -0
  108. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/api/queries/__init__.py +0 -0
  109. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/api/queries/user.py +0 -0
  110. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/__init__.py +0 -0
  111. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/app.py +0 -0
  112. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/build.py +0 -0
  113. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/context.py +0 -0
  114. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/datacenter.py +0 -0
  115. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/deploy.py +0 -0
  116. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/dev.py +0 -0
  117. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/discovery.py +0 -0
  118. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/discovery_state.py +0 -0
  119. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/entrypoint.py +0 -0
  120. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/errors.py +0 -0
  121. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/gpu.py +0 -0
  122. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/handles.py +0 -0
  123. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/images.py +0 -0
  124. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/init.py +0 -0
  125. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/invoker.py +0 -0
  126. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/job.py +0 -0
  127. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/logs.py +0 -0
  128. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/manage.py +0 -0
  129. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/markers.py +0 -0
  130. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/model.py +0 -0
  131. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/monitor.py +0 -0
  132. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/placement.py +0 -0
  133. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/protocol.py +0 -0
  134. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/registry.py +0 -0
  135. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/schedule.py +0 -0
  136. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/secret.py +0 -0
  137. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/serialization.py +0 -0
  138. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/shim.py +0 -0
  139. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/spec.py +0 -0
  140. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/stubs.py +0 -0
  141. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/targets.py +0 -0
  142. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/tasks.py +0 -0
  143. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/utils/__init__.py +0 -0
  144. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/utils/client.py +0 -0
  145. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/utils/events.py +0 -0
  146. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/utils/lookup.py +0 -0
  147. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/utils/names.py +0 -0
  148. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/utils/network.py +0 -0
  149. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/volume.py +0 -0
  150. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/apps/watch.py +0 -0
  151. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/cli/__init__.py +0 -0
  152. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/cli/entry.py +0 -0
  153. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/cli/groups/__init__.py +0 -0
  154. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/cli/groups/config/__init__.py +0 -0
  155. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/cli/groups/config/functions.py +0 -0
  156. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/cli/groups/pod/__init__.py +0 -0
  157. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/cli/groups/pod/commands.py +0 -0
  158. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/cli/groups/ssh/__init__.py +0 -0
  159. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/cli/groups/ssh/commands.py +0 -0
  160. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/cli/groups/ssh/functions.py +0 -0
  161. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/cli/utils/__init__.py +0 -0
  162. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/cli/utils/rp_info.py +0 -0
  163. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/cli/utils/rp_runpodignore.py +0 -0
  164. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/cli/utils/rp_userspace.py +0 -0
  165. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/cli/utils/ssh_cmd.py +0 -0
  166. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/endpoint/__init__.py +0 -0
  167. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/endpoint/asyncio/__init__.py +0 -0
  168. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/endpoint/asyncio/asyncio_runner.py +0 -0
  169. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/endpoint/helpers.py +0 -0
  170. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/endpoint/runner.py +0 -0
  171. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/http_client.py +0 -0
  172. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/rp_cli/__init__.py +0 -0
  173. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/rp_cli/api.py +0 -0
  174. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/rp_cli/catalog.py +0 -0
  175. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/rp_cli/compute.py +0 -0
  176. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/rp_cli/console.py +0 -0
  177. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/rp_cli/endpoint.py +0 -0
  178. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/rp_cli/endpoint_config.py +0 -0
  179. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/rp_cli/logs.py +0 -0
  180. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/rp_cli/options.py +0 -0
  181. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/rp_cli/output.py +0 -0
  182. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/rp_cli/pod.py +0 -0
  183. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/rp_cli/update.py +0 -0
  184. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/rp_cli/volume.py +0 -0
  185. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/serverless/__init__.py +0 -0
  186. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/serverless/binaries/README.md +0 -0
  187. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/serverless/binaries/__init__.py +0 -0
  188. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/serverless/binaries/gpu_test +0 -0
  189. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/serverless/modules/__init__.py +0 -0
  190. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/serverless/modules/rp_capture.py +0 -0
  191. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/serverless/modules/rp_fitness.py +0 -0
  192. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/serverless/modules/rp_gpu_fitness.py +0 -0
  193. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/serverless/modules/rp_handler.py +0 -0
  194. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/serverless/modules/rp_http.py +0 -0
  195. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/serverless/modules/rp_job.py +0 -0
  196. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/serverless/modules/rp_local.py +0 -0
  197. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/serverless/modules/rp_logger.py +0 -0
  198. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/serverless/modules/rp_ping.py +0 -0
  199. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/serverless/modules/rp_prestart.py +0 -0
  200. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/serverless/modules/rp_progress.py +0 -0
  201. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/serverless/modules/rp_scale.py +0 -0
  202. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/serverless/modules/rp_system_fitness.py +0 -0
  203. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/serverless/modules/rp_tips.py +0 -0
  204. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/serverless/modules/worker_state.py +0 -0
  205. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/serverless/utils/__init__.py +0 -0
  206. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/serverless/utils/rp_cleanup.py +0 -0
  207. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/serverless/utils/rp_cuda.py +0 -0
  208. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/serverless/utils/rp_debugger.py +0 -0
  209. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/serverless/utils/rp_download.py +0 -0
  210. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/serverless/utils/rp_model_cache.py +0 -0
  211. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/serverless/utils/rp_ssrf.py +0 -0
  212. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/serverless/utils/rp_upload.py +0 -0
  213. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/serverless/utils/rp_validator.py +0 -0
  214. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/serverless/utils/rp_volume_cache.py +0 -0
  215. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/serverless/worker.py +0 -0
  216. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/user_agent.py +0 -0
  217. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod/version.py +0 -0
  218. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod.egg-info/dependency_links.txt +0 -0
  219. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod.egg-info/entry_points.txt +0 -0
  220. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod.egg-info/requires.txt +0 -0
  221. {runpod-2.0.1.dev1 → runpod-2.1.0}/runpod.egg-info/top_level.txt +0 -0
  222. {runpod-2.0.1.dev1 → runpod-2.1.0}/scripts/README.md +0 -0
  223. {runpod-2.0.1.dev1 → runpod-2.1.0}/scripts/benchmark_cold_start.sh +0 -0
  224. {runpod-2.0.1.dev1 → runpod-2.1.0}/scripts/compare_benchmarks.py +0 -0
  225. {runpod-2.0.1.dev1 → runpod-2.1.0}/setup.cfg +0 -0
  226. {runpod-2.0.1.dev1 → runpod-2.1.0}/setup.py +0 -0
  227. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/__init__.py +0 -0
  228. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/e2e/__init__.py +0 -0
  229. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/e2e/apps_live_smoke.py +0 -0
  230. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/e2e/conftest.py +0 -0
  231. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/e2e/e2e_provisioner.py +0 -0
  232. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/e2e/examples/01_hello_world.py +0 -0
  233. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/e2e/examples/02_invocation_styles.py +0 -0
  234. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/e2e/examples/03_gpu.py +0 -0
  235. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/e2e/examples/04_dependencies.py +0 -0
  236. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/e2e/examples/05_api_service.py +0 -0
  237. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/e2e/examples/06_tasks.py +0 -0
  238. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/e2e/examples/07_pipelines.py +0 -0
  239. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/e2e/examples/08_volumes.py +0 -0
  240. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/e2e/examples/09_secrets.py +0 -0
  241. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/e2e/examples/10_cached_models.py +0 -0
  242. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/e2e/examples/11_train_eval.py +0 -0
  243. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/e2e/examples/12_custom_image.py +0 -0
  244. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/e2e/examples/13_streaming.py +0 -0
  245. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/e2e/examples/README.md +0 -0
  246. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/e2e/examples/run_all.sh +0 -0
  247. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/e2e/fixtures/cold_start/handler.py +0 -0
  248. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/e2e/fixtures/cold_start/pyproject.toml +0 -0
  249. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/e2e/matrix/main.py +0 -0
  250. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/e2e/matrix/run.py +0 -0
  251. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/e2e/task_pod_smoke.py +0 -0
  252. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/e2e/test_cold_start.py +0 -0
  253. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/e2e/test_mock_worker.py +0 -0
  254. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/e2e/tests.json +0 -0
  255. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/fixtures/mock_gpu_test +0 -0
  256. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_agent.py +0 -0
  257. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_api/__init__.py +0 -0
  258. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_api/test_ctl_commands.py +0 -0
  259. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_api/test_rest.py +0 -0
  260. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_apps/__init__.py +0 -0
  261. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_apps/conftest.py +0 -0
  262. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_apps/test_agent_auth.py +0 -0
  263. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_apps/test_app.py +0 -0
  264. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_apps/test_build.py +0 -0
  265. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_apps/test_compat_lock.py +0 -0
  266. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_apps/test_config_params.py +0 -0
  267. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_apps/test_deploy.py +0 -0
  268. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_apps/test_dev.py +0 -0
  269. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_apps/test_discovery.py +0 -0
  270. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_apps/test_dispatch.py +0 -0
  271. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_apps/test_gpu_catalog.py +0 -0
  272. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_apps/test_images.py +0 -0
  273. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_apps/test_job.py +0 -0
  274. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_apps/test_logs.py +0 -0
  275. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_apps/test_manage.py +0 -0
  276. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_apps/test_markers.py +0 -0
  277. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_apps/test_model.py +0 -0
  278. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_apps/test_monitor.py +0 -0
  279. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_apps/test_placement.py +0 -0
  280. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_apps/test_registry.py +0 -0
  281. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_apps/test_retry.py +0 -0
  282. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_apps/test_secret.py +0 -0
  283. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_apps/test_shim.py +0 -0
  284. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_apps/test_targets.py +0 -0
  285. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_apps/test_tasks.py +0 -0
  286. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_apps/test_volume.py +0 -0
  287. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_apps/test_watch.py +0 -0
  288. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_cli/__init__.py +0 -0
  289. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_cli/test_agent_login.py +0 -0
  290. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_cli/test_catalog_command.py +0 -0
  291. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_cli/test_cli_api.py +0 -0
  292. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_cli/test_cli_groups/__init__.py +0 -0
  293. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_cli/test_cli_groups/test_config_functions.py +0 -0
  294. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_cli/test_cli_groups/test_pod_commands.py +0 -0
  295. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_cli/test_cli_groups/test_ssh_commands.py +0 -0
  296. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_cli/test_cli_groups/test_ssh_dispatch.py +0 -0
  297. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_cli/test_cli_groups/test_ssh_functions.py +0 -0
  298. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_cli/test_cli_logs.py +0 -0
  299. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_cli/test_cli_options.py +0 -0
  300. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_cli/test_cli_output.py +0 -0
  301. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_cli/test_cli_utils/__init__.py +0 -0
  302. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_cli/test_cli_utils/test_info.py +0 -0
  303. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_cli/test_cli_utils/test_runpodignore.py +0 -0
  304. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_cli/test_cli_utils/test_ssh_cmd.py +0 -0
  305. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_cli/test_cli_utils/test_userspace.py +0 -0
  306. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_cli/test_compute.py +0 -0
  307. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_cli/test_console.py +0 -0
  308. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_cli/test_deploy_command.py +0 -0
  309. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_cli/test_endpoint_command.py +0 -0
  310. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_cli/test_init_command.py +0 -0
  311. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_cli/test_pod_command.py +0 -0
  312. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_cli/test_update_command.py +0 -0
  313. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_cli/test_volume_command.py +0 -0
  314. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_endpoint/__init__.py +0 -0
  315. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_endpoint/test_asyncio_init.py +0 -0
  316. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_endpoint/test_asyncio_runner.py +0 -0
  317. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_endpoint/test_init.py +0 -0
  318. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_endpoint/test_runner.py +0 -0
  319. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_error.py +0 -0
  320. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_init.py +0 -0
  321. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_performance/__init__.py +0 -0
  322. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_performance/test_cold_start.py +0 -0
  323. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/__init__.py +0 -0
  324. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/local_sim/.env_example +0 -0
  325. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/local_sim/Makefile +0 -0
  326. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/local_sim/localhost.py +0 -0
  327. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/local_sim/worker.py +0 -0
  328. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_capture.py +0 -0
  329. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_init.py +0 -0
  330. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_integration_worker_state.py +0 -0
  331. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_modules/__init__.py +0 -0
  332. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_modules/test_fastapi.py +0 -0
  333. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_modules/test_fitness/__init__.py +0 -0
  334. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_modules/test_fitness/conftest.py +0 -0
  335. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_modules/test_fitness/test_force_kill.py +0 -0
  336. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_modules/test_fitness/test_gpu_checks.py +0 -0
  337. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_modules/test_fitness/test_gpu_integration.py +0 -0
  338. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_modules/test_fitness/test_registration.py +0 -0
  339. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_modules/test_fitness/test_safe_startup.py +0 -0
  340. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_modules/test_fitness/test_startup.py +0 -0
  341. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_modules/test_fitness/test_system_checks.py +0 -0
  342. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_modules/test_handler.py +0 -0
  343. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_modules/test_http.py +0 -0
  344. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_modules/test_job.py +0 -0
  345. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_modules/test_local.py +0 -0
  346. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_modules/test_logger.py +0 -0
  347. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_modules/test_ping.py +0 -0
  348. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_modules/test_progress.py +0 -0
  349. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_modules/test_scale.py +0 -0
  350. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_modules/test_state.py +0 -0
  351. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_modules/test_tips.py +0 -0
  352. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_prestart.py +0 -0
  353. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_prestart_lifecycle.py +0 -0
  354. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_rp_scale.py +0 -0
  355. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_scale_mirror.py +0 -0
  356. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_utils/__init__.py +0 -0
  357. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_utils/test_cleanup.py +0 -0
  358. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_utils/test_cuda.py +0 -0
  359. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_utils/test_debugger.py +0 -0
  360. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_utils/test_download.py +0 -0
  361. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_utils/test_model_cache.py +0 -0
  362. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_utils/test_rp_volume_cache.py +0 -0
  363. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_utils/test_ssrf.py +0 -0
  364. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_utils/test_upload.py +0 -0
  365. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_utils/test_validate.py +0 -0
  366. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_utils_init.py +0 -0
  367. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_serverless/test_worker.py +0 -0
  368. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_shared/__init__.py +0 -0
  369. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_shared/test_auth.py +0 -0
  370. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_user_agent.py +0 -0
  371. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_version.py +0 -0
  372. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/test_whatever.py +0 -0
  373. {runpod-2.0.1.dev1 → runpod-2.1.0}/tests/whatever.py +0 -0
@@ -8,12 +8,26 @@
8
8
 
9
9
  ### Bug Fixes
10
10
 
11
+ * [BREAKING] `rp login` asks you to paste the API key shown in the console after approval. It no longer collects the key by polling the auth request, and it fails fast without an interactive terminal.
11
12
  * Task calls support indefinite execution with bounded interruption cleanup. Failed deletion retains the pod ID for recovery.
12
13
  * Network volume creation intersects catalog storage support with every sharing resource's hardware stock and datacenter constraints. Unsupported explicit pins fail before creation; existing volumes retain their authoritative IDs and datacenters.
13
14
  * Global volume attachments require GPU compute for tasks and endpoints.
14
15
  * Deployment artifacts apply project ignore rules and mandatory secret-file exclusions while preserving vendored dependency assets.
15
16
  * Synchronous calls wait across polling timeouts on Python 3.10 and propagate operation timeouts.
16
17
 
18
+ ## [2.1.0](https://github.com/runpod/runpod-python/compare/v2.0.0...v2.1.0) (2026-11-03)
19
+
20
+
21
+ ### Features
22
+
23
+ * rp login prompts for the api key instead of polling ([#675](https://github.com/runpod/runpod-python/issues/675)) ([2217b6d](https://github.com/runpod/runpod-python/commit/2217b6d15a0554e2652506068f438d740392ad37))
24
+
25
+
26
+ ### Bug Fixes
27
+
28
+ * coordinate REST API rate limits and bound retries ([#618](https://github.com/runpod/runpod-python/issues/618)) ([d6ce9a8](https://github.com/runpod/runpod-python/commit/d6ce9a8c572ac117f0641846f45dadaa40954e57))
29
+ * require https for documented cli installation ([#674](https://github.com/runpod/runpod-python/issues/674)) ([946afb4](https://github.com/runpod/runpod-python/commit/946afb4ea45ac526c81106783faeac73caf17ee6))
30
+
17
31
  ## [2.1.0](https://github.com/runpod/runpod-python/compare/v2.0.0...v2.1.0) (2026-10-28)
18
32
 
19
33
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: runpod
3
- Version: 2.0.1.dev1
3
+ Version: 2.1.0
4
4
  Summary: 🐍 | Python library for Runpod API and serverless worker SDK.
5
5
  Home-page: https://runpod.io
6
6
  Author: Runpod
@@ -0,0 +1,69 @@
1
+ # Handling API errors
2
+
3
+ Authentication failures raise `AuthenticationError`:
4
+
5
+ ```python
6
+ import runpod
7
+
8
+ try:
9
+ runpod.get_pods()
10
+ except runpod.error.AuthenticationError as err:
11
+ print(err)
12
+ ```
13
+
14
+ REST API problem responses raise `QueryError`. The exception includes the HTTP
15
+ status code, request method and path, and request-validation errors when present:
16
+
17
+ ```python
18
+ try:
19
+ runpod.create_pod(
20
+ "training",
21
+ "runpod/pytorch:1.0.2-cu1281-torch280-ubuntu2404",
22
+ "NVIDIA GeForce RTX 4090",
23
+ )
24
+ except runpod.error.QueryError as err:
25
+ print(err.status_code)
26
+ print(err.query)
27
+ print(err.errors)
28
+ ```
29
+
30
+ ## REST rate limits
31
+
32
+ REST calls share Runpod's minute, hour, and day quotas across routes within the
33
+ process, keyed by API origin and API key. Both synchronous and asynchronous calls
34
+ reserve the remaining budget advertised by the `RateLimit` response header and
35
+ wait when a window is exhausted. Separate processes and different API keys for the
36
+ same account cannot coordinate their local budgets.
37
+
38
+ Quota estimates account for outstanding requests separately from response
39
+ snapshots. A window refreshes when the response timing establishes a later reset;
40
+ out-of-order snapshots within a window cannot restore spent quota. Discovery and
41
+ reset probes coordinate concurrent callers without consuming their rate-limit
42
+ wait budgets.
43
+
44
+ HTTP 429 responses honor `Retry-After` in seconds or HTTP-date format. Missing or
45
+ invalid retry hints use capped exponential backoff with jitter. By default, a call
46
+ allows three retries and at most 60 seconds of cumulative rate-limit sleeps.
47
+ Network failures and other HTTP errors are not retried, since a failed response
48
+ can follow a successful resource mutation.
49
+
50
+ An exhausted retry or wait budget raises `RateLimitError`, a `QueryError` subclass
51
+ with `status_code=429` and a `retry_after` delay in seconds. A successful call that
52
+ uses the last available token still returns immediately; subsequent calls wait or
53
+ raise if the required delay exceeds their wait budget.
54
+
55
+ ```python
56
+ try:
57
+ pods = runpod.get_pods()
58
+ except runpod.error.RateLimitError as err:
59
+ print(f"Quota exhausted. Retry after {err.retry_after:.1f} seconds.")
60
+ ```
61
+
62
+ The low-level `runpod.api.rest.run_rest_request` and `run_rest_request_async`
63
+ functions accept `max_retries` and `max_rate_limit_wait` keyword arguments. Set
64
+ `max_retries=0` to surface the first 429, or `max_rate_limit_wait=0` to fail rather
65
+ than sleep for an exhausted quota. `timeout` applies separately to each HTTP
66
+ attempt. Async quota waits are cancellable and do not block the event loop.
67
+
68
+ These protections apply to the management REST API transport. GraphQL calls and
69
+ Serverless job execution use separate transports.
@@ -34,11 +34,14 @@ rp --help
34
34
  ### Authenticate
35
35
 
36
36
  ```bash
37
- rp login # browser approval
37
+ rp login # browser approval, then paste the key
38
38
  rp login --api-key YOUR_KEY # store a key directly
39
39
  rp login --profile staging # save browser credentials in a named profile
40
40
  ```
41
41
 
42
+ `rp login` opens the console to approve the request. After you approve, the console shows a new API key; paste it at the hidden prompt. The CLI checks the key before saving it.
43
+ `rp login` needs an interactive terminal. In CI or scripts, set `RUNPOD_API_KEY` or use `rp login --api-key`.
44
+
42
45
  Credentials are stored in `~/.runpod/config.toml`.
43
46
  Use `--profile NAME` with either login method to update only that profile; the default is `default`.
44
47
 
@@ -8,7 +8,7 @@ Note: This CLI is not the same as runpodctl and provides a different set of feat
8
8
 
9
9
  ### Authenticate
10
10
 
11
- Store your Runpod API key by running `rp login` (browser approval) or `rp login --api-key YOUR_KEY`. Credentials are stored in `~/.runpod/config.toml`.
11
+ Store your Runpod API key by running `rp login` (approve in the browser, then paste the key the console shows) or `rp login --api-key YOUR_KEY`. Credentials are stored in `~/.runpod/config.toml`.
12
12
 
13
13
  Pass `--profile staging` with either login method to update a named credential profile without changing the others. The default profile is `default`.
14
14
 
@@ -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.1.dev1'
22
- __version_tuple__ = version_tuple = (2, 0, 1, 'dev1')
21
+ __version__ = version = '2.1.0'
22
+ __version_tuple__ = version_tuple = (2, 1, 0)
23
23
 
24
- __commit_id__ = commit_id = 'ga4ba07898'
24
+ __commit_id__ = commit_id = 'g0d59088b9'
@@ -0,0 +1,259 @@
1
+ """Shared REST quotas for threads and asynchronous callers."""
2
+
3
+ import hmac
4
+ import math
5
+ import os
6
+ import random
7
+ import re
8
+ import threading
9
+ import time
10
+ from concurrent.futures import Future
11
+ from contextlib import contextmanager
12
+ from datetime import timezone
13
+ from email.utils import parsedate_to_datetime
14
+ from typing import Mapping, Optional
15
+ from urllib.parse import urlsplit
16
+
17
+ from runpod.error import RateLimitError
18
+
19
+
20
+ def _retry_after(value: Optional[str]) -> Optional[float]:
21
+ if value is None:
22
+ return None
23
+ try:
24
+ delay = float(value)
25
+ except ValueError:
26
+ try:
27
+ date = parsedate_to_datetime(value)
28
+ if date.tzinfo is None:
29
+ date = date.replace(tzinfo=timezone.utc)
30
+ delay = date.timestamp() - time.time()
31
+ except (ValueError, TypeError, OverflowError):
32
+ return None
33
+ delay = max(0.0, delay)
34
+ return delay if math.isfinite(delay) and delay >= 0 else None
35
+
36
+
37
+ def _windows(value: str) -> dict[str, tuple[int, float]]:
38
+ windows = {}
39
+ for member in value.split(","):
40
+ parts = member.strip().split(";")
41
+ if parts[0] not in {'"minute"', '"hour"', '"day"'}:
42
+ continue
43
+ parameters = {}
44
+ for part in parts[1:]:
45
+ match = re.fullmatch(r"\s*([a-z]+)\s*=\s*([0-9]{1,15})\s*", part)
46
+ if match:
47
+ parameters[match[1]] = int(match[2])
48
+ if "r" in parameters and "t" in parameters:
49
+ windows[parts[0]] = (parameters["r"], float(parameters["t"]))
50
+ return windows
51
+
52
+
53
+ class _RetryBudget:
54
+ def __init__(self, max_retries: int, max_wait: float, query: str):
55
+ self.max_retries = max_retries
56
+ self.remaining_wait = max_wait
57
+ self.query = query
58
+
59
+ def wait(self, delay: float) -> None:
60
+ if delay > self.remaining_wait:
61
+ raise RateLimitError(
62
+ "Rate limit wait budget exceeded", self.query, retry_after=delay
63
+ )
64
+ self.remaining_wait -= delay
65
+
66
+ def can_retry(self, attempt: int, retry_after: Optional[float]) -> bool:
67
+ return (
68
+ retry_after is not None
69
+ and attempt < self.max_retries
70
+ and retry_after <= self.remaining_wait
71
+ )
72
+
73
+
74
+ class _Bucket:
75
+ def __init__(self):
76
+ self.lock = threading.Lock()
77
+ self.waiters: set[Future[None]] = set()
78
+ self.windows: dict[str, tuple[Optional[int], float]] = {}
79
+ self.estimates: dict[str, int] = {}
80
+ self.reset_lower_bounds: dict[str, float] = {}
81
+ self.blocked_until = 0.0
82
+ self.discovered = False
83
+ self.in_flight = 0
84
+ self.users = 0
85
+ self.last_used = time.monotonic()
86
+
87
+ def acquire(self, budget: Optional[_RetryBudget] = None) -> float | Future[None]:
88
+ with self.lock:
89
+ now = time.monotonic()
90
+ delay = max(0.0, self.blocked_until - now)
91
+ for name, (remaining, reset_at) in self.windows.items():
92
+ if remaining is not None and now >= reset_at:
93
+ self.windows[name] = (None, reset_at)
94
+ if remaining == 0:
95
+ self.discovered = False
96
+ elif remaining == 0:
97
+ delay = max(delay, reset_at - now)
98
+ if delay:
99
+ if budget is not None:
100
+ budget.wait(delay)
101
+ return delay
102
+ if self.in_flight and (
103
+ not self.discovered or (self.blocked_until and not delay)
104
+ ):
105
+ waiter: Future[None] = Future()
106
+ self.waiters.add(waiter)
107
+ waiter.add_done_callback(self._remove_waiter)
108
+ return waiter
109
+ self.in_flight += 1
110
+ for name, (remaining, reset_at) in self.windows.items():
111
+ if remaining is not None:
112
+ self.windows[name] = (max(0, remaining - 1), reset_at)
113
+ self.estimates[name] = max(0, self.estimates[name] - 1)
114
+ return 0.0
115
+
116
+ def _remove_waiter(self, waiter: Future[None]) -> None:
117
+ with self.lock:
118
+ self.waiters.discard(waiter)
119
+
120
+ def _notify_waiters(self) -> None:
121
+ with self.lock:
122
+ waiters, self.waiters = self.waiters, set()
123
+ for waiter in waiters:
124
+ if waiter.set_running_or_notify_cancel():
125
+ waiter.set_result(None)
126
+
127
+ def update(
128
+ self,
129
+ status: int,
130
+ headers: Mapping[str, str],
131
+ attempt: int,
132
+ *,
133
+ started_at: Optional[float] = None,
134
+ ) -> Optional[float]:
135
+ windows = _windows(
136
+ ", ".join(
137
+ value for key, value in headers.items() if key.lower() == "ratelimit"
138
+ )
139
+ )
140
+ headers = {key.lower(): value for key, value in headers.items()}
141
+ retry_after = None
142
+ if status == 429:
143
+ retry_after = _retry_after(headers.get("retry-after"))
144
+ if retry_after is None:
145
+ retry_after = random.uniform(0.5, 1.0) * 2 ** min(attempt, 6)
146
+ with self.lock:
147
+ now = time.monotonic()
148
+ self.discovered = True
149
+ if self.blocked_until <= now:
150
+ self.blocked_until = 0.0
151
+ for name, (remaining, reset_after) in windows.items():
152
+ reset_at = now + reset_after
153
+ reset_lower = (now if started_at is None else started_at) + max(
154
+ 0.0, reset_after - 1.0
155
+ )
156
+ previous, previous_reset = self.windows.get(name, (None, 0.0))
157
+ previous_lower = self.reset_lower_bounds.get(name, 0.0)
158
+ if reset_at < previous_lower:
159
+ continue
160
+ if (
161
+ previous is not None
162
+ and now < previous_reset
163
+ and reset_lower <= previous_reset
164
+ ):
165
+ remaining = min(remaining, self.estimates[name])
166
+ reset_at = min(reset_at, previous_reset)
167
+ reset_lower = max(reset_lower, previous_lower)
168
+ self.estimates[name] = remaining
169
+ self.reset_lower_bounds[name] = reset_lower
170
+ self.windows[name] = (
171
+ max(0, remaining - max(0, self.in_flight - 1)),
172
+ reset_at,
173
+ )
174
+ for name in list(self.windows):
175
+ if name not in windows and self.windows[name][1] <= now:
176
+ del self.windows[name]
177
+ del self.estimates[name]
178
+ del self.reset_lower_bounds[name]
179
+ if retry_after is not None:
180
+ retry_after = max(
181
+ retry_after,
182
+ self.blocked_until - now,
183
+ *(
184
+ reset - now
185
+ for name, (_, reset) in self.windows.items()
186
+ if self.estimates[name] == 0
187
+ ),
188
+ 0.0,
189
+ )
190
+ self.blocked_until = max(self.blocked_until, now + retry_after)
191
+ self._notify_waiters()
192
+ return retry_after
193
+
194
+ def release(self):
195
+ with self.lock:
196
+ self.in_flight -= 1
197
+ for name, (remaining, reset_at) in self.windows.items():
198
+ if remaining is not None:
199
+ self.windows[name] = (
200
+ max(0, self.estimates[name] - self.in_flight),
201
+ reset_at,
202
+ )
203
+ self._notify_waiters()
204
+
205
+
206
+ class _RateLimits:
207
+ def __init__(self):
208
+ self.lock = threading.Lock()
209
+ self.fingerprint_key = os.urandom(32)
210
+ self.buckets: dict[tuple[str, str, bytes], _Bucket] = {}
211
+ self.next_cleanup = 0.0
212
+
213
+ @contextmanager
214
+ def bucket(self, url: str, api_key: str):
215
+ origin = urlsplit(url)
216
+ key = (
217
+ origin.scheme.lower(),
218
+ origin.netloc.lower(),
219
+ hmac.digest(self.fingerprint_key, api_key.encode(), "sha256"),
220
+ )
221
+ with self.lock:
222
+ now = time.monotonic()
223
+ if now >= self.next_cleanup:
224
+ for cached_key, bucket in list(self.buckets.items()):
225
+ if bucket.users or now - bucket.last_used < 300:
226
+ continue
227
+ with bucket.lock:
228
+ if bucket.blocked_until <= now and all(
229
+ reset <= now for _, reset in bucket.windows.values()
230
+ ):
231
+ del self.buckets[cached_key]
232
+ self.next_cleanup = now + 60
233
+ bucket = self.buckets.setdefault(key, _Bucket())
234
+ bucket.users += 1
235
+ try:
236
+ yield bucket
237
+ finally:
238
+ with self.lock:
239
+ bucket.users -= 1
240
+ bucket.last_used = time.monotonic()
241
+
242
+
243
+ def _validate_retry_options(max_retries: int, max_rate_limit_wait: float) -> None:
244
+ if (
245
+ isinstance(max_retries, bool)
246
+ or not isinstance(max_retries, int)
247
+ or max_retries < 0
248
+ ):
249
+ raise ValueError("max_retries must be a non-negative integer")
250
+ if (
251
+ isinstance(max_rate_limit_wait, bool)
252
+ or not isinstance(max_rate_limit_wait, (int, float))
253
+ or not math.isfinite(max_rate_limit_wait)
254
+ or max_rate_limit_wait < 0
255
+ ):
256
+ raise ValueError("max_rate_limit_wait must be finite and non-negative")
257
+
258
+
259
+ rate_limits = _RateLimits()
@@ -98,9 +98,8 @@ async def run_graphql_query_async(
98
98
  variables: Optional GraphQL variables to send with the query.
99
99
  timeout: Total request timeout in seconds.
100
100
  anonymous: Send the request without credentials even when a key
101
- is available. The browser-login flow requires this: the
102
- server only releases the granted key to guest requests, so
103
- a stale stored key would make the poll spin forever.
101
+ is available. Login creates its auth request this way so a
102
+ stale stored key cannot fail the request.
104
103
  """
105
104
  import aiohttp # pylint: disable=import-outside-toplevel
106
105
 
@@ -66,13 +66,12 @@ query getFlashEnvironmentByName($input: FlashEnvironmentByNameInput!) {
66
66
  }
67
67
  """
68
68
 
69
+ # no apiKey: the console shows the key only to the approver.
69
70
  QUERY_FLASH_AUTH_REQUEST_STATUS = """
70
71
  query flashAuthRequestStatus($flashAuthRequestId: String!) {
71
72
  flashAuthRequestStatus(flashAuthRequestId: $flashAuthRequestId) {
72
73
  id
73
74
  status
74
- expiresAt
75
- apiKey
76
75
  }
77
76
  }
78
77
  """