modAL 1.4.3.dev24__py3-none-any.whl → 1.4.3.dev26__py3-none-any.whl

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.
modal/cli/cluster.py CHANGED
@@ -94,7 +94,19 @@ async def shell(cluster_id: str, rank: int = 0):
94
94
  await command_router_client.exec_start(start_req)
95
95
 
96
96
  if pty:
97
- await _ContainerProcess(process_id, task_id, client, command_router_client=command_router_client).attach()
97
+ # PTY output is raw terminal bytes (control sequences, mouse events,
98
+ # glyphs in non-UTF-8 locales, etc.) — not text. Strict UTF-8 decode on
99
+ # this stream crashes the shell as soon as anything emits a byte that
100
+ # isn't valid UTF-8, e.g. vim drawing a Latin-1 file under LC_CTYPE=C.
101
+ # Pass bytes through unmodified; `attach()` writes them straight to the
102
+ # local fd.
103
+ await _ContainerProcess(
104
+ process_id,
105
+ task_id,
106
+ client,
107
+ command_router_client=command_router_client,
108
+ text=False,
109
+ ).attach()
98
110
  else:
99
111
  await _ContainerProcess(
100
112
  process_id,
modal/cli/container.py CHANGED
@@ -294,7 +294,19 @@ async def _exec_impl(
294
294
  await command_router_client.exec_start(start_req)
295
295
 
296
296
  if pty:
297
- await _ContainerProcess(process_id, container_id, client, command_router_client=command_router_client).attach()
297
+ # PTY output is raw terminal bytes (control sequences, mouse events,
298
+ # glyphs in non-UTF-8 locales, etc.) — not text. Strict UTF-8 decode on
299
+ # this stream crashes the shell as soon as anything emits a byte that
300
+ # isn't valid UTF-8, e.g. vim drawing a Latin-1 file under LC_CTYPE=C.
301
+ # Pass bytes through unmodified; `attach()` writes them straight to the
302
+ # local fd.
303
+ await _ContainerProcess(
304
+ process_id,
305
+ container_id,
306
+ client,
307
+ command_router_client=command_router_client,
308
+ text=False,
309
+ ).attach()
298
310
  else:
299
311
  await _ContainerProcess(
300
312
  process_id,
modal/cli/shell.py CHANGED
@@ -100,7 +100,10 @@ def _start_shell_in_sandbox_container(sandbox_id: str, container_name: str, cmd:
100
100
  try:
101
101
  sandbox_container = sandbox._experimental_containers.get(name=container_name)
102
102
  if pty:
103
- process = sandbox_container.exec(*shlex.split(cmd), pty=pty)
103
+ # PTY output is raw terminal bytes, not text; strict UTF-8 decode
104
+ # crashes on the first non-UTF-8 byte (e.g. vim drawing a Latin-1
105
+ # file under LC_CTYPE=C). See the matching call in `_exec_impl`.
106
+ process = sandbox_container.exec(*shlex.split(cmd), pty=pty, text=False)
104
107
  process.attach()
105
108
  else:
106
109
  process = sandbox_container.exec(
modal/client.pyi CHANGED
@@ -35,7 +35,7 @@ class _Client:
35
35
  server_url: str,
36
36
  client_type: int,
37
37
  credentials: typing.Optional[tuple[str, str]],
38
- version: str = "1.4.3.dev24",
38
+ version: str = "1.4.3.dev26",
39
39
  ):
40
40
  """mdmd:hidden
41
41
  The Modal client object is not intended to be instantiated directly by users.
@@ -175,7 +175,7 @@ class Client:
175
175
  server_url: str,
176
176
  client_type: int,
177
177
  credentials: typing.Optional[tuple[str, str]],
178
- version: str = "1.4.3.dev24",
178
+ version: str = "1.4.3.dev26",
179
179
  ):
180
180
  """mdmd:hidden
181
181
  The Modal client object is not intended to be instantiated directly by users.
@@ -76,7 +76,7 @@ class _FlashManager:
76
76
  except (OSError, asyncio.TimeoutError):
77
77
  await asyncio.sleep(0.1)
78
78
 
79
- return False, Exception(f"Waited too long for port {self.port} to start accepting connections")
79
+ return False, Exception(f"Waited too long for port {self.port} to accept connections")
80
80
 
81
81
  async def _start(self):
82
82
  self.tunnel = await self.tunnel_manager.__aenter__()
@@ -152,16 +152,16 @@ class _FlashManager:
152
152
  logger.warning(f"Listening at {resp.url} over {self.tunnel.url} for task_id {self.task_id}")
153
153
  return True
154
154
  except asyncio.CancelledError:
155
- logger.warning("Waited too long for port to start accepting connections. Shutting down...")
155
+ logger.warning("Healthcheck cancelled while waiting for port to accept connections. Shutting down...")
156
156
  raise
157
157
  except Exception as e:
158
- logger.error(f"Error waiting for port to start accepting connections: {e}")
158
+ logger.error(f"Error waiting for port to accept connections: {e}")
159
159
  try:
160
160
  await asyncio.sleep(1)
161
161
  except asyncio.CancelledError:
162
- logger.warning("Waited too long for port to start accepting connections. Shutting down...")
162
+ logger.warning("Healthcheck cancelled while waiting for port to accept connections. Shutting down...")
163
163
  raise
164
- raise TimeoutError("Waited too long for port to start accepting connections. Shutting down...")
164
+ raise TimeoutError("Timed out while waiting for port to accept connections. Shutting down...")
165
165
 
166
166
  async def _run_heartbeat(self, host: str, port: int):
167
167
  while True:
modal/functions.pyi CHANGED
@@ -347,7 +347,7 @@ class Function(
347
347
 
348
348
  _call_generator: ___call_generator_spec
349
349
 
350
- class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
350
+ class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER]):
351
351
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER:
352
352
  """Calls the function remotely, executing it with the given arguments and returning the execution's result."""
353
353
  ...
@@ -356,7 +356,7 @@ class Function(
356
356
  """Calls the function remotely, executing it with the given arguments and returning the execution's result."""
357
357
  ...
358
358
 
359
- remote: __remote_spec[modal._functions.ReturnType, modal._functions.P]
359
+ remote: __remote_spec[modal._functions.P, modal._functions.ReturnType]
360
360
 
361
361
  class __remote_gen_spec(typing_extensions.Protocol):
362
362
  def __call__(self, /, *args, **kwargs) -> typing.Generator[typing.Any, None, None]:
@@ -383,7 +383,7 @@ class Function(
383
383
  """
384
384
  ...
385
385
 
386
- class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
386
+ class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER]):
387
387
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
388
388
  """[Experimental] Calls the function with the given arguments, without waiting for the results.
389
389
 
@@ -406,7 +406,7 @@ class Function(
406
406
  """
407
407
  ...
408
408
 
409
- _experimental_spawn: ___experimental_spawn_spec[modal._functions.ReturnType, modal._functions.P]
409
+ _experimental_spawn: ___experimental_spawn_spec[modal._functions.P, modal._functions.ReturnType]
410
410
 
411
411
  class ___spawn_map_inner_spec(typing_extensions.Protocol[P_INNER]):
412
412
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> None: ...
@@ -414,7 +414,7 @@ class Function(
414
414
 
415
415
  _spawn_map_inner: ___spawn_map_inner_spec[modal._functions.P]
416
416
 
417
- class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
417
+ class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER]):
418
418
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
419
419
  """Calls the function with the given arguments, without waiting for the results.
420
420
 
@@ -435,7 +435,7 @@ class Function(
435
435
  """
436
436
  ...
437
437
 
438
- spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P]
438
+ spawn: __spawn_spec[modal._functions.P, modal._functions.ReturnType]
439
439
 
440
440
  def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]:
441
441
  """Return the inner Python object wrapped by this Modal Function."""
modal/sandbox.py CHANGED
@@ -37,9 +37,11 @@ from .client import _Client
37
37
  from .container_process import _ContainerProcess
38
38
  from .exception import (
39
39
  ClientClosed,
40
+ ConflictError,
40
41
  Error,
41
42
  ExecutionError,
42
43
  InvalidError,
44
+ NotFoundError,
43
45
  SandboxTerminatedError,
44
46
  SandboxTimeoutError,
45
47
  TimeoutError,
@@ -230,7 +232,7 @@ class _Sandbox(_Object, type_prefix="sb"):
230
232
  mounts: Sequence[_Mount] = (),
231
233
  network_file_systems: dict[Union[str, os.PathLike], _NetworkFileSystem] = {},
232
234
  block_network: bool = False,
233
- cidr_allowlist: Optional[Sequence[str]] = None,
235
+ outbound_cidr_allowlist: Optional[Sequence[str]] = None,
234
236
  inbound_cidr_allowlist: Optional[Sequence[str]] = None,
235
237
  volumes: dict[Union[str, os.PathLike], Union[_Volume, _CloudBucketMount]] = {},
236
238
  pty: bool = False,
@@ -319,15 +321,14 @@ class _Sandbox(_Object, type_prefix="sb"):
319
321
  )
320
322
 
321
323
  if block_network:
322
- # If the network is blocked, cidr_allowlist is invalid as we don't allow any network access.
323
- if cidr_allowlist is not None:
324
- raise InvalidError("`cidr_allowlist` cannot be used when `block_network` is enabled")
324
+ if outbound_cidr_allowlist is not None:
325
+ raise InvalidError("`outbound_cidr_allowlist` cannot be used when `block_network` is enabled")
325
326
  if inbound_cidr_allowlist is not None:
326
327
  raise InvalidError("`inbound_cidr_allowlist` cannot be used when `block_network` is enabled")
327
328
  network_access = api_pb2.NetworkAccess(
328
329
  network_access_type=api_pb2.NetworkAccess.NetworkAccessType.BLOCKED,
329
330
  )
330
- elif cidr_allowlist is None:
331
+ elif outbound_cidr_allowlist is None:
331
332
  # If the allowlist is empty, we allow all network access.
332
333
  network_access = api_pb2.NetworkAccess(
333
334
  network_access_type=api_pb2.NetworkAccess.NetworkAccessType.OPEN,
@@ -335,7 +336,7 @@ class _Sandbox(_Object, type_prefix="sb"):
335
336
  else:
336
337
  network_access = api_pb2.NetworkAccess(
337
338
  network_access_type=api_pb2.NetworkAccess.NetworkAccessType.ALLOWLIST,
338
- allowed_cidrs=cidr_allowlist,
339
+ allowed_cidrs=outbound_cidr_allowlist,
339
340
  )
340
341
 
341
342
  ephemeral_disk = None # Ephemeral disk requests not supported on Sandboxes.
@@ -410,7 +411,7 @@ class _Sandbox(_Object, type_prefix="sb"):
410
411
  memory: Optional[Union[int, tuple[int, int]]] = None,
411
412
  block_network: bool = False, # Whether to block network access
412
413
  # List of CIDRs the sandbox is allowed to access. If None, all CIDRs are allowed.
413
- cidr_allowlist: Optional[Sequence[str]] = None,
414
+ outbound_cidr_allowlist: Optional[Sequence[str]] = None,
414
415
  # List of CIDRs allowed to connect inbound to the sandbox (tunnels and connection tokens).
415
416
  inbound_cidr_allowlist: Optional[Sequence[str]] = None,
416
417
  volumes: dict[
@@ -441,6 +442,7 @@ class _Sandbox(_Object, type_prefix="sb"):
441
442
  client: Optional[_Client] = None,
442
443
  environment_name: Optional[str] = None, # *DEPRECATED* Optionally override the default environment
443
444
  pty_info: Optional[api_pb2.PTYInfo] = None, # *DEPRECATED* Use `pty` instead. `pty` will override `pty_info`.
445
+ cidr_allowlist: Optional[Sequence[str]] = None, # *DEPRECATED* Use outbound_cidr_allowlist instead.
444
446
  ) -> "_Sandbox":
445
447
  """
446
448
  Create a new Sandbox to run untrusted, arbitrary code.
@@ -470,6 +472,16 @@ class _Sandbox(_Object, type_prefix="sb"):
470
472
  "Set the `pty` parameter to `True` instead.",
471
473
  )
472
474
 
475
+ if cidr_allowlist is not None:
476
+ if outbound_cidr_allowlist is not None:
477
+ raise InvalidError("Cannot specify both `cidr_allowlist` and `outbound_cidr_allowlist`.")
478
+ deprecation_warning(
479
+ (2026, 5, 11),
480
+ "The `cidr_allowlist` parameter has been renamed to `outbound_cidr_allowlist`. "
481
+ "`cidr_allowlist` will be removed in a future release.",
482
+ )
483
+ outbound_cidr_allowlist = cidr_allowlist
484
+
473
485
  secrets = secrets or []
474
486
  if env:
475
487
  secrets = [*secrets, _Secret.from_dict(env)]
@@ -490,7 +502,7 @@ class _Sandbox(_Object, type_prefix="sb"):
490
502
  cpu=cpu,
491
503
  memory=memory,
492
504
  block_network=block_network,
493
- cidr_allowlist=cidr_allowlist,
505
+ outbound_cidr_allowlist=outbound_cidr_allowlist,
494
506
  inbound_cidr_allowlist=inbound_cidr_allowlist,
495
507
  volumes=volumes,
496
508
  pty=pty,
@@ -529,7 +541,7 @@ class _Sandbox(_Object, type_prefix="sb"):
529
541
  cpu: Optional[Union[float, tuple[float, float]]] = None,
530
542
  memory: Optional[Union[int, tuple[int, int]]] = None,
531
543
  block_network: bool = False,
532
- cidr_allowlist: Optional[Sequence[str]] = None,
544
+ outbound_cidr_allowlist: Optional[Sequence[str]] = None,
533
545
  inbound_cidr_allowlist: Optional[Sequence[str]] = None,
534
546
  volumes: dict[Union[str, os.PathLike], Union[_Volume, _CloudBucketMount]] = {},
535
547
  pty: bool = False,
@@ -582,7 +594,7 @@ class _Sandbox(_Object, type_prefix="sb"):
582
594
  mounts=mounts,
583
595
  network_file_systems=network_file_systems,
584
596
  block_network=block_network,
585
- cidr_allowlist=cidr_allowlist,
597
+ outbound_cidr_allowlist=outbound_cidr_allowlist,
586
598
  inbound_cidr_allowlist=inbound_cidr_allowlist,
587
599
  volumes=volumes,
588
600
  pty=pty,
@@ -1261,12 +1273,15 @@ class _Sandbox(_Object, type_prefix="sb"):
1261
1273
 
1262
1274
  async def _get_command_router_client(self, task_id: str) -> TaskCommandRouterClient:
1263
1275
  if self._command_router_client is None:
1264
- if self._is_v2:
1265
- self._command_router_client = await TaskCommandRouterClient.init_v2(
1266
- self._client, self.object_id, task_id
1267
- )
1268
- else:
1269
- self._command_router_client = await TaskCommandRouterClient.init(self._client, task_id)
1276
+ try:
1277
+ if self._is_v2:
1278
+ self._command_router_client = await TaskCommandRouterClient.init_v2(
1279
+ self._client, self.object_id, task_id
1280
+ )
1281
+ else:
1282
+ self._command_router_client = await TaskCommandRouterClient.init(self._client, task_id)
1283
+ except ConflictError as e:
1284
+ raise NotFoundError(str(e)) from e
1270
1285
  return self._command_router_client
1271
1286
 
1272
1287
  @property
modal/sandbox.pyi CHANGED
@@ -172,7 +172,7 @@ class _Sandbox(modal._object._Object):
172
172
  mounts: collections.abc.Sequence[modal.mount._Mount] = (),
173
173
  network_file_systems: dict[typing.Union[str, os.PathLike], modal.network_file_system._NetworkFileSystem] = {},
174
174
  block_network: bool = False,
175
- cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
175
+ outbound_cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
176
176
  inbound_cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
177
177
  volumes: dict[
178
178
  typing.Union[str, os.PathLike],
@@ -214,7 +214,7 @@ class _Sandbox(modal._object._Object):
214
214
  cpu: typing.Union[float, tuple[float, float], None] = None,
215
215
  memory: typing.Union[int, tuple[int, int], None] = None,
216
216
  block_network: bool = False,
217
- cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
217
+ outbound_cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
218
218
  inbound_cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
219
219
  volumes: dict[
220
220
  typing.Union[str, os.PathLike],
@@ -234,6 +234,7 @@ class _Sandbox(modal._object._Object):
234
234
  client: typing.Optional[modal.client._Client] = None,
235
235
  environment_name: typing.Optional[str] = None,
236
236
  pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
237
+ cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
237
238
  ) -> _Sandbox:
238
239
  """Create a new Sandbox to run untrusted, arbitrary code.
239
240
 
@@ -270,7 +271,7 @@ class _Sandbox(modal._object._Object):
270
271
  cpu: typing.Union[float, tuple[float, float], None] = None,
271
272
  memory: typing.Union[int, tuple[int, int], None] = None,
272
273
  block_network: bool = False,
273
- cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
274
+ outbound_cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
274
275
  inbound_cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
275
276
  volumes: dict[
276
277
  typing.Union[str, os.PathLike],
@@ -926,7 +927,7 @@ class Sandbox(modal.object.Object):
926
927
  mounts: collections.abc.Sequence[modal.mount.Mount] = (),
927
928
  network_file_systems: dict[typing.Union[str, os.PathLike], modal.network_file_system.NetworkFileSystem] = {},
928
929
  block_network: bool = False,
929
- cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
930
+ outbound_cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
930
931
  inbound_cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
931
932
  volumes: dict[
932
933
  typing.Union[str, os.PathLike], typing.Union[modal.volume.Volume, modal.cloud_bucket_mount.CloudBucketMount]
@@ -971,7 +972,7 @@ class Sandbox(modal.object.Object):
971
972
  cpu: typing.Union[float, tuple[float, float], None] = None,
972
973
  memory: typing.Union[int, tuple[int, int], None] = None,
973
974
  block_network: bool = False,
974
- cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
975
+ outbound_cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
975
976
  inbound_cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
976
977
  volumes: dict[
977
978
  typing.Union[str, os.PathLike],
@@ -991,6 +992,7 @@ class Sandbox(modal.object.Object):
991
992
  client: typing.Optional[modal.client.Client] = None,
992
993
  environment_name: typing.Optional[str] = None,
993
994
  pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
995
+ cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
994
996
  ) -> Sandbox:
995
997
  """Create a new Sandbox to run untrusted, arbitrary code.
996
998
 
@@ -1029,7 +1031,7 @@ class Sandbox(modal.object.Object):
1029
1031
  cpu: typing.Union[float, tuple[float, float], None] = None,
1030
1032
  memory: typing.Union[int, tuple[int, int], None] = None,
1031
1033
  block_network: bool = False,
1032
- cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
1034
+ outbound_cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
1033
1035
  inbound_cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
1034
1036
  volumes: dict[
1035
1037
  typing.Union[str, os.PathLike],
@@ -1049,6 +1051,7 @@ class Sandbox(modal.object.Object):
1049
1051
  client: typing.Optional[modal.client.Client] = None,
1050
1052
  environment_name: typing.Optional[str] = None,
1051
1053
  pty_info: typing.Optional[modal_proto.api_pb2.PTYInfo] = None,
1054
+ cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
1052
1055
  ) -> Sandbox:
1053
1056
  """Create a new Sandbox to run untrusted, arbitrary code.
1054
1057
 
@@ -1091,7 +1094,7 @@ class Sandbox(modal.object.Object):
1091
1094
  cpu: typing.Union[float, tuple[float, float], None] = None,
1092
1095
  memory: typing.Union[int, tuple[int, int], None] = None,
1093
1096
  block_network: bool = False,
1094
- cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
1097
+ outbound_cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
1095
1098
  inbound_cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
1096
1099
  volumes: dict[
1097
1100
  typing.Union[str, os.PathLike],
@@ -1142,7 +1145,7 @@ class Sandbox(modal.object.Object):
1142
1145
  cpu: typing.Union[float, tuple[float, float], None] = None,
1143
1146
  memory: typing.Union[int, tuple[int, int], None] = None,
1144
1147
  block_network: bool = False,
1145
- cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
1148
+ outbound_cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
1146
1149
  inbound_cidr_allowlist: typing.Optional[collections.abc.Sequence[str]] = None,
1147
1150
  volumes: dict[
1148
1151
  typing.Union[str, os.PathLike],
modal/sandbox_fs.py CHANGED
@@ -112,7 +112,7 @@ class _SandboxFilesystem:
112
112
 
113
113
  **Usage**
114
114
 
115
- ```python fixture:sandbox
115
+ ```python fixture:sandbox fixture:tmpdir
116
116
  import tempfile
117
117
  from pathlib import Path
118
118
 
@@ -173,7 +173,7 @@ class _SandboxFilesystem:
173
173
 
174
174
  **Usage**
175
175
 
176
- ```python fixture:sandbox
176
+ ```python fixture:sandbox fixture:tmpdir
177
177
  sandbox.filesystem.write_text("Hello, world!\\n", "/tmp/hello.txt")
178
178
  sandbox.filesystem.copy_to_local("/tmp/hello.txt", "/tmp/local-hello.txt")
179
179
  ```
modal/sandbox_fs.pyi CHANGED
@@ -101,7 +101,7 @@ class _SandboxFilesystem:
101
101
 
102
102
  **Usage**
103
103
 
104
- ```python fixture:sandbox
104
+ ```python fixture:sandbox fixture:tmpdir
105
105
  import tempfile
106
106
  from pathlib import Path
107
107
 
@@ -131,7 +131,7 @@ class _SandboxFilesystem:
131
131
 
132
132
  **Usage**
133
133
 
134
- ```python fixture:sandbox
134
+ ```python fixture:sandbox fixture:tmpdir
135
135
  sandbox.filesystem.write_text("Hello, world!\n", "/tmp/hello.txt")
136
136
  sandbox.filesystem.copy_to_local("/tmp/hello.txt", "/tmp/local-hello.txt")
137
137
  ```
@@ -365,7 +365,7 @@ class SandboxFilesystem:
365
365
 
366
366
  **Usage**
367
367
 
368
- ```python fixture:sandbox
368
+ ```python fixture:sandbox fixture:tmpdir
369
369
  import tempfile
370
370
  from pathlib import Path
371
371
 
@@ -395,7 +395,7 @@ class SandboxFilesystem:
395
395
 
396
396
  **Usage**
397
397
 
398
- ```python fixture:sandbox
398
+ ```python fixture:sandbox fixture:tmpdir
399
399
  import tempfile
400
400
  from pathlib import Path
401
401
 
@@ -428,7 +428,7 @@ class SandboxFilesystem:
428
428
 
429
429
  **Usage**
430
430
 
431
- ```python fixture:sandbox
431
+ ```python fixture:sandbox fixture:tmpdir
432
432
  sandbox.filesystem.write_text("Hello, world!\n", "/tmp/hello.txt")
433
433
  sandbox.filesystem.copy_to_local("/tmp/hello.txt", "/tmp/local-hello.txt")
434
434
  ```
@@ -454,7 +454,7 @@ class SandboxFilesystem:
454
454
 
455
455
  **Usage**
456
456
 
457
- ```python fixture:sandbox
457
+ ```python fixture:sandbox fixture:tmpdir
458
458
  sandbox.filesystem.write_text("Hello, world!\n", "/tmp/hello.txt")
459
459
  sandbox.filesystem.copy_to_local("/tmp/hello.txt", "/tmp/local-hello.txt")
460
460
  ```
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.4.3.dev24
3
+ Version: 1.4.3.dev26
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0
@@ -27,7 +27,7 @@ modal/app.pyi,sha256=-9667eLtUgoinLTRrJ-Z3SOgMR0S89Qw_vpwEZfp2Pk,53910
27
27
  modal/billing.py,sha256=B2zXa7KeMnpqi2AQINycEk-KSXVYaLTTTAQTtuf2Gfs,257
28
28
  modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
29
29
  modal/client.py,sha256=jjT4eLFHGcvIF6Xw4kPptIx-VmExtphRN8XJ1JA7fWI,15502
30
- modal/client.pyi,sha256=el3f1cVbAcF8Tws2AZauC4tXWiL6Yk_MaVZjKUPFByA,15703
30
+ modal/client.pyi,sha256=_BRq_T4QPNBayMoxYzbcS7HbL-wF-Isrw1FbtRorLyM,15703
31
31
  modal/cloud_bucket_mount.py,sha256=KkaJVy5vpO_u2NtDYtv1JM-M3y4J0UtWBapTDKpQRcA,6034
32
32
  modal/cloud_bucket_mount.pyi,sha256=mGDeEd1XvxGpCE6O5jRBSfIvMXvZ5AtIybea5k-0w9w,7560
33
33
  modal/cls.py,sha256=Yrl5b_tQhZMhc5avInM-i5UlGuqeog6Gs4rX2d9lLjI,40390
@@ -44,7 +44,7 @@ modal/file_io.py,sha256=DYQXrPUNkKG-0lyzc81E3rh7g56Fg5y6eIQkTqhfqbU,22015
44
44
  modal/file_io.pyi,sha256=DPaq0BhySaiCX3g56QHhyIMdU1izbfWsjpSp30JoJUU,15745
45
45
  modal/file_pattern_matcher.py,sha256=A_Kdkej6q7YQyhM_2-BvpFmPqJ0oHb54B6yf9VqvPVE,8116
46
46
  modal/functions.py,sha256=EZRcHFfnoYbvawUJnAHF5nIvoo9GX2Djy7mUmOCXmXc,258
47
- modal/functions.pyi,sha256=_sZ27MFClkElRg0iyVXmn9pGXwm9F-r49sHjrYhH4Sg,34255
47
+ modal/functions.pyi,sha256=m2uNU9DMi9LWprk9PJykCM1Zs_oEG2-Vl2FaJMGSNpY,34255
48
48
  modal/image.py,sha256=K-105RcY39Kq8uC4toG6WOMTnAVlPn_6P8J_DiNxiyQ,113430
49
49
  modal/image.pyi,sha256=FUAY23rnaUmpRBVazrzh3Z3b9tYTMikbyk2SbuIpihI,81004
50
50
  modal/io_streams.py,sha256=15dSqTktIFvqANRDO8pjFjuwgybGAdRBorEM-3ag-fw,25000
@@ -69,10 +69,10 @@ modal/retries.py,sha256=vPWWKP-FRolJwTBuLPXdWqHV7aJfSQFgEW7vhlBBXRQ,5067
69
69
  modal/runner.py,sha256=GLx_JwZ2hpA5yuHMY2b2Gt8Ri-KxJZHF2Zp1N_qVLD4,30144
70
70
  modal/runner.pyi,sha256=7QHbZawq1P5N9DiSrrEYtXtBCDAWlRibNonBO0q6MdM,9570
71
71
  modal/running_app.py,sha256=v61mapYNV1-O-Uaho5EfJlryMLvIT9We0amUOSvSGx8,1188
72
- modal/sandbox.py,sha256=gibxetDNoZivsDn_BmJvOJHeTLzjWGko60Q8sj3ZM1U,81851
73
- modal/sandbox.pyi,sha256=zigvkGY9j7Zrw1eD2cmiKgHuFnIJI4OFmc-YtlUhi64,84785
74
- modal/sandbox_fs.py,sha256=bivQ3c27PqG1HazInNWMVcKSjqJhspe5A_Qc2z3Cm8c,22442
75
- modal/sandbox_fs.pyi,sha256=zsSDGNSTkjo_gJYQH3J7if8Y6J8_Uy4f9u0CypCo5uc,34946
72
+ modal/sandbox.py,sha256=b7LOE6k1Y1Pd6-NdK7jCUxfXwBP7wpxN2SBB5Jm2hz4,82618
73
+ modal/sandbox.pyi,sha256=dE_OtbiDZr2o1abgk-rO_kj8kWdzSOyHEjcgfB9rPPA,85102
74
+ modal/sandbox_fs.py,sha256=3EGten2nvZ7H451FnH0gbFXcdLvQtQNVX-7qI0TtT3c,22472
75
+ modal/sandbox_fs.pyi,sha256=01VyLN0AIplD45qh8F6NJXYj5YxGPaqRETMp4rDLZcQ,35036
76
76
  modal/schedule.py,sha256=ng0g0AqNY5GQI9KhkXZQ5Wam5G42glbkqVQsNpBtbDE,3078
77
77
  modal/scheduler_placement.py,sha256=BAREdOY5HzHpzSBqt6jDVR6YC_jYfHMVqOzkyqQfngU,1235
78
78
  modal/secret.py,sha256=N44g75jJ8r8kcit4U_m5Vu9AdQwi9pXFehpKviP8amQ,18655
@@ -150,9 +150,9 @@ modal/cli/app.py,sha256=3tvOLATCUA85-kYSkpoVk9NV9rvmsz8BGl-km_zL_X4,21991
150
150
  modal/cli/billing.py,sha256=afXmnVmak5gzPF9n-3v_WQ7jTr2idyZCKk7gKRKuYmo,5727
151
151
  modal/cli/bootstrap.py,sha256=tnEHGvLWc3oLy5hv2spB2-8AIcck8gLWgHDEI126Wl8,5300
152
152
  modal/cli/changelog.py,sha256=RSem_IBx4Zb6BsJbHUFs50PElm37O7fEjcL2f6FalSc,5848
153
- modal/cli/cluster.py,sha256=F6txQYZ7nK3T3JApLWefklij8Zs_on1lxq28aSRPCv8,3903
153
+ modal/cli/cluster.py,sha256=7v2o0BYzsGy32mmskmITQqaUYcscvag6JnBmg2r5PKs,4402
154
154
  modal/cli/config.py,sha256=2_PV3TKd7BA-5EhhskGS3_tUVMGUptimjw8KBEHvSQY,1857
155
- modal/cli/container.py,sha256=kP7XDtEs0DDr2He80S2sZWWGz8ggY69pfniTU0EDb74,12071
155
+ modal/cli/container.py,sha256=0OZE3hmrJPjJGJQJyllkgML3V6XgddfIntzVYk6Z5H4,12570
156
156
  modal/cli/dashboard.py,sha256=u3HzFNb4BgSBTW8ve034XNO0Tdt_z5P1kr8RcGFMxcw,941
157
157
  modal/cli/dict.py,sha256=S1bN8fakoxlnAUMhYlRACM7iTP2PuNLGQ8kQnWgMS9M,5088
158
158
  modal/cli/entry_point.py,sha256=0g7C_BGDg6GPJSCKEOty9GPGXhkjlRI6UoP9oZVQdDI,4647
@@ -166,7 +166,7 @@ modal/cli/queues.py,sha256=88C8hrLjly96f87b7tCJdLMrR9sc2pvB1W4Xipftv-g,6568
166
166
  modal/cli/run.py,sha256=W6VcY1vjpLoEVM5h9zHfgpExXB786QfXnbaat8Qfark,22651
167
167
  modal/cli/secret.py,sha256=fB7MSj8ad65uXKZe4fci2m64ZfHXqsNVnLN-1Yarzwg,8255
168
168
  modal/cli/selector.py,sha256=OJr9Ua3K_l-qu3N3-I8z8oVq8lBakws2-XgfheR5p-c,4741
169
- modal/cli/shell.py,sha256=_MDwAnDaK0Ww5XsRfSezr2UbXBRP3PnlW63ZajttQ-g,14725
169
+ modal/cli/shell.py,sha256=tzcNuCCZk_0nhUT-jQK824BzDQYuWc_N73nhmwwmROU,14970
170
170
  modal/cli/token.py,sha256=Km7V7pHXsNKyjOCEdioQT8-o04Rh2-PzmNpiL1_i8tw,4352
171
171
  modal/cli/utils.py,sha256=CUNFXLcV8EJymZCTXettHDJTDBJi73uHMywayr0ytow,6305
172
172
  modal/cli/volume.py,sha256=pP1yqWCwzy1IP-_BbUKZKysdx7sMI5RT5jZUkj1DPsc,11514
@@ -174,10 +174,10 @@ modal/cli/programs/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxD
174
174
  modal/cli/programs/run_jupyter.py,sha256=IJw8nds8Cjl9j4dxBqMGxhz-bIyVX0kle7jbt8HFOhM,2688
175
175
  modal/cli/programs/vscode.py,sha256=E1aJPU7b8RWWj-JX71DifsCoLYsCAnre7lATiBJjUms,3386
176
176
  modal/experimental/__init__.py,sha256=Wb1YoAKAnNxH_0c2wA0T12I7bSu9PSO2nS_LuLska7I,7130
177
- modal/experimental/flash.py,sha256=ATGhVO8AleJ-TOe2PGolI5T7zfCEtj0kl_ivMPp7Rqg,33362
177
+ modal/experimental/flash.py,sha256=_WPRMr8NnhHKwV9rRFOzch80jYZAsLUZbvaG8wpyvPQ,33365
178
178
  modal/experimental/flash.pyi,sha256=R4h2nQl7-oiwqq_twCp0YqID_LnPjPGqxywXvxbiLIk,17011
179
179
  modal/experimental/ipython.py,sha256=TrCfmol9LGsRZMeDoeMPx3Hv3BFqQhYnmD_iH0pqdhk,2904
180
- modal-1.4.3.dev24.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
180
+ modal-1.4.3.dev26.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
181
181
  modal_docs/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,28
182
182
  modal_docs/gen_cli_docs.py,sha256=bfUSLscHfoQoKkKjIqJvQMe3fURzqcQK7gF1h9WQLtM,3742
183
183
  modal_docs/gen_cli_docs_main.py,sha256=UPGr7O1DYTIRLwfINe2rbdh89J3cPVTU1OWL-SjxnOQ,198
@@ -187,22 +187,22 @@ modal_docs/mdmd/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,2
187
187
  modal_docs/mdmd/mdmd.py,sha256=z9T3lMktTq3N4r6T6MSSyw8lebbqKc3Igw8l9EdMJNM,9998
188
188
  modal_docs/mdmd/signatures.py,sha256=XJaZrK7Mdepk5fdX51A8uENiLFNil85Ud0d4MH8H5f0,3218
189
189
  modal_proto/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
190
- modal_proto/api_grpc.py,sha256=H4Z21x81592KlGzgDIcgltk5WixY5vs5EyOH8Q89wvA,153140
191
- modal_proto/api_pb2.py,sha256=aidSPW5uZb4F35XlDp95dzZcT6E2ocEll7QEjI4VZLg,424219
192
- modal_proto/api_pb2.pyi,sha256=af6_IDtrNplPKkonrmJwQeiUfoLceP00USGR23r5eFw,594154
193
- modal_proto/api_pb2_grpc.py,sha256=dWnojBtfuaqHvBos1FL-JMNEB7LsNHM1UGUpKXO5IFY,329867
194
- modal_proto/api_pb2_grpc.pyi,sha256=qDqqKIs18olWaoBjLOJ8Z6Gn8_GjpNn3Z1Pw1yG_M4I,77024
195
- modal_proto/modal_api_grpc.py,sha256=bBPhTehvCriGANMHxXNxKa58ew_2QbSvO88zhKCr4CE,24177
190
+ modal_proto/api_grpc.py,sha256=m0qewo8OuWxTAQkTN0-_mBX9V3DJTWJ0Gw-jo4Ug_x8,153908
191
+ modal_proto/api_pb2.py,sha256=2R1raG2ju66IEvAAwwrUzXkq3r2npv-T791v0VA0lU4,431942
192
+ modal_proto/api_pb2.pyi,sha256=XCZFwUl2EYydwjtIg8p6ZclVoqGu9PIyhYcE3BoFTjI,605617
193
+ modal_proto/api_pb2_grpc.py,sha256=-ulRQTC92M00MDa7O2OKABgwgq3w5MYKbQAem9QLBTg,331488
194
+ modal_proto/api_pb2_grpc.pyi,sha256=R3HeGMYv8cTBuzohYodGZf63g_1EILXUj9IfuWdQJMQ,77441
195
+ modal_proto/modal_api_grpc.py,sha256=uLaaPRcnDBe_MQkShJ2Zpw1MtcbF10yjMEF5YNFtjmw,24293
196
196
  modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
197
197
  modal_proto/task_command_router_grpc.py,sha256=zogBeCYu_EYOz9Gv3699R-cM_ZRc7rtH8Ve5A0d5eKU,15467
198
198
  modal_proto/task_command_router_pb2.py,sha256=FQkKO2A32g8EnaIZvtmYK4qD7gzjNdLDBw5Ar2Wur60,29760
199
199
  modal_proto/task_command_router_pb2.pyi,sha256=bDyqZqe0_UBdBedYEEbc74V6-lW5l5yCxd85L0-VRFI,33421
200
200
  modal_proto/task_command_router_pb2_grpc.py,sha256=BCg4KzeMxsWJlKTTk7Kp5EOKzE_O6IyuEAe5mTi9mRs,30645
201
201
  modal_proto/task_command_router_pb2_grpc.pyi,sha256=5PzLp8Mddys_uygAmzM23bB1dSwhwXDIgj-twKGaKMo,9594
202
- modal_version/__init__.py,sha256=uRoZbpu1gEgo7Nh9M4GRBWh5CmQPUjO7a4xyDfTiURI,121
202
+ modal_version/__init__.py,sha256=thN7WwLUD39HzDXk41e-FgfzQK3q_GpiKIjqY--tQwM,121
203
203
  modal_version/__main__.py,sha256=nLUbubxKx7GXNzJIkTB7M7_u8080LyLM0IS0rzFkC_c,119
204
- modal-1.4.3.dev24.dist-info/METADATA,sha256=GEFtkzKy9Cr8cCdKlG1RBEf2hZy4caSYZo-gb_0ChfU,2339
205
- modal-1.4.3.dev24.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
206
- modal-1.4.3.dev24.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
207
- modal-1.4.3.dev24.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
208
- modal-1.4.3.dev24.dist-info/RECORD,,
204
+ modal-1.4.3.dev26.dist-info/METADATA,sha256=fY3-gD-wTvkaVziNPYoIOxDJSnF87uoFnqsVZVY02wo,2339
205
+ modal-1.4.3.dev26.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
206
+ modal-1.4.3.dev26.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
207
+ modal-1.4.3.dev26.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
208
+ modal-1.4.3.dev26.dist-info/RECORD,,
modal_proto/api_grpc.py CHANGED
@@ -259,6 +259,10 @@ class ModalClientBase(abc.ABC):
259
259
  async def DomainList(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.DomainListRequest, modal_proto.api_pb2.DomainListResponse]') -> None:
260
260
  pass
261
261
 
262
+ @abc.abstractmethod
263
+ async def EndpointCreate(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.EndpointCreateRequest, modal_proto.api_pb2.EndpointCreateResponse]') -> None:
264
+ pass
265
+
262
266
  @abc.abstractmethod
263
267
  async def EnvironmentCreate(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.EnvironmentCreateRequest, google.protobuf.empty_pb2.Empty]') -> None:
264
268
  pass
@@ -1173,6 +1177,12 @@ class ModalClientBase(abc.ABC):
1173
1177
  modal_proto.api_pb2.DomainListRequest,
1174
1178
  modal_proto.api_pb2.DomainListResponse,
1175
1179
  ),
1180
+ '/modal.client.ModalClient/EndpointCreate': grpclib.const.Handler(
1181
+ self.EndpointCreate,
1182
+ grpclib.const.Cardinality.UNARY_UNARY,
1183
+ modal_proto.api_pb2.EndpointCreateRequest,
1184
+ modal_proto.api_pb2.EndpointCreateResponse,
1185
+ ),
1176
1186
  '/modal.client.ModalClient/EnvironmentCreate': grpclib.const.Handler(
1177
1187
  self.EnvironmentCreate,
1178
1188
  grpclib.const.Cardinality.UNARY_UNARY,
@@ -2367,6 +2377,12 @@ class ModalClientStub:
2367
2377
  modal_proto.api_pb2.DomainListRequest,
2368
2378
  modal_proto.api_pb2.DomainListResponse,
2369
2379
  )
2380
+ self.EndpointCreate = grpclib.client.UnaryUnaryMethod(
2381
+ channel,
2382
+ '/modal.client.ModalClient/EndpointCreate',
2383
+ modal_proto.api_pb2.EndpointCreateRequest,
2384
+ modal_proto.api_pb2.EndpointCreateResponse,
2385
+ )
2370
2386
  self.EnvironmentCreate = grpclib.client.UnaryUnaryMethod(
2371
2387
  channel,
2372
2388
  '/modal.client.ModalClient/EnvironmentCreate',