modal 0.70.2__py3-none-any.whl → 0.70.4__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.
@@ -337,14 +337,17 @@ def call_function(
337
337
  signal.signal(signal.SIGUSR1, usr1_handler) # reset signal handler
338
338
 
339
339
 
340
- def get_active_app_fallback(function_def: api_pb2.Function) -> Optional[_App]:
340
+ def get_active_app_fallback(function_def: api_pb2.Function) -> _App:
341
341
  # This branch is reached in the special case that the imported function/class is:
342
342
  # 1) not serialized, and
343
343
  # 2) isn't a FunctionHandle - i.e, not decorated at definition time
344
344
  # Look at all instantiated apps - if there is only one with the indicated name, use that one
345
345
  app_name: Optional[str] = function_def.app_name or None # coalesce protobuf field to None
346
346
  matching_apps = _App._all_apps.get(app_name, [])
347
- active_app = None
347
+ if len(matching_apps) == 1:
348
+ active_app: _App = matching_apps[0]
349
+ return active_app
350
+
348
351
  if len(matching_apps) > 1:
349
352
  if app_name is not None:
350
353
  warning_sub_message = f"app with the same name ('{app_name}')"
@@ -354,12 +357,10 @@ def get_active_app_fallback(function_def: api_pb2.Function) -> Optional[_App]:
354
357
  f"You have more than one {warning_sub_message}. "
355
358
  "It's recommended to name all your Apps uniquely when using multiple apps"
356
359
  )
357
- elif len(matching_apps) == 1:
358
- (active_app,) = matching_apps
359
- # there could also technically be zero found apps, but that should probably never be an
360
- # issue since that would mean user won't use is_inside or other function handles anyway
361
360
 
362
- return active_app
361
+ # If we don't have an active app, create one on the fly
362
+ # The app object is used to carry the app layout etc
363
+ return _App()
363
364
 
364
365
 
365
366
  def call_lifecycle_functions(
@@ -403,7 +404,7 @@ def main(container_args: api_pb2.ContainerArguments, client: Client):
403
404
  # This is a bit weird but we need both the blocking and async versions of ContainerIOManager.
404
405
  # At some point, we should fix that by having built-in support for running "user code"
405
406
  container_io_manager = ContainerIOManager(container_args, client)
406
- active_app: Optional[_App] = None
407
+ active_app: _App
407
408
  service: Service
408
409
  function_def = container_args.function_def
409
410
  is_auto_snapshot: bool = function_def.is_auto_snapshot
@@ -450,8 +451,9 @@ def main(container_args: api_pb2.ContainerArguments, client: Client):
450
451
  )
451
452
 
452
453
  # If the cls/function decorator was applied in local scope, but the app is global, we can look it up
453
- active_app = service.app
454
- if active_app is None:
454
+ if service.app is not None:
455
+ active_app = service.app
456
+ else:
455
457
  # if the app can't be inferred by the imported function, use name-based fallback
456
458
  active_app = get_active_app_fallback(function_def)
457
459
 
@@ -468,9 +470,8 @@ def main(container_args: api_pb2.ContainerArguments, client: Client):
468
470
 
469
471
  # Initialize objects on the app.
470
472
  # This is basically only functions and classes - anything else is deprecated and will be unsupported soon
471
- if active_app is not None:
472
- app: App = synchronizer._translate_out(active_app)
473
- app._init_container(client, container_app)
473
+ app: App = synchronizer._translate_out(active_app)
474
+ app._init_container(client, container_app)
474
475
 
475
476
  # Hydrate all function dependencies.
476
477
  # TODO(erikbern): we an remove this once we
@@ -457,12 +457,7 @@ class _ContainerIOManager:
457
457
  resp = await retry_transient_errors(self._client.stub.AppGetLayout, req)
458
458
  app_layout = resp.app_layout
459
459
 
460
- return running_app_from_layout(
461
- self.app_id,
462
- app_layout,
463
- self._client,
464
- environment_name=self._environment_name,
465
- )
460
+ return running_app_from_layout(self.app_id, app_layout)
466
461
 
467
462
  async def get_serialized_function(self) -> tuple[Optional[Any], Optional[Callable[..., Any]]]:
468
463
  # Fetch the serialized function definition
modal/app.py CHANGED
@@ -168,7 +168,7 @@ class _App:
168
168
  """
169
169
 
170
170
  _all_apps: ClassVar[dict[Optional[str], list["_App"]]] = {}
171
- _container_app: ClassVar[Optional[RunningApp]] = None
171
+ _container_app: ClassVar[Optional["_App"]] = None
172
172
 
173
173
  _name: Optional[str]
174
174
  _description: Optional[str]
@@ -294,12 +294,7 @@ class _App:
294
294
  app = _App(name)
295
295
  app._app_id = response.app_id
296
296
  app._client = client
297
- app._running_app = RunningApp(
298
- response.app_id,
299
- client=client,
300
- environment_name=environment_name,
301
- interactive=False,
302
- )
297
+ app._running_app = RunningApp(response.app_id, interactive=False)
303
298
  return app
304
299
 
305
300
  def set_description(self, description: str):
@@ -463,8 +458,8 @@ class _App:
463
458
  if self._running_app:
464
459
  # If this is inside a container, then objects can be defined after app initialization.
465
460
  # So we may have to initialize objects once they get bound to the app.
466
- if function.tag in self._running_app.tag_to_object_id:
467
- object_id: str = self._running_app.tag_to_object_id[function.tag]
461
+ if function.tag in self._running_app.function_ids:
462
+ object_id: str = self._running_app.function_ids[function.tag]
468
463
  metadata: Message = self._running_app.object_handle_metadata[object_id]
469
464
  function._hydrate(object_id, self._client, metadata)
470
465
 
@@ -476,8 +471,8 @@ class _App:
476
471
  if self._running_app:
477
472
  # If this is inside a container, then objects can be defined after app initialization.
478
473
  # So we may have to initialize objects once they get bound to the app.
479
- if tag in self._running_app.tag_to_object_id:
480
- object_id: str = self._running_app.tag_to_object_id[tag]
474
+ if tag in self._running_app.class_ids:
475
+ object_id: str = self._running_app.class_ids[tag]
481
476
  metadata: Message = self._running_app.object_handle_metadata[object_id]
482
477
  cls._hydrate(object_id, self._client, metadata)
483
478
 
@@ -488,21 +483,21 @@ class _App:
488
483
  self._running_app = running_app
489
484
  self._client = client
490
485
 
491
- _App._container_app = running_app
492
-
493
- # Hydrate objects on app -- hydrating functions first so that when a class is being hydrated its
494
- # corresponding class service function is already hydrated.
495
- def hydrate_objects(objects_dict):
496
- for tag, object_id in running_app.tag_to_object_id.items():
497
- if tag in objects_dict:
498
- obj = objects_dict[tag]
499
- handle_metadata = running_app.object_handle_metadata[object_id]
500
- obj._hydrate(object_id, client, handle_metadata)
486
+ _App._container_app = self
501
487
 
502
488
  # Hydrate function objects
503
- hydrate_objects(self._functions)
489
+ for tag, object_id in running_app.function_ids.items():
490
+ if tag in self._functions:
491
+ obj = self._functions[tag]
492
+ handle_metadata = running_app.object_handle_metadata[object_id]
493
+ obj._hydrate(object_id, client, handle_metadata)
494
+
504
495
  # Hydrate class objects
505
- hydrate_objects(self._classes)
496
+ for tag, object_id in running_app.class_ids.items():
497
+ if tag in self._classes:
498
+ obj = self._classes[tag]
499
+ handle_metadata = running_app.object_handle_metadata[object_id]
500
+ obj._hydrate(object_id, client, handle_metadata)
506
501
 
507
502
  @property
508
503
  def registered_functions(self) -> dict[str, _Function]:
@@ -1047,6 +1042,13 @@ class _App:
1047
1042
  if log.data:
1048
1043
  yield log.data
1049
1044
 
1045
+ @classmethod
1046
+ def container_app(cls) -> Optional["_App"]:
1047
+ """Returns the `App` running inside a container.
1048
+
1049
+ This will return `None` outside of a Modal container."""
1050
+ return cls._container_app
1051
+
1050
1052
  @classmethod
1051
1053
  def _reset_container_app(cls):
1052
1054
  """Only used for tests."""
modal/app.pyi CHANGED
@@ -73,7 +73,7 @@ class _FunctionDecoratorType:
73
73
 
74
74
  class _App:
75
75
  _all_apps: typing.ClassVar[dict[typing.Optional[str], list[_App]]]
76
- _container_app: typing.ClassVar[typing.Optional[modal.running_app.RunningApp]]
76
+ _container_app: typing.ClassVar[typing.Optional[_App]]
77
77
  _name: typing.Optional[str]
78
78
  _description: typing.Optional[str]
79
79
  _functions: dict[str, modal.functions._Function]
@@ -266,11 +266,13 @@ class _App:
266
266
  self, client: typing.Optional[modal.client._Client] = None
267
267
  ) -> collections.abc.AsyncGenerator[str, None]: ...
268
268
  @classmethod
269
+ def container_app(cls) -> typing.Optional[_App]: ...
270
+ @classmethod
269
271
  def _reset_container_app(cls): ...
270
272
 
271
273
  class App:
272
274
  _all_apps: typing.ClassVar[dict[typing.Optional[str], list[App]]]
273
- _container_app: typing.ClassVar[typing.Optional[modal.running_app.RunningApp]]
275
+ _container_app: typing.ClassVar[typing.Optional[App]]
274
276
  _name: typing.Optional[str]
275
277
  _description: typing.Optional[str]
276
278
  _functions: dict[str, modal.functions.Function]
@@ -530,6 +532,8 @@ class App:
530
532
 
531
533
  _logs: ___logs_spec
532
534
 
535
+ @classmethod
536
+ def container_app(cls) -> typing.Optional[App]: ...
533
537
  @classmethod
534
538
  def _reset_container_app(cls): ...
535
539
 
modal/client.pyi CHANGED
@@ -26,7 +26,7 @@ class _Client:
26
26
  _stub: typing.Optional[modal_proto.api_grpc.ModalClientStub]
27
27
 
28
28
  def __init__(
29
- self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.70.2"
29
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.70.4"
30
30
  ): ...
31
31
  def is_closed(self) -> bool: ...
32
32
  @property
@@ -81,7 +81,7 @@ class Client:
81
81
  _stub: typing.Optional[modal_proto.api_grpc.ModalClientStub]
82
82
 
83
83
  def __init__(
84
- self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.70.2"
84
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.70.4"
85
85
  ): ...
86
86
  def is_closed(self) -> bool: ...
87
87
  @property
modal/runner.py CHANGED
@@ -6,7 +6,7 @@ import time
6
6
  import typing
7
7
  from collections.abc import AsyncGenerator
8
8
  from multiprocessing.synchronize import Event
9
- from typing import TYPE_CHECKING, Any, Callable, Optional, TypeVar
9
+ from typing import TYPE_CHECKING, Any, Optional, TypeVar
10
10
 
11
11
  from grpclib import GRPCError, Status
12
12
  from synchronicity.async_wrap import asynccontextmanager
@@ -64,7 +64,6 @@ async def _init_local_app_existing(client: _Client, existing_app_id: str, enviro
64
64
  return running_app_from_layout(
65
65
  existing_app_id,
66
66
  obj_resp.app_layout,
67
- client,
68
67
  app_page_url=app_page_url,
69
68
  )
70
69
 
@@ -89,10 +88,8 @@ async def _init_local_app_new(
89
88
  logger.debug(f"Created new app with id {app_resp.app_id}")
90
89
  return RunningApp(
91
90
  app_resp.app_id,
92
- client=client,
93
91
  app_page_url=app_resp.app_page_url,
94
92
  app_logs_url=app_resp.app_logs_url,
95
- environment_name=environment_name,
96
93
  interactive=interactive,
97
94
  )
98
95
 
@@ -124,10 +121,12 @@ async def _init_local_app_from_name(
124
121
  async def _create_all_objects(
125
122
  client: _Client,
126
123
  running_app: RunningApp,
127
- indexed_objects: dict[str, _Object],
124
+ functions: dict[str, _Function],
125
+ classes: dict[str, _Cls],
128
126
  environment_name: str,
129
127
  ) -> None:
130
128
  """Create objects that have been defined but not created on the server."""
129
+ indexed_objects: dict[str, _Object] = {**functions, **classes}
131
130
  resolver = Resolver(
132
131
  client,
133
132
  environment_name=environment_name,
@@ -135,8 +134,9 @@ async def _create_all_objects(
135
134
  )
136
135
  with resolver.display():
137
136
  # Get current objects, and reset all objects
138
- tag_to_object_id = running_app.tag_to_object_id
139
- running_app.tag_to_object_id = {}
137
+ tag_to_object_id = {**running_app.function_ids, **running_app.class_ids}
138
+ running_app.function_ids = {}
139
+ running_app.class_ids = {}
140
140
 
141
141
  # Assign all objects
142
142
  for tag, obj in indexed_objects.items():
@@ -163,7 +163,12 @@ async def _create_all_objects(
163
163
  async def _load(tag, obj):
164
164
  existing_object_id = tag_to_object_id.get(tag)
165
165
  await resolver.load(obj, existing_object_id)
166
- running_app.tag_to_object_id[tag] = obj.object_id
166
+ if _Function._is_id_type(obj.object_id):
167
+ running_app.function_ids[tag] = obj.object_id
168
+ elif _Cls._is_id_type(obj.object_id):
169
+ running_app.class_ids[tag] = obj.object_id
170
+ else:
171
+ raise RuntimeError(f"Unexpected object {obj.object_id}")
167
172
 
168
173
  await TaskContext.gather(*(_load(tag, obj) for tag, obj in indexed_objects.items()))
169
174
 
@@ -172,30 +177,22 @@ async def _publish_app(
172
177
  client: _Client,
173
178
  running_app: RunningApp,
174
179
  app_state: int, # api_pb2.AppState.value
175
- indexed_objects: dict[str, _Object],
180
+ functions: dict[str, _Function],
181
+ classes: dict[str, _Cls],
176
182
  name: str = "", # Only relevant for deployments
177
183
  tag: str = "", # Only relevant for deployments
178
184
  ) -> tuple[str, list[api_pb2.Warning]]:
179
185
  """Wrapper for AppPublish RPC."""
180
186
 
181
- # Could simplify this function some changing the internal representation to use
182
- # function_ids / class_ids rather than the current tag_to_object_id (i.e. "indexed_objects")
183
- def filter_values(full_dict: dict[str, V], condition: Callable[[V], bool]) -> dict[str, V]:
184
- return {k: v for k, v in full_dict.items() if condition(v)}
185
-
186
- function_ids = filter_values(running_app.tag_to_object_id, _Function._is_id_type)
187
- class_ids = filter_values(running_app.tag_to_object_id, _Cls._is_id_type)
188
-
189
- function_objs = filter_values(indexed_objects, lambda v: v.object_id in function_ids.values())
190
- definition_ids = {obj.object_id: obj._get_metadata().definition_id for obj in function_objs.values()} # type: ignore
187
+ definition_ids = {obj.object_id: obj._get_metadata().definition_id for obj in functions.values()} # type: ignore
191
188
 
192
189
  request = api_pb2.AppPublishRequest(
193
190
  app_id=running_app.app_id,
194
191
  name=name,
195
192
  deployment_tag=tag,
196
193
  app_state=app_state, # type: ignore : should be a api_pb2.AppState.value
197
- function_ids=function_ids,
198
- class_ids=class_ids,
194
+ function_ids=running_app.function_ids,
195
+ class_ids=running_app.class_ids,
199
196
  definition_ids=definition_ids,
200
197
  )
201
198
  try:
@@ -329,13 +326,11 @@ async def _run_app(
329
326
  )
330
327
 
331
328
  try:
332
- indexed_objects = dict(**app._functions, **app._classes) # TODO(erikbern): remove
333
-
334
329
  # Create all members
335
- await _create_all_objects(client, running_app, indexed_objects, environment_name)
330
+ await _create_all_objects(client, running_app, app._functions, app._classes, environment_name)
336
331
 
337
332
  # Publish the app
338
- await _publish_app(client, running_app, app_state, indexed_objects)
333
+ await _publish_app(client, running_app, app_state, app._functions, app._classes)
339
334
  except asyncio.CancelledError as e:
340
335
  # this typically happens on sigint/ctrl-C during setup (the KeyboardInterrupt happens in the main thread)
341
336
  if output_mgr := _get_output_manager():
@@ -428,18 +423,17 @@ async def _serve_update(
428
423
  try:
429
424
  running_app: RunningApp = await _init_local_app_existing(client, existing_app_id, environment_name)
430
425
 
431
- indexed_objects = dict(**app._functions, **app._classes) # TODO(erikbern): remove
432
-
433
426
  # Create objects
434
427
  await _create_all_objects(
435
428
  client,
436
429
  running_app,
437
- indexed_objects,
430
+ app._functions,
431
+ app._classes,
438
432
  environment_name,
439
433
  )
440
434
 
441
435
  # Publish the updated app
442
- await _publish_app(client, running_app, api_pb2.APP_STATE_UNSPECIFIED, indexed_objects)
436
+ await _publish_app(client, running_app, api_pb2.APP_STATE_UNSPECIFIED, app._functions, app._classes)
443
437
 
444
438
  # Communicate to the parent process
445
439
  is_ready.set()
@@ -527,19 +521,18 @@ async def _deploy_app(
527
521
 
528
522
  tc.infinite_loop(heartbeat, sleep=HEARTBEAT_INTERVAL)
529
523
 
530
- indexed_objects = dict(**app._functions, **app._classes) # TODO(erikbern): remove
531
-
532
524
  try:
533
525
  # Create all members
534
526
  await _create_all_objects(
535
527
  client,
536
528
  running_app,
537
- indexed_objects,
529
+ app._functions,
530
+ app._classes,
538
531
  environment_name=environment_name,
539
532
  )
540
533
 
541
534
  app_url, warnings = await _publish_app(
542
- client, running_app, api_pb2.APP_STATE_DEPLOYED, indexed_objects, name, tag
535
+ client, running_app, api_pb2.APP_STATE_DEPLOYED, app._functions, app._classes, name, tag
543
536
  )
544
537
  except Exception as e:
545
538
  # Note that AppClientDisconnect only stops the app if it's still initializing, and is a no-op otherwise.
modal/runner.pyi CHANGED
@@ -1,5 +1,6 @@
1
1
  import modal.client
2
- import modal.object
2
+ import modal.cls
3
+ import modal.functions
3
4
  import modal.running_app
4
5
  import modal_proto.api_pb2
5
6
  import multiprocessing.synchronize
@@ -28,14 +29,16 @@ async def _init_local_app_from_name(
28
29
  async def _create_all_objects(
29
30
  client: modal.client._Client,
30
31
  running_app: modal.running_app.RunningApp,
31
- indexed_objects: dict[str, modal.object._Object],
32
+ functions: dict[str, modal.functions._Function],
33
+ classes: dict[str, modal.cls._Cls],
32
34
  environment_name: str,
33
35
  ) -> None: ...
34
36
  async def _publish_app(
35
37
  client: modal.client._Client,
36
38
  running_app: modal.running_app.RunningApp,
37
39
  app_state: int,
38
- indexed_objects: dict[str, modal.object._Object],
40
+ functions: dict[str, modal.functions._Function],
41
+ classes: dict[str, modal.cls._Cls],
39
42
  name: str = "",
40
43
  tag: str = "",
41
44
  ) -> tuple[str, list[modal_proto.api_pb2.Warning]]: ...
modal/running_app.py CHANGED
@@ -7,17 +7,14 @@ from google.protobuf.message import Message
7
7
  from modal._utils.grpc_utils import get_proto_oneof
8
8
  from modal_proto import api_pb2
9
9
 
10
- from .client import _Client
11
-
12
10
 
13
11
  @dataclass
14
12
  class RunningApp:
15
13
  app_id: str
16
- client: _Client
17
- environment_name: Optional[str] = None
18
14
  app_page_url: Optional[str] = None
19
15
  app_logs_url: Optional[str] = None
20
- tag_to_object_id: dict[str, str] = field(default_factory=dict)
16
+ function_ids: dict[str, str] = field(default_factory=dict)
17
+ class_ids: dict[str, str] = field(default_factory=dict)
21
18
  object_handle_metadata: dict[str, Optional[Message]] = field(default_factory=dict)
22
19
  interactive: bool = False
23
20
 
@@ -25,11 +22,8 @@ class RunningApp:
25
22
  def running_app_from_layout(
26
23
  app_id: str,
27
24
  app_layout: api_pb2.AppLayout,
28
- client: _Client,
29
- environment_name: Optional[str] = None,
30
25
  app_page_url: Optional[str] = None,
31
26
  ) -> RunningApp:
32
- tag_to_object_id = dict(**app_layout.function_ids, **app_layout.class_ids)
33
27
  object_handle_metadata = {}
34
28
  for obj in app_layout.objects:
35
29
  handle_metadata: Optional[Message] = get_proto_oneof(obj, "handle_metadata_oneof")
@@ -37,9 +31,8 @@ def running_app_from_layout(
37
31
 
38
32
  return RunningApp(
39
33
  app_id,
40
- client,
41
- environment_name=environment_name,
42
- tag_to_object_id=tag_to_object_id,
34
+ function_ids=dict(app_layout.function_ids),
35
+ class_ids=dict(app_layout.class_ids),
43
36
  object_handle_metadata=object_handle_metadata,
44
37
  app_page_url=app_page_url,
45
38
  )
modal/sandbox.py CHANGED
@@ -276,9 +276,9 @@ class _Sandbox(_Object, type_prefix="sb"):
276
276
 
277
277
  app_id = app.app_id
278
278
  app_client = app._client
279
- elif _App._container_app is not None:
280
- app_id = _App._container_app.app_id
281
- app_client = _App._container_app.client
279
+ elif (container_app := _App.container_app()) is not None:
280
+ app_id = container_app.app_id
281
+ app_client = container_app._client
282
282
  else:
283
283
  arglist = ", ".join(repr(s) for s in entrypoint_args)
284
284
  deprecation_error(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: modal
3
- Version: 0.70.2
3
+ Version: 0.70.4
4
4
  Summary: Python client library for Modal
5
5
  Author: Modal Labs
6
6
  Author-email: support@modal.com
@@ -2,7 +2,7 @@ modal/__init__.py,sha256=3NJLLHb0TRc2tc68kf8NHzORx38GbtbZvPEWDWrQ6N4,2234
2
2
  modal/__main__.py,sha256=scYhGFqh8OJcVDo-VOxIT6CCwxOgzgflYWMnIZiMRqE,2871
3
3
  modal/_clustered_functions.py,sha256=kTf-9YBXY88NutC1akI-gCbvf01RhMPCw-zoOI_YIUE,2700
4
4
  modal/_clustered_functions.pyi,sha256=vllkegc99A0jrUOWa8mdlSbdp6uz36TsHhGxysAOpaQ,771
5
- modal/_container_entrypoint.py,sha256=7EAMe-85a-uWDaRAjRy9m4nnCAmjRJnh9x7FgW_QjLU,28935
5
+ modal/_container_entrypoint.py,sha256=d4Mgwkv93Kvt92msYSNGyhH4QXhrt08wosz4WfDp0Aw,28830
6
6
  modal/_ipython.py,sha256=TW1fkVOmZL3YYqdS2YlM1hqpf654Yf8ZyybHdBnlhSw,301
7
7
  modal/_location.py,sha256=S3lSxIU3h9HkWpkJ3Pwo0pqjIOSB1fjeSgUsY3x7eec,1202
8
8
  modal/_output.py,sha256=0fWX_KQwhER--U81ys16CL-pA5A-LN20C0EZjElKGJQ,25410
@@ -15,11 +15,11 @@ modal/_traceback.py,sha256=IZQzB3fVlUfMHOSyKUgw0H6qv4yHnpyq-XVCNZKfUdA,5023
15
15
  modal/_tunnel.py,sha256=o-jJhS4vQ6-XswDhHcJWGMZZmD03SC0e9i8fEu1JTjo,6310
16
16
  modal/_tunnel.pyi,sha256=JmmDYAy9F1FpgJ_hWx0xkom2nTOFQjn4mTPYlU3PFo4,1245
17
17
  modal/_watcher.py,sha256=K6LYnlmSGQB4tWWI9JADv-tvSvQ1j522FwT71B51CX8,3584
18
- modal/app.py,sha256=JWefPs4yB70BKQwSZejB_4_muhxn63cC9UmnNvpQ9XY,45526
19
- modal/app.pyi,sha256=FYPCEJNhof4YF6HIuNP_2yG6s2PgZnKW9tO1hFE6sfA,25194
18
+ modal/app.py,sha256=LhAdfXUfqcVvmqTC8HM-LxMlGWCW2U8Xuhb6URyj9RI,45577
19
+ modal/app.pyi,sha256=ojqaDubpV6wdhqewMe2T_AKt2RPXSG4nySZu8_lHpvc,25292
20
20
  modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
21
21
  modal/client.py,sha256=JAnd4-GCN093BwkvOFAK5a6iy5ycxofjpUncMxlrIMw,15253
22
- modal/client.pyi,sha256=WXHQoJWWNrFs3BR6wW3LRHc5XaH4_KTOFt0eo7HGJGs,7278
22
+ modal/client.pyi,sha256=tK1--wDx9xNyIcEiYHIHtti3ZVkYOuTVjeZXSRT1-HY,7278
23
23
  modal/cloud_bucket_mount.py,sha256=G7T7jWLD0QkmrfKR75mSTwdUZ2xNfj7pkVqb4ipmxmI,5735
24
24
  modal/cloud_bucket_mount.pyi,sha256=CEi7vrH3kDUF4LAy4qP6tfImy2UJuFRcRbsgRNM1wo8,1403
25
25
  modal/cls.py,sha256=3hjb0JcoPjxKZNeK22f5rR43bZRBjoRI7_EMZXY7YrE,31172
@@ -60,10 +60,10 @@ modal/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
60
60
  modal/queue.py,sha256=zMUQtdAyqZzBg-2iAo3c3G54HLP7TEWfVhiQXLjewb4,18556
61
61
  modal/queue.pyi,sha256=gGV97pWelSSYqMV9Bl4ys3mSP7q82fS71oqSWeAwyDE,9818
62
62
  modal/retries.py,sha256=HKR2Q9aNPWkMjQ5nwobqYTuZaSuw0a8lI2zrtY5IW98,5230
63
- modal/runner.py,sha256=1nPBsIfef2sOr2ebQ348EmDemvYFDhp1-_Gr3BKsjdM,24542
64
- modal/runner.pyi,sha256=BvMS1ZVzWSn8B8q0KnIZOJKPkN5L-i5b-USbV6SWWHQ,5177
65
- modal/running_app.py,sha256=FSr0XoL4mPLPCBMj2TozWuEvcvApdY_t68nUowwf8x4,1372
66
- modal/sandbox.py,sha256=c-Qli3QJPN7bBQzsTk4iS51zurNlq--InZ2eRR-B6No,28106
63
+ modal/runner.py,sha256=mhqgRdjD5cUDpBQIokiX7OCfVblpGV6aWmZ-WvWJgGg,24114
64
+ modal/runner.pyi,sha256=YmP4EOCNjjkwSIPi2Gl6hF_ji_ytkxz9dw3iB9KXaOI,5275
65
+ modal/running_app.py,sha256=v61mapYNV1-O-Uaho5EfJlryMLvIT9We0amUOSvSGx8,1188
66
+ modal/sandbox.py,sha256=_r-Cj5L6BE5tOzwIUxGP99BH9EsCTupQSV3jcP61aZM,28115
67
67
  modal/sandbox.pyi,sha256=k8_vHjN3oigxSCF13Cm2HfcSHuliGuSb8ryd3CGqwoA,19815
68
68
  modal/schedule.py,sha256=0ZFpKs1bOxeo5n3HZjoL7OE2ktsb-_oGtq-WJEPO4tY,2615
69
69
  modal/scheduler_placement.py,sha256=BAREdOY5HzHpzSBqt6jDVR6YC_jYfHMVqOzkyqQfngU,1235
@@ -78,7 +78,7 @@ modal/volume.py,sha256=T-pLxCYqmqRO6OolpAXlPxomMu0RWjti2e4kUpaj2cQ,29229
78
78
  modal/volume.pyi,sha256=eekb2dnAAwFK_NO9ciAOOTthl8NP1iAmMFrCGgjDA2k,11100
79
79
  modal/_runtime/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
80
80
  modal/_runtime/asgi.py,sha256=Mjs859pSgOmtZL-YmEsSKN557v1A2Ax_5-ERgPfj55E,21920
81
- modal/_runtime/container_io_manager.py,sha256=8k84QF8dNPY344tm0jz6DySCnIwIlUH66KKKh95FjWY,43878
81
+ modal/_runtime/container_io_manager.py,sha256=HgDLjE78yy1P7WZTmsEVDf89YnFFWG63Ddes8uYLVDY,43764
82
82
  modal/_runtime/execution_context.py,sha256=E6ofm6j1POXGPxS841X3V7JU6NheVb8OkQc7JpLq4Kg,2712
83
83
  modal/_runtime/telemetry.py,sha256=T1RoAGyjBDr1swiM6pPsGRSITm7LI5FDK18oNXxY08U,5163
84
84
  modal/_runtime/user_code_imports.py,sha256=n4CQOojzSdf0jwSKSy6MEnVX3IWl3t3Dq54-x9VS2Ds,14663
@@ -165,10 +165,10 @@ modal_proto/options_pb2_grpc.pyi,sha256=CImmhxHsYnF09iENPoe8S4J-n93jtgUYD2JPAc0y
165
165
  modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
166
166
  modal_version/__init__.py,sha256=N9Kh4DrM2649_INTJG4Lp3NKdux7cxGuiDtXpq_hkFY,470
167
167
  modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
168
- modal_version/_version_generated.py,sha256=-cO-pRiLwIwgch5Ik_s4Jq9WJA6bUJuOEZ1MivmPWJ8,148
169
- modal-0.70.2.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
170
- modal-0.70.2.dist-info/METADATA,sha256=FuikYBU1iOwsYxHyv1pnYclfrOMwXJgY7JkfqndJnnQ,2328
171
- modal-0.70.2.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
172
- modal-0.70.2.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
173
- modal-0.70.2.dist-info/top_level.txt,sha256=1nvYbOSIKcmU50fNrpnQnrrOpj269ei3LzgB6j9xGqg,64
174
- modal-0.70.2.dist-info/RECORD,,
168
+ modal_version/_version_generated.py,sha256=ZMPi8R_MjME99Sf5PLcfgOuAzbE-RYxPuF4CSKV1_vg,148
169
+ modal-0.70.4.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
170
+ modal-0.70.4.dist-info/METADATA,sha256=cQc9rstYeBi9SeZxjUL12d2OZjHF2UAua4d8C4acz3Y,2328
171
+ modal-0.70.4.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
172
+ modal-0.70.4.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
173
+ modal-0.70.4.dist-info/top_level.txt,sha256=1nvYbOSIKcmU50fNrpnQnrrOpj269ei3LzgB6j9xGqg,64
174
+ modal-0.70.4.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
- # Copyright Modal Labs 2024
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 = 2 # git: c8504ed
4
+ build_number = 4 # git: ce4e96a
File without changes