opensandbox 0.1.8.dev0__tar.gz → 0.1.8.dev2__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 (212) hide show
  1. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/.gitignore +2 -0
  2. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/PKG-INFO +6 -5
  3. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/README.md +5 -4
  4. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/pyproject.toml +1 -0
  5. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/__init__.py +0 -4
  6. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/converter/exception_converter.py +4 -1
  7. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/converter/response_handler.py +16 -6
  8. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/api/snapshots/delete_snapshots_snapshot_id.py +28 -8
  9. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/api/snapshots/post_sandboxes_sandbox_id_snapshots.py +16 -8
  10. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/async_redis_pool_store.py +101 -42
  11. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/pool.py +0 -4
  12. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/pool_async.py +11 -10
  13. opensandbox-0.1.8.dev2/src/opensandbox/pool_redis.py +27 -0
  14. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/redis_pool_store.py +47 -7
  15. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/pool.py +11 -11
  16. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/LICENSE +0 -0
  17. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/_async_pool_reconciler.py +0 -0
  18. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/_async_pool_store.py +0 -0
  19. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/_pool_reconciler.py +0 -0
  20. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/_pool_store.py +0 -0
  21. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/__init__.py +0 -0
  22. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/command_adapter.py +0 -0
  23. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/converter/__init__.py +0 -0
  24. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/converter/command_model_converter.py +0 -0
  25. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/converter/event_node.py +0 -0
  26. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/converter/execution_converter.py +0 -0
  27. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/converter/execution_event_dispatcher.py +0 -0
  28. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/converter/filesystem_model_converter.py +0 -0
  29. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/converter/metrics_model_converter.py +0 -0
  30. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/converter/sandbox_model_converter.py +0 -0
  31. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/egress_adapter.py +0 -0
  32. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/factory.py +0 -0
  33. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/filesystem_adapter.py +0 -0
  34. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/health_adapter.py +0 -0
  35. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/metrics_adapter.py +0 -0
  36. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/sandboxes_adapter.py +0 -0
  37. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/__init__.py +0 -0
  38. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/egress/__init__.py +0 -0
  39. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/egress/api/__init__.py +0 -0
  40. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/egress/api/policy/__init__.py +0 -0
  41. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/egress/api/policy/get_policy.py +0 -0
  42. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/egress/api/policy/patch_policy.py +0 -0
  43. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/egress/client.py +0 -0
  44. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/egress/errors.py +0 -0
  45. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/egress/models/__init__.py +0 -0
  46. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/egress/models/network_policy.py +0 -0
  47. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/egress/models/network_policy_default_action.py +0 -0
  48. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/egress/models/network_rule.py +0 -0
  49. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/egress/models/network_rule_action.py +0 -0
  50. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/egress/models/policy_status_response.py +0 -0
  51. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/egress/py.typed +0 -0
  52. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/egress/types.py +0 -0
  53. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/__init__.py +0 -0
  54. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/__init__.py +0 -0
  55. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/code_interpreting/__init__.py +0 -0
  56. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/code_interpreting/create_code_context.py +0 -0
  57. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/code_interpreting/delete_context.py +0 -0
  58. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/code_interpreting/delete_contexts_by_language.py +0 -0
  59. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/code_interpreting/get_context.py +0 -0
  60. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/code_interpreting/interrupt_code.py +0 -0
  61. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/code_interpreting/list_contexts.py +0 -0
  62. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/code_interpreting/run_code.py +0 -0
  63. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/command/__init__.py +0 -0
  64. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/command/create_session.py +0 -0
  65. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/command/delete_session.py +0 -0
  66. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/command/get_background_command_logs.py +0 -0
  67. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/command/get_command_status.py +0 -0
  68. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/command/interrupt_command.py +0 -0
  69. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/command/run_command.py +0 -0
  70. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/command/run_in_session.py +0 -0
  71. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/filesystem/__init__.py +0 -0
  72. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/filesystem/chmod_files.py +0 -0
  73. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/filesystem/download_file.py +0 -0
  74. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/filesystem/get_files_info.py +0 -0
  75. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/filesystem/make_dirs.py +0 -0
  76. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/filesystem/remove_dirs.py +0 -0
  77. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/filesystem/remove_files.py +0 -0
  78. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/filesystem/rename_files.py +0 -0
  79. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/filesystem/replace_content.py +0 -0
  80. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/filesystem/search_files.py +0 -0
  81. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/filesystem/upload_file.py +0 -0
  82. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/health/__init__.py +0 -0
  83. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/health/ping.py +0 -0
  84. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/metric/__init__.py +0 -0
  85. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/metric/get_metrics.py +0 -0
  86. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/metric/watch_metrics.py +0 -0
  87. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/client.py +0 -0
  88. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/errors.py +0 -0
  89. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/__init__.py +0 -0
  90. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/chmod_files_body.py +0 -0
  91. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/code_context.py +0 -0
  92. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/code_context_request.py +0 -0
  93. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/command_status_response.py +0 -0
  94. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/create_session_request.py +0 -0
  95. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/create_session_response.py +0 -0
  96. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/error_response.py +0 -0
  97. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/file_info.py +0 -0
  98. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/file_metadata.py +0 -0
  99. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/get_files_info_response_200.py +0 -0
  100. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/make_dirs_body.py +0 -0
  101. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/metrics.py +0 -0
  102. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/permission.py +0 -0
  103. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/rename_file_item.py +0 -0
  104. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/replace_content_body.py +0 -0
  105. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/replace_file_content_item.py +0 -0
  106. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/run_code_request.py +0 -0
  107. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/run_command_request.py +0 -0
  108. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/run_command_request_envs.py +0 -0
  109. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/run_in_session_request.py +0 -0
  110. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/server_stream_event.py +0 -0
  111. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/server_stream_event_error.py +0 -0
  112. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/server_stream_event_results.py +0 -0
  113. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/server_stream_event_type.py +0 -0
  114. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/upload_file_body.py +0 -0
  115. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/py.typed +0 -0
  116. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/types.py +0 -0
  117. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/__init__.py +0 -0
  118. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/api/__init__.py +0 -0
  119. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/api/sandboxes/__init__.py +0 -0
  120. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/api/sandboxes/delete_sandboxes_sandbox_id.py +0 -0
  121. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/api/sandboxes/get_sandboxes.py +0 -0
  122. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/api/sandboxes/get_sandboxes_sandbox_id.py +0 -0
  123. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/api/sandboxes/get_sandboxes_sandbox_id_endpoints_port.py +0 -0
  124. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/api/sandboxes/post_sandboxes.py +0 -0
  125. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/api/sandboxes/post_sandboxes_sandbox_id_pause.py +0 -0
  126. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/api/sandboxes/post_sandboxes_sandbox_id_renew_expiration.py +0 -0
  127. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/api/sandboxes/post_sandboxes_sandbox_id_resume.py +0 -0
  128. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/api/snapshots/__init__.py +0 -0
  129. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/api/snapshots/get_snapshots.py +0 -0
  130. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/api/snapshots/get_snapshots_snapshot_id.py +0 -0
  131. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/client.py +0 -0
  132. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/errors.py +0 -0
  133. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/__init__.py +0 -0
  134. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/create_sandbox_request.py +0 -0
  135. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/create_sandbox_request_env.py +0 -0
  136. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/create_sandbox_request_extensions.py +0 -0
  137. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/create_sandbox_request_metadata.py +0 -0
  138. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/create_sandbox_response.py +0 -0
  139. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/create_sandbox_response_metadata.py +0 -0
  140. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/create_snapshot_request.py +0 -0
  141. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/endpoint.py +0 -0
  142. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/endpoint_headers.py +0 -0
  143. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/error_response.py +0 -0
  144. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/host.py +0 -0
  145. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/image_spec.py +0 -0
  146. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/image_spec_auth.py +0 -0
  147. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/list_sandboxes_response.py +0 -0
  148. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/list_snapshots_response.py +0 -0
  149. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/network_policy.py +0 -0
  150. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/network_policy_default_action.py +0 -0
  151. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/network_rule.py +0 -0
  152. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/network_rule_action.py +0 -0
  153. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/ossfs.py +0 -0
  154. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/ossfs_version.py +0 -0
  155. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/pagination_info.py +0 -0
  156. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/platform_spec.py +0 -0
  157. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/platform_spec_arch.py +0 -0
  158. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/platform_spec_os.py +0 -0
  159. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/pvc.py +0 -0
  160. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/renew_sandbox_expiration_request.py +0 -0
  161. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/renew_sandbox_expiration_response.py +0 -0
  162. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/resource_limits.py +0 -0
  163. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/sandbox.py +0 -0
  164. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/sandbox_metadata.py +0 -0
  165. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/sandbox_status.py +0 -0
  166. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/snapshot.py +0 -0
  167. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/snapshot_status.py +0 -0
  168. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/volume.py +0 -0
  169. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/py.typed +0 -0
  170. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/types.py +0 -0
  171. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/config/__init__.py +0 -0
  172. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/config/connection.py +0 -0
  173. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/config/connection_sync.py +0 -0
  174. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/constants.py +0 -0
  175. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/exceptions/__init__.py +0 -0
  176. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/exceptions/sandbox.py +0 -0
  177. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/manager.py +0 -0
  178. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/models/__init__.py +0 -0
  179. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/models/execd.py +0 -0
  180. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/models/execd_sync.py +0 -0
  181. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/models/filesystem.py +0 -0
  182. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/models/sandboxes.py +0 -0
  183. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/pool_types.py +0 -0
  184. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/py.typed +0 -0
  185. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/sandbox.py +0 -0
  186. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/services/__init__.py +0 -0
  187. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/services/command.py +0 -0
  188. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/services/egress.py +0 -0
  189. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/services/filesystem.py +0 -0
  190. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/services/health.py +0 -0
  191. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/services/metrics.py +0 -0
  192. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/services/sandbox.py +0 -0
  193. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/__init__.py +0 -0
  194. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/adapters/__init__.py +0 -0
  195. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/adapters/command_adapter.py +0 -0
  196. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/adapters/converter/__init__.py +0 -0
  197. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/adapters/converter/execution_event_dispatcher.py +0 -0
  198. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/adapters/egress_adapter.py +0 -0
  199. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/adapters/factory.py +0 -0
  200. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/adapters/filesystem_adapter.py +0 -0
  201. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/adapters/health_adapter.py +0 -0
  202. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/adapters/metrics_adapter.py +0 -0
  203. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/adapters/sandboxes_adapter.py +0 -0
  204. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/manager.py +0 -0
  205. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/sandbox.py +0 -0
  206. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/services/__init__.py +0 -0
  207. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/services/command.py +0 -0
  208. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/services/egress.py +0 -0
  209. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/services/filesystem.py +0 -0
  210. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/services/health.py +0 -0
  211. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/services/metrics.py +0 -0
  212. {opensandbox-0.1.8.dev0 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/services/sandbox.py +0 -0
@@ -274,3 +274,5 @@ kubernetes/test/kind/gvisor/runsc
274
274
  kubernetes/test/kind/gvisor/containerd-shim-runsc-v1
275
275
  bin/
276
276
  obj/
277
+
278
+ .qoder/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opensandbox
3
- Version: 0.1.8.dev0
3
+ Version: 0.1.8.dev2
4
4
  Summary: OpenSandbox Python SDK - Secure, isolated execution environments
5
5
  Project-URL: Homepage, https://open-sandbox.ai
6
6
  Project-URL: Repository, https://github.com/alibaba/OpenSandbox
@@ -420,8 +420,9 @@ The store does not create or close Redis clients.
420
420
  ```python
421
421
  import redis
422
422
 
423
- from opensandbox import PoolCreationSpec, RedisPoolStateStore, SandboxPoolSync
423
+ from opensandbox import PoolCreationSpec, SandboxPoolSync
424
424
  from opensandbox.config import ConnectionConfigSync
425
+ from opensandbox.pool_redis import RedisPoolStateStore
425
426
 
426
427
  redis_client = redis.Redis.from_url(
427
428
  "redis://user:password@redis.example.com:6379/0",
@@ -453,9 +454,9 @@ Notes:
453
454
  lock owner and is not the pool identifier.
454
455
  - All nodes sharing one pool must use the same creation and warmup definition. If
455
456
  that definition changes, use a new `pool_name` or `key_prefix` and drain the old pool.
456
- - `resize(max_idle)` can be called from any node. The new idle target is stored in
457
- the shared state store and the current primary applies replenish or shrink work
458
- during reconcile.
457
+ - `resize(max_idle)` can be called from any node. The call returns after the new
458
+ idle target is stored in the shared state store; the current primary applies
459
+ replenish or shrink work during periodic reconcile.
459
460
  - Use `resize(0)` and wait for `snapshot().idle_count == 0` to drain a distributed
460
461
  idle buffer. `release_all_idle()` is only a best-effort cleanup pass in distributed
461
462
  mode because another primary may put new idle sandboxes concurrently unless the
@@ -187,8 +187,9 @@ The store does not create or close Redis clients.
187
187
  ```python
188
188
  import redis
189
189
 
190
- from opensandbox import PoolCreationSpec, RedisPoolStateStore, SandboxPoolSync
190
+ from opensandbox import PoolCreationSpec, SandboxPoolSync
191
191
  from opensandbox.config import ConnectionConfigSync
192
+ from opensandbox.pool_redis import RedisPoolStateStore
192
193
 
193
194
  redis_client = redis.Redis.from_url(
194
195
  "redis://user:password@redis.example.com:6379/0",
@@ -220,9 +221,9 @@ Notes:
220
221
  lock owner and is not the pool identifier.
221
222
  - All nodes sharing one pool must use the same creation and warmup definition. If
222
223
  that definition changes, use a new `pool_name` or `key_prefix` and drain the old pool.
223
- - `resize(max_idle)` can be called from any node. The new idle target is stored in
224
- the shared state store and the current primary applies replenish or shrink work
225
- during reconcile.
224
+ - `resize(max_idle)` can be called from any node. The call returns after the new
225
+ idle target is stored in the shared state store; the current primary applies
226
+ replenish or shrink work during periodic reconcile.
226
227
  - Use `resize(0)` and wait for `snapshot().idle_count == 0` to drain a distributed
227
228
  idle buffer. `release_all_idle()` is only a best-effort cleanup pass in distributed
228
229
  mode because another primary may put new idle sandboxes concurrently unless the
@@ -142,6 +142,7 @@ branch = true
142
142
 
143
143
  [dependency-groups]
144
144
  dev = [
145
+ "redis>=5.0,<6.0",
145
146
  "pytest>=7.0.0",
146
147
  "pytest-asyncio>=0.21.0",
147
148
  "pytest-cov>=4.0.0",
@@ -102,12 +102,10 @@ from importlib.metadata import version as _pkg_version
102
102
  from opensandbox.manager import SandboxManager
103
103
  from opensandbox.pool import (
104
104
  AcquirePolicy,
105
- AsyncRedisPoolStateStore,
106
105
  AsyncSandboxPool,
107
106
  InMemoryAsyncPoolStateStore,
108
107
  InMemoryPoolStateStore,
109
108
  PoolCreationSpec,
110
- RedisPoolStateStore,
111
109
  SandboxPool,
112
110
  SandboxPoolAsync,
113
111
  )
@@ -133,6 +131,4 @@ __all__ = [
133
131
  "PoolCreationSpec",
134
132
  "InMemoryAsyncPoolStateStore",
135
133
  "InMemoryPoolStateStore",
136
- "AsyncRedisPoolStateStore",
137
- "RedisPoolStateStore",
138
134
  ]
@@ -234,8 +234,11 @@ def _parse_error_body(body: Any) -> SandboxError | None:
234
234
  message=body,
235
235
  )
236
236
 
237
- # Extract code and message from dict
237
+ # FastAPI HTTPException bodies are commonly wrapped as {"detail": {"code": ..., "message": ...}}.
238
238
  if isinstance(body, dict):
239
+ if isinstance(body.get("detail"), dict):
240
+ body = body["detail"]
241
+
239
242
  code: str | None = body.get("code")
240
243
  message: str | None = body.get("message")
241
244
 
@@ -28,7 +28,7 @@ import logging
28
28
  from http import HTTPStatus
29
29
  from typing import Any, TypeVar
30
30
 
31
- from opensandbox.exceptions import SandboxApiException
31
+ from opensandbox.exceptions import SandboxApiException, SandboxError
32
32
 
33
33
  logger = logging.getLogger(__name__)
34
34
 
@@ -118,17 +118,27 @@ def handle_api_error(response_obj: Any, operation_name: str = "API call") -> Non
118
118
 
119
119
  if status_code >= 300:
120
120
  error_message = f"{operation_name} failed: HTTP {status_code}"
121
+ sandbox_error: SandboxError | None = None
121
122
 
122
123
  if hasattr(response_obj, "parsed") and response_obj.parsed is not None:
123
- if hasattr(response_obj.parsed, "message"):
124
- error_message = (
125
- f"{operation_name} failed: {response_obj.parsed.message}"
124
+ parsed = response_obj.parsed
125
+ parsed_code = getattr(parsed, "code", None)
126
+ parsed_message = getattr(parsed, "message", None)
127
+
128
+ if parsed_message:
129
+ error_message = f"{operation_name} failed: {parsed_message}"
130
+ elif parsed_code:
131
+ error_message = f"{operation_name} failed: {parsed_code}"
132
+
133
+ if parsed_code:
134
+ sandbox_error = SandboxError(
135
+ code=str(parsed_code),
136
+ message=str(parsed_message or ""),
126
137
  )
127
- elif hasattr(response_obj.parsed, "code"):
128
- error_message = f"{operation_name} failed: {response_obj.parsed.code}"
129
138
 
130
139
  raise SandboxApiException(
131
140
  message=error_message,
132
141
  status_code=status_code,
133
142
  request_id=request_id,
143
+ error=sandbox_error,
134
144
  )
@@ -91,8 +91,13 @@ def sync_detailed(
91
91
  ) -> Response[Any | ErrorResponse]:
92
92
  """Delete a snapshot
93
93
 
94
- Delete a persistent sandbox snapshot by id. Snapshots that are still being created cannot be
95
- deleted.
94
+ Delete a persistent sandbox snapshot by id. Snapshots that are still
95
+ being created cannot be deleted.
96
+
97
+ For Kubernetes-backed snapshots, deletion removes OpenSandbox metadata
98
+ and Kubernetes coordination resources, but does not guarantee removal
99
+ of pushed OCI images from the configured registry. Use registry
100
+ retention or garbage collection policies for image lifecycle cleanup.
96
101
 
97
102
  Args:
98
103
  snapshot_id (str):
@@ -123,8 +128,13 @@ def sync(
123
128
  ) -> Any | ErrorResponse | None:
124
129
  """Delete a snapshot
125
130
 
126
- Delete a persistent sandbox snapshot by id. Snapshots that are still being created cannot be
127
- deleted.
131
+ Delete a persistent sandbox snapshot by id. Snapshots that are still
132
+ being created cannot be deleted.
133
+
134
+ For Kubernetes-backed snapshots, deletion removes OpenSandbox metadata
135
+ and Kubernetes coordination resources, but does not guarantee removal
136
+ of pushed OCI images from the configured registry. Use registry
137
+ retention or garbage collection policies for image lifecycle cleanup.
128
138
 
129
139
  Args:
130
140
  snapshot_id (str):
@@ -150,8 +160,13 @@ async def asyncio_detailed(
150
160
  ) -> Response[Any | ErrorResponse]:
151
161
  """Delete a snapshot
152
162
 
153
- Delete a persistent sandbox snapshot by id. Snapshots that are still being created cannot be
154
- deleted.
163
+ Delete a persistent sandbox snapshot by id. Snapshots that are still
164
+ being created cannot be deleted.
165
+
166
+ For Kubernetes-backed snapshots, deletion removes OpenSandbox metadata
167
+ and Kubernetes coordination resources, but does not guarantee removal
168
+ of pushed OCI images from the configured registry. Use registry
169
+ retention or garbage collection policies for image lifecycle cleanup.
155
170
 
156
171
  Args:
157
172
  snapshot_id (str):
@@ -180,8 +195,13 @@ async def asyncio(
180
195
  ) -> Any | ErrorResponse | None:
181
196
  """Delete a snapshot
182
197
 
183
- Delete a persistent sandbox snapshot by id. Snapshots that are still being created cannot be
184
- deleted.
198
+ Delete a persistent sandbox snapshot by id. Snapshots that are still
199
+ being created cannot be deleted.
200
+
201
+ For Kubernetes-backed snapshots, deletion removes OpenSandbox metadata
202
+ and Kubernetes coordination resources, but does not guarantee removal
203
+ of pushed OCI images from the configured registry. Use registry
204
+ retention or garbage collection policies for image lifecycle cleanup.
185
205
 
186
206
  Args:
187
207
  snapshot_id (str):
@@ -115,8 +115,10 @@ def sync_detailed(
115
115
  """Create a snapshot from a sandbox
116
116
 
117
117
  Create a persistent point-in-time snapshot from the sandbox's current state.
118
- The returned snapshot id identifies the created artifact. Snapshot creation may
119
- temporarily pause the sandbox while the runtime captures provider-supported state.
118
+ The source sandbox must be `Running`. The returned snapshot id identifies
119
+ the created artifact. Snapshot creation may temporarily pause the sandbox
120
+ while the runtime captures provider-supported state, then the source
121
+ sandbox continues running.
120
122
 
121
123
  Args:
122
124
  sandbox_id (str):
@@ -151,8 +153,10 @@ def sync(
151
153
  """Create a snapshot from a sandbox
152
154
 
153
155
  Create a persistent point-in-time snapshot from the sandbox's current state.
154
- The returned snapshot id identifies the created artifact. Snapshot creation may
155
- temporarily pause the sandbox while the runtime captures provider-supported state.
156
+ The source sandbox must be `Running`. The returned snapshot id identifies
157
+ the created artifact. Snapshot creation may temporarily pause the sandbox
158
+ while the runtime captures provider-supported state, then the source
159
+ sandbox continues running.
156
160
 
157
161
  Args:
158
162
  sandbox_id (str):
@@ -182,8 +186,10 @@ async def asyncio_detailed(
182
186
  """Create a snapshot from a sandbox
183
187
 
184
188
  Create a persistent point-in-time snapshot from the sandbox's current state.
185
- The returned snapshot id identifies the created artifact. Snapshot creation may
186
- temporarily pause the sandbox while the runtime captures provider-supported state.
189
+ The source sandbox must be `Running`. The returned snapshot id identifies
190
+ the created artifact. Snapshot creation may temporarily pause the sandbox
191
+ while the runtime captures provider-supported state, then the source
192
+ sandbox continues running.
187
193
 
188
194
  Args:
189
195
  sandbox_id (str):
@@ -216,8 +222,10 @@ async def asyncio(
216
222
  """Create a snapshot from a sandbox
217
223
 
218
224
  Create a persistent point-in-time snapshot from the sandbox's current state.
219
- The returned snapshot id identifies the created artifact. Snapshot creation may
220
- temporarily pause the sandbox while the runtime captures provider-supported state.
225
+ The source sandbox must be `Running`. The returned snapshot id identifies
226
+ the created artifact. Snapshot creation may temporarily pause the sandbox
227
+ while the runtime captures provider-supported state, then the source
228
+ sandbox continues running.
221
229
 
222
230
  Args:
223
231
  sandbox_id (str):
@@ -20,17 +20,26 @@ from __future__ import annotations
20
20
  import base64
21
21
  from collections.abc import Awaitable, Callable
22
22
  from datetime import datetime, timedelta, timezone
23
- from typing import Any, TypeVar
23
+ from typing import Any, TypeVar, cast
24
24
 
25
25
  from opensandbox.exceptions import PoolStateStoreUnavailableException
26
26
  from opensandbox.pool_types import IdleEntry, StoreCounters
27
27
  from opensandbox.redis_pool_store import (
28
+ _REQUIRED_REDIS_METHODS,
29
+ Redis,
28
30
  RedisPoolStateStore,
29
31
  _decode,
30
32
  _millis,
31
33
  _validate_owner_and_ttl,
32
34
  )
33
35
 
36
+ try:
37
+ from redis.asyncio import Redis as AsyncRedis
38
+ except ImportError as exc: # pragma: no cover
39
+ raise ImportError(
40
+ 'Install opensandbox[pool-redis] to use opensandbox.pool_redis'
41
+ ) from exc
42
+
34
43
  T = TypeVar("T")
35
44
 
36
45
 
@@ -39,7 +48,8 @@ class AsyncRedisPoolStateStore:
39
48
 
40
49
  DEFAULT_KEY_PREFIX = RedisPoolStateStore.DEFAULT_KEY_PREFIX
41
50
 
42
- def __init__(self, redis: Any, key_prefix: str = DEFAULT_KEY_PREFIX) -> None:
51
+ def __init__(self, redis: AsyncRedis, key_prefix: str = DEFAULT_KEY_PREFIX) -> None:
52
+ _validate_async_redis_client(redis)
43
53
  self._redis = redis
44
54
  self._key_prefix = key_prefix
45
55
  self._default_idle_ttl = timedelta(hours=24)
@@ -48,11 +58,14 @@ class AsyncRedisPoolStateStore:
48
58
  result = await self._execute(
49
59
  "try_take_idle",
50
60
  pool_name,
51
- lambda: self._redis.eval(
52
- RedisPoolStateStore._TAKE_IDLE_SCRIPT,
53
- 2,
54
- self._idle_list_key(pool_name),
55
- self._idle_expires_key(pool_name),
61
+ lambda: cast(
62
+ Awaitable[Any],
63
+ self._redis.eval(
64
+ RedisPoolStateStore._TAKE_IDLE_SCRIPT,
65
+ 2,
66
+ self._idle_list_key(pool_name),
67
+ self._idle_expires_key(pool_name),
68
+ ),
56
69
  ),
57
70
  )
58
71
  return _decode(result) if result is not None else None
@@ -64,13 +77,16 @@ class AsyncRedisPoolStateStore:
64
77
  await self._execute(
65
78
  "put_idle",
66
79
  pool_name,
67
- lambda: self._redis.eval(
68
- RedisPoolStateStore._PUT_IDLE_SCRIPT,
69
- 2,
70
- self._idle_list_key(pool_name),
71
- self._idle_expires_key(pool_name),
72
- sandbox_id,
73
- str(ttl_millis),
80
+ lambda: cast(
81
+ Awaitable[Any],
82
+ self._redis.eval(
83
+ RedisPoolStateStore._PUT_IDLE_SCRIPT,
84
+ 2,
85
+ self._idle_list_key(pool_name),
86
+ self._idle_expires_key(pool_name),
87
+ sandbox_id,
88
+ str(ttl_millis),
89
+ ),
74
90
  ),
75
91
  )
76
92
 
@@ -79,8 +95,14 @@ class AsyncRedisPoolStateStore:
79
95
  "remove_idle",
80
96
  pool_name,
81
97
  lambda: _gather_tuple(
82
- self._redis.hdel(self._idle_expires_key(pool_name), sandbox_id),
83
- self._redis.lrem(self._idle_list_key(pool_name), 0, sandbox_id),
98
+ cast(
99
+ Awaitable[Any],
100
+ self._redis.hdel(self._idle_expires_key(pool_name), sandbox_id),
101
+ ),
102
+ cast(
103
+ Awaitable[Any],
104
+ self._redis.lrem(self._idle_list_key(pool_name), 0, sandbox_id),
105
+ ),
84
106
  ),
85
107
  )
86
108
 
@@ -109,12 +131,15 @@ class AsyncRedisPoolStateStore:
109
131
  result = await self._execute(
110
132
  "renew_primary_lock",
111
133
  pool_name,
112
- lambda: self._redis.eval(
113
- RedisPoolStateStore._RENEW_LOCK_SCRIPT,
114
- 1,
115
- self._primary_lock_key(pool_name),
116
- owner_id,
117
- str(max(1, _millis(ttl))),
134
+ lambda: cast(
135
+ Awaitable[Any],
136
+ self._redis.eval(
137
+ RedisPoolStateStore._RENEW_LOCK_SCRIPT,
138
+ 1,
139
+ self._primary_lock_key(pool_name),
140
+ owner_id,
141
+ str(max(1, _millis(ttl))),
142
+ ),
118
143
  ),
119
144
  )
120
145
  return result == 1 or result == b"1"
@@ -123,11 +148,14 @@ class AsyncRedisPoolStateStore:
123
148
  await self._execute(
124
149
  "release_primary_lock",
125
150
  pool_name,
126
- lambda: self._redis.eval(
127
- RedisPoolStateStore._RELEASE_LOCK_SCRIPT,
128
- 1,
129
- self._primary_lock_key(pool_name),
130
- owner_id,
151
+ lambda: cast(
152
+ Awaitable[Any],
153
+ self._redis.eval(
154
+ RedisPoolStateStore._RELEASE_LOCK_SCRIPT,
155
+ 1,
156
+ self._primary_lock_key(pool_name),
157
+ owner_id,
158
+ ),
131
159
  ),
132
160
  )
133
161
 
@@ -135,35 +163,52 @@ class AsyncRedisPoolStateStore:
135
163
  await self._execute(
136
164
  "reap_expired_idle",
137
165
  pool_name,
138
- lambda: self._redis.eval(
139
- RedisPoolStateStore._REAP_EXPIRED_SCRIPT,
140
- 2,
141
- self._idle_list_key(pool_name),
142
- self._idle_expires_key(pool_name),
166
+ lambda: cast(
167
+ Awaitable[Any],
168
+ self._redis.eval(
169
+ RedisPoolStateStore._REAP_EXPIRED_SCRIPT,
170
+ 2,
171
+ self._idle_list_key(pool_name),
172
+ self._idle_expires_key(pool_name),
173
+ ),
143
174
  ),
144
175
  )
145
176
 
146
177
  async def snapshot_counters(self, pool_name: str) -> StoreCounters:
147
178
  async def op() -> StoreCounters:
148
- await self.reap_expired_idle(pool_name, datetime.now(timezone.utc))
179
+ idle_count = cast(
180
+ int,
181
+ await cast(
182
+ Awaitable[Any],
183
+ self._redis.hlen(self._idle_expires_key(pool_name)),
184
+ ),
185
+ )
149
186
  return StoreCounters(
150
- idle_count=int(await self._redis.hlen(self._idle_expires_key(pool_name)))
187
+ idle_count=idle_count
151
188
  )
152
189
 
153
190
  return await self._execute("snapshot_counters", pool_name, op)
154
191
 
155
192
  async def snapshot_idle_entries(self, pool_name: str) -> list[IdleEntry]:
156
193
  async def op() -> list[IdleEntry]:
157
- await self.reap_expired_idle(pool_name, datetime.now(timezone.utc))
158
- ids = [
159
- _decode(v)
160
- for v in await self._redis.lrange(self._idle_list_key(pool_name), 0, -1)
161
- ]
194
+ raw_ids = cast(
195
+ list[Any],
196
+ await cast(
197
+ Awaitable[Any],
198
+ self._redis.lrange(self._idle_list_key(pool_name), 0, -1),
199
+ ),
200
+ )
201
+ ids = [_decode(v) for v in raw_ids]
202
+ raw_expires_by_id = cast(
203
+ dict[Any, Any],
204
+ await cast(
205
+ Awaitable[Any],
206
+ self._redis.hgetall(self._idle_expires_key(pool_name)),
207
+ ),
208
+ )
162
209
  expires_by_id = {
163
210
  _decode(k): _decode(v)
164
- for k, v in (
165
- await self._redis.hgetall(self._idle_expires_key(pool_name))
166
- ).items()
211
+ for k, v in raw_expires_by_id.items()
167
212
  }
168
213
  entries: list[IdleEntry] = []
169
214
  for sandbox_id in ids:
@@ -267,3 +312,17 @@ async def _gather_tuple(*awaitables: Awaitable[Any]) -> tuple[Any, ...]:
267
312
  import asyncio
268
313
 
269
314
  return tuple(await asyncio.gather(*awaitables))
315
+
316
+
317
+ def _validate_async_redis_client(redis: Any) -> None:
318
+ if isinstance(redis, Redis) or not isinstance(redis, AsyncRedis):
319
+ raise TypeError(
320
+ "AsyncRedisPoolStateStore requires a redis.asyncio.Redis client; "
321
+ "use RedisPoolStateStore for redis.Redis"
322
+ )
323
+ for method_name in _REQUIRED_REDIS_METHODS:
324
+ method = getattr(redis, method_name, None)
325
+ if not callable(method):
326
+ raise TypeError(
327
+ f"AsyncRedisPoolStateStore requires a Redis client with callable {method_name}()"
328
+ )
@@ -17,7 +17,6 @@
17
17
 
18
18
  from opensandbox._async_pool_store import InMemoryAsyncPoolStateStore
19
19
  from opensandbox._pool_store import InMemoryPoolStateStore
20
- from opensandbox.async_redis_pool_store import AsyncRedisPoolStateStore
21
20
  from opensandbox.pool_async import AsyncSandboxPool, SandboxPoolAsync
22
21
  from opensandbox.pool_types import (
23
22
  AcquirePolicy,
@@ -32,7 +31,6 @@ from opensandbox.pool_types import (
32
31
  PoolStateStore,
33
32
  StoreCounters,
34
33
  )
35
- from opensandbox.redis_pool_store import RedisPoolStateStore
36
34
  from opensandbox.sync.pool import SandboxPoolSync
37
35
 
38
36
  SandboxPool = SandboxPoolSync
@@ -41,7 +39,6 @@ __all__ = [
41
39
  "AcquirePolicy",
42
40
  "AsyncPoolConfig",
43
41
  "AsyncPoolStateStore",
44
- "AsyncRedisPoolStateStore",
45
42
  "AsyncSandboxPool",
46
43
  "IdleEntry",
47
44
  "InMemoryAsyncPoolStateStore",
@@ -52,7 +49,6 @@ __all__ = [
52
49
  "PoolSnapshot",
53
50
  "PoolState",
54
51
  "PoolStateStore",
55
- "RedisPoolStateStore",
56
52
  "SandboxPoolAsync",
57
53
  "SandboxPool",
58
54
  "SandboxPoolSync",
@@ -135,10 +135,9 @@ class SandboxPoolAsync:
135
135
  await self._state_store.set_idle_entry_ttl(
136
136
  self._config.pool_name, self._config.idle_timeout
137
137
  )
138
- if await self._state_store.get_max_idle(self._config.pool_name) is None:
139
- await self._state_store.set_max_idle(
140
- self._config.pool_name, self._config.max_idle
141
- )
138
+ await self._state_store.set_max_idle(
139
+ self._config.pool_name, self._config.max_idle
140
+ )
142
141
  stop_event = asyncio.Event()
143
142
  self._stop_event = stop_event
144
143
  self._lifecycle_state = PoolLifecycleState.RUNNING
@@ -220,11 +219,6 @@ class SandboxPoolAsync:
220
219
  raise ValueError("max_idle must be >= 0")
221
220
  await self._state_store.set_max_idle(self._config.pool_name, max_idle)
222
221
  self._current_max_idle = max_idle
223
- if self._lifecycle_state == PoolLifecycleState.RUNNING:
224
- asyncio.create_task(
225
- self._run_reconcile_tick(),
226
- name=f"sandbox-pool-resize-{self._config.pool_name}",
227
- )
228
222
 
229
223
  async def release_all_idle(self) -> int:
230
224
  pool_name = self._config.pool_name
@@ -434,7 +428,14 @@ class SandboxPoolAsync:
434
428
  skip_health_check=self._config.acquire_skip_health_check,
435
429
  )
436
430
  if sandbox_timeout is not None:
437
- await sandbox.renew(sandbox_timeout)
431
+ try:
432
+ await sandbox.renew(sandbox_timeout)
433
+ except BaseException:
434
+ try:
435
+ await sandbox.kill()
436
+ finally:
437
+ await sandbox.close()
438
+ raise
438
439
  return sandbox
439
440
 
440
441
  async def _resolve_max_idle(self) -> int:
@@ -0,0 +1,27 @@
1
+ #
2
+ # Copyright 2025 Alibaba Group Holding Ltd.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+ """Redis-backed sandbox pool state stores.
17
+
18
+ Install ``opensandbox[pool-redis]`` and pass a caller-managed redis-py client.
19
+ """
20
+
21
+ from opensandbox.async_redis_pool_store import AsyncRedisPoolStateStore
22
+ from opensandbox.redis_pool_store import RedisPoolStateStore
23
+
24
+ __all__ = [
25
+ "AsyncRedisPoolStateStore",
26
+ "RedisPoolStateStore",
27
+ ]