modAL 1.4.1.dev5__py3-none-any.whl → 1.4.2.dev0__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.
@@ -12,6 +12,7 @@ from ..exception import (
12
12
  Error as ModalError,
13
13
  InvalidError,
14
14
  NotFoundError,
15
+ SandboxFilesystemDirectoryNotEmptyError,
15
16
  SandboxFilesystemError,
16
17
  SandboxFilesystemFileTooLargeError,
17
18
  SandboxFilesystemIsADirectoryError,
@@ -153,6 +154,40 @@ def make_read_file_command(remote_path: str) -> str:
153
154
  return json.dumps({"ReadFile": {"path": remote_path}})
154
155
 
155
156
 
157
+ def make_remove_command(remote_path: str, recursive: bool) -> str:
158
+ """Build the JSON command string for a Remove operation.
159
+
160
+ The returned JSON must match the `Command` enum in the modal-sandbox-fs-tools
161
+ Rust crate (crates/modal-sandbox-fs-tools/src/lib.rs). Treat changes to
162
+ this schema like protobuf changes: fields must not be removed or renamed,
163
+ only added with backwards-compatible defaults.
164
+ """
165
+ return json.dumps({"Remove": {"path": remote_path, "recursive": recursive}})
166
+
167
+
168
+ def raise_remove_error(returncode: int, stderr: Union[str, bytes], remote_path: str) -> NoReturn:
169
+ if payload := try_parse_error_payload(stderr):
170
+ logger.debug(
171
+ f"sandbox-fs-tools remove error: path={remote_path}, "
172
+ f"error_kind={payload.error_kind}, message={payload.message}, detail={payload.detail}"
173
+ )
174
+ if payload.error_kind == "NotFound":
175
+ raise SandboxFilesystemNotFoundError(f"{payload.message}: {remote_path}")
176
+ if payload.error_kind == "DirectoryNotEmpty":
177
+ raise SandboxFilesystemDirectoryNotEmptyError(f"{payload.message}: {remote_path}")
178
+ if payload.error_kind == "NotSupported":
179
+ raise InvalidError(
180
+ f"{payload.message}: {remote_path} - this operation is not supported for CloudBucketMounts"
181
+ )
182
+ if payload.error_kind == "PermissionDenied":
183
+ raise SandboxFilesystemPermissionError(f"{payload.message}: {remote_path}")
184
+ raise SandboxFilesystemError(payload.message)
185
+
186
+ if stderr_text := _stderr_to_text(stderr):
187
+ logger.debug(f"Unstructured modal-sandbox-fs-tools stderr: {stderr_text}")
188
+ raise SandboxFilesystemError(f"Operation on '{remote_path}' failed with exit code {returncode}")
189
+
190
+
156
191
  def validate_absolute_remote_path(remote_path: str, operation: str) -> None:
157
192
  if not PurePosixPath(remote_path).is_absolute():
158
193
  raise InvalidError(f"Sandbox.filesystem.{operation}() currently only supports absolute remote_path values")
@@ -164,6 +164,61 @@ def parse_date_range(s: str, tz: Optional[tzinfo] = None) -> tuple[datetime, dat
164
164
  raise ValueError(f"Unrecognized range: '{s}'. Accepted values: {accepted}")
165
165
 
166
166
 
167
+ def relative_timestamp(dt: datetime) -> str:
168
+ """Convert a tz-aware datetime to a human-readable relative time string.
169
+
170
+ Examples: "just now", "30 seconds ago", "5 minutes ago", "2 hours ago",
171
+ "yesterday", "3 days ago", "2 weeks ago", "3 months ago", "1 year ago".
172
+
173
+ Raises ValueError if the datetime is naive (no tzinfo).
174
+ """
175
+ if dt.tzinfo is None:
176
+ raise ValueError("datetime must be timezone-aware")
177
+
178
+ now = datetime.now(timezone.utc)
179
+ delta = now - dt
180
+ total_seconds = int(delta.total_seconds())
181
+
182
+ if total_seconds < 0:
183
+ return "just now"
184
+
185
+ if total_seconds < 10:
186
+ return "just now"
187
+ if total_seconds < 60:
188
+ return f"{total_seconds} seconds ago"
189
+ if total_seconds < 120:
190
+ return "1 minute ago"
191
+
192
+ minutes = total_seconds // 60
193
+ if minutes < 60:
194
+ return f"{minutes} minutes ago"
195
+ if minutes < 120:
196
+ return "1 hour ago"
197
+
198
+ hours = minutes // 60
199
+ if hours < 24:
200
+ return f"{hours} hours ago"
201
+ if hours < 48:
202
+ return "yesterday"
203
+
204
+ days = hours // 24
205
+ if days < 14:
206
+ return f"{days} days ago"
207
+
208
+ weeks = days // 7
209
+ if days < 60:
210
+ return f"{weeks} weeks ago"
211
+
212
+ months = days // 30
213
+ if days < 365:
214
+ return f"{months} months ago"
215
+
216
+ years = days // 365
217
+ if years == 1:
218
+ return "1 year ago"
219
+ return f"{years} years ago"
220
+
221
+
167
222
  def locale_tz() -> tzinfo:
168
223
  return datetime.now().astimezone().tzinfo
169
224
 
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.1.dev5",
38
+ version: str = "1.4.2.dev0",
39
39
  ):
40
40
  """mdmd:hidden
41
41
  The Modal client object is not intended to be instantiated directly by users.
@@ -171,7 +171,7 @@ class Client:
171
171
  server_url: str,
172
172
  client_type: int,
173
173
  credentials: typing.Optional[tuple[str, str]],
174
- version: str = "1.4.1.dev5",
174
+ version: str = "1.4.2.dev0",
175
175
  ):
176
176
  """mdmd:hidden
177
177
  The Modal client object is not intended to be instantiated directly by users.
modal/exception.py CHANGED
@@ -351,6 +351,12 @@ class SandboxFilesystemNotFoundError(SandboxFilesystemError):
351
351
  pass
352
352
 
353
353
 
354
+ class SandboxFilesystemDirectoryNotEmptyError(SandboxFilesystemError):
355
+ """Raised when a directory is not empty."""
356
+
357
+ pass
358
+
359
+
354
360
  class SandboxFilesystemIsADirectoryError(SandboxFilesystemError):
355
361
  """Raised when a file operation in the sandbox targets a directory when it should target a non-directory file."""
356
362
 
modal/sandbox.py CHANGED
@@ -123,24 +123,25 @@ class SandboxConnectCredentials:
123
123
  class Probe:
124
124
  """Probe configuration for the Sandbox Readiness Probe.
125
125
 
126
- Usage:
127
- ```py notest
128
- # Wait until a file exists.
129
- readiness_probe = modal.Probe.with_exec(
130
- "sh", "-c", "test -f /tmp/ready",
131
- )
132
-
133
- # Wait until a TCP port is accepting connections.
134
- readiness_probe = modal.Probe.with_tcp(8080)
135
-
136
- app = modal.App.lookup('sandbox-readiness-probe', create_if_missing=True)
137
- sandbox = modal.Sandbox.create(
138
- "python3", "-m", "http.server", "8080",
139
- readiness_probe=readiness_probe,
140
- app=app,
141
- )
142
- sandbox.wait_until_ready()
143
- ```
126
+ **Usage**
127
+
128
+ ```python notest
129
+ # Wait until a file exists.
130
+ readiness_probe = modal.Probe.with_exec(
131
+ "sh", "-c", "test -f /tmp/ready",
132
+ )
133
+
134
+ # Wait until a TCP port is accepting connections.
135
+ readiness_probe = modal.Probe.with_tcp(8080)
136
+
137
+ app = modal.App.lookup('sandbox-readiness-probe', create_if_missing=True)
138
+ sandbox = modal.Sandbox.create(
139
+ "python3", "-m", "http.server", "8080",
140
+ readiness_probe=readiness_probe,
141
+ app=app,
142
+ )
143
+ sandbox.wait_until_ready()
144
+ ```
144
145
  """
145
146
 
146
147
  tcp_port: Optional[int] = None
modal/sandbox.pyi CHANGED
@@ -74,24 +74,25 @@ class SandboxConnectCredentials:
74
74
  class Probe:
75
75
  """Probe configuration for the Sandbox Readiness Probe.
76
76
 
77
- Usage:
78
- ```py notest
79
- # Wait until a file exists.
80
- readiness_probe = modal.Probe.with_exec(
81
- "sh", "-c", "test -f /tmp/ready",
82
- )
83
-
84
- # Wait until a TCP port is accepting connections.
85
- readiness_probe = modal.Probe.with_tcp(8080)
86
-
87
- app = modal.App.lookup('sandbox-readiness-probe', create_if_missing=True)
88
- sandbox = modal.Sandbox.create(
89
- "python3", "-m", "http.server", "8080",
90
- readiness_probe=readiness_probe,
91
- app=app,
92
- )
93
- sandbox.wait_until_ready()
94
- ```
77
+ **Usage**
78
+
79
+ ```python notest
80
+ # Wait until a file exists.
81
+ readiness_probe = modal.Probe.with_exec(
82
+ "sh", "-c", "test -f /tmp/ready",
83
+ )
84
+
85
+ # Wait until a TCP port is accepting connections.
86
+ readiness_probe = modal.Probe.with_tcp(8080)
87
+
88
+ app = modal.App.lookup('sandbox-readiness-probe', create_if_missing=True)
89
+ sandbox = modal.Sandbox.create(
90
+ "python3", "-m", "http.server", "8080",
91
+ readiness_probe=readiness_probe,
92
+ app=app,
93
+ )
94
+ sandbox.wait_until_ready()
95
+ ```
95
96
  """
96
97
 
97
98
  tcp_port: typing.Optional[int]
modal/sandbox_fs.py CHANGED
@@ -12,8 +12,10 @@ from ._utils.async_utils import synchronize_api
12
12
  from ._utils.logger import logger
13
13
  from ._utils.sandbox_fs_utils import (
14
14
  make_read_file_command,
15
+ make_remove_command,
15
16
  make_write_file_command,
16
17
  raise_read_file_error,
18
+ raise_remove_error,
17
19
  raise_write_file_error,
18
20
  translate_exec_errors,
19
21
  validate_absolute_remote_path,
@@ -303,5 +305,51 @@ class _SandboxFilesystem:
303
305
  dur_s = max(time.monotonic() - t0, 0.001)
304
306
  _log_throughput(f"copy_from_local {local_path} -> {remote_path}", total_bytes, dur_s)
305
307
 
308
+ async def remove(self, remote_path: str, *, recursive: bool = False) -> None:
309
+ """Remove a file or directory in the Sandbox.
310
+
311
+ `remote_path` must be an absolute path in the Sandbox.
312
+
313
+ When `remote_path` is a directory and `recursive` is `False` (the
314
+ default), removes it only if it is empty. When `recursive` is `True`,
315
+ removes the directory and all its contents.
316
+
317
+ Recursive directory removal is not supported on all mounts.
318
+ In particular, `CloudBucketMount` does not support it. An
319
+ `InvalidError` is raised in that case.
320
+
321
+ **Raises**
322
+
323
+ - `SandboxFilesystemNotFoundError`: the path does not exist.
324
+ - `SandboxFilesystemDirectoryNotEmptyError`: `recursive` is `False` and the directory is not empty.
325
+ - `SandboxFilesystemPermissionError`: removal is not permitted.
326
+ - `InvalidError`: the operation is not supported by the mount.
327
+ - `SandboxFilesystemError`: the command fails for any other reason.
328
+
329
+ **Usage**
330
+
331
+ To remove a file:
332
+
333
+ ```python fixture:sandbox
334
+ sandbox.filesystem.write_bytes(b"Hello, world!\\n", "/tmp/hello.bin")
335
+ sandbox.filesystem.remove("/tmp/hello.bin")
336
+ ```
337
+
338
+ To remove a directory and all its contents:
339
+
340
+ ```python fixture:sandbox
341
+ sandbox.exec("mkdir", "-p", "/tmp/mydir/subdir").wait()
342
+ sandbox.filesystem.remove("/tmp/mydir", recursive=True)
343
+ ```
344
+ """
345
+ validate_absolute_remote_path(remote_path, "remove")
346
+
347
+ with translate_exec_errors("remove", remote_path):
348
+ process = await self._sandbox.exec(_SANDBOX_FS_TOOLS_PATH, make_remove_command(remote_path, recursive))
349
+ stderr, returncode = await asyncio.gather(process.stderr.read(), process.wait())
350
+
351
+ if returncode != 0:
352
+ raise_remove_error(returncode, stderr, remote_path)
353
+
306
354
 
307
355
  SandboxFilesystem = synchronize_api(_SandboxFilesystem)
modal/sandbox_fs.pyi CHANGED
@@ -168,6 +168,45 @@ class _SandboxFilesystem:
168
168
  """
169
169
  ...
170
170
 
171
+ async def remove(self, remote_path: str, *, recursive: bool = False) -> None:
172
+ """Remove a file or directory in the Sandbox.
173
+
174
+ `remote_path` must be an absolute path in the Sandbox.
175
+
176
+ When `remote_path` is a directory and `recursive` is `False` (the
177
+ default), removes it only if it is empty. When `recursive` is `True`,
178
+ removes the directory and all its contents.
179
+
180
+ Recursive directory removal is not supported on all mounts.
181
+ In particular, `CloudBucketMount` does not support it. An
182
+ `InvalidError` is raised in that case.
183
+
184
+ **Raises**
185
+
186
+ - `SandboxFilesystemNotFoundError`: the path does not exist.
187
+ - `SandboxFilesystemDirectoryNotEmptyError`: `recursive` is `False` and the directory is not empty.
188
+ - `SandboxFilesystemPermissionError`: removal is not permitted.
189
+ - `InvalidError`: the operation is not supported by the mount.
190
+ - `SandboxFilesystemError`: the command fails for any other reason.
191
+
192
+ **Usage**
193
+
194
+ To remove a file:
195
+
196
+ ```python fixture:sandbox
197
+ sandbox.filesystem.write_bytes(b"Hello, world!\n", "/tmp/hello.bin")
198
+ sandbox.filesystem.remove("/tmp/hello.bin")
199
+ ```
200
+
201
+ To remove a directory and all its contents:
202
+
203
+ ```python fixture:sandbox
204
+ sandbox.exec("mkdir", "-p", "/tmp/mydir/subdir").wait()
205
+ sandbox.filesystem.remove("/tmp/mydir", recursive=True)
206
+ ```
207
+ """
208
+ ...
209
+
171
210
  class SandboxFilesystem:
172
211
  """mdmd:namespace
173
212
  Namespace for Sandbox filesystem APIs.
@@ -505,3 +544,84 @@ class SandboxFilesystem:
505
544
  ...
506
545
 
507
546
  copy_from_local: __copy_from_local_spec
547
+
548
+ class __remove_spec(typing_extensions.Protocol):
549
+ def __call__(self, /, remote_path: str, *, recursive: bool = False) -> None:
550
+ """Remove a file or directory in the Sandbox.
551
+
552
+ `remote_path` must be an absolute path in the Sandbox.
553
+
554
+ When `remote_path` is a directory and `recursive` is `False` (the
555
+ default), removes it only if it is empty. When `recursive` is `True`,
556
+ removes the directory and all its contents.
557
+
558
+ Recursive directory removal is not supported on all mounts.
559
+ In particular, `CloudBucketMount` does not support it. An
560
+ `InvalidError` is raised in that case.
561
+
562
+ **Raises**
563
+
564
+ - `SandboxFilesystemNotFoundError`: the path does not exist.
565
+ - `SandboxFilesystemDirectoryNotEmptyError`: `recursive` is `False` and the directory is not empty.
566
+ - `SandboxFilesystemPermissionError`: removal is not permitted.
567
+ - `InvalidError`: the operation is not supported by the mount.
568
+ - `SandboxFilesystemError`: the command fails for any other reason.
569
+
570
+ **Usage**
571
+
572
+ To remove a file:
573
+
574
+ ```python fixture:sandbox
575
+ sandbox.filesystem.write_bytes(b"Hello, world!\n", "/tmp/hello.bin")
576
+ sandbox.filesystem.remove("/tmp/hello.bin")
577
+ ```
578
+
579
+ To remove a directory and all its contents:
580
+
581
+ ```python fixture:sandbox
582
+ sandbox.exec("mkdir", "-p", "/tmp/mydir/subdir").wait()
583
+ sandbox.filesystem.remove("/tmp/mydir", recursive=True)
584
+ ```
585
+ """
586
+ ...
587
+
588
+ async def aio(self, /, remote_path: str, *, recursive: bool = False) -> None:
589
+ """Remove a file or directory in the Sandbox.
590
+
591
+ `remote_path` must be an absolute path in the Sandbox.
592
+
593
+ When `remote_path` is a directory and `recursive` is `False` (the
594
+ default), removes it only if it is empty. When `recursive` is `True`,
595
+ removes the directory and all its contents.
596
+
597
+ Recursive directory removal is not supported on all mounts.
598
+ In particular, `CloudBucketMount` does not support it. An
599
+ `InvalidError` is raised in that case.
600
+
601
+ **Raises**
602
+
603
+ - `SandboxFilesystemNotFoundError`: the path does not exist.
604
+ - `SandboxFilesystemDirectoryNotEmptyError`: `recursive` is `False` and the directory is not empty.
605
+ - `SandboxFilesystemPermissionError`: removal is not permitted.
606
+ - `InvalidError`: the operation is not supported by the mount.
607
+ - `SandboxFilesystemError`: the command fails for any other reason.
608
+
609
+ **Usage**
610
+
611
+ To remove a file:
612
+
613
+ ```python fixture:sandbox
614
+ sandbox.filesystem.write_bytes(b"Hello, world!\n", "/tmp/hello.bin")
615
+ sandbox.filesystem.remove("/tmp/hello.bin")
616
+ ```
617
+
618
+ To remove a directory and all its contents:
619
+
620
+ ```python fixture:sandbox
621
+ sandbox.exec("mkdir", "-p", "/tmp/mydir/subdir").wait()
622
+ sandbox.filesystem.remove("/tmp/mydir", recursive=True)
623
+ ```
624
+ """
625
+ ...
626
+
627
+ remove: __remove_spec
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.4.1.dev5
3
+ Version: 1.4.2.dev0
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0
@@ -23,7 +23,7 @@ Requires-Dist: grpclib<0.4.10,>=0.4.7; python_version < "3.14"
23
23
  Requires-Dist: grpclib<0.4.10,>=0.4.9; python_version >= "3.14"
24
24
  Requires-Dist: protobuf!=4.24.0,<7.0,>=3.19
25
25
  Requires-Dist: rich>=12.0.0
26
- Requires-Dist: synchronicity~=0.12.0
26
+ Requires-Dist: synchronicity~=0.12.1
27
27
  Requires-Dist: toml
28
28
  Requires-Dist: typer>=0.9
29
29
  Requires-Dist: types-certifi
@@ -26,7 +26,7 @@ modal/app.pyi,sha256=vU6tli-ELcF3do5sCF-Wch1gFRP3xjPLoq112SCq_z8,54009
26
26
  modal/billing.py,sha256=B2zXa7KeMnpqi2AQINycEk-KSXVYaLTTTAQTtuf2Gfs,257
27
27
  modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
28
28
  modal/client.py,sha256=yxMDF2vQZAwgYCatTem1lyRLS90u9T0BFnHBUAg1XU0,14998
29
- modal/client.pyi,sha256=-xKqsfLxpU8u6xrUzMLgvDtYBu4HQ0BNASPJaUfqV3o,14930
29
+ modal/client.pyi,sha256=Kcu2T0IX1YXQ2tG9E49LYZDgI-B90LEFhpfSA41EdZw,14930
30
30
  modal/cloud_bucket_mount.py,sha256=KkaJVy5vpO_u2NtDYtv1JM-M3y4J0UtWBapTDKpQRcA,6034
31
31
  modal/cloud_bucket_mount.pyi,sha256=mGDeEd1XvxGpCE6O5jRBSfIvMXvZ5AtIybea5k-0w9w,7560
32
32
  modal/cls.py,sha256=Yrl5b_tQhZMhc5avInM-i5UlGuqeog6Gs4rX2d9lLjI,40390
@@ -38,7 +38,7 @@ modal/dict.py,sha256=_N4glRNKlm9DM5cONV_hRKk1GvIGzBR1BJ-KicBJnlo,23240
38
38
  modal/dict.pyi,sha256=9JGE-90THghjmc1Mw6bkdpEyVrXH-YuyAThEDGVrqUA,33878
39
39
  modal/environments.py,sha256=wMiMNpPkvIl_BdxRb81CY9Djzhczp_y4GEYKWp0JyBc,6135
40
40
  modal/environments.pyi,sha256=bMIxvwahckgaolGD3-hP2iovRHDXewvgbVs_nx4ze0o,3808
41
- modal/exception.py,sha256=AMvcFzlJxiFqaYe2An7fNgC4_2VFLUoNUDxr6ODwVkU,11447
41
+ modal/exception.py,sha256=MWjoIDFMspyZcX4hhXVy1Xu7aEl6A_ZKld1xqbdQA9c,11578
42
42
  modal/file_io.py,sha256=AKTaKR4y2JO1vsmJ8tRxMhgJtHmtb-TCe1ZZYDte-w4,22221
43
43
  modal/file_io.pyi,sha256=DPaq0BhySaiCX3g56QHhyIMdU1izbfWsjpSp30JoJUU,15745
44
44
  modal/file_pattern_matcher.py,sha256=A_Kdkej6q7YQyhM_2-BvpFmPqJ0oHb54B6yf9VqvPVE,8116
@@ -68,10 +68,10 @@ modal/retries.py,sha256=vPWWKP-FRolJwTBuLPXdWqHV7aJfSQFgEW7vhlBBXRQ,5067
68
68
  modal/runner.py,sha256=yIB1PE5EGdvLWPPymyT4P0KYliREnFf-MAtULl3tVN0,30062
69
69
  modal/runner.pyi,sha256=ySefjmlMQ5FsvJgJkCWiUZKvcPNfEqdB5hwxI1fGtDo,9445
70
70
  modal/running_app.py,sha256=v61mapYNV1-O-Uaho5EfJlryMLvIT9We0amUOSvSGx8,1188
71
- modal/sandbox.py,sha256=zzFMaD07l_Yc74is9Mxzoirvk0l8xWoC90Hfklu8BLU,79825
72
- modal/sandbox.pyi,sha256=IS4YlpL-e7VpqTwYeZ0CfaS_yIYmf3XRNzaCaXvfgYU,83941
73
- modal/sandbox_fs.py,sha256=mEgfFKGgLFO7Ncvw_iQc54htHZ66E1i56ADNnXj9NHQ,13080
74
- modal/sandbox_fs.pyi,sha256=qr3PWlWwHqKRa_fPe9aI2ecj-iNF-NKY52GaL_MXc1U,21245
71
+ modal/sandbox.py,sha256=gSETJMPyFlEjxbIB12ZOYvfpW9bNLyVGdW3NkGdhgzM,79773
72
+ modal/sandbox.pyi,sha256=0kaPiSCiPaphGVbYJI6kzVt5b10BQE5_acNbI2LyLtU,83889
73
+ modal/sandbox_fs.py,sha256=l3VahQQ-qeEN8LaxmjUfRp_kYi-ecX3oS4ma1V24uI0,15020
74
+ modal/sandbox_fs.pyi,sha256=Y9wtkztsBx0OuDozzpUIkBil9L6mgmDZ4gcVnm9AgEA,25985
75
75
  modal/schedule.py,sha256=ng0g0AqNY5GQI9KhkXZQ5Wam5G42glbkqVQsNpBtbDE,3078
76
76
  modal/scheduler_placement.py,sha256=BAREdOY5HzHpzSBqt6jDVR6YC_jYfHMVqOzkyqQfngU,1235
77
77
  modal/secret.py,sha256=vPwpS_toTsDNq3qZ87Rlqb0g6JvU2qYEjENJHDXPXto,18671
@@ -124,10 +124,10 @@ modal/_utils/name_utils.py,sha256=CIQL0Y4z47tq0rfNL2Kdo5dxsGrxoN2hR9Ko0HapeE4,25
124
124
  modal/_utils/package_utils.py,sha256=jYE7WFVzYWIfO0ZJhno_BYHb6Skp48uupOiw1a3yWzw,2056
125
125
  modal/_utils/pattern_utils.py,sha256=ZUffaECfe2iYBhH6cvCB-0-UWhmEBTZEl_TwG_So3ag,6714
126
126
  modal/_utils/rand_pb_testing.py,sha256=oNtQB1BY9CDh7vDXEkJitCuCu9a2ufk0KRcEuZ15d0k,4229
127
- modal/_utils/sandbox_fs_utils.py,sha256=n0YWapQJ4HcjifNz9FHa7NGsisXcS14f_AMjZRQ8LyE,7060
127
+ modal/_utils/sandbox_fs_utils.py,sha256=XoSeelz7aMFUAPw7fTmnxwfEmvP9-wL5RfmoYu6DVbE,8871
128
128
  modal/_utils/shell_utils.py,sha256=GjMrO1YxnBr4_UlpA2ZYQKCHxV78QTg0mqxAMHWGTE4,3427
129
129
  modal/_utils/task_command_router_client.py,sha256=PFGwH7sFpdOJ9g50JNGGeldlzWWfg4eIpi_22tsV-rs,26049
130
- modal/_utils/time_utils.py,sha256=VVrIwm7YMv2vMznNGknkGzyATDp5iJ29F_7NZPfm5b0,7062
130
+ modal/_utils/time_utils.py,sha256=PBMHCGzysBVaEnsjc0BSf0bPdSOSXeV11rtTAu5vvus,8451
131
131
  modal/_vendor/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
132
132
  modal/_vendor/a2wsgi_wsgi.py,sha256=Q1AsjpV_Q_vzQsz_cSqmP9jWzsGsB-ARFU6vpQYml8k,21878
133
133
  modal/_vendor/cloudpickle.py,sha256=N0PN-AwRn3GWGVWBdr1rYcgKVzLOk66oz0hBSjocG4o,59166
@@ -173,7 +173,7 @@ modal/experimental/__init__.py,sha256=Wb1YoAKAnNxH_0c2wA0T12I7bSu9PSO2nS_LuLska7
173
173
  modal/experimental/flash.py,sha256=ATGhVO8AleJ-TOe2PGolI5T7zfCEtj0kl_ivMPp7Rqg,33362
174
174
  modal/experimental/flash.pyi,sha256=R4h2nQl7-oiwqq_twCp0YqID_LnPjPGqxywXvxbiLIk,17011
175
175
  modal/experimental/ipython.py,sha256=TrCfmol9LGsRZMeDoeMPx3Hv3BFqQhYnmD_iH0pqdhk,2904
176
- modal-1.4.1.dev5.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
176
+ modal-1.4.2.dev0.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
177
177
  modal_docs/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,28
178
178
  modal_docs/gen_cli_docs.py,sha256=5sVABw4uGUqPXjcWT0Nqjkpe5KLs89C3tFnla58-D0M,3756
179
179
  modal_docs/gen_cli_docs_main.py,sha256=UPGr7O1DYTIRLwfINe2rbdh89J3cPVTU1OWL-SjxnOQ,198
@@ -195,10 +195,10 @@ modal_proto/task_command_router_pb2.py,sha256=EHjVM4Q-RDoXyJQ2VjFv6FmSXSFPWPYwl1
195
195
  modal_proto/task_command_router_pb2.pyi,sha256=yxufDIGZpoTlIBiKH2HPgrq0WTUmxq2kaJE3wGcgjT4,26498
196
196
  modal_proto/task_command_router_pb2_grpc.py,sha256=YoinPeLc-uwFShig8gE0MluOi6n-qqJZQ3YvnKwKUz8,23194
197
197
  modal_proto/task_command_router_pb2_grpc.pyi,sha256=VUSE5l1O6ATPT8Wbj4M3faMiIy5vFxSoS5ZlKfTEpHc,7449
198
- modal_version/__init__.py,sha256=Akj2bIyT5kPJCeyKDETecQ1GaAk7_dXohbKrNEoeSzA,120
198
+ modal_version/__init__.py,sha256=EBn10qhH2CdxgGERkQ0BQFu2-hVx69hAXMzmXl47FjY,120
199
199
  modal_version/__main__.py,sha256=nLUbubxKx7GXNzJIkTB7M7_u8080LyLM0IS0rzFkC_c,119
200
- modal-1.4.1.dev5.dist-info/METADATA,sha256=zDMtcUNGeF3L3B5lSlOAQqKyuxlfPajA-s_ifnRXe9s,2364
201
- modal-1.4.1.dev5.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
202
- modal-1.4.1.dev5.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
203
- modal-1.4.1.dev5.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
204
- modal-1.4.1.dev5.dist-info/RECORD,,
200
+ modal-1.4.2.dev0.dist-info/METADATA,sha256=Kk8x3Yk08lBWbX60G0Q5_WAZOBILx7GFtHPNXQ9pTlk,2364
201
+ modal-1.4.2.dev0.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
202
+ modal-1.4.2.dev0.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
203
+ modal-1.4.2.dev0.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
204
+ modal-1.4.2.dev0.dist-info/RECORD,,
modal_version/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # Copyright Modal Labs 2026
2
2
  """Supplies the current version of the modal client library."""
3
3
 
4
- __version__ = "1.4.1.dev5"
4
+ __version__ = "1.4.2.dev0"