chalkcompute 2.2.0__tar.gz → 2.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/PKG-INFO +1 -1
  2. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/__init__.py +0 -2
  3. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/__init__.pyi +0 -4
  4. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_connect.py +4 -0
  5. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_container.py +253 -73
  6. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_function.py +231 -13
  7. chalkcompute-2.3.0/chalkcompute/_gen/chalk/container/v1/service_pb2.py +282 -0
  8. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/container/v1/service_pb2.pyi +111 -2
  9. chalkcompute-2.3.0/chalkcompute/_gen/chalk/sandbox/v1/service_pb2.py +246 -0
  10. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/sandbox/v1/service_pb2.pyi +5 -0
  11. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_image.py +18 -0
  12. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_remote_call_client.py +30 -9
  13. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_sandbox.py +29 -2
  14. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/pyproject.toml +1 -1
  15. chalkcompute-2.2.0/chalkcompute/_gen/chalk/container/v1/service_pb2.py +0 -262
  16. chalkcompute-2.2.0/chalkcompute/_gen/chalk/sandbox/v1/service_pb2.py +0 -243
  17. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/README.md +0 -0
  18. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_arrow_schema.py +0 -0
  19. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_auth_config.py +0 -0
  20. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_cls.py +0 -0
  21. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_concurrency.py +0 -0
  22. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/__init__.py +0 -0
  23. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/buf/__init__.py +0 -0
  24. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/buf/validate/__init__.py +0 -0
  25. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/buf/validate/validate_pb2.py +0 -0
  26. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/buf/validate/validate_pb2.pyi +0 -0
  27. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/buf/validate/validate_pb2_grpc.py +0 -0
  28. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/buf/validate/validate_pb2_grpc.pyi +0 -0
  29. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/__init__.py +0 -0
  30. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/argo/__init__.py +0 -0
  31. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/argo/v1/__init__.py +0 -0
  32. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/argo/v1/workflow_pb2.py +0 -0
  33. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/argo/v1/workflow_pb2.pyi +0 -0
  34. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/argo/v1/workflow_pb2_grpc.py +0 -0
  35. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/argo/v1/workflow_pb2_grpc.pyi +0 -0
  36. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/arrow/__init__.py +0 -0
  37. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/arrow/v1/__init__.py +0 -0
  38. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/arrow/v1/arrow_pb2.py +0 -0
  39. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/arrow/v1/arrow_pb2.pyi +0 -0
  40. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/arrow/v1/arrow_pb2_grpc.py +0 -0
  41. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/arrow/v1/arrow_pb2_grpc.pyi +0 -0
  42. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/auth/__init__.py +0 -0
  43. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/auth/v1/__init__.py +0 -0
  44. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/auth/v1/agent_pb2.py +0 -0
  45. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/auth/v1/agent_pb2.pyi +0 -0
  46. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/auth/v1/agent_pb2_grpc.py +0 -0
  47. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/auth/v1/agent_pb2_grpc.pyi +0 -0
  48. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/auth/v1/audit_pb2.py +0 -0
  49. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/auth/v1/audit_pb2.pyi +0 -0
  50. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/auth/v1/audit_pb2_grpc.py +0 -0
  51. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/auth/v1/audit_pb2_grpc.pyi +0 -0
  52. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/auth/v1/displayagent_pb2.py +0 -0
  53. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/auth/v1/displayagent_pb2.pyi +0 -0
  54. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/auth/v1/displayagent_pb2_grpc.py +0 -0
  55. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/auth/v1/displayagent_pb2_grpc.pyi +0 -0
  56. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/auth/v1/featurepermission_pb2.py +0 -0
  57. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/auth/v1/featurepermission_pb2.pyi +0 -0
  58. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/auth/v1/featurepermission_pb2_grpc.py +0 -0
  59. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/auth/v1/featurepermission_pb2_grpc.pyi +0 -0
  60. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/auth/v1/permissions_pb2.py +0 -0
  61. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/auth/v1/permissions_pb2.pyi +0 -0
  62. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/auth/v1/permissions_pb2_grpc.py +0 -0
  63. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/auth/v1/permissions_pb2_grpc.pyi +0 -0
  64. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/common/__init__.py +0 -0
  65. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/common/v1/__init__.py +0 -0
  66. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/common/v1/chalk_error_pb2.py +0 -0
  67. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/common/v1/chalk_error_pb2.pyi +0 -0
  68. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/common/v1/chalk_error_pb2_grpc.py +0 -0
  69. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/common/v1/chalk_error_pb2_grpc.pyi +0 -0
  70. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/common/v1/log_pb2.py +0 -0
  71. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/common/v1/log_pb2.pyi +0 -0
  72. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/common/v1/log_pb2_grpc.py +0 -0
  73. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/common/v1/log_pb2_grpc.pyi +0 -0
  74. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/container/__init__.py +0 -0
  75. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/container/v1/__init__.py +0 -0
  76. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/container/v1/service_pb2_grpc.py +0 -0
  77. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/container/v1/service_pb2_grpc.pyi +0 -0
  78. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/externalfunctioncatalog/__init__.py +0 -0
  79. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/externalfunctioncatalog/v1/__init__.py +0 -0
  80. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/externalfunctioncatalog/v1/service_pb2.py +0 -0
  81. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/externalfunctioncatalog/v1/service_pb2.pyi +0 -0
  82. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/externalfunctioncatalog/v1/service_pb2_grpc.py +0 -0
  83. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/externalfunctioncatalog/v1/service_pb2_grpc.pyi +0 -0
  84. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/flags/__init__.py +0 -0
  85. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/flags/v1/__init__.py +0 -0
  86. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/flags/v1/flags_pb2.py +0 -0
  87. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/flags/v1/flags_pb2.pyi +0 -0
  88. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/flags/v1/flags_pb2_grpc.py +0 -0
  89. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/flags/v1/flags_pb2_grpc.pyi +0 -0
  90. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/runtime/__init__.py +0 -0
  91. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/runtime/v1/__init__.py +0 -0
  92. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/runtime/v1/remote_python_call_pb2.py +0 -0
  93. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/runtime/v1/remote_python_call_pb2.pyi +0 -0
  94. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/runtime/v1/remote_python_call_pb2_grpc.py +0 -0
  95. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/runtime/v1/remote_python_call_pb2_grpc.pyi +0 -0
  96. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/sandbox/__init__.py +0 -0
  97. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/sandbox/v1/__init__.py +0 -0
  98. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/sandbox/v1/service_pb2_grpc.py +0 -0
  99. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/sandbox/v1/service_pb2_grpc.pyi +0 -0
  100. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/scalinggroup/__init__.py +0 -0
  101. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/scalinggroup/v1/__init__.py +0 -0
  102. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/scalinggroup/v1/service_pb2.py +0 -0
  103. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/scalinggroup/v1/service_pb2.pyi +0 -0
  104. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/scalinggroup/v1/service_pb2_grpc.py +0 -0
  105. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/scalinggroup/v1/service_pb2_grpc.pyi +0 -0
  106. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/utils/__init__.py +0 -0
  107. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/utils/v1/__init__.py +0 -0
  108. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/utils/v1/encoding_pb2.py +0 -0
  109. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/utils/v1/encoding_pb2.pyi +0 -0
  110. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/utils/v1/encoding_pb2_grpc.py +0 -0
  111. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/utils/v1/encoding_pb2_grpc.pyi +0 -0
  112. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/utils/v1/field_change_pb2.py +0 -0
  113. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/utils/v1/field_change_pb2.pyi +0 -0
  114. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/utils/v1/field_change_pb2_grpc.py +0 -0
  115. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/utils/v1/field_change_pb2_grpc.pyi +0 -0
  116. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/utils/v1/sensitive_pb2.py +0 -0
  117. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/utils/v1/sensitive_pb2.pyi +0 -0
  118. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/utils/v1/sensitive_pb2_grpc.py +0 -0
  119. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/utils/v1/sensitive_pb2_grpc.pyi +0 -0
  120. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/volume/__init__.py +0 -0
  121. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/volume/v1/__init__.py +0 -0
  122. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/volume/v1/volume_pb2.py +0 -0
  123. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/volume/v1/volume_pb2.pyi +0 -0
  124. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/volume/v1/volume_pb2_grpc.py +0 -0
  125. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/volume/v1/volume_pb2_grpc.pyi +0 -0
  126. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/volume/v2/__init__.py +0 -0
  127. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/volume/v2/volume_pb2.py +0 -0
  128. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/volume/v2/volume_pb2.pyi +0 -0
  129. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/volume/v2/volume_pb2_grpc.py +0 -0
  130. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_gen/chalk/volume/v2/volume_pb2_grpc.pyi +0 -0
  131. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_ignore.py +0 -0
  132. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_local_dev.py +0 -0
  133. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_logging.py +0 -0
  134. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_model.py +0 -0
  135. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_naming.py +0 -0
  136. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_native.pyi +0 -0
  137. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_progress.py +0 -0
  138. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_proto_pool.py +0 -0
  139. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_quota.py +0 -0
  140. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_rate_limit.py +0 -0
  141. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_remote_function_tracing.py +0 -0
  142. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_retry.py +0 -0
  143. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_scaling_group.py +0 -0
  144. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_secret.py +0 -0
  145. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_tracing.py +0 -0
  146. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_volume.py +0 -0
  147. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/_vvolume.py +0 -0
  148. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/chalkcompute/py.typed +0 -0
  149. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/rust/chalkcompute_native/Cargo.lock +0 -0
  150. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/rust/chalkcompute_native/Cargo.toml +0 -0
  151. {chalkcompute-2.2.0 → chalkcompute-2.3.0}/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.2.0
3
+ Version: 2.3.0
4
4
  Requires-Dist: grpcio>=1.60.0
5
5
  Requires-Dist: protobuf>=4.25.0
6
6
  Requires-Dist: httpx>=0.25.0
@@ -16,12 +16,10 @@ _LAZY_MAP: dict[str, tuple[str, str]] = {
16
16
  "ContainerSession": ("chalkcompute._container", "ContainerSession"),
17
17
  "ContainerSecurityPolicy": ("chalkcompute._container", "ContainerSecurityPolicy"),
18
18
  "ContainerExecResult": ("chalkcompute._container", "ExecResult"),
19
- "AllowedRoute": ("chalkcompute._container", "AllowedRoute"),
20
19
  "ImageBuildError": ("chalkcompute._container", "ImageBuildError"),
21
20
  "KernelPolicy": ("chalkcompute._container", "KernelPolicy"),
22
21
  "ComputeClass": ("chalkcompute._container", "ComputeClass"),
23
22
  "NetworkPolicy": ("chalkcompute._container", "NetworkPolicy"),
24
- "PortRange": ("chalkcompute._container", "PortRange"),
25
23
  # _cls.py
26
24
  "RemoteClass": ("chalkcompute._cls", "RemoteClass"),
27
25
  "ClassError": ("chalkcompute._cls", "ClassError"),
@@ -11,7 +11,6 @@ from chalkcompute._connect import build_image_from_spec as build_image
11
11
  from chalkcompute._connect import (
12
12
  build_image_with_volumes_from_spec as build_image_with_volumes,
13
13
  )
14
- from chalkcompute._container import AllowedRoute as AllowedRoute
15
14
  from chalkcompute._container import Container as Container
16
15
  from chalkcompute._container import ContainerError as ContainerError
17
16
  from chalkcompute._container import ContainerInfo as ContainerInfo
@@ -22,7 +21,6 @@ from chalkcompute._container import ImageBuildError as ImageBuildError
22
21
  from chalkcompute._container import ComputeClass as ComputeClass
23
22
  from chalkcompute._container import KernelPolicy as KernelPolicy
24
23
  from chalkcompute._container import NetworkPolicy as NetworkPolicy
25
- from chalkcompute._container import PortRange as PortRange
26
24
  from chalkcompute._function import DeployedFunction as DeployedFunction
27
25
  from chalkcompute._function import FunctionBuildError as FunctionBuildError
28
26
  from chalkcompute._function import FunctionCallHandle as FunctionCallHandle
@@ -83,12 +81,10 @@ __all__ = [
83
81
  "ContainerSession",
84
82
  "ContainerSecurityPolicy",
85
83
  "ContainerExecResult",
86
- "AllowedRoute",
87
84
  "ImageBuildError",
88
85
  "ComputeClass",
89
86
  "KernelPolicy",
90
87
  "NetworkPolicy",
91
- "PortRange",
92
88
  "RemoteClass",
93
89
  "ClassError",
94
90
  "ClassBuildError",
@@ -363,6 +363,10 @@ def build_image(
363
363
  ConnectError
364
364
  If the build fails or times out.
365
365
  """
366
+ image_uri = image.as_image_uri_if_possible()
367
+ if image_uri is not None:
368
+ return image_uri
369
+
366
370
  from google.protobuf.json_format import MessageToDict
367
371
 
368
372
  spec_dict = MessageToDict(image.to_proto())
@@ -19,16 +19,15 @@ destination path's common ancestor.
19
19
  from __future__ import annotations
20
20
 
21
21
  import base64
22
- import ipaddress
23
22
  import logging
24
23
  import queue
25
24
  import time
26
25
  import uuid
27
26
  from collections import deque
28
- from collections.abc import Iterator
27
+ from collections.abc import Iterable, Iterator, Mapping
29
28
  from dataclasses import dataclass, field
30
29
  from enum import Enum
31
- from typing import TYPE_CHECKING, Any, NoReturn
30
+ from typing import TYPE_CHECKING, Any, NoReturn, cast
32
31
 
33
32
  import grpc
34
33
 
@@ -57,8 +56,6 @@ from chalkcompute._vvolume import VersionedVolumeClient
57
56
  logger = logging.getLogger(__name__)
58
57
 
59
58
  if TYPE_CHECKING:
60
- from collections.abc import Iterable
61
-
62
59
  from chalkcompute._image import Image as _Image
63
60
  from chalkcompute._secret import Secret
64
61
 
@@ -145,92 +142,275 @@ class ContainerSecurityPolicy:
145
142
  return {"kernelPolicy": self.kernel_policy.value}
146
143
 
147
144
 
148
- @dataclass
149
- class PortRange:
150
- """Inclusive destination port range for a network policy route."""
145
+ _PortRangeInput = tuple[int, int] | int
146
+
147
+
148
+ class NetworkPolicy:
149
+ """Network egress policy for a container.
151
150
 
152
- start_port: int
153
- """Inclusive start port. Must be between 1 and 65535."""
151
+ For HOST containers, missing and empty policies deny application egress
152
+ unless allowed routes or hosts are configured.
153
+ """
154
+
155
+ @dataclass(init=False)
156
+ class Route:
157
+ """Allowed destination CIDR and optional destination port ranges."""
158
+
159
+ route: str
160
+ """Destination CIDR, e.g. ``"10.0.0.0/8"``."""
161
+
162
+ ports: list[tuple[int, int]]
163
+ """Destination port ranges. Empty means all ports."""
164
+
165
+ def __init__(
166
+ self,
167
+ route: str,
168
+ *,
169
+ ports: Iterable[_PortRangeInput] | None = None,
170
+ ) -> None:
171
+ if not isinstance(route, str):
172
+ raise TypeError("route must be a string")
173
+ if not route:
174
+ raise ValueError("route must not be empty")
175
+ self.route = route
176
+ self.ports = [_coerce_port_range(port) for port in ports or []]
177
+
178
+ def to_dict(self) -> dict[str, Any]:
179
+ body: dict[str, Any] = {"route": self.route}
180
+ if self.ports:
181
+ body["portRanges"] = [
182
+ {"startPort": start_port, "endPort": end_port}
183
+ for start_port, end_port in self.ports
184
+ ]
185
+ return body
186
+
187
+ @dataclass
188
+ class Matcher:
189
+ """String matcher for path, query-string, or header values."""
190
+
191
+ exact: str | None = None
192
+ starts_with: str | None = None
193
+ regex: str | None = None
194
+
195
+ def __post_init__(self) -> None:
196
+ values = [
197
+ self.exact is not None,
198
+ self.starts_with is not None,
199
+ self.regex is not None,
200
+ ]
201
+ if sum(values) != 1:
202
+ raise ValueError("set exactly one of exact, starts_with, or regex")
203
+ for name, value in (
204
+ ("exact", self.exact),
205
+ ("starts_with", self.starts_with),
206
+ ("regex", self.regex),
207
+ ):
208
+ if value is not None and not isinstance(value, str):
209
+ raise TypeError(f"{name} must be a string")
210
+
211
+ def to_dict(self) -> dict[str, str]:
212
+ if self.exact is not None:
213
+ return {"exact": self.exact}
214
+ if self.starts_with is not None:
215
+ return {"startsWith": self.starts_with}
216
+ assert self.regex is not None
217
+ return {"regex": self.regex}
154
218
 
155
- end_port: int | None = None
156
- """Inclusive end port. Defaults to ``start_port``."""
219
+ @dataclass
220
+ class KeyValueMatcher:
221
+ """Matcher for a query-string or header entry."""
222
+
223
+ key: NetworkPolicy.Matcher
224
+ value: NetworkPolicy.Matcher | None = None
225
+
226
+ def __post_init__(self) -> None:
227
+ if not isinstance(self.key, NetworkPolicy.Matcher):
228
+ raise TypeError("key must be a NetworkPolicy.Matcher")
229
+ if self.value is not None and not isinstance(
230
+ self.value, NetworkPolicy.Matcher
231
+ ):
232
+ raise TypeError("value must be a NetworkPolicy.Matcher")
233
+
234
+ def to_dict(self) -> dict[str, Any]:
235
+ body: dict[str, Any] = {"key": self.key.to_dict()}
236
+ if self.value is not None:
237
+ body["value"] = self.value.to_dict()
238
+ return body
157
239
 
158
- def __post_init__(self) -> None:
159
- if self.end_port is None:
160
- self.end_port = self.start_port
161
- _validate_port_range(self.start_port, self.end_port)
240
+ @dataclass
241
+ class Match:
242
+ """Request matcher for host policy rules."""
243
+
244
+ path: NetworkPolicy.Matcher | None = None
245
+ methods: list[str] = field(default_factory=list)
246
+ query_strings: list[NetworkPolicy.KeyValueMatcher] = field(default_factory=list)
247
+ headers: list[NetworkPolicy.KeyValueMatcher] = field(default_factory=list)
248
+
249
+ def __post_init__(self) -> None:
250
+ if self.path is not None and not isinstance(
251
+ self.path, NetworkPolicy.Matcher
252
+ ):
253
+ raise TypeError("path must be a NetworkPolicy.Matcher")
254
+ if isinstance(self.methods, str):
255
+ raise TypeError("methods must be a list of strings")
256
+ self.methods = list(self.methods or [])
257
+ for method in self.methods:
258
+ if not isinstance(method, str):
259
+ raise TypeError("methods must be a list of strings")
260
+ self.query_strings = list(self.query_strings or [])
261
+ for matcher in self.query_strings:
262
+ if not isinstance(matcher, NetworkPolicy.KeyValueMatcher):
263
+ raise TypeError(
264
+ "query_strings must be a list of NetworkPolicy.KeyValueMatcher"
265
+ )
266
+ self.headers = list(self.headers or [])
267
+ for matcher in self.headers:
268
+ if not isinstance(matcher, NetworkPolicy.KeyValueMatcher):
269
+ raise TypeError(
270
+ "headers must be a list of NetworkPolicy.KeyValueMatcher"
271
+ )
162
272
 
163
- def to_dict(self) -> dict[str, int]:
164
- assert self.end_port is not None
165
- return {"startPort": self.start_port, "endPort": self.end_port}
273
+ def to_dict(self) -> dict[str, Any]:
274
+ body: dict[str, Any] = {}
275
+ if self.path is not None:
276
+ body["path"] = self.path.to_dict()
277
+ if self.methods:
278
+ body["method"] = list(self.methods)
279
+ if self.query_strings:
280
+ body["queryString"] = [
281
+ matcher.to_dict() for matcher in self.query_strings
282
+ ]
283
+ if self.headers:
284
+ body["headers"] = [matcher.to_dict() for matcher in self.headers]
285
+ return body
166
286
 
287
+ @dataclass
288
+ class Transformer:
289
+ """Request transform for a host policy rule."""
167
290
 
168
- PortRangeInput = PortRange | tuple[int, int] | int
291
+ headers: dict[str, str] = field(default_factory=dict)
169
292
 
293
+ def __post_init__(self) -> None:
294
+ self.headers = dict(self.headers or {})
295
+ for key, value in self.headers.items():
296
+ if not isinstance(key, str) or not isinstance(value, str):
297
+ raise TypeError("headers must be a dict[str, str]")
170
298
 
171
- @dataclass(init=False)
172
- class AllowedRoute:
173
- """Allowed destination CIDR and optional destination port ranges.
299
+ def to_dict(self) -> dict[str, Any]:
300
+ return {"headers": dict(self.headers)}
174
301
 
175
- Empty ``port_ranges`` means all destination ports for the route.
176
- """
302
+ @dataclass
303
+ class Rule:
304
+ """Host policy rule for matching, transforming, or forwarding requests."""
305
+
306
+ transforms: list[NetworkPolicy.Transformer] = field(default_factory=list)
307
+ match: NetworkPolicy.Match | None = None
308
+ forward_url: str | None = None
309
+
310
+ def __post_init__(self) -> None:
311
+ self.transforms = list(self.transforms or [])
312
+ for transformer in self.transforms:
313
+ if not isinstance(transformer, NetworkPolicy.Transformer):
314
+ raise TypeError(
315
+ "transforms must be a list of NetworkPolicy.Transformer"
316
+ )
317
+ if self.match is not None and not isinstance(
318
+ self.match, NetworkPolicy.Match
319
+ ):
320
+ raise TypeError("match must be a NetworkPolicy.Match")
321
+ if self.forward_url is not None and not isinstance(self.forward_url, str):
322
+ raise TypeError("forward_url must be a string")
323
+
324
+ def to_dict(self) -> dict[str, Any]:
325
+ body: dict[str, Any] = {}
326
+ if self.transforms:
327
+ body["transform"] = [
328
+ transformer.to_dict() for transformer in self.transforms
329
+ ]
330
+ if self.match is not None:
331
+ body["match"] = self.match.to_dict()
332
+ if self.forward_url is not None:
333
+ body["forwardUrl"] = self.forward_url
334
+ return body
335
+
336
+ allowed_routes: list[Route]
337
+ """Allowed destination routes and their permitted destination ports."""
177
338
 
178
- route: str
179
- """Destination CIDR, e.g. ``"10.0.0.0/8"``."""
339
+ denied_routes: list[str]
340
+ """Destination CIDRs to deny before evaluating allowed routes or hosts."""
180
341
 
181
- port_ranges: list[PortRange]
182
- """Destination port ranges allowed for this route."""
342
+ allowed_hosts: dict[str, list[Rule]]
343
+ """Host patterns mapped to request rules. Empty rule lists allow the host."""
183
344
 
184
345
  def __init__(
185
346
  self,
186
- route: str | None = None,
187
- port_ranges: Iterable[PortRangeInput] | None = None,
188
347
  *,
189
- cidr: str | None = None,
190
- ports: Iterable[PortRangeInput] | None = None,
348
+ allowed_routes: Iterable[Route] | None = None,
349
+ denied_routes: Iterable[str] | None = None,
350
+ allowed_hosts: Iterable[str] | Mapping[str, Iterable[Rule]] | None = None,
191
351
  ) -> None:
192
- if route is None:
193
- route = cidr
194
- elif cidr is not None and cidr != route:
195
- raise ValueError("Pass only one of route or cidr")
196
- if route is None:
197
- raise ValueError("route is required")
198
- if port_ranges is not None and ports is not None:
199
- raise ValueError("Pass only one of port_ranges or ports")
200
-
201
- ipaddress.ip_network(route)
202
- self.route = route
203
- self.port_ranges = [
204
- _coerce_port_range(port_range)
205
- for port_range in (port_ranges if port_ranges is not None else ports or [])
206
- ]
207
-
208
- def to_dict(self) -> dict[str, Any]:
209
- body: dict[str, Any] = {"route": self.route}
210
- if self.port_ranges:
211
- body["portRanges"] = [
212
- port_range.to_dict() for port_range in self.port_ranges
213
- ]
214
- return body
215
-
216
-
217
- @dataclass
218
- class NetworkPolicy:
219
- """Network egress allowlist for a container.
352
+ self.allowed_routes = list(allowed_routes or [])
353
+ for route in self.allowed_routes:
354
+ if not isinstance(route, NetworkPolicy.Route):
355
+ raise TypeError("allowed_routes must be a list of NetworkPolicy.Route")
356
+
357
+ if isinstance(denied_routes, str):
358
+ raise TypeError("denied_routes must be a list of strings")
359
+ self.denied_routes = list(denied_routes or [])
360
+ for route in self.denied_routes:
361
+ if not isinstance(route, str):
362
+ raise TypeError("denied_routes must be a list of strings")
363
+
364
+ self.allowed_hosts = {}
365
+ if allowed_hosts is None:
366
+ return
220
367
 
221
- A missing policy leaves networking unrestricted for backwards compatibility.
222
- A present policy allows only its ``allowed_routes``.
223
- """
368
+ if isinstance(allowed_hosts, Mapping):
369
+ for host, rules in allowed_hosts.items():
370
+ if not isinstance(host, str):
371
+ raise TypeError("allowed_hosts entries must be strings")
372
+ self._validate_host(host)
373
+ if isinstance(rules, NetworkPolicy.Rule):
374
+ raise TypeError(
375
+ "allowed_hosts values must be lists of NetworkPolicy.Rule"
376
+ )
377
+ self.allowed_hosts[host] = list(
378
+ cast(Iterable[NetworkPolicy.Rule], rules)
379
+ )
380
+ for rule in self.allowed_hosts[host]:
381
+ if not isinstance(rule, NetworkPolicy.Rule):
382
+ raise TypeError(
383
+ "allowed_hosts values must be lists of NetworkPolicy.Rule"
384
+ )
385
+ return
224
386
 
225
- allowed_routes: list[AllowedRoute] = field(default_factory=list)
226
- """Allowed destination routes and their permitted destination ports."""
387
+ if isinstance(allowed_hosts, str):
388
+ raise TypeError("allowed_hosts must be a list of strings or a dict")
389
+ for host in allowed_hosts:
390
+ self._validate_host(host)
391
+ self.allowed_hosts[host] = []
227
392
 
228
393
  def to_dict(self) -> dict[str, Any]:
229
- return {
394
+ body: dict[str, Any] = {
230
395
  "allowedRoutes": [
231
396
  allowed_route.to_dict() for allowed_route in self.allowed_routes
232
397
  ]
233
398
  }
399
+ if self.denied_routes:
400
+ body["deniedRoutes"] = list(self.denied_routes)
401
+ if self.allowed_hosts:
402
+ body["allowedHosts"] = {
403
+ host: {"rules": [rule.to_dict() for rule in rules]}
404
+ for host, rules in self.allowed_hosts.items()
405
+ }
406
+ return body
407
+
408
+ @staticmethod
409
+ def _validate_host(host: str) -> None:
410
+ if not isinstance(host, str):
411
+ raise TypeError("allowed_hosts entries must be strings")
412
+ if not host:
413
+ raise ValueError("allowed_hosts entries must not be empty")
234
414
 
235
415
 
236
416
  @dataclass
@@ -1773,15 +1953,15 @@ def _handle_container_grpc_error(
1773
1953
  raise ContainerError(f"Container session RPC failed: {details}") from e
1774
1954
 
1775
1955
 
1776
- def _coerce_port_range(port_range: PortRangeInput) -> PortRange:
1777
- if isinstance(port_range, PortRange):
1778
- return port_range
1956
+ def _coerce_port_range(port_range: _PortRangeInput) -> tuple[int, int]:
1779
1957
  if isinstance(port_range, int) and not isinstance(port_range, bool):
1780
- return PortRange(port_range)
1958
+ _validate_port_range(port_range, port_range)
1959
+ return port_range, port_range
1781
1960
  if isinstance(port_range, tuple) and len(port_range) == 2:
1782
1961
  start_port, end_port = port_range
1783
- return PortRange(start_port, end_port)
1784
- raise TypeError("port ranges must be PortRange, int, or (start_port, end_port)")
1962
+ _validate_port_range(start_port, end_port)
1963
+ return start_port, end_port
1964
+ raise TypeError("ports must be integers or (start_port, end_port) tuples")
1785
1965
 
1786
1966
 
1787
1967
  def _validate_port_range(start_port: int, end_port: int) -> None: