chalkcompute 2.13.2__tar.gz → 2.13.4__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 (204) hide show
  1. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/PKG-INFO +89 -11
  2. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/README.md +88 -10
  3. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/__init__.py +2 -0
  4. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/__init__.pyi +4 -0
  5. chalkcompute-2.13.4/chalkcompute/_async_utils.py +41 -0
  6. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_client_cache.py +2 -2
  7. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_containerspec.py +114 -17
  8. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_function.py +4 -58
  9. chalkcompute-2.13.4/chalkcompute/_gather.py +55 -0
  10. chalkcompute-2.13.4/chalkcompute/_git.py +164 -0
  11. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_image.py +272 -1
  12. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_sandbox.py +10 -6
  13. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_tracing.py +5 -3
  14. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/pyproject.toml +1 -1
  15. chalkcompute-2.13.2/chalkcompute/_git.py +0 -86
  16. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_arrow_schema.py +0 -0
  17. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_arrow_serde.py +0 -0
  18. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_artifact.py +0 -0
  19. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_auth_config.py +0 -0
  20. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_call_context.py +0 -0
  21. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_cls.py +0 -0
  22. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_concurrency.py +0 -0
  23. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_connect.py +0 -0
  24. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_container.py +0 -0
  25. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_dataset.py +0 -0
  26. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_deployment_proto.py +0 -0
  27. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_env.py +0 -0
  28. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_evaluation.py +0 -0
  29. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_function_deployment.py +0 -0
  30. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_function_error.py +0 -0
  31. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/__init__.py +0 -0
  32. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/buf/__init__.py +0 -0
  33. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/buf/validate/__init__.py +0 -0
  34. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/buf/validate/validate_pb.py +0 -0
  35. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/__init__.py +0 -0
  36. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/argo/__init__.py +0 -0
  37. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/argo/v1/__init__.py +0 -0
  38. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/argo/v1/workflow_pb.py +0 -0
  39. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/arrow/__init__.py +0 -0
  40. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/arrow/v1/__init__.py +0 -0
  41. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/arrow/v1/arrow_pb.py +0 -0
  42. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/artifactstore/__init__.py +0 -0
  43. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/artifactstore/v1/__init__.py +0 -0
  44. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/artifactstore/v1/service_connect.py +0 -0
  45. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/artifactstore/v1/service_pb.py +0 -0
  46. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/auth/__init__.py +0 -0
  47. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/auth/v1/__init__.py +0 -0
  48. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/auth/v1/agent_pb.py +0 -0
  49. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/auth/v1/audit_pb.py +0 -0
  50. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/auth/v1/displayagent_pb.py +0 -0
  51. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/auth/v1/featurepermission_pb.py +0 -0
  52. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/auth/v1/permissions_pb.py +0 -0
  53. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/chart/__init__.py +0 -0
  54. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/chart/v1/__init__.py +0 -0
  55. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/chart/v1/densetimeserieschart_pb.py +0 -0
  56. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/common/__init__.py +0 -0
  57. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/common/v1/__init__.py +0 -0
  58. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/common/v1/chalk_error_pb.py +0 -0
  59. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/common/v1/column_profile_pb.py +0 -0
  60. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/common/v1/log_pb.py +0 -0
  61. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/common/v1/resources_pb.py +0 -0
  62. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/common/v2/__init__.py +0 -0
  63. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/common/v2/table_pb.py +0 -0
  64. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/container/__init__.py +0 -0
  65. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/container/v1/__init__.py +0 -0
  66. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/container/v1/service_connect.py +0 -0
  67. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/container/v1/service_pb.py +0 -0
  68. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/container/v1/snapshot_compatibility_pb.py +0 -0
  69. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/evaluation/__init__.py +0 -0
  70. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/evaluation/v1/__init__.py +0 -0
  71. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/evaluation/v1/service_connect.py +0 -0
  72. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/evaluation/v1/service_pb.py +0 -0
  73. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/externalfunctioncatalog/__init__.py +0 -0
  74. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/externalfunctioncatalog/v1/__init__.py +0 -0
  75. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/externalfunctioncatalog/v1/service_connect.py +0 -0
  76. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/externalfunctioncatalog/v1/service_pb.py +0 -0
  77. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/flags/__init__.py +0 -0
  78. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/flags/v1/__init__.py +0 -0
  79. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/flags/v1/flags_pb.py +0 -0
  80. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/graph/__init__.py +0 -0
  81. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/graph/v1/__init__.py +0 -0
  82. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/graph/v1/source_file_reference_pb.py +0 -0
  83. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/lsp/__init__.py +0 -0
  84. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/lsp/v1/__init__.py +0 -0
  85. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/lsp/v1/range_pb.py +0 -0
  86. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/manager/__init__.py +0 -0
  87. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/manager/v1/__init__.py +0 -0
  88. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/manager/v1/dataset_download_connect.py +0 -0
  89. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/manager/v1/dataset_download_pb.py +0 -0
  90. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/modeldeployment/__init__.py +0 -0
  91. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/modeldeployment/v1/__init__.py +0 -0
  92. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/modeldeployment/v1/service_connect.py +0 -0
  93. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/modeldeployment/v1/service_pb.py +0 -0
  94. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/models/__init__.py +0 -0
  95. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/models/v1/__init__.py +0 -0
  96. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/models/v1/model_artifact_pb.py +0 -0
  97. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/models/v1/model_version_pb.py +0 -0
  98. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/numericutils/__init__.py +0 -0
  99. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/numericutils/v1/__init__.py +0 -0
  100. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/numericutils/v1/values_pb.py +0 -0
  101. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/protosql/__init__.py +0 -0
  102. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/protosql/v1/__init__.py +0 -0
  103. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/protosql/v1/sql_service_connect.py +0 -0
  104. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/protosql/v1/sql_service_pb.py +0 -0
  105. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/runtime/__init__.py +0 -0
  106. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/runtime/v1/__init__.py +0 -0
  107. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/runtime/v1/remote_python_call_connect.py +0 -0
  108. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/runtime/v1/remote_python_call_pb.py +0 -0
  109. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/sandbox/__init__.py +0 -0
  110. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/sandbox/v1/__init__.py +0 -0
  111. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/sandbox/v1/runtime_service_connect.py +0 -0
  112. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/sandbox/v1/runtime_service_pb.py +0 -0
  113. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/sandbox/v1/service_connect.py +0 -0
  114. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/sandbox/v1/service_pb.py +0 -0
  115. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/sandbox/v1/service_pb2.py +0 -0
  116. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/sandbox/v2/__init__.py +0 -0
  117. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/sandbox/v2/filesystem_service_connect.py +0 -0
  118. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/sandbox/v2/filesystem_service_pb.py +0 -0
  119. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/sandbox/v2/process_service_connect.py +0 -0
  120. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/sandbox/v2/process_service_pb.py +0 -0
  121. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/sandbox/v2/service_connect.py +0 -0
  122. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/sandbox/v2/service_pb.py +0 -0
  123. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/scalinggroup/__init__.py +0 -0
  124. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/scalinggroup/v1/__init__.py +0 -0
  125. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/scalinggroup/v1/service_connect.py +0 -0
  126. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/scalinggroup/v1/service_pb.py +0 -0
  127. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/searchaggregates/__init__.py +0 -0
  128. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/searchaggregates/v1/__init__.py +0 -0
  129. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/searchaggregates/v1/aggregation_pb.py +0 -0
  130. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/__init__.py +0 -0
  131. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/__init__.py +0 -0
  132. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/auth_connect.py +0 -0
  133. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/auth_pb.py +0 -0
  134. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/cloud_config_pb.py +0 -0
  135. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/cluster_class_pb.py +0 -0
  136. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/datasets_connect.py +0 -0
  137. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/datasets_pb.py +0 -0
  138. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/environment_pb.py +0 -0
  139. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/environment_secrets_connect.py +0 -0
  140. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/environment_secrets_pb.py +0 -0
  141. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/github_app_connect.py +0 -0
  142. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/github_app_pb.py +0 -0
  143. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/link_pb.py +0 -0
  144. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/materialized_aggregate_tiles_pb.py +0 -0
  145. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/model_registry_connect.py +0 -0
  146. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/model_registry_pb.py +0 -0
  147. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/sql_queries_connect.py +0 -0
  148. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/sql_queries_pb.py +0 -0
  149. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/team_connect.py +0 -0
  150. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/team_pb.py +0 -0
  151. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/trace_connect.py +0 -0
  152. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/trace_pb.py +0 -0
  153. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/training_runs_connect.py +0 -0
  154. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/training_runs_pb.py +0 -0
  155. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/utils/__init__.py +0 -0
  156. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/utils/v1/__init__.py +0 -0
  157. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/utils/v1/encoding_pb.py +0 -0
  158. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/utils/v1/field_change_pb.py +0 -0
  159. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/utils/v1/sensitive_pb.py +0 -0
  160. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/volume/__init__.py +0 -0
  161. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/volume/v1/__init__.py +0 -0
  162. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/volume/v1/volume_pb.py +0 -0
  163. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/volume/v2/__init__.py +0 -0
  164. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/volume/v2/volume_connect.py +0 -0
  165. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/volume/v2/volume_pb.py +0 -0
  166. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/google/__init__.py +0 -0
  167. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/google/api/__init__.py +0 -0
  168. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/google/api/field_behavior_pb.py +0 -0
  169. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_http.py +0 -0
  170. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_ignore.py +0 -0
  171. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_image_build.py +0 -0
  172. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_local_dev.py +0 -0
  173. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_logging.py +0 -0
  174. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_model.py +0 -0
  175. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_naming.py +0 -0
  176. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_native.pyi +0 -0
  177. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_notebook.py +0 -0
  178. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_progress.py +0 -0
  179. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_proto_compat.py +0 -0
  180. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_quota.py +0 -0
  181. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_rate_limit.py +0 -0
  182. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_remote_call_client.py +0 -0
  183. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_remote_function_tracing.py +0 -0
  184. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_retry.py +0 -0
  185. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_rpc.py +0 -0
  186. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_sandbox_fs.py +0 -0
  187. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_sandbox_git.py +0 -0
  188. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_scaling_group.py +0 -0
  189. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_secret.py +0 -0
  190. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_sql.py +0 -0
  191. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_telemetry_export.py +0 -0
  192. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_terminal.py +0 -0
  193. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_trace.py +0 -0
  194. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_traceback.py +0 -0
  195. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_training.py +0 -0
  196. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_volume.py +0 -0
  197. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_web_identity.py +0 -0
  198. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/py.typed +0 -0
  199. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/scorers.py +0 -0
  200. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/training/__init__.py +0 -0
  201. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/training/entrypoint.py +0 -0
  202. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/rust/chalkcompute_native/Cargo.lock +0 -0
  203. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/rust/chalkcompute_native/Cargo.toml +0 -0
  204. {chalkcompute-2.13.2 → chalkcompute-2.13.4}/rust/chalkcompute_native/src/lib.rs +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chalkcompute
3
- Version: 2.13.2
3
+ Version: 2.13.4
4
4
  Requires-Dist: chalk-remote-call-python>=1.10.4
5
5
  Requires-Dist: pyyaml>=6.0.0
6
6
  Requires-Dist: opentelemetry-api>=1.20.0
@@ -34,27 +34,33 @@ or local `chalk-remote-call-python` changes, see
34
34
  ## Install
35
35
 
36
36
  ```
37
- pip install grpcio protobuf
37
+ pip install chalkcompute
38
38
  ```
39
39
 
40
40
  ## Quick start
41
41
 
42
42
  ```python
43
- from chalkcompute import SandboxClient
44
-
45
- with SandboxClient.from_env() as client:
46
- # Create a sandbox from a pre-built image
47
- sandbox = client.create(image="ubuntu:latest")
43
+ import chalkcompute as cc
48
44
 
49
- # Run a command
45
+ # Omitting image= uses Image.sandbox(): Python, Git and SSH in /workspace.
46
+ # Credentials come from the environment, as with SandboxClient.from_env().
47
+ sandbox = cc.Sandbox(
48
+ image=cc.Image.sandbox().with_tmux(),
49
+ cpu="1",
50
+ memory="2Gi",
51
+ ).run()
52
+ try:
50
53
  result = sandbox.exec("echo", "hello world")
51
- print(result.stdout_text) # "hello world"
54
+ print(result.stdout_text) # "hello world\n"
52
55
  print(result.exit_code) # 0
53
-
54
- # Clean up
56
+ finally:
55
57
  sandbox.terminate()
56
58
  ```
57
59
 
60
+ `Sandbox(...)` only describes the sandbox; `.run()` builds the image, starts it
61
+ and waits until it is ready. See [Coding agent tools](#coding-agent-tools) for a
62
+ sandbox with Claude Code, `gh` and Git identity configured.
63
+
58
64
  ## Logging and progress
59
65
 
60
66
  The SDK leaves Python logging configuration to your application. Importing
@@ -279,6 +285,78 @@ img = (
279
285
  )
280
286
  ```
281
287
 
288
+ ### Coding agent tools
289
+
290
+ `with_claude_code`, `with_gh` and `with_tmux` install common agent tooling on
291
+ Debian or Ubuntu images. `with_claude_code` installs the native binary from
292
+ Anthropic's apt repository, pins the version (apt installs don't auto-update),
293
+ and pre-accepts Claude Code's onboarding and trust prompts for the directories
294
+ you list, so interactive `claude` starts without a login screen:
295
+
296
+ ```python
297
+ image = (
298
+ Image.sandbox()
299
+ .with_claude_code(
300
+ version="2.1.270",
301
+ trusted_dirs=["/workspace/app"], # the repo root, not a parent of it
302
+ permission_mode="bypassPermissions", # optional
303
+ )
304
+ .with_gh() # from GitHub's apt repo; authenticates from GH_TOKEN
305
+ .with_tmux()
306
+ .workdir("/workspace/app")
307
+ )
308
+ ```
309
+
310
+ The bypass-permissions warning is skipped by default
311
+ (`skip_dangerous_mode_prompt=True`). Bypass mode works as root because Chalk
312
+ sets `IS_SANDBOX=1` in every sandbox. If you supply `ANTHROPIC_API_KEY` instead
313
+ of `CLAUDE_CODE_OAUTH_TOKEN`, pass `api_key_env="ANTHROPIC_API_KEY"` to skip the
314
+ key-approval prompt. Anything else goes in `settings`, which is merged into
315
+ `~/.claude/settings.json`. The theme defaults to `"dark"` (`theme=`), and
316
+ `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1` is set in the image so telemetry
317
+ and update checks don't hit hosts a network policy blocks
318
+ (`disable_nonessential_traffic=False` to keep them). See the `with_claude_code`
319
+ docstring for how directory trust works.
320
+
321
+ To keep the token itself out of the sandbox, use `add_credentials` (see
322
+ [Environment and secrets](#environment-variables-and-secrets)):
323
+ `sandbox.add_credentials("api.anthropic.com",
324
+ Secret.from_chalk_env("CLAUDE_TOKEN", alias="CLAUDE_CODE_OAUTH_TOKEN"))`.
325
+
326
+ Set Git identity per sandbox, not per image, with `GitConfig`. It is a mapping
327
+ of `GIT_*` environment variables, so it can be unpacked into `env`:
328
+
329
+ ```python
330
+ sandbox = Sandbox(
331
+ image=image,
332
+ env={
333
+ **GitConfig(
334
+ name="Ada Lovelace",
335
+ email="ada@example.com",
336
+ config={"pull.rebase": "true"},
337
+ ),
338
+ "LOG_LEVEL": "debug",
339
+ },
340
+ ).run()
341
+ ```
342
+
343
+ To push and pull over SSH, add `SshDest.github` to `managed_ssh`. It is pinned
344
+ to GitHub's published host keys and reads the private key from a Chalk secret,
345
+ so `git@github.com:org/repo.git` URLs work unchanged:
346
+
347
+ ```python
348
+ sandbox = Sandbox(
349
+ image=image,
350
+ compute_class=ComputeClass.HOST,
351
+ managed_ssh=[SshDest.github("GITHUB_DEPLOY_KEY")],
352
+ ).run()
353
+ sandbox.git.clone("git@github.com:org/repo.git", "/workspace/repo")
354
+ ```
355
+
356
+ Pass `port_443=True` for networks that block port 22. To use more than one key,
357
+ give each a distinct `alias` and use it in remote URLs, as in
358
+ `git@github-work:org/repo.git`.
359
+
282
360
  ### Immutable composition
283
361
 
284
362
  Each builder method returns a new `Image`, so intermediate images can be shared:
@@ -9,27 +9,33 @@ or local `chalk-remote-call-python` changes, see
9
9
  ## Install
10
10
 
11
11
  ```
12
- pip install grpcio protobuf
12
+ pip install chalkcompute
13
13
  ```
14
14
 
15
15
  ## Quick start
16
16
 
17
17
  ```python
18
- from chalkcompute import SandboxClient
19
-
20
- with SandboxClient.from_env() as client:
21
- # Create a sandbox from a pre-built image
22
- sandbox = client.create(image="ubuntu:latest")
18
+ import chalkcompute as cc
23
19
 
24
- # Run a command
20
+ # Omitting image= uses Image.sandbox(): Python, Git and SSH in /workspace.
21
+ # Credentials come from the environment, as with SandboxClient.from_env().
22
+ sandbox = cc.Sandbox(
23
+ image=cc.Image.sandbox().with_tmux(),
24
+ cpu="1",
25
+ memory="2Gi",
26
+ ).run()
27
+ try:
25
28
  result = sandbox.exec("echo", "hello world")
26
- print(result.stdout_text) # "hello world"
29
+ print(result.stdout_text) # "hello world\n"
27
30
  print(result.exit_code) # 0
28
-
29
- # Clean up
31
+ finally:
30
32
  sandbox.terminate()
31
33
  ```
32
34
 
35
+ `Sandbox(...)` only describes the sandbox; `.run()` builds the image, starts it
36
+ and waits until it is ready. See [Coding agent tools](#coding-agent-tools) for a
37
+ sandbox with Claude Code, `gh` and Git identity configured.
38
+
33
39
  ## Logging and progress
34
40
 
35
41
  The SDK leaves Python logging configuration to your application. Importing
@@ -254,6 +260,78 @@ img = (
254
260
  )
255
261
  ```
256
262
 
263
+ ### Coding agent tools
264
+
265
+ `with_claude_code`, `with_gh` and `with_tmux` install common agent tooling on
266
+ Debian or Ubuntu images. `with_claude_code` installs the native binary from
267
+ Anthropic's apt repository, pins the version (apt installs don't auto-update),
268
+ and pre-accepts Claude Code's onboarding and trust prompts for the directories
269
+ you list, so interactive `claude` starts without a login screen:
270
+
271
+ ```python
272
+ image = (
273
+ Image.sandbox()
274
+ .with_claude_code(
275
+ version="2.1.270",
276
+ trusted_dirs=["/workspace/app"], # the repo root, not a parent of it
277
+ permission_mode="bypassPermissions", # optional
278
+ )
279
+ .with_gh() # from GitHub's apt repo; authenticates from GH_TOKEN
280
+ .with_tmux()
281
+ .workdir("/workspace/app")
282
+ )
283
+ ```
284
+
285
+ The bypass-permissions warning is skipped by default
286
+ (`skip_dangerous_mode_prompt=True`). Bypass mode works as root because Chalk
287
+ sets `IS_SANDBOX=1` in every sandbox. If you supply `ANTHROPIC_API_KEY` instead
288
+ of `CLAUDE_CODE_OAUTH_TOKEN`, pass `api_key_env="ANTHROPIC_API_KEY"` to skip the
289
+ key-approval prompt. Anything else goes in `settings`, which is merged into
290
+ `~/.claude/settings.json`. The theme defaults to `"dark"` (`theme=`), and
291
+ `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1` is set in the image so telemetry
292
+ and update checks don't hit hosts a network policy blocks
293
+ (`disable_nonessential_traffic=False` to keep them). See the `with_claude_code`
294
+ docstring for how directory trust works.
295
+
296
+ To keep the token itself out of the sandbox, use `add_credentials` (see
297
+ [Environment and secrets](#environment-variables-and-secrets)):
298
+ `sandbox.add_credentials("api.anthropic.com",
299
+ Secret.from_chalk_env("CLAUDE_TOKEN", alias="CLAUDE_CODE_OAUTH_TOKEN"))`.
300
+
301
+ Set Git identity per sandbox, not per image, with `GitConfig`. It is a mapping
302
+ of `GIT_*` environment variables, so it can be unpacked into `env`:
303
+
304
+ ```python
305
+ sandbox = Sandbox(
306
+ image=image,
307
+ env={
308
+ **GitConfig(
309
+ name="Ada Lovelace",
310
+ email="ada@example.com",
311
+ config={"pull.rebase": "true"},
312
+ ),
313
+ "LOG_LEVEL": "debug",
314
+ },
315
+ ).run()
316
+ ```
317
+
318
+ To push and pull over SSH, add `SshDest.github` to `managed_ssh`. It is pinned
319
+ to GitHub's published host keys and reads the private key from a Chalk secret,
320
+ so `git@github.com:org/repo.git` URLs work unchanged:
321
+
322
+ ```python
323
+ sandbox = Sandbox(
324
+ image=image,
325
+ compute_class=ComputeClass.HOST,
326
+ managed_ssh=[SshDest.github("GITHUB_DEPLOY_KEY")],
327
+ ).run()
328
+ sandbox.git.clone("git@github.com:org/repo.git", "/workspace/repo")
329
+ ```
330
+
331
+ Pass `port_443=True` for networks that block port 22. To use more than one key,
332
+ give each a distinct `alias` and use it in remote URLs, as in
333
+ `git@github-work:org/repo.git`.
334
+
257
335
  ### Immutable composition
258
336
 
259
337
  Each builder method returns a new `Image`, so intermediate images can be shared:
@@ -45,6 +45,7 @@ _LAZY_MAP: dict[str, tuple[str, str]] = {
45
45
  "FunctionCallHandle": ("chalkcompute._function", "FunctionCallHandle"),
46
46
  "FunctionVersionInfo": ("chalkcompute._function", "FunctionVersionInfo"),
47
47
  "gather": ("chalkcompute._function", "gather"),
48
+ "gather_handles": ("chalkcompute._gather", "gather_handles"),
48
49
  # _sql.py
49
50
  "SQLClient": ("chalkcompute._sql", "SQLClient"),
50
51
  "SQLResult": ("chalkcompute._sql", "SQLResult"),
@@ -140,6 +141,7 @@ _LAZY_MAP: dict[str, tuple[str, str]] = {
140
141
  "GitFileStatus": ("chalkcompute._sandbox_git", "GitFileStatus"),
141
142
  "GitStatus": ("chalkcompute._sandbox_git", "GitStatus"),
142
143
  "GitRemote": ("chalkcompute._sandbox_git", "GitRemote"),
144
+ "GitConfig": ("chalkcompute._git", "GitConfig"),
143
145
  "GitError": ("chalkcompute._git", "GitError"),
144
146
  # _sandbox.py
145
147
  "ExecError": ("chalkcompute._sandbox", "ExecError"),
@@ -68,6 +68,7 @@ from chalkcompute._function import FunctionVersionInfo as FunctionVersionInfo
68
68
  from chalkcompute._function import RemoteFunction as RemoteFunction
69
69
  from chalkcompute._function import function as function
70
70
  from chalkcompute._function import gather as gather
71
+ from chalkcompute._gather import gather_handles as gather_handles
71
72
  from chalkcompute._function_error import RemoteExecutionError as RemoteExecutionError
72
73
  from chalkcompute._http import HTTPHeaders as HTTPHeaders
73
74
  from chalkcompute._http import HTTPResponse as HTTPResponse
@@ -161,6 +162,7 @@ from chalkcompute._sandbox_git import SandboxGit as SandboxGit
161
162
  from chalkcompute._sandbox_git import GitFileStatus as GitFileStatus
162
163
  from chalkcompute._sandbox_git import GitStatus as GitStatus
163
164
  from chalkcompute._sandbox_git import GitRemote as GitRemote
165
+ from chalkcompute._git import GitConfig as GitConfig
164
166
  from chalkcompute._git import GitError as GitError
165
167
 
166
168
  __all__ = [
@@ -169,6 +171,7 @@ __all__ = [
169
171
  "GitFileStatus",
170
172
  "GitStatus",
171
173
  "GitRemote",
174
+ "GitConfig",
172
175
  "GitError",
173
176
  "SQLClient",
174
177
  "SQLResult",
@@ -208,6 +211,7 @@ __all__ = [
208
211
  "FunctionCallHandle",
209
212
  "FunctionVersionInfo",
210
213
  "gather",
214
+ "gather_handles",
211
215
  "get_call_context",
212
216
  "get_row_metadata",
213
217
  "RetryPolicy",
@@ -0,0 +1,41 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import contextvars
5
+ import threading
6
+ from typing import Any, Callable
7
+
8
+
9
+ def _has_running_event_loop() -> bool:
10
+ try:
11
+ asyncio.get_running_loop()
12
+ except RuntimeError:
13
+ return False
14
+ return True
15
+
16
+
17
+ async def _run_in_worker_thread(
18
+ func: Callable[..., Any], *args: Any, **kwargs: Any
19
+ ) -> Any:
20
+ loop = asyncio.get_running_loop()
21
+ future: asyncio.Future[Any] = loop.create_future()
22
+ context = contextvars.copy_context()
23
+
24
+ def complete_with_result(value: Any) -> None:
25
+ if not future.cancelled():
26
+ future.set_result(value)
27
+
28
+ def complete_with_exception(exc: BaseException) -> None:
29
+ if not future.cancelled():
30
+ future.set_exception(exc)
31
+
32
+ def run() -> None:
33
+ try:
34
+ value = context.run(func, *args, **kwargs)
35
+ except BaseException as exc:
36
+ loop.call_soon_threadsafe(complete_with_exception, exc)
37
+ else:
38
+ loop.call_soon_threadsafe(complete_with_result, value)
39
+
40
+ threading.Thread(target=run, daemon=True).start()
41
+ return await future
@@ -4,7 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import logging
6
6
  import threading
7
- from collections.abc import Callable, Iterator
7
+ from collections.abc import Callable, Generator
8
8
  from contextlib import contextmanager
9
9
  from dataclasses import dataclass
10
10
  from typing import Generic, TypeVar
@@ -59,7 +59,7 @@ class ClientCache(Generic[ClientT]):
59
59
  return self._acquire(key, borrow=False).client
60
60
 
61
61
  @contextmanager
62
- def borrow(self, *, key: object = None) -> Iterator[ClientT]:
62
+ def borrow(self, *, key: object = None) -> Generator[ClientT, None, None]:
63
63
  entry = self._acquire(key, borrow=True)
64
64
  try:
65
65
  yield entry.client
@@ -73,6 +73,10 @@ class SSHDest:
73
73
  SSH destination port. Defaults to 22.
74
74
  username
75
75
  Optional SSH username written to the generated alias's ``User`` setting.
76
+ alias
77
+ Name the destination is reachable by inside the sandbox, as in
78
+ ``ssh <alias>``. Used when ``managed_ssh`` is a list; defaults to
79
+ ``host``. A ``managed_ssh`` mapping's keys take precedence.
76
80
  """
77
81
 
78
82
  host: str
@@ -81,6 +85,53 @@ class SSHDest:
81
85
  host_keys: Sequence[str] | None = None
82
86
  port: int | None = None
83
87
  username: str | None = None
88
+ alias: str | None = None
89
+
90
+ @classmethod
91
+ def github(
92
+ cls,
93
+ private_key_secret: str,
94
+ *,
95
+ alias: str = "github.com",
96
+ port_443: bool = False,
97
+ ) -> SSHDest:
98
+ """GitHub over SSH, pinned to GitHub's published host keys.
99
+
100
+ ``git@github.com:org/repo.git`` URLs work unchanged inside the sandbox.
101
+
102
+ Parameters
103
+ ----------
104
+ private_key_secret
105
+ Name of the Chalk secret containing a key registered with GitHub,
106
+ such as a deploy key or a user's SSH key.
107
+ alias
108
+ SSH alias for the destination. Use distinct aliases to reach
109
+ GitHub with more than one key, as in
110
+ ``git@github-work:org/repo.git``.
111
+ port_443
112
+ Connect to ``ssh.github.com:443`` instead of ``github.com:22``,
113
+ for networks that block port 22.
114
+
115
+ Example
116
+ -------
117
+ ```python
118
+ import chalkcompute as cc
119
+
120
+ sandbox = cc.Sandbox(
121
+ compute_class=cc.ComputeClass.HOST,
122
+ managed_ssh=[cc.SshDest.github("GITHUB_DEPLOY_KEY")],
123
+ ).run()
124
+ sandbox.git.clone(url="git@github.com:org/repo.git", path="/workspace/repo")
125
+ ```
126
+ """
127
+ return cls(
128
+ host="ssh.github.com" if port_443 else "github.com",
129
+ private_key_secret=private_key_secret,
130
+ host_keys=GITHUB_SSH_HOST_KEYS,
131
+ port=443 if port_443 else None,
132
+ username="git",
133
+ alias=alias,
134
+ )
84
135
 
85
136
  def __post_init__(self) -> None:
86
137
  for name, value in (
@@ -101,6 +152,8 @@ class SSHDest:
101
152
  _validate_port(self.port, "port")
102
153
  if self.username is not None and not isinstance(self.username, str):
103
154
  raise TypeError("username must be a string")
155
+ if self.alias is not None:
156
+ _validate_ssh_alias(self.alias)
104
157
 
105
158
  def to_dict(self) -> dict[str, Any]:
106
159
  destination: dict[str, Any] = {
@@ -120,6 +173,57 @@ class SSHDest:
120
173
  SshDest = SSHDest
121
174
 
122
175
 
176
+ # GitHub's SSH host keys, from https://api.github.com/meta. ssh.github.com:443
177
+ # serves the same keys.
178
+ GITHUB_SSH_HOST_KEYS = (
179
+ "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl",
180
+ "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=",
181
+ "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZaMgRK6+PKCUXaDbC7qtbW8gIkhL7aGCsOr/C56SJMy/BCZfxd1nWzAOxSDPgVsmerOBYfNqltV9/hWCqBywINIR+5dIg6JTJ72pcEpEjcYgXkE2YEFXV1JHnsKgbLWNlhScqb2UmyRkQyytRLtL+38TGxkxCflmO+5Z8CSSNY7GidjMIZ7Q4zMjA2n1nGrlTDkzwDCsw+wqFPGQA179cnfGWOWRVruj16z6XyvxvjJwbz0wQZ75XK5tKSb7FNyeIEs4TT4jk+S4dhPeAUC5y+bDYirYgM4GC7uEnztnZyaVWQ7B381AK4Qdrwt51ZqExKbQpTUNn+EjqoTwvqNj4kqx5QUCI0ThS/YkOxJCXmPUWZbhjpCg56i+2aB6CmK2JGhn57K5mj0MNdBXA4/WnwH6XoPWJzK5Nyu2zB3nAZp+S5hpQs+p1vN1/wsjk=",
182
+ )
183
+
184
+
185
+ def _validate_ssh_alias(alias: object) -> None:
186
+ if not isinstance(alias, str):
187
+ raise TypeError("managed_ssh aliases must be strings")
188
+ if not alias:
189
+ raise ValueError("managed_ssh aliases must not be empty")
190
+
191
+
192
+ def normalize_managed_ssh(
193
+ managed_ssh: Mapping[str, SSHDest] | Sequence[SSHDest] | None,
194
+ ) -> dict[str, SSHDest]:
195
+ """Validate ``managed_ssh`` and return it keyed by alias.
196
+
197
+ A list keys each destination by its ``alias``, falling back to ``host``.
198
+ """
199
+ if managed_ssh is None:
200
+ return {}
201
+ if isinstance(managed_ssh, Mapping):
202
+ items = list(managed_ssh.items())
203
+ elif isinstance(managed_ssh, (str, bytes)) or not isinstance(managed_ssh, Sequence):
204
+ raise TypeError(
205
+ "managed_ssh must be a list of SSHDest or a mapping of aliases to SSHDest"
206
+ )
207
+ else:
208
+ items = []
209
+ for destination in managed_ssh:
210
+ if not isinstance(destination, SSHDest):
211
+ raise TypeError("managed_ssh destinations must be SSHDest instances")
212
+ items.append((destination.alias or destination.host, destination))
213
+ result: dict[str, SSHDest] = {}
214
+ for alias, destination in items:
215
+ _validate_ssh_alias(alias)
216
+ if not isinstance(destination, SSHDest):
217
+ raise TypeError("managed_ssh destinations must be SSHDest instances")
218
+ if alias in result:
219
+ raise ValueError(
220
+ f"managed_ssh has more than one destination for alias {alias!r}; "
221
+ "give each a distinct alias="
222
+ )
223
+ result[alias] = destination
224
+ return result
225
+
226
+
123
227
  @dataclass
124
228
  class ContainerSecurityPolicy:
125
229
  """Security policy settings for a container."""
@@ -406,7 +510,7 @@ class ContainerSpec:
406
510
  secret_refs: Sequence[Secret | Mapping[str, Any]] = ()
407
511
  port: int | None = None
408
512
  lifetime: str | None = None
409
- managed_ssh: Mapping[str, SSHDest] = field(default_factory=dict)
513
+ managed_ssh: Mapping[str, SSHDest] | Sequence[SSHDest] = field(default_factory=dict)
410
514
  tags: Mapping[str, str] = field(default_factory=dict)
411
515
  protocol: str | None = None
412
516
  authentication: str | None = None
@@ -461,7 +565,7 @@ class ContainerSpec:
461
565
  object.__setattr__(
462
566
  self,
463
567
  "managed_ssh",
464
- dict(self.managed_ssh),
568
+ normalize_managed_ssh(self.managed_ssh),
465
569
  )
466
570
  object.__setattr__(
467
571
  self,
@@ -494,7 +598,7 @@ class ContainerSpec:
494
598
  tags: Mapping[str, str] | None = None,
495
599
  restart_policy: RestartPolicy | None = None,
496
600
  chalk_identity: bool = False,
497
- managed_ssh: Mapping[str, SSHDest] | None = None,
601
+ managed_ssh: Mapping[str, SSHDest] | Sequence[SSHDest] | None = None,
498
602
  ) -> None:
499
603
  if image is not None and not isinstance(image, str):
500
604
  if not isinstance(image, Image):
@@ -569,18 +673,7 @@ class ContainerSpec:
569
673
  raise TypeError("restart_policy must be a RestartPolicy")
570
674
  if not isinstance(chalk_identity, bool):
571
675
  raise TypeError("chalk_identity must be a bool")
572
- if managed_ssh is not None and not isinstance(managed_ssh, Mapping):
573
- raise TypeError("managed_ssh must be a mapping of aliases to destinations")
574
- if managed_ssh is not None:
575
- for alias, destination in managed_ssh.items():
576
- if not isinstance(alias, str):
577
- raise TypeError("managed_ssh aliases must be strings")
578
- if not alias:
579
- raise ValueError("managed_ssh aliases must not be empty")
580
- if not isinstance(destination, SSHDest):
581
- raise TypeError(
582
- "managed_ssh destinations must be SSHDest instances"
583
- )
676
+ normalize_managed_ssh(managed_ssh)
584
677
 
585
678
  def to_dict(self) -> dict[str, Any]:
586
679
  spec: dict[str, Any] = {"image": self.image}
@@ -615,7 +708,9 @@ class ContainerSpec:
615
708
  if self.managed_ssh:
616
709
  spec["managedSsh"] = {
617
710
  alias: destination.to_dict()
618
- for alias, destination in self.managed_ssh.items()
711
+ for alias, destination in normalize_managed_ssh(
712
+ self.managed_ssh
713
+ ).items()
619
714
  }
620
715
  if self.tags:
621
716
  spec["tags"] = dict(self.tags)
@@ -697,7 +792,9 @@ class ContainerSpec:
697
792
  port=destination.port,
698
793
  username=destination.username,
699
794
  )
700
- for alias, destination in self.managed_ssh.items()
795
+ for alias, destination in normalize_managed_ssh(
796
+ self.managed_ssh
797
+ ).items()
701
798
  },
702
799
  tags=dict(self.tags),
703
800
  protocol=self.protocol,
@@ -68,7 +68,9 @@ from protobuf import Oneof
68
68
  from protobuf.wkt import FieldMask
69
69
 
70
70
  from chalkcompute import _local_dev
71
+ from chalkcompute._async_utils import _has_running_event_loop, _run_in_worker_thread
71
72
  from chalkcompute._client_cache import ClientCache
73
+ from chalkcompute._gather import gather_handles
72
74
  from chalkcompute._logging import _StatusLogger
73
75
  from chalkcompute._traceback import format_exception, format_frame
74
76
  from chalkcompute._terminal import styled, supports_color
@@ -232,7 +234,7 @@ def _await_deploy_predecessor(predecessor: Future[None] | None) -> None:
232
234
 
233
235
 
234
236
  @contextlib.contextmanager
235
- def _manual_deploy_slot(registered_name: str) -> typing.Iterator[None]:
237
+ def _manual_deploy_slot(registered_name: str) -> typing.Generator[None, None, None]:
236
238
  """Hold a caller-initiated deploy's place in its registered name's chain.
237
239
 
238
240
  Without a slot a manual ``deploy()`` is invisible to the chain, so a
@@ -897,14 +899,6 @@ def _should_call_via_api() -> bool:
897
899
  return os.environ.get(_CALL_VIA_API_ENV_VAR, "") == "1"
898
900
 
899
901
 
900
- def _has_running_event_loop() -> bool:
901
- try:
902
- asyncio.get_running_loop()
903
- except RuntimeError:
904
- return False
905
- return True
906
-
907
-
908
902
  def _replica_count(value: Any) -> int:
909
903
  if value is None:
910
904
  return 0
@@ -966,33 +960,6 @@ def _format_knowledge_cutoff(value: KnowledgeCutoff) -> str:
966
960
  return value.isoformat()
967
961
 
968
962
 
969
- async def _run_in_worker_thread(
970
- func: Callable[..., Any], *args: Any, **kwargs: Any
971
- ) -> Any:
972
- loop = asyncio.get_running_loop()
973
- future: asyncio.Future[Any] = loop.create_future()
974
- context = contextvars.copy_context()
975
-
976
- def complete_with_result(value: Any) -> None:
977
- if not future.cancelled():
978
- future.set_result(value)
979
-
980
- def complete_with_exception(exc: BaseException) -> None:
981
- if not future.cancelled():
982
- future.set_exception(exc)
983
-
984
- def run() -> None:
985
- try:
986
- value = context.run(func, *args, **kwargs)
987
- except BaseException as exc:
988
- loop.call_soon_threadsafe(complete_with_exception, exc)
989
- else:
990
- loop.call_soon_threadsafe(complete_with_result, value)
991
-
992
- threading.Thread(target=run, daemon=True).start()
993
- return await future
994
-
995
-
996
963
  def _remote_call_error_message(error: BaseException) -> str:
997
964
  """Return a best-effort transport error string without assuming error type."""
998
965
  details = None
@@ -1358,16 +1325,6 @@ class FunctionCallHandle:
1358
1325
  await asyncio.to_thread(self._resubmit)
1359
1326
 
1360
1327
 
1361
- async def _gather_async(
1362
- handles: list[FunctionCallHandle],
1363
- *,
1364
- timeout: float | None = None,
1365
- ) -> list[Any]:
1366
- return list(
1367
- await asyncio.gather(*(handle.get(timeout=timeout) for handle in handles))
1368
- )
1369
-
1370
-
1371
1328
  def gather(
1372
1329
  handles: typing.Iterable[FunctionCallHandle],
1373
1330
  *,
@@ -1378,18 +1335,7 @@ def gather(
1378
1335
  In synchronous code this blocks and returns ``list[Any]``. Inside a running
1379
1336
  event loop it returns an awaitable, matching :meth:`FunctionCallHandle.get`.
1380
1337
  """
1381
- handle_list = list(handles)
1382
- for i, handle in enumerate(handle_list):
1383
- if not callable(getattr(handle, "get", None)):
1384
- raise TypeError(
1385
- f"gather() expects deferred-call handles from .defer(), but item "
1386
- f"{i} is a {type(handle).__name__}. .remote() runs the call and "
1387
- f"returns its result, not a handle — use .defer() to fan out, e.g. "
1388
- f"gather(fn.defer(x) for x in xs)."
1389
- )
1390
- if _has_running_event_loop():
1391
- return _gather_async(handle_list, timeout=timeout)
1392
- return asyncio.run(_gather_async(handle_list, timeout=timeout))
1338
+ return gather_handles(handles, timeout=timeout)
1393
1339
 
1394
1340
 
1395
1341
  def _source_text_for_deploy(source_file: str) -> str: