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.
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/PKG-INFO +89 -11
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/README.md +88 -10
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/__init__.py +2 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/__init__.pyi +4 -0
- chalkcompute-2.13.4/chalkcompute/_async_utils.py +41 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_client_cache.py +2 -2
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_containerspec.py +114 -17
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_function.py +4 -58
- chalkcompute-2.13.4/chalkcompute/_gather.py +55 -0
- chalkcompute-2.13.4/chalkcompute/_git.py +164 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_image.py +272 -1
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_sandbox.py +10 -6
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_tracing.py +5 -3
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/pyproject.toml +1 -1
- chalkcompute-2.13.2/chalkcompute/_git.py +0 -86
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_arrow_schema.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_arrow_serde.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_artifact.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_auth_config.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_call_context.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_cls.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_concurrency.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_connect.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_container.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_dataset.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_deployment_proto.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_env.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_evaluation.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_function_deployment.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_function_error.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/buf/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/buf/validate/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/buf/validate/validate_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/argo/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/argo/v1/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/argo/v1/workflow_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/arrow/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/arrow/v1/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/arrow/v1/arrow_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/artifactstore/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/artifactstore/v1/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/artifactstore/v1/service_connect.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/artifactstore/v1/service_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/auth/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/auth/v1/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/auth/v1/agent_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/auth/v1/audit_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/auth/v1/displayagent_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/auth/v1/featurepermission_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/auth/v1/permissions_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/chart/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/chart/v1/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/chart/v1/densetimeserieschart_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/common/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/common/v1/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/common/v1/chalk_error_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/common/v1/column_profile_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/common/v1/log_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/common/v1/resources_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/common/v2/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/common/v2/table_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/container/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/container/v1/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/container/v1/service_connect.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/container/v1/service_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/container/v1/snapshot_compatibility_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/evaluation/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/evaluation/v1/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/evaluation/v1/service_connect.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/evaluation/v1/service_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/externalfunctioncatalog/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/externalfunctioncatalog/v1/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/externalfunctioncatalog/v1/service_connect.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/externalfunctioncatalog/v1/service_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/flags/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/flags/v1/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/flags/v1/flags_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/graph/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/graph/v1/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/graph/v1/source_file_reference_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/lsp/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/lsp/v1/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/lsp/v1/range_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/manager/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/manager/v1/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/manager/v1/dataset_download_connect.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/manager/v1/dataset_download_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/modeldeployment/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/modeldeployment/v1/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/modeldeployment/v1/service_connect.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/modeldeployment/v1/service_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/models/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/models/v1/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/models/v1/model_artifact_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/models/v1/model_version_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/numericutils/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/numericutils/v1/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/numericutils/v1/values_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/protosql/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/protosql/v1/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/protosql/v1/sql_service_connect.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/protosql/v1/sql_service_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/runtime/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/runtime/v1/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/runtime/v1/remote_python_call_connect.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/runtime/v1/remote_python_call_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/sandbox/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/sandbox/v1/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/sandbox/v1/runtime_service_connect.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/sandbox/v1/runtime_service_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/sandbox/v1/service_connect.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/sandbox/v1/service_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/sandbox/v1/service_pb2.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/sandbox/v2/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/sandbox/v2/filesystem_service_connect.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/sandbox/v2/filesystem_service_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/sandbox/v2/process_service_connect.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/sandbox/v2/process_service_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/sandbox/v2/service_connect.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/sandbox/v2/service_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/scalinggroup/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/scalinggroup/v1/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/scalinggroup/v1/service_connect.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/scalinggroup/v1/service_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/searchaggregates/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/searchaggregates/v1/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/searchaggregates/v1/aggregation_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/auth_connect.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/auth_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/cloud_config_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/cluster_class_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/datasets_connect.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/datasets_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/environment_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/environment_secrets_connect.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/environment_secrets_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/github_app_connect.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/github_app_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/link_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/materialized_aggregate_tiles_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/model_registry_connect.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/model_registry_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/sql_queries_connect.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/sql_queries_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/team_connect.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/team_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/trace_connect.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/trace_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/training_runs_connect.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/server/v1/training_runs_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/utils/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/utils/v1/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/utils/v1/encoding_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/utils/v1/field_change_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/utils/v1/sensitive_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/volume/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/volume/v1/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/volume/v1/volume_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/volume/v2/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/volume/v2/volume_connect.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/chalk/volume/v2/volume_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/google/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/google/api/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_gen/google/api/field_behavior_pb.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_http.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_ignore.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_image_build.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_local_dev.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_logging.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_model.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_naming.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_native.pyi +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_notebook.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_progress.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_proto_compat.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_quota.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_rate_limit.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_remote_call_client.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_remote_function_tracing.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_retry.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_rpc.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_sandbox_fs.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_sandbox_git.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_scaling_group.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_secret.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_sql.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_telemetry_export.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_terminal.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_trace.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_traceback.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_training.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_volume.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/_web_identity.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/py.typed +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/scorers.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/training/__init__.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/chalkcompute/training/entrypoint.py +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/rust/chalkcompute_native/Cargo.lock +0 -0
- {chalkcompute-2.13.2 → chalkcompute-2.13.4}/rust/chalkcompute_native/Cargo.toml +0 -0
- {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.
|
|
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
|
|
37
|
+
pip install chalkcompute
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
## Quick start
|
|
41
41
|
|
|
42
42
|
```python
|
|
43
|
-
|
|
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
|
-
|
|
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
|
|
12
|
+
pip install chalkcompute
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
## Quick start
|
|
16
16
|
|
|
17
17
|
```python
|
|
18
|
-
|
|
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
|
-
|
|
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,
|
|
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) ->
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
|
|
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:
|