modal 0.74.6__py3-none-any.whl → 0.74.7__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/_resolver.py CHANGED
@@ -1,6 +1,8 @@
1
1
  # Copyright Modal Labs 2023
2
2
  import asyncio
3
3
  import contextlib
4
+ import os
5
+ import tempfile
4
6
  import typing
5
7
  from asyncio import Future
6
8
  from collections.abc import Hashable
@@ -46,6 +48,7 @@ class Resolver:
46
48
  _app_id: Optional[str]
47
49
  _deduplication_cache: dict[Hashable, Future]
48
50
  _client: _Client
51
+ _build_start: float
49
52
 
50
53
  def __init__(
51
54
  self,
@@ -73,6 +76,11 @@ class Resolver:
73
76
  self._environment_name = environment_name
74
77
  self._deduplication_cache = {}
75
78
 
79
+ with tempfile.TemporaryFile() as temp_file:
80
+ # Use file mtime to track build start time because we will later compare this baseline
81
+ # to the mtime on mounted files, and want those measurements to have the same resolution.
82
+ self._build_start = os.fstat(temp_file.fileno()).st_mtime
83
+
76
84
  @property
77
85
  def app_id(self) -> Optional[str]:
78
86
  return self._app_id
@@ -85,6 +93,10 @@ class Resolver:
85
93
  def environment_name(self):
86
94
  return self._environment_name
87
95
 
96
+ @property
97
+ def build_start(self) -> float:
98
+ return self._build_start
99
+
88
100
  async def preload(self, obj, existing_object_id: Optional[str]):
89
101
  if obj._preload is not None:
90
102
  await obj._preload(obj, self, existing_object_id)
@@ -292,6 +292,7 @@ async def blob_iter(blob_id: str, stub: ModalClientModal) -> AsyncIterator[bytes
292
292
  class FileUploadSpec:
293
293
  source: Callable[[], Union[AbstractContextManager, BinaryIO]]
294
294
  source_description: Any
295
+ source_is_path: bool
295
296
  mount_filename: str
296
297
 
297
298
  use_blob: bool
@@ -328,6 +329,7 @@ def _get_file_upload_spec(
328
329
  return FileUploadSpec(
329
330
  source=source,
330
331
  source_description=source_description,
332
+ source_is_path=isinstance(source_description, Path),
331
333
  mount_filename=mount_filename.as_posix(),
332
334
  use_blob=use_blob,
333
335
  content=content,
modal/client.pyi CHANGED
@@ -27,7 +27,7 @@ class _Client:
27
27
  _snapshotted: bool
28
28
 
29
29
  def __init__(
30
- self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.74.6"
30
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.74.7"
31
31
  ): ...
32
32
  def is_closed(self) -> bool: ...
33
33
  @property
@@ -85,7 +85,7 @@ class Client:
85
85
  _snapshotted: bool
86
86
 
87
87
  def __init__(
88
- self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.74.6"
88
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.74.7"
89
89
  ): ...
90
90
  def is_closed(self) -> bool: ...
91
91
  @property
modal/config.py CHANGED
@@ -87,7 +87,7 @@ import os
87
87
  import typing
88
88
  import warnings
89
89
  from textwrap import dedent
90
- from typing import Any, Optional
90
+ from typing import Any, Callable, Optional
91
91
 
92
92
  from google.protobuf.empty_pb2 import Empty
93
93
 
@@ -199,6 +199,15 @@ def _to_boolean(x: object) -> bool:
199
199
  return str(x).lower() not in {"", "0", "false"}
200
200
 
201
201
 
202
+ def _check_value(options: list[str]) -> Callable[[str], str]:
203
+ def checker(x: str) -> str:
204
+ if x not in options:
205
+ raise ValueError(f"Must be one of {options}.")
206
+ return x
207
+
208
+ return checker
209
+
210
+
202
211
  class _Setting(typing.NamedTuple):
203
212
  default: typing.Any = None
204
213
  transform: typing.Callable[[str], typing.Any] = lambda x: x # noqa: E731
@@ -232,6 +241,7 @@ _SETTINGS = {
232
241
  "snapshot_debug": _Setting(False, transform=_to_boolean),
233
242
  "cuda_checkpoint_path": _Setting("/__modal/.bin/cuda-checkpoint"), # Used for snapshotting GPU memory.
234
243
  "function_schemas": _Setting(False, transform=_to_boolean),
244
+ "build_validation": _Setting("error", transform=_check_value(["error", "warn", "ignore"])),
235
245
  }
236
246
 
237
247
 
@@ -253,10 +263,17 @@ class Config:
253
263
  profile = _profile
254
264
  s = _SETTINGS[key]
255
265
  env_var_key = "MODAL_" + key.upper()
266
+
267
+ def transform(val: str) -> Any:
268
+ try:
269
+ return s.transform(val)
270
+ except Exception as e:
271
+ raise InvalidError(f"Invalid value for {key} config ({val!r}): {e}")
272
+
256
273
  if use_env and env_var_key in os.environ:
257
- return s.transform(os.environ[env_var_key])
274
+ return transform(os.environ[env_var_key])
258
275
  elif profile in _user_config and key in _user_config[profile]:
259
- return s.transform(_user_config[profile][key])
276
+ return transform(_user_config[profile][key])
260
277
  else:
261
278
  return s.default
262
279
 
modal/mount.py CHANGED
@@ -9,6 +9,7 @@ import sys
9
9
  import sysconfig
10
10
  import time
11
11
  import typing
12
+ import warnings
12
13
  from collections.abc import AsyncGenerator
13
14
  from pathlib import Path, PurePosixPath
14
15
  from typing import Callable, Optional, Sequence, Union
@@ -532,6 +533,17 @@ class _Mount(_Object, type_prefix="mo"):
532
533
  n_finished += 1
533
534
  return mount_file
534
535
 
536
+ # Try to catch cases where user modified their local files (e.g. changed git branches)
537
+ # between triggering a build and Modal actually uploading the file
538
+ if config.get("build_validation") != "ignore" and file_spec.source_is_path:
539
+ mtime = os.stat(file_spec.source_description).st_mtime
540
+ if mtime > resolver.build_start:
541
+ msg = f"{file_spec.source_description} was modified during build process."
542
+ if config.get("build_validation") == "error":
543
+ raise modal.exception.ExecutionError(msg)
544
+ elif config.get("build_validation") == "warn":
545
+ warnings.warn(msg)
546
+
535
547
  request = api_pb2.MountPutFileRequest(sha256_hex=file_spec.sha256_hex)
536
548
  accounted_hashes.add(file_spec.sha256_hex)
537
549
  response = await retry_transient_errors(resolver.client.stub.MountPutFile, request, base_delay=1)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 0.74.6
3
+ Version: 0.74.7
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -10,7 +10,7 @@ modal/_object.py,sha256=JBIECWdfpRKCaCxVWZbC3Q1kF5Whk_EKvY9f4Y6AFyg,11446
10
10
  modal/_output.py,sha256=Z0nngPh2mKHMQc4MQ92YjVPc3ewOLa3I4dFBlL9nvQY,25656
11
11
  modal/_partial_function.py,sha256=8mmd5lvjZaC7qi0KAnLR1H590MlxNslAE2_Kr9biJUA,39704
12
12
  modal/_pty.py,sha256=JZfPDDpzqICZqtyPI_oMJf_9w-p_lLNuzHhwhodUXio,1329
13
- modal/_resolver.py,sha256=RtoXoYzSllPlFu0D1vel_FWiEmDO7RyToiC2bxeN8ZY,6917
13
+ modal/_resolver.py,sha256=owmQ72ZuGvrTpHxguTMYJyodnfeYcSP0MPV8wvkGa74,7375
14
14
  modal/_resources.py,sha256=5qmcirXUI8dSH926nwkUaeX9H25mqYu9mXD_KuT79-o,1733
15
15
  modal/_serialization.py,sha256=wAgaALThfr-DBV9LMhM4qY_PCH7SRhA9xgoHL2bapBk,22963
16
16
  modal/_traceback.py,sha256=IZQzB3fVlUfMHOSyKUgw0H6qv4yHnpyq-XVCNZKfUdA,5023
@@ -22,12 +22,12 @@ modal/app.py,sha256=4EeD3MXXpaeSFatuWt80xGbMH9cSYS3b9m9z3PQDlwU,48144
22
22
  modal/app.pyi,sha256=SkqXNrdnGIZ4MmNNvpGtzNLoUdyuvi9IjQQR_DRiRHk,26968
23
23
  modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
24
24
  modal/client.py,sha256=U-YKSw0n7J1ZLREt9cbEJCtmHe5YoPKFxl0xlkan2yc,15565
25
- modal/client.pyi,sha256=pdWAlWauZyI5IxKVwSnijyh7wswJdoxxFqqemBHSEDs,7591
25
+ modal/client.pyi,sha256=aosc4b3QOba0inpXwYjn8FmQTcUn4oFHS7rS5gNr4fw,7591
26
26
  modal/cloud_bucket_mount.py,sha256=YOe9nnvSr4ZbeCn587d7_VhE9IioZYRvF9VYQTQux08,5914
27
27
  modal/cloud_bucket_mount.pyi,sha256=30T3K1a89l6wzmEJ_J9iWv9SknoGqaZDx59Xs-ZQcmk,1607
28
28
  modal/cls.py,sha256=GvaNl8R5UsH7Vg88WEOyerdjvZEPK7xxi3nqHlyOW_c,33497
29
29
  modal/cls.pyi,sha256=pTYO9JsRENmsa5pDgzfoRJGm_NpCvEjEx--vs-jJkj8,10902
30
- modal/config.py,sha256=FlqVyh6LVukMahhmEGQVTwWtwtfoPfHqEo3GDn13EOA,11687
30
+ modal/config.py,sha256=nKlX60bC1O-qAEsbGq-efRX1q25h13RyVnoM_0bnhSw,12229
31
31
  modal/container_process.py,sha256=vvyK3DVPUMsuqvkKdUiQ49cDLF9JawGrxpglLk5vfgI,6208
32
32
  modal/container_process.pyi,sha256=bXs2KHe7nxVuLAm6RRBqXCvDKelANGX9gFY8qIuZYDs,2898
33
33
  modal/dict.py,sha256=3Pb45IkfqcDGXu3VVStJVbC_QYk6RTRXrMbZxtByAAk,13354
@@ -45,7 +45,7 @@ modal/image.py,sha256=I-9_YZL0SSfnuGPywa3-4PlxDmJ-53p7ce3gP74SrOA,92877
45
45
  modal/image.pyi,sha256=89zv12C1sFrJs7Es9SnX23_m208ASAdeNGCVTrhjzHI,25632
46
46
  modal/io_streams.py,sha256=YDZVQSDv05DeXg5TwcucC9Rj5hQBx2GXdluan9rIUpw,15467
47
47
  modal/io_streams.pyi,sha256=RpXIWFm6fQkLJRc1uxd0KkQ2wTLCB65jRlyGplU6CQE,5100
48
- modal/mount.py,sha256=_v1VYxryY3BcF4XxyRfOWm7AxAz79D2VRWh5tOHhWuc,31945
48
+ modal/mount.py,sha256=VruBgKrCXD_mmv7iXEfSYERQ3CwH7iqymA3v-5xLGMc,32685
49
49
  modal/mount.pyi,sha256=CmHa7zKSxHA_7-vMQLnGfw_ZXvAvHlafvUEVJcQ1LQA,12535
50
50
  modal/network_file_system.py,sha256=WXdyL7du_fvjvuG6hSAREyJ83sSEP2xSLAIAhBsisdI,14869
51
51
  modal/network_file_system.pyi,sha256=4N3eqMbTSlqmS8VV_aJK-uvrgJC8xnf_YtW5FHfRfc8,8156
@@ -92,7 +92,7 @@ modal/_runtime/user_code_imports.py,sha256=kAv37Pl1TmGKduv0Kozum0xNTD42bDLloSIsT
92
92
  modal/_utils/__init__.py,sha256=waLjl5c6IPDhSsdWAm9Bji4e2PVxamYABKAze6CHVXY,28
93
93
  modal/_utils/app_utils.py,sha256=88BT4TPLWfYAQwKTHcyzNQRHg8n9B-QE2UyJs96iV-0,108
94
94
  modal/_utils/async_utils.py,sha256=b2TJyKY1Hq7df7M-fo3qlFM95mGdo3dCuqRPPcV5hsE,27445
95
- modal/_utils/blob_utils.py,sha256=RB1G6T7eC1Poe-O45qYLaxwCr2jkM-Q6Nexk1J3wk_w,14505
95
+ modal/_utils/blob_utils.py,sha256=jWJovk4g-YNG3CvkvglOds4a6D1M0Tcal_59v7y9VsM,14591
96
96
  modal/_utils/bytes_io_segment_payload.py,sha256=uunxVJS4PE1LojF_UpURMzVK9GuvmYWRqQo_bxEj5TU,3385
97
97
  modal/_utils/deprecation.py,sha256=EXP1beU4pmEqEzWMLw6E3kUfNfpmNA_VOp6i0EHi93g,4856
98
98
  modal/_utils/docker_utils.py,sha256=h1uETghR40mp_y3fSWuZAfbIASH1HMzuphJHghAL6DU,3722
@@ -145,7 +145,7 @@ modal/requirements/2024.10.txt,sha256=qD-5cVIVM9wXesJ6JC89Ew-3m2KjEElUz3jaw_MddR
145
145
  modal/requirements/PREVIEW.txt,sha256=qD-5cVIVM9wXesJ6JC89Ew-3m2KjEElUz3jaw_MddRo,296
146
146
  modal/requirements/README.md,sha256=9tK76KP0Uph7O0M5oUgsSwEZDj5y-dcUPsnpR0Sc-Ik,854
147
147
  modal/requirements/base-images.json,sha256=57vMSqzMbLBxw5tFWSaMiIkkVEps4JfX5PAtXGnkS4U,740
148
- modal-0.74.6.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
148
+ modal-0.74.7.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
149
149
  modal_docs/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,28
150
150
  modal_docs/gen_cli_docs.py,sha256=c1yfBS_x--gL5bs0N4ihMwqwX8l3IBWSkBAKNNIi6bQ,3801
151
151
  modal_docs/gen_reference_docs.py,sha256=cvTgltucqYLLIX84QxAwf51Z5Vc2n6cLxS8VcrxNCAo,6401
@@ -170,9 +170,9 @@ modal_proto/options_pb2_grpc.pyi,sha256=CImmhxHsYnF09iENPoe8S4J-n93jtgUYD2JPAc0y
170
170
  modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
171
171
  modal_version/__init__.py,sha256=m94xZNWIjH8oUtJk4l9xfovzDJede2o7X-q0MHVECtM,470
172
172
  modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
173
- modal_version/_version_generated.py,sha256=6eAc8YcPAZ4O3SrxUQmYc-ZMTJIY09o9f_9kCQ9b0P0,148
174
- modal-0.74.6.dist-info/METADATA,sha256=jyd1jI7Vs2q0YnrEghXL9Z0d-YkTusYBjZhkpnIg36o,2473
175
- modal-0.74.6.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
176
- modal-0.74.6.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
177
- modal-0.74.6.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
178
- modal-0.74.6.dist-info/RECORD,,
173
+ modal_version/_version_generated.py,sha256=PxczQaV6MUfmLB286M66JcCtkVQoLcQkaEYPUiP6SWU,148
174
+ modal-0.74.7.dist-info/METADATA,sha256=kGNkT7G4UznAdhFmP8zEUJa_uSOeQXAtsPRnbBFysy4,2473
175
+ modal-0.74.7.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
176
+ modal-0.74.7.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
177
+ modal-0.74.7.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
178
+ modal-0.74.7.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  # Copyright Modal Labs 2025
2
2
 
3
3
  # Note: Reset this value to -1 whenever you make a minor `0.X` release of the client.
4
- build_number = 6 # git: e51abe8
4
+ build_number = 7 # git: 52311ba
File without changes