opensandbox 0.1.8.dev1__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.dev1 → opensandbox-0.1.8.dev2}/.gitignore +2 -0
  2. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/PKG-INFO +3 -2
  3. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/README.md +2 -1
  4. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/pyproject.toml +1 -0
  5. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/__init__.py +0 -4
  6. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/api/snapshots/delete_snapshots_snapshot_id.py +28 -8
  7. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/api/snapshots/post_sandboxes_sandbox_id_snapshots.py +16 -8
  8. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/async_redis_pool_store.py +101 -42
  9. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/pool.py +0 -4
  10. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/pool_async.py +8 -1
  11. opensandbox-0.1.8.dev2/src/opensandbox/pool_redis.py +27 -0
  12. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/redis_pool_store.py +47 -7
  13. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/pool.py +8 -1
  14. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/LICENSE +0 -0
  15. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/_async_pool_reconciler.py +0 -0
  16. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/_async_pool_store.py +0 -0
  17. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/_pool_reconciler.py +0 -0
  18. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/_pool_store.py +0 -0
  19. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/__init__.py +0 -0
  20. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/command_adapter.py +0 -0
  21. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/converter/__init__.py +0 -0
  22. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/converter/command_model_converter.py +0 -0
  23. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/converter/event_node.py +0 -0
  24. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/converter/exception_converter.py +0 -0
  25. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/converter/execution_converter.py +0 -0
  26. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/converter/execution_event_dispatcher.py +0 -0
  27. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/converter/filesystem_model_converter.py +0 -0
  28. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/converter/metrics_model_converter.py +0 -0
  29. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/converter/response_handler.py +0 -0
  30. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/converter/sandbox_model_converter.py +0 -0
  31. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/egress_adapter.py +0 -0
  32. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/factory.py +0 -0
  33. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/filesystem_adapter.py +0 -0
  34. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/health_adapter.py +0 -0
  35. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/metrics_adapter.py +0 -0
  36. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/adapters/sandboxes_adapter.py +0 -0
  37. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/__init__.py +0 -0
  38. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/egress/__init__.py +0 -0
  39. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/egress/api/__init__.py +0 -0
  40. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/egress/api/policy/__init__.py +0 -0
  41. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/egress/api/policy/get_policy.py +0 -0
  42. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/egress/api/policy/patch_policy.py +0 -0
  43. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/egress/client.py +0 -0
  44. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/egress/errors.py +0 -0
  45. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/egress/models/__init__.py +0 -0
  46. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/egress/models/network_policy.py +0 -0
  47. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/egress/models/network_policy_default_action.py +0 -0
  48. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/egress/models/network_rule.py +0 -0
  49. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/egress/models/network_rule_action.py +0 -0
  50. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/egress/models/policy_status_response.py +0 -0
  51. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/egress/py.typed +0 -0
  52. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/egress/types.py +0 -0
  53. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/__init__.py +0 -0
  54. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/__init__.py +0 -0
  55. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/code_interpreting/__init__.py +0 -0
  56. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/code_interpreting/create_code_context.py +0 -0
  57. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/code_interpreting/delete_context.py +0 -0
  58. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/code_interpreting/delete_contexts_by_language.py +0 -0
  59. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/code_interpreting/get_context.py +0 -0
  60. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/code_interpreting/interrupt_code.py +0 -0
  61. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/code_interpreting/list_contexts.py +0 -0
  62. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/code_interpreting/run_code.py +0 -0
  63. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/command/__init__.py +0 -0
  64. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/command/create_session.py +0 -0
  65. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/command/delete_session.py +0 -0
  66. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/command/get_background_command_logs.py +0 -0
  67. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/command/get_command_status.py +0 -0
  68. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/command/interrupt_command.py +0 -0
  69. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/command/run_command.py +0 -0
  70. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/command/run_in_session.py +0 -0
  71. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/filesystem/__init__.py +0 -0
  72. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/filesystem/chmod_files.py +0 -0
  73. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/filesystem/download_file.py +0 -0
  74. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/filesystem/get_files_info.py +0 -0
  75. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/filesystem/make_dirs.py +0 -0
  76. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/filesystem/remove_dirs.py +0 -0
  77. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/filesystem/remove_files.py +0 -0
  78. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/filesystem/rename_files.py +0 -0
  79. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/filesystem/replace_content.py +0 -0
  80. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/filesystem/search_files.py +0 -0
  81. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/filesystem/upload_file.py +0 -0
  82. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/health/__init__.py +0 -0
  83. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/health/ping.py +0 -0
  84. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/metric/__init__.py +0 -0
  85. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/metric/get_metrics.py +0 -0
  86. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/api/metric/watch_metrics.py +0 -0
  87. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/client.py +0 -0
  88. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/errors.py +0 -0
  89. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/__init__.py +0 -0
  90. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/chmod_files_body.py +0 -0
  91. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/code_context.py +0 -0
  92. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/code_context_request.py +0 -0
  93. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/command_status_response.py +0 -0
  94. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/create_session_request.py +0 -0
  95. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/create_session_response.py +0 -0
  96. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/error_response.py +0 -0
  97. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/file_info.py +0 -0
  98. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/file_metadata.py +0 -0
  99. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/get_files_info_response_200.py +0 -0
  100. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/make_dirs_body.py +0 -0
  101. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/metrics.py +0 -0
  102. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/permission.py +0 -0
  103. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/rename_file_item.py +0 -0
  104. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/replace_content_body.py +0 -0
  105. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/replace_file_content_item.py +0 -0
  106. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/run_code_request.py +0 -0
  107. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/run_command_request.py +0 -0
  108. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/run_command_request_envs.py +0 -0
  109. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/run_in_session_request.py +0 -0
  110. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/server_stream_event.py +0 -0
  111. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/server_stream_event_error.py +0 -0
  112. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/server_stream_event_results.py +0 -0
  113. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/server_stream_event_type.py +0 -0
  114. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/models/upload_file_body.py +0 -0
  115. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/py.typed +0 -0
  116. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/execd/types.py +0 -0
  117. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/__init__.py +0 -0
  118. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/api/__init__.py +0 -0
  119. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/api/sandboxes/__init__.py +0 -0
  120. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/api/sandboxes/delete_sandboxes_sandbox_id.py +0 -0
  121. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/api/sandboxes/get_sandboxes.py +0 -0
  122. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/api/sandboxes/get_sandboxes_sandbox_id.py +0 -0
  123. {opensandbox-0.1.8.dev1 → 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.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/api/sandboxes/post_sandboxes.py +0 -0
  125. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/api/sandboxes/post_sandboxes_sandbox_id_pause.py +0 -0
  126. {opensandbox-0.1.8.dev1 → 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.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/api/sandboxes/post_sandboxes_sandbox_id_resume.py +0 -0
  128. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/api/snapshots/__init__.py +0 -0
  129. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/api/snapshots/get_snapshots.py +0 -0
  130. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/api/snapshots/get_snapshots_snapshot_id.py +0 -0
  131. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/client.py +0 -0
  132. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/errors.py +0 -0
  133. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/__init__.py +0 -0
  134. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/create_sandbox_request.py +0 -0
  135. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/create_sandbox_request_env.py +0 -0
  136. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/create_sandbox_request_extensions.py +0 -0
  137. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/create_sandbox_request_metadata.py +0 -0
  138. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/create_sandbox_response.py +0 -0
  139. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/create_sandbox_response_metadata.py +0 -0
  140. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/create_snapshot_request.py +0 -0
  141. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/endpoint.py +0 -0
  142. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/endpoint_headers.py +0 -0
  143. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/error_response.py +0 -0
  144. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/host.py +0 -0
  145. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/image_spec.py +0 -0
  146. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/image_spec_auth.py +0 -0
  147. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/list_sandboxes_response.py +0 -0
  148. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/list_snapshots_response.py +0 -0
  149. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/network_policy.py +0 -0
  150. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/network_policy_default_action.py +0 -0
  151. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/network_rule.py +0 -0
  152. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/network_rule_action.py +0 -0
  153. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/ossfs.py +0 -0
  154. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/ossfs_version.py +0 -0
  155. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/pagination_info.py +0 -0
  156. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/platform_spec.py +0 -0
  157. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/platform_spec_arch.py +0 -0
  158. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/platform_spec_os.py +0 -0
  159. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/pvc.py +0 -0
  160. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/renew_sandbox_expiration_request.py +0 -0
  161. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/renew_sandbox_expiration_response.py +0 -0
  162. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/resource_limits.py +0 -0
  163. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/sandbox.py +0 -0
  164. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/sandbox_metadata.py +0 -0
  165. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/sandbox_status.py +0 -0
  166. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/snapshot.py +0 -0
  167. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/snapshot_status.py +0 -0
  168. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/models/volume.py +0 -0
  169. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/py.typed +0 -0
  170. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/api/lifecycle/types.py +0 -0
  171. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/config/__init__.py +0 -0
  172. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/config/connection.py +0 -0
  173. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/config/connection_sync.py +0 -0
  174. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/constants.py +0 -0
  175. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/exceptions/__init__.py +0 -0
  176. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/exceptions/sandbox.py +0 -0
  177. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/manager.py +0 -0
  178. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/models/__init__.py +0 -0
  179. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/models/execd.py +0 -0
  180. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/models/execd_sync.py +0 -0
  181. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/models/filesystem.py +0 -0
  182. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/models/sandboxes.py +0 -0
  183. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/pool_types.py +0 -0
  184. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/py.typed +0 -0
  185. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/sandbox.py +0 -0
  186. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/services/__init__.py +0 -0
  187. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/services/command.py +0 -0
  188. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/services/egress.py +0 -0
  189. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/services/filesystem.py +0 -0
  190. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/services/health.py +0 -0
  191. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/services/metrics.py +0 -0
  192. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/services/sandbox.py +0 -0
  193. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/__init__.py +0 -0
  194. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/adapters/__init__.py +0 -0
  195. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/adapters/command_adapter.py +0 -0
  196. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/adapters/converter/__init__.py +0 -0
  197. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/adapters/converter/execution_event_dispatcher.py +0 -0
  198. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/adapters/egress_adapter.py +0 -0
  199. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/adapters/factory.py +0 -0
  200. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/adapters/filesystem_adapter.py +0 -0
  201. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/adapters/health_adapter.py +0 -0
  202. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/adapters/metrics_adapter.py +0 -0
  203. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/adapters/sandboxes_adapter.py +0 -0
  204. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/manager.py +0 -0
  205. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/sandbox.py +0 -0
  206. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/services/__init__.py +0 -0
  207. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/services/command.py +0 -0
  208. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/services/egress.py +0 -0
  209. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/services/filesystem.py +0 -0
  210. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/services/health.py +0 -0
  211. {opensandbox-0.1.8.dev1 → opensandbox-0.1.8.dev2}/src/opensandbox/sync/services/metrics.py +0 -0
  212. {opensandbox-0.1.8.dev1 → 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.dev1
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",
@@ -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",
@@ -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
  ]
@@ -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",
@@ -428,7 +428,14 @@ class SandboxPoolAsync:
428
428
  skip_health_check=self._config.acquire_skip_health_check,
429
429
  )
430
430
  if sandbox_timeout is not None:
431
- 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
432
439
  return sandbox
433
440
 
434
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
+ ]
@@ -19,11 +19,29 @@ from __future__ import annotations
19
19
 
20
20
  import base64
21
21
  from datetime import datetime, timedelta, timezone
22
- from typing import Any
22
+ from typing import Any, cast
23
23
 
24
24
  from opensandbox.exceptions import PoolStateStoreUnavailableException
25
25
  from opensandbox.pool_types import IdleEntry, StoreCounters
26
26
 
27
+ try:
28
+ from redis import Redis
29
+ except ImportError as exc: # pragma: no cover
30
+ raise ImportError(
31
+ 'Install opensandbox[pool-redis] to use opensandbox.pool_redis'
32
+ ) from exc
33
+
34
+ _REQUIRED_REDIS_METHODS = (
35
+ "eval",
36
+ "get",
37
+ "set",
38
+ "hdel",
39
+ "lrem",
40
+ "hlen",
41
+ "lrange",
42
+ "hgetall",
43
+ )
44
+
27
45
 
28
46
  class RedisPoolStateStore:
29
47
  """Distributed pool store backed by a caller-managed Redis client."""
@@ -91,7 +109,8 @@ end
91
109
  return 1
92
110
  """
93
111
 
94
- def __init__(self, redis: Any, key_prefix: str = DEFAULT_KEY_PREFIX) -> None:
112
+ def __init__(self, redis: Redis, key_prefix: str = DEFAULT_KEY_PREFIX) -> None:
113
+ _validate_sync_redis_client(redis)
95
114
  self._redis = redis
96
115
  self._key_prefix = key_prefix
97
116
  self._default_idle_ttl = timedelta(hours=24)
@@ -195,18 +214,25 @@ return 1
195
214
 
196
215
  def snapshot_counters(self, pool_name: str) -> StoreCounters:
197
216
  def op() -> StoreCounters:
198
- self.reap_expired_idle(pool_name, datetime.now(timezone.utc))
199
- return StoreCounters(idle_count=int(self._redis.hlen(self._idle_expires_key(pool_name))))
217
+ idle_count = cast(int, self._redis.hlen(self._idle_expires_key(pool_name)))
218
+ return StoreCounters(idle_count=idle_count)
200
219
 
201
220
  return self._execute("snapshot_counters", pool_name, op)
202
221
 
203
222
  def snapshot_idle_entries(self, pool_name: str) -> list[IdleEntry]:
204
223
  def op() -> list[IdleEntry]:
205
- self.reap_expired_idle(pool_name, datetime.now(timezone.utc))
206
- ids = [_decode(v) for v in self._redis.lrange(self._idle_list_key(pool_name), 0, -1)]
224
+ raw_ids = cast(
225
+ list[Any],
226
+ self._redis.lrange(self._idle_list_key(pool_name), 0, -1),
227
+ )
228
+ ids = [_decode(v) for v in raw_ids]
229
+ raw_expires_by_id = cast(
230
+ dict[Any, Any],
231
+ self._redis.hgetall(self._idle_expires_key(pool_name)),
232
+ )
207
233
  expires_by_id = {
208
234
  _decode(k): _decode(v)
209
- for k, v in self._redis.hgetall(self._idle_expires_key(pool_name)).items()
235
+ for k, v in raw_expires_by_id.items()
210
236
  }
211
237
  entries: list[IdleEntry] = []
212
238
  for sandbox_id in ids:
@@ -317,3 +343,17 @@ def _validate_owner_and_ttl(owner_id: str, ttl: timedelta) -> None:
317
343
  raise ValueError("owner_id must not be blank")
318
344
  if ttl.total_seconds() <= 0:
319
345
  raise ValueError("ttl must be positive")
346
+
347
+
348
+ def _validate_sync_redis_client(redis: Any) -> None:
349
+ if not isinstance(redis, Redis):
350
+ raise TypeError(
351
+ "RedisPoolStateStore requires a redis.Redis client; "
352
+ "use AsyncRedisPoolStateStore for redis.asyncio.Redis"
353
+ )
354
+ for method_name in _REQUIRED_REDIS_METHODS:
355
+ method = getattr(redis, method_name, None)
356
+ if not callable(method):
357
+ raise TypeError(
358
+ f"RedisPoolStateStore requires a Redis client with callable {method_name}()"
359
+ )
@@ -410,7 +410,14 @@ class SandboxPoolSync:
410
410
  skip_health_check=self._config.acquire_skip_health_check,
411
411
  )
412
412
  if sandbox_timeout is not None:
413
- sandbox.renew(sandbox_timeout)
413
+ try:
414
+ sandbox.renew(sandbox_timeout)
415
+ except BaseException:
416
+ try:
417
+ sandbox.kill()
418
+ finally:
419
+ sandbox.close()
420
+ raise
414
421
  return sandbox
415
422
 
416
423
  def _resolve_max_idle(self) -> int: