modal 1.2.1.dev6__py3-none-any.whl → 1.2.1.dev8__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.

Potentially problematic release.


This version of modal might be problematic. Click here for more details.

modal/app.py CHANGED
@@ -2,6 +2,7 @@
2
2
  import inspect
3
3
  import typing
4
4
  from collections.abc import AsyncGenerator, Collection, Coroutine, Mapping, Sequence
5
+ from dataclasses import dataclass
5
6
  from pathlib import PurePosixPath
6
7
  from textwrap import dedent
7
8
  from typing import (
@@ -114,6 +115,22 @@ class _FunctionDecoratorType:
114
115
  def __call__(self, func): ...
115
116
 
116
117
 
118
+ @dataclass()
119
+ class _LocalAppState:
120
+ """All state for apps that's part of the local/definition state"""
121
+
122
+ functions: dict[str, _Function]
123
+ classes: dict[str, _Cls]
124
+ image_default: Optional[_Image]
125
+ web_endpoints: list[str] # Used by the CLI
126
+ local_entrypoints: dict[str, _LocalEntrypoint]
127
+ tags: dict[str, str]
128
+
129
+ include_source_default: bool
130
+ secrets_default: Sequence[_Secret]
131
+ volumes_default: dict[Union[str, PurePosixPath], _Volume]
132
+
133
+
117
134
  class _App:
118
135
  """A Modal App is a group of functions and classes that are deployed together.
119
136
 
@@ -151,23 +168,21 @@ class _App:
151
168
 
152
169
  _name: Optional[str]
153
170
  _description: Optional[str]
154
- _tags: dict[str, str]
155
-
156
- _functions: dict[str, _Function]
157
- _classes: dict[str, _Cls]
158
171
 
159
- _image: Optional[_Image]
160
- _secrets: Sequence[_Secret]
161
- _volumes: dict[Union[str, PurePosixPath], _Volume]
162
- _web_endpoints: list[str] # Used by the CLI
163
- _local_entrypoints: dict[str, _LocalEntrypoint]
172
+ _local_state_attr: Optional[_LocalAppState] = None
164
173
 
165
174
  # Running apps only (container apps or running local)
166
175
  _app_id: Optional[str] # Kept after app finishes
167
176
  _running_app: Optional[RunningApp] # Various app info
168
177
  _client: Optional[_Client]
169
178
 
170
- _include_source_default: Optional[bool] = None
179
+ @property
180
+ def _local_state(self) -> _LocalAppState:
181
+ """For internal use only. Do not use this property directly."""
182
+
183
+ if self._local_state_attr is None:
184
+ raise AttributeError("Local state is not initialized - app is not locally available")
185
+ return self._local_state_attr
171
186
 
172
187
  def __init__(
173
188
  self,
@@ -196,8 +211,6 @@ class _App:
196
211
 
197
212
  self._name = name
198
213
  self._description = name
199
- self._tags = tags or {}
200
- self._include_source_default = include_source
201
214
 
202
215
  check_sequence(secrets, _Secret, "`secrets=` has to be a list or tuple of `modal.Secret` objects")
203
216
  validate_volumes(volumes)
@@ -205,16 +218,24 @@ class _App:
205
218
  if image is not None and not isinstance(image, _Image):
206
219
  raise InvalidError("`image=` has to be a `modal.Image` object")
207
220
 
208
- self._functions = {}
209
- self._classes = {}
210
- self._image = image
211
- self._secrets = secrets
212
- self._volumes = volumes
213
- self._local_entrypoints = {}
214
- self._web_endpoints = []
221
+ self._local_state_attr = _LocalAppState(
222
+ functions={},
223
+ classes={},
224
+ image_default=image,
225
+ secrets_default=secrets,
226
+ volumes_default=volumes,
227
+ include_source_default=include_source,
228
+ web_endpoints=[],
229
+ local_entrypoints={},
230
+ tags=tags or {},
231
+ )
215
232
 
233
+ # Running apps only
216
234
  self._app_id = None
217
235
  self._running_app = None # Set inside container, OR during the time an app is running locally
236
+
237
+ # Client is special - needed to be set just before the app is "hydrated" or running at the latest
238
+ # Guaranteed to be set for running apps, but also needed to actually *hydrate* the app and make it running
218
239
  self._client = None
219
240
 
220
241
  # Register this app. This is used to look up the app in the container, when we can't get it from the function
@@ -283,7 +304,8 @@ class _App:
283
304
 
284
305
  response = await retry_transient_errors(client.stub.AppGetOrCreate, request)
285
306
 
286
- app = _App(name)
307
+ app = _App(name) # TODO: this should probably be a distinct constructor, possibly even a distinct type
308
+ app._local_state_attr = None # this is not a locally defined App, so no local state
287
309
  app._app_id = response.app_id
288
310
  app._client = client
289
311
  app._running_app = RunningApp(response.app_id, interactive=False)
@@ -310,18 +332,19 @@ class _App:
310
332
  App that is retrieved via `modal.App.lookup`. It is likely to be deprecated in the future.
311
333
 
312
334
  """
313
- return self._image
335
+ return self._local_state.image_default
314
336
 
315
337
  @image.setter
316
338
  def image(self, value):
317
339
  """mdmd:hidden"""
318
- self._image = value
340
+ self._local_state.image_default = value
319
341
 
320
342
  def _uncreate_all_objects(self):
321
343
  # TODO(erikbern): this doesn't unhydrate objects that aren't tagged
322
- for obj in self._functions.values():
344
+ local_state = self._local_state
345
+ for obj in local_state.functions.values():
323
346
  obj._unhydrate()
324
- for obj in self._classes.values():
347
+ for obj in local_state.classes.values():
325
348
  obj._unhydrate()
326
349
 
327
350
  @asynccontextmanager
@@ -457,8 +480,9 @@ class _App:
457
480
  return self
458
481
 
459
482
  def _get_default_image(self):
460
- if self._image:
461
- return self._image
483
+ local_state = self._local_state
484
+ if local_state.image_default:
485
+ return local_state.image_default
462
486
  else:
463
487
  return _default_image
464
488
 
@@ -473,7 +497,8 @@ class _App:
473
497
  return [m for m in all_mounts if m.is_local()]
474
498
 
475
499
  def _add_function(self, function: _Function, is_web_endpoint: bool):
476
- if old_function := self._functions.get(function.tag, None):
500
+ local_state = self._local_state
501
+ if old_function := local_state.functions.get(function.tag, None):
477
502
  if old_function is function:
478
503
  return # already added the same exact instance, ignore
479
504
 
@@ -484,7 +509,7 @@ class _App:
484
509
  f"[{old_function._info.module_name}].{old_function._info.function_name}"
485
510
  f" with new function [{function._info.module_name}].{function._info.function_name}"
486
511
  )
487
- if function.tag in self._classes:
512
+ if function.tag in local_state.classes:
488
513
  logger.warning(f"Warning: tag {function.tag} exists but is overridden by function")
489
514
 
490
515
  if self._running_app:
@@ -495,9 +520,9 @@ class _App:
495
520
  metadata: Message = self._running_app.object_handle_metadata[object_id]
496
521
  function._hydrate(object_id, self._client, metadata)
497
522
 
498
- self._functions[function.tag] = function
523
+ local_state.functions[function.tag] = function
499
524
  if is_web_endpoint:
500
- self._web_endpoints.append(function.tag)
525
+ local_state.web_endpoints.append(function.tag)
501
526
 
502
527
  def _add_class(self, tag: str, cls: _Cls):
503
528
  if self._running_app:
@@ -508,7 +533,7 @@ class _App:
508
533
  metadata: Message = self._running_app.object_handle_metadata[object_id]
509
534
  cls._hydrate(object_id, self._client, metadata)
510
535
 
511
- self._classes[tag] = cls
536
+ self._local_state.classes[tag] = cls
512
537
 
513
538
  def _init_container(self, client: _Client, running_app: RunningApp):
514
539
  self._app_id = running_app.app_id
@@ -516,18 +541,18 @@ class _App:
516
541
  self._client = client
517
542
 
518
543
  _App._container_app = self
519
-
544
+ local_state = self._local_state
520
545
  # Hydrate function objects
521
546
  for tag, object_id in running_app.function_ids.items():
522
- if tag in self._functions:
523
- obj = self._functions[tag]
547
+ if tag in local_state.functions:
548
+ obj = local_state.functions[tag]
524
549
  handle_metadata = running_app.object_handle_metadata[object_id]
525
550
  obj._hydrate(object_id, client, handle_metadata)
526
551
 
527
552
  # Hydrate class objects
528
553
  for tag, object_id in running_app.class_ids.items():
529
- if tag in self._classes:
530
- obj = self._classes[tag]
554
+ if tag in local_state.classes:
555
+ obj = local_state.classes[tag]
531
556
  handle_metadata = running_app.object_handle_metadata[object_id]
532
557
  obj._hydrate(object_id, client, handle_metadata)
533
558
 
@@ -541,7 +566,7 @@ class _App:
541
566
  This method is likely to be deprecated in the future in favor of a different
542
567
  approach for retrieving the layout of a deployed App.
543
568
  """
544
- return self._functions
569
+ return self._local_state.functions
545
570
 
546
571
  @property
547
572
  def registered_classes(self) -> dict[str, _Cls]:
@@ -553,7 +578,7 @@ class _App:
553
578
  This method is likely to be deprecated in the future in favor of a different
554
579
  approach for retrieving the layout of a deployed App.
555
580
  """
556
- return self._classes
581
+ return self._local_state.classes
557
582
 
558
583
  @property
559
584
  def registered_entrypoints(self) -> dict[str, _LocalEntrypoint]:
@@ -564,7 +589,7 @@ class _App:
564
589
  expected to work when a deplyoed App has been retrieved via `modal.App.lookup`.
565
590
  This method is likely to be deprecated in the future.
566
591
  """
567
- return self._local_entrypoints
592
+ return self._local_state.local_entrypoints
568
593
 
569
594
  @property
570
595
  def registered_web_endpoints(self) -> list[str]:
@@ -576,7 +601,7 @@ class _App:
576
601
  This method is likely to be deprecated in the future in favor of a different
577
602
  approach for retrieving the layout of a deployed App.
578
603
  """
579
- return self._web_endpoints
604
+ return self._local_state.web_endpoints
580
605
 
581
606
  def local_entrypoint(
582
607
  self, _warn_parentheses_missing: Any = None, *, name: Optional[str] = None
@@ -637,10 +662,11 @@ class _App:
637
662
  def wrapped(raw_f: Callable[..., Any]) -> _LocalEntrypoint:
638
663
  info = FunctionInfo(raw_f)
639
664
  tag = name if name is not None else raw_f.__qualname__
640
- if tag in self._local_entrypoints:
665
+ local_state = self._local_state
666
+ if tag in local_state.local_entrypoints:
641
667
  # TODO: get rid of this limitation.
642
668
  raise InvalidError(f"Duplicate local entrypoint name: {tag}. Local entrypoint names must be unique.")
643
- entrypoint = self._local_entrypoints[tag] = _LocalEntrypoint(info, self)
669
+ entrypoint = local_state.local_entrypoints[tag] = _LocalEntrypoint(info, self)
644
670
  return entrypoint
645
671
 
646
672
  return wrapped
@@ -732,7 +758,8 @@ class _App:
732
758
  secrets = secrets or []
733
759
  if env:
734
760
  secrets = [*secrets, _Secret.from_dict(env)]
735
- secrets = [*self._secrets, *secrets]
761
+ local_state = self._local_state
762
+ secrets = [*local_state.secrets_default, *secrets]
736
763
 
737
764
  def wrapped(
738
765
  f: Union[_PartialFunction, Callable[..., Any], None],
@@ -840,7 +867,7 @@ class _App:
840
867
  is_generator=is_generator,
841
868
  gpu=gpu,
842
869
  network_file_systems=network_file_systems,
843
- volumes={**self._volumes, **volumes},
870
+ volumes={**local_state.volumes_default, **volumes},
844
871
  cpu=cpu,
845
872
  memory=memory,
846
873
  ephemeral_disk=ephemeral_disk,
@@ -866,7 +893,7 @@ class _App:
866
893
  i6pn_enabled=i6pn_enabled,
867
894
  cluster_size=cluster_size, # Experimental: Clustered functions
868
895
  rdma=rdma,
869
- include_source=include_source if include_source is not None else self._include_source_default,
896
+ include_source=include_source if include_source is not None else local_state.include_source_default,
870
897
  experimental_options={k: str(v) for k, v in (experimental_options or {}).items()},
871
898
  _experimental_proxy_ip=_experimental_proxy_ip,
872
899
  restrict_output=_experimental_restrict_output,
@@ -963,6 +990,7 @@ class _App:
963
990
  secrets = [*secrets, _Secret.from_dict(env)]
964
991
 
965
992
  def wrapper(wrapped_cls: Union[CLS_T, _PartialFunction]) -> CLS_T:
993
+ local_state = self._local_state
966
994
  # Check if the decorated object is a class
967
995
  if isinstance(wrapped_cls, _PartialFunction):
968
996
  wrapped_cls.registered = True
@@ -1029,10 +1057,10 @@ class _App:
1029
1057
  info,
1030
1058
  app=self,
1031
1059
  image=image or self._get_default_image(),
1032
- secrets=[*self._secrets, *secrets],
1060
+ secrets=[*local_state.secrets_default, *secrets],
1033
1061
  gpu=gpu,
1034
1062
  network_file_systems=network_file_systems,
1035
- volumes={**self._volumes, **volumes},
1063
+ volumes={**local_state.volumes_default, **volumes},
1036
1064
  cpu=cpu,
1037
1065
  memory=memory,
1038
1066
  ephemeral_disk=ephemeral_disk,
@@ -1057,7 +1085,7 @@ class _App:
1057
1085
  i6pn_enabled=i6pn_enabled,
1058
1086
  cluster_size=cluster_size,
1059
1087
  rdma=rdma,
1060
- include_source=include_source if include_source is not None else self._include_source_default,
1088
+ include_source=include_source if include_source is not None else local_state.include_source_default,
1061
1089
  experimental_options={k: str(v) for k, v in (experimental_options or {}).items()},
1062
1090
  _experimental_proxy_ip=_experimental_proxy_ip,
1063
1091
  _experimental_custom_scaling_factor=_experimental_custom_scaling_factor,
@@ -1067,6 +1095,11 @@ class _App:
1067
1095
  self._add_function(cls_func, is_web_endpoint=False)
1068
1096
 
1069
1097
  cls: _Cls = _Cls.from_local(user_cls, self, cls_func)
1098
+ for method_name, partial_function in cls._method_partials.items():
1099
+ if partial_function.params.webhook_config is not None:
1100
+ full_name = f"{user_cls.__name__}.{method_name}"
1101
+ local_state.web_endpoints.append(full_name)
1102
+ partial_function.registered = True
1070
1103
 
1071
1104
  tag: str = user_cls.__name__
1072
1105
  self._add_class(tag, cls)
@@ -1102,11 +1135,14 @@ class _App:
1102
1135
  (with this App's tags taking precedence in the case of conflicts).
1103
1136
 
1104
1137
  """
1105
- for tag, function in other_app._functions.items():
1138
+ other_app_local_state = other_app._local_state
1139
+ this_local_state = self._local_state
1140
+
1141
+ for tag, function in other_app_local_state.functions.items():
1106
1142
  self._add_function(function, False) # TODO(erikbern): webhook config?
1107
1143
 
1108
- for tag, cls in other_app._classes.items():
1109
- existing_cls = self._classes.get(tag)
1144
+ for tag, cls in other_app_local_state.classes.items():
1145
+ existing_cls = this_local_state.classes.get(tag)
1110
1146
  if existing_cls and existing_cls != cls:
1111
1147
  logger.warning(
1112
1148
  f"Named app class {tag} with existing value {existing_cls} is being "
@@ -1116,7 +1152,7 @@ class _App:
1116
1152
  self._add_class(tag, cls)
1117
1153
 
1118
1154
  if inherit_tags:
1119
- self._tags = {**other_app._tags, **self._tags}
1155
+ this_local_state.tags = {**other_app_local_state.tags, **this_local_state.tags}
1120
1156
 
1121
1157
  return self
1122
1158
 
@@ -1132,7 +1168,7 @@ class _App:
1132
1168
 
1133
1169
  """
1134
1170
  # Note that we are requiring the App to be "running" before we set the tags.
1135
- # Alternatively, we could hold onto the tags (i.e. in `self._tags`) and then pass
1171
+ # Alternatively, we could hold onto the tags (i.e. in `self._local_state.tags`) and then pass
1136
1172
  # then up when AppPublish gets called. I'm not certain we want to support it, though.
1137
1173
  # It might not be obvious to users that `.set_tags()` is eager and has immediate effect
1138
1174
  # when the App is running, but lazy (and potentially ignored) otherwise. There would be
modal/app.pyi CHANGED
@@ -74,6 +74,42 @@ class _FunctionDecoratorType:
74
74
  self, func: collections.abc.Callable[P, ReturnType]
75
75
  ) -> modal.functions.Function[P, ReturnType, ReturnType]: ...
76
76
 
77
+ class _LocalAppState:
78
+ """All state for apps that's part of the local/definition state"""
79
+
80
+ functions: dict[str, modal._functions._Function]
81
+ classes: dict[str, modal.cls._Cls]
82
+ image_default: typing.Optional[modal.image._Image]
83
+ web_endpoints: list[str]
84
+ local_entrypoints: dict[str, _LocalEntrypoint]
85
+ tags: dict[str, str]
86
+ include_source_default: bool
87
+ secrets_default: collections.abc.Sequence[modal.secret._Secret]
88
+ volumes_default: dict[typing.Union[str, pathlib.PurePosixPath], modal.volume._Volume]
89
+
90
+ def __init__(
91
+ self,
92
+ functions: dict[str, modal._functions._Function],
93
+ classes: dict[str, modal.cls._Cls],
94
+ image_default: typing.Optional[modal.image._Image],
95
+ web_endpoints: list[str],
96
+ local_entrypoints: dict[str, _LocalEntrypoint],
97
+ tags: dict[str, str],
98
+ include_source_default: bool,
99
+ secrets_default: collections.abc.Sequence[modal.secret._Secret],
100
+ volumes_default: dict[typing.Union[str, pathlib.PurePosixPath], modal.volume._Volume],
101
+ ) -> None:
102
+ """Initialize self. See help(type(self)) for accurate signature."""
103
+ ...
104
+
105
+ def __repr__(self):
106
+ """Return repr(self)."""
107
+ ...
108
+
109
+ def __eq__(self, other):
110
+ """Return self==value."""
111
+ ...
112
+
77
113
  class _App:
78
114
  """A Modal App is a group of functions and classes that are deployed together.
79
115
 
@@ -110,18 +146,15 @@ class _App:
110
146
  _container_app: typing.ClassVar[typing.Optional[_App]]
111
147
  _name: typing.Optional[str]
112
148
  _description: typing.Optional[str]
113
- _tags: dict[str, str]
114
- _functions: dict[str, modal._functions._Function]
115
- _classes: dict[str, modal.cls._Cls]
116
- _image: typing.Optional[modal.image._Image]
117
- _secrets: collections.abc.Sequence[modal.secret._Secret]
118
- _volumes: dict[typing.Union[str, pathlib.PurePosixPath], modal.volume._Volume]
119
- _web_endpoints: list[str]
120
- _local_entrypoints: dict[str, _LocalEntrypoint]
149
+ _local_state_attr: typing.Optional[_LocalAppState]
121
150
  _app_id: typing.Optional[str]
122
151
  _running_app: typing.Optional[modal.running_app.RunningApp]
123
152
  _client: typing.Optional[modal.client._Client]
124
- _include_source_default: typing.Optional[bool]
153
+
154
+ @property
155
+ def _local_state(self) -> _LocalAppState:
156
+ """For internal use only. Do not use this property directly."""
157
+ ...
125
158
 
126
159
  def __init__(
127
160
  self,
@@ -630,18 +663,10 @@ class App:
630
663
  _container_app: typing.ClassVar[typing.Optional[App]]
631
664
  _name: typing.Optional[str]
632
665
  _description: typing.Optional[str]
633
- _tags: dict[str, str]
634
- _functions: dict[str, modal.functions.Function]
635
- _classes: dict[str, modal.cls.Cls]
636
- _image: typing.Optional[modal.image.Image]
637
- _secrets: collections.abc.Sequence[modal.secret.Secret]
638
- _volumes: dict[typing.Union[str, pathlib.PurePosixPath], modal.volume.Volume]
639
- _web_endpoints: list[str]
640
- _local_entrypoints: dict[str, LocalEntrypoint]
666
+ _local_state_attr: typing.Optional[_LocalAppState]
641
667
  _app_id: typing.Optional[str]
642
668
  _running_app: typing.Optional[modal.running_app.RunningApp]
643
669
  _client: typing.Optional[modal.client.Client]
644
- _include_source_default: typing.Optional[bool]
645
670
 
646
671
  def __init__(
647
672
  self,
@@ -664,6 +689,11 @@ class App:
664
689
  """
665
690
  ...
666
691
 
692
+ @property
693
+ def _local_state(self) -> _LocalAppState:
694
+ """For internal use only. Do not use this property directly."""
695
+ ...
696
+
667
697
  @property
668
698
  def name(self) -> typing.Optional[str]:
669
699
  """The user-provided name of the App."""
modal/client.pyi CHANGED
@@ -33,7 +33,7 @@ class _Client:
33
33
  server_url: str,
34
34
  client_type: int,
35
35
  credentials: typing.Optional[tuple[str, str]],
36
- version: str = "1.2.1.dev6",
36
+ version: str = "1.2.1.dev8",
37
37
  ):
38
38
  """mdmd:hidden
39
39
  The Modal client object is not intended to be instantiated directly by users.
@@ -164,7 +164,7 @@ class Client:
164
164
  server_url: str,
165
165
  client_type: int,
166
166
  credentials: typing.Optional[tuple[str, str]],
167
- version: str = "1.2.1.dev6",
167
+ version: str = "1.2.1.dev8",
168
168
  ):
169
169
  """mdmd:hidden
170
170
  The Modal client object is not intended to be instantiated directly by users.
modal/cls.py CHANGED
@@ -577,22 +577,15 @@ More information on class parameterization can be found here: https://modal.com/
577
577
  # validate signature
578
578
  _Cls.validate_construction_mechanism(user_cls)
579
579
 
580
- method_partials: dict[str, _PartialFunction] = _find_partial_methods_for_user_cls(
581
- user_cls, _PartialFunctionFlags.interface_flags()
582
- )
583
-
584
- for method_name, partial_function in method_partials.items():
585
- if partial_function.params.webhook_config is not None:
586
- full_name = f"{user_cls.__name__}.{method_name}"
587
- app._web_endpoints.append(full_name)
588
- partial_function.registered = True
589
-
590
580
  # Disable the warning that lifecycle methods are not wrapped
591
- for partial_function in _find_partial_methods_for_user_cls(
581
+ lifecycle_method_partials = _find_partial_methods_for_user_cls(
592
582
  user_cls, ~_PartialFunctionFlags.interface_flags()
593
- ).values():
583
+ )
584
+ for partial_function in lifecycle_method_partials.values():
594
585
  partial_function.registered = True
595
586
 
587
+ method_partials = _find_partial_methods_for_user_cls(user_cls, _PartialFunctionFlags.interface_flags())
588
+
596
589
  # Get all callables
597
590
  callables: dict[str, Callable] = {
598
591
  k: pf.raw_f
modal/functions.pyi CHANGED
@@ -401,7 +401,7 @@ class Function(
401
401
 
402
402
  _call_generator: ___call_generator_spec[typing_extensions.Self]
403
403
 
404
- class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
404
+ class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
405
405
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER:
406
406
  """Calls the function remotely, executing it with the given arguments and returning the execution's result."""
407
407
  ...
@@ -410,7 +410,7 @@ class Function(
410
410
  """Calls the function remotely, executing it with the given arguments and returning the execution's result."""
411
411
  ...
412
412
 
413
- remote: __remote_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
413
+ remote: __remote_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
414
414
 
415
415
  class __remote_gen_spec(typing_extensions.Protocol[SUPERSELF]):
416
416
  def __call__(self, /, *args, **kwargs) -> typing.Generator[typing.Any, None, None]:
@@ -437,7 +437,7 @@ class Function(
437
437
  """
438
438
  ...
439
439
 
440
- class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
440
+ class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
441
441
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
442
442
  """[Experimental] Calls the function with the given arguments, without waiting for the results.
443
443
 
@@ -461,7 +461,7 @@ class Function(
461
461
  ...
462
462
 
463
463
  _experimental_spawn: ___experimental_spawn_spec[
464
- modal._functions.ReturnType, modal._functions.P, typing_extensions.Self
464
+ modal._functions.P, modal._functions.ReturnType, typing_extensions.Self
465
465
  ]
466
466
 
467
467
  class ___spawn_map_inner_spec(typing_extensions.Protocol[P_INNER, SUPERSELF]):
@@ -470,7 +470,7 @@ class Function(
470
470
 
471
471
  _spawn_map_inner: ___spawn_map_inner_spec[modal._functions.P, typing_extensions.Self]
472
472
 
473
- class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
473
+ class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
474
474
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
475
475
  """Calls the function with the given arguments, without waiting for the results.
476
476
 
@@ -491,7 +491,7 @@ class Function(
491
491
  """
492
492
  ...
493
493
 
494
- spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
494
+ spawn: __spawn_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
495
495
 
496
496
  def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]:
497
497
  """Return the inner Python object wrapped by this Modal Function."""
modal/runner.py CHANGED
@@ -43,9 +43,7 @@ from .secret import _Secret
43
43
  from .stream_type import StreamType
44
44
 
45
45
  if TYPE_CHECKING:
46
- from .app import _App
47
- else:
48
- _App = TypeVar("_App")
46
+ import modal.app
49
47
 
50
48
 
51
49
  V = TypeVar("V")
@@ -126,12 +124,11 @@ async def _init_local_app_from_name(
126
124
  async def _create_all_objects(
127
125
  client: _Client,
128
126
  running_app: RunningApp,
129
- functions: dict[str, _Function],
130
- classes: dict[str, _Cls],
127
+ local_app_state: "modal.app._LocalAppState",
131
128
  environment_name: str,
132
129
  ) -> None:
133
130
  """Create objects that have been defined but not created on the server."""
134
- indexed_objects: dict[str, _Object] = {**functions, **classes}
131
+ indexed_objects: dict[str, _Object] = {**local_app_state.functions, **local_app_state.classes}
135
132
  resolver = Resolver(
136
133
  client,
137
134
  environment_name=environment_name,
@@ -182,21 +179,19 @@ async def _publish_app(
182
179
  client: _Client,
183
180
  running_app: RunningApp,
184
181
  app_state: int, # api_pb2.AppState.value
185
- functions: dict[str, _Function],
186
- classes: dict[str, _Cls],
182
+ app_local_state: "modal.app._LocalAppState",
187
183
  name: str = "",
188
- tags: dict[str, str] = {}, # Additional App metadata
189
184
  deployment_tag: str = "", # Only relevant for deployments
190
185
  commit_info: Optional[api_pb2.CommitInfo] = None, # Git commit information
191
186
  ) -> tuple[str, list[api_pb2.Warning]]:
192
187
  """Wrapper for AppPublish RPC."""
193
-
188
+ functions = app_local_state.functions
194
189
  definition_ids = {obj.object_id: obj._get_metadata().definition_id for obj in functions.values()} # type: ignore
195
190
 
196
191
  request = api_pb2.AppPublishRequest(
197
192
  app_id=running_app.app_id,
198
193
  name=name,
199
- tags=tags,
194
+ tags=app_local_state.tags,
200
195
  deployment_tag=deployment_tag,
201
196
  commit_info=commit_info,
202
197
  app_state=app_state, # type: ignore : should be a api_pb2.AppState.value
@@ -260,13 +255,13 @@ async def _status_based_disconnect(client: _Client, app_id: str, exc_info: Optio
260
255
 
261
256
  @asynccontextmanager
262
257
  async def _run_app(
263
- app: _App,
258
+ app: "modal.app._App",
264
259
  *,
265
260
  client: Optional[_Client] = None,
266
261
  detach: bool = False,
267
262
  environment_name: Optional[str] = None,
268
263
  interactive: bool = False,
269
- ) -> AsyncGenerator[_App, None]:
264
+ ) -> AsyncGenerator["modal.app._App", None]:
270
265
  """mdmd:hidden"""
271
266
  if environment_name is None:
272
267
  environment_name = typing.cast(str, config.get("environment"))
@@ -338,12 +333,13 @@ async def _run_app(
338
333
  get_app_logs_loop(client, output_mgr, app_id=running_app.app_id, app_logs_url=running_app.app_logs_url)
339
334
  )
340
335
 
336
+ local_app_state = app._local_state
341
337
  try:
342
338
  # Create all members
343
- await _create_all_objects(client, running_app, app._functions, app._classes, environment_name)
339
+ await _create_all_objects(client, running_app, local_app_state, environment_name)
344
340
 
345
341
  # Publish the app
346
- await _publish_app(client, running_app, app_state, app._functions, app._classes, tags=app._tags)
342
+ await _publish_app(client, running_app, app_state, local_app_state)
347
343
  except asyncio.CancelledError as e:
348
344
  # this typically happens on sigint/ctrl-C during setup (the KeyboardInterrupt happens in the main thread)
349
345
  if output_mgr := _get_output_manager():
@@ -355,6 +351,14 @@ async def _run_app(
355
351
  await _status_based_disconnect(client, running_app.app_id, e)
356
352
  raise
357
353
 
354
+ detached_disconnect_msg = (
355
+ "The detached App will keep running. You can track its progress on the Dashboard: "
356
+ f"[magenta underline]{running_app.app_page_url}[/magenta underline]"
357
+ "\n"
358
+ f"\nStream logs: [green]modal app logs {running_app.app_id}[/green]"
359
+ f"\nStop the App: [green]modal app stop {running_app.app_id}[/green]"
360
+ )
361
+
358
362
  try:
359
363
  # Show logs from dynamically created images.
360
364
  # TODO: better way to do this
@@ -377,11 +381,7 @@ async def _run_app(
377
381
  if detach:
378
382
  if output_mgr := _get_output_manager():
379
383
  output_mgr.print(output_mgr.step_completed("Shutting down Modal client."))
380
- output_mgr.print(
381
- "The detached app keeps running. You can track its progress at: "
382
- f"[magenta]{running_app.app_page_url}[/magenta]"
383
- ""
384
- )
384
+ output_mgr.print(detached_disconnect_msg)
385
385
  if logs_loop:
386
386
  logs_loop.cancel()
387
387
  await _status_based_disconnect(client, running_app.app_id, e)
@@ -409,10 +409,8 @@ async def _run_app(
409
409
  # If we lose connection to the server after a detached App has started running, it will continue
410
410
  # I think we can only exit "nicely" if we are able to print output though, otherwise we should raise
411
411
  if detach and (output_mgr := _get_output_manager()):
412
- output_mgr.print(
413
- "Connection lost, but detached App will continue running: "
414
- f"[magenta]{running_app.app_page_url}[/magenta]"
415
- )
412
+ output_mgr.print(":white_exclamation_mark: Connection lost!")
413
+ output_mgr.print(detached_disconnect_msg)
416
414
  return
417
415
  raise
418
416
  except BaseException as e:
@@ -438,7 +436,7 @@ async def _run_app(
438
436
 
439
437
 
440
438
  async def _serve_update(
441
- app: _App,
439
+ app: "modal.app._App",
442
440
  existing_app_id: str,
443
441
  is_ready: Event,
444
442
  environment_name: str,
@@ -448,13 +446,12 @@ async def _serve_update(
448
446
  client = await _Client.from_env()
449
447
  try:
450
448
  running_app: RunningApp = await _init_local_app_existing(client, existing_app_id, environment_name)
451
-
449
+ local_app_state = app._local_state
452
450
  # Create objects
453
451
  await _create_all_objects(
454
452
  client,
455
453
  running_app,
456
- app._functions,
457
- app._classes,
454
+ local_app_state,
458
455
  environment_name,
459
456
  )
460
457
 
@@ -463,9 +460,7 @@ async def _serve_update(
463
460
  client,
464
461
  running_app,
465
462
  app_state=api_pb2.APP_STATE_UNSPECIFIED,
466
- functions=app._functions,
467
- classes=app._classes,
468
- tags=app._tags,
463
+ app_local_state=local_app_state,
469
464
  )
470
465
 
471
466
  # Communicate to the parent process
@@ -486,7 +481,7 @@ class DeployResult:
486
481
 
487
482
 
488
483
  async def _deploy_app(
489
- app: _App,
484
+ app: "modal.app._App",
490
485
  name: Optional[str] = None,
491
486
  namespace: Any = None, # mdmd:line-hidden
492
487
  client: Optional[_Client] = None,
@@ -544,8 +539,7 @@ async def _deploy_app(
544
539
  await _create_all_objects(
545
540
  client,
546
541
  running_app,
547
- app._functions,
548
- app._classes,
542
+ app._local_state,
549
543
  environment_name=environment_name,
550
544
  )
551
545
 
@@ -558,11 +552,9 @@ async def _deploy_app(
558
552
  app_url, warnings = await _publish_app(
559
553
  client,
560
554
  running_app,
561
- app_state=api_pb2.APP_STATE_DEPLOYED,
562
- functions=app._functions,
563
- classes=app._classes,
555
+ api_pb2.APP_STATE_DEPLOYED,
556
+ app._local_state,
564
557
  name=name,
565
- tags=app._tags,
566
558
  deployment_tag=tag,
567
559
  commit_info=commit_info,
568
560
  )
@@ -584,7 +576,7 @@ async def _deploy_app(
584
576
 
585
577
 
586
578
  async def _interactive_shell(
587
- _app: _App, cmds: list[str], environment_name: str = "", pty: bool = True, **kwargs: Any
579
+ _app: "modal.app._App", cmds: list[str], environment_name: str = "", pty: bool = True, **kwargs: Any
588
580
  ) -> None:
589
581
  """Run an interactive shell (like `bash`) within the image for this app.
590
582
 
modal/runner.pyi CHANGED
@@ -1,6 +1,5 @@
1
- import modal._functions
1
+ import modal.app
2
2
  import modal.client
3
- import modal.cls
4
3
  import modal.running_app
5
4
  import modal_proto.api_pb2
6
5
  import multiprocessing.synchronize
@@ -8,8 +7,6 @@ import synchronicity.combined_types
8
7
  import typing
9
8
  import typing_extensions
10
9
 
11
- _App = typing.TypeVar("_App")
12
-
13
10
  V = typing.TypeVar("V")
14
11
 
15
12
  async def _heartbeat(client: modal.client._Client, app_id: str) -> None: ...
@@ -29,8 +26,7 @@ async def _init_local_app_from_name(
29
26
  async def _create_all_objects(
30
27
  client: modal.client._Client,
31
28
  running_app: modal.running_app.RunningApp,
32
- functions: dict[str, modal._functions._Function],
33
- classes: dict[str, modal.cls._Cls],
29
+ local_app_state: modal.app._LocalAppState,
34
30
  environment_name: str,
35
31
  ) -> None:
36
32
  """Create objects that have been defined but not created on the server."""
@@ -40,10 +36,8 @@ async def _publish_app(
40
36
  client: modal.client._Client,
41
37
  running_app: modal.running_app.RunningApp,
42
38
  app_state: int,
43
- functions: dict[str, modal._functions._Function],
44
- classes: dict[str, modal.cls._Cls],
39
+ app_local_state: modal.app._LocalAppState,
45
40
  name: str = "",
46
- tags: dict[str, str] = {},
47
41
  deployment_tag: str = "",
48
42
  commit_info: typing.Optional[modal_proto.api_pb2.CommitInfo] = None,
49
43
  ) -> tuple[str, list[modal_proto.api_pb2.Warning]]:
@@ -66,18 +60,18 @@ async def _status_based_disconnect(
66
60
  ...
67
61
 
68
62
  def _run_app(
69
- app: _App,
63
+ app: modal.app._App,
70
64
  *,
71
65
  client: typing.Optional[modal.client._Client] = None,
72
66
  detach: bool = False,
73
67
  environment_name: typing.Optional[str] = None,
74
68
  interactive: bool = False,
75
- ) -> typing.AsyncContextManager[_App]:
69
+ ) -> typing.AsyncContextManager[modal.app._App]:
76
70
  """mdmd:hidden"""
77
71
  ...
78
72
 
79
73
  async def _serve_update(
80
- app: _App, existing_app_id: str, is_ready: multiprocessing.synchronize.Event, environment_name: str
74
+ app: modal.app._App, existing_app_id: str, is_ready: multiprocessing.synchronize.Event, environment_name: str
81
75
  ) -> None:
82
76
  """mdmd:hidden"""
83
77
  ...
@@ -115,7 +109,7 @@ class DeployResult:
115
109
  ...
116
110
 
117
111
  async def _deploy_app(
118
- app: _App,
112
+ app: modal.app._App,
119
113
  name: typing.Optional[str] = None,
120
114
  namespace: typing.Any = None,
121
115
  client: typing.Optional[modal.client._Client] = None,
@@ -129,7 +123,7 @@ async def _deploy_app(
129
123
  ...
130
124
 
131
125
  async def _interactive_shell(
132
- _app: _App, cmds: list[str], environment_name: str = "", pty: bool = True, **kwargs: typing.Any
126
+ _app: modal.app._App, cmds: list[str], environment_name: str = "", pty: bool = True, **kwargs: typing.Any
133
127
  ) -> None:
134
128
  """Run an interactive shell (like `bash`) within the image for this app.
135
129
 
@@ -159,26 +153,26 @@ class __run_app_spec(typing_extensions.Protocol):
159
153
  def __call__(
160
154
  self,
161
155
  /,
162
- app: _App,
156
+ app: modal.app.App,
163
157
  *,
164
158
  client: typing.Optional[modal.client.Client] = None,
165
159
  detach: bool = False,
166
160
  environment_name: typing.Optional[str] = None,
167
161
  interactive: bool = False,
168
- ) -> synchronicity.combined_types.AsyncAndBlockingContextManager[_App]:
162
+ ) -> synchronicity.combined_types.AsyncAndBlockingContextManager[modal.app.App]:
169
163
  """mdmd:hidden"""
170
164
  ...
171
165
 
172
166
  def aio(
173
167
  self,
174
168
  /,
175
- app: _App,
169
+ app: modal.app.App,
176
170
  *,
177
171
  client: typing.Optional[modal.client.Client] = None,
178
172
  detach: bool = False,
179
173
  environment_name: typing.Optional[str] = None,
180
174
  interactive: bool = False,
181
- ) -> typing.AsyncContextManager[_App]:
175
+ ) -> typing.AsyncContextManager[modal.app.App]:
182
176
  """mdmd:hidden"""
183
177
  ...
184
178
 
@@ -186,13 +180,23 @@ run_app: __run_app_spec
186
180
 
187
181
  class __serve_update_spec(typing_extensions.Protocol):
188
182
  def __call__(
189
- self, /, app: _App, existing_app_id: str, is_ready: multiprocessing.synchronize.Event, environment_name: str
183
+ self,
184
+ /,
185
+ app: modal.app.App,
186
+ existing_app_id: str,
187
+ is_ready: multiprocessing.synchronize.Event,
188
+ environment_name: str,
190
189
  ) -> None:
191
190
  """mdmd:hidden"""
192
191
  ...
193
192
 
194
193
  async def aio(
195
- self, /, app: _App, existing_app_id: str, is_ready: multiprocessing.synchronize.Event, environment_name: str
194
+ self,
195
+ /,
196
+ app: modal.app.App,
197
+ existing_app_id: str,
198
+ is_ready: multiprocessing.synchronize.Event,
199
+ environment_name: str,
196
200
  ) -> None:
197
201
  """mdmd:hidden"""
198
202
  ...
@@ -203,7 +207,7 @@ class __deploy_app_spec(typing_extensions.Protocol):
203
207
  def __call__(
204
208
  self,
205
209
  /,
206
- app: _App,
210
+ app: modal.app.App,
207
211
  name: typing.Optional[str] = None,
208
212
  namespace: typing.Any = None,
209
213
  client: typing.Optional[modal.client.Client] = None,
@@ -219,7 +223,7 @@ class __deploy_app_spec(typing_extensions.Protocol):
219
223
  async def aio(
220
224
  self,
221
225
  /,
222
- app: _App,
226
+ app: modal.app.App,
223
227
  name: typing.Optional[str] = None,
224
228
  namespace: typing.Any = None,
225
229
  client: typing.Optional[modal.client.Client] = None,
@@ -236,7 +240,13 @@ deploy_app: __deploy_app_spec
236
240
 
237
241
  class __interactive_shell_spec(typing_extensions.Protocol):
238
242
  def __call__(
239
- self, /, _app: _App, cmds: list[str], environment_name: str = "", pty: bool = True, **kwargs: typing.Any
243
+ self,
244
+ /,
245
+ _app: modal.app.App,
246
+ cmds: list[str],
247
+ environment_name: str = "",
248
+ pty: bool = True,
249
+ **kwargs: typing.Any,
240
250
  ) -> None:
241
251
  """Run an interactive shell (like `bash`) within the image for this app.
242
252
 
@@ -263,7 +273,13 @@ class __interactive_shell_spec(typing_extensions.Protocol):
263
273
  ...
264
274
 
265
275
  async def aio(
266
- self, /, _app: _App, cmds: list[str], environment_name: str = "", pty: bool = True, **kwargs: typing.Any
276
+ self,
277
+ /,
278
+ _app: modal.app.App,
279
+ cmds: list[str],
280
+ environment_name: str = "",
281
+ pty: bool = True,
282
+ **kwargs: typing.Any,
267
283
  ) -> None:
268
284
  """Run an interactive shell (like `bash`) within the image for this app.
269
285
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.2.1.dev6
3
+ Version: 1.2.1.dev8
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -19,15 +19,15 @@ modal/_tunnel.py,sha256=zTBxBiuH1O22tS1OliAJdIsSmaZS8PlnifS_6S5z-mk,6320
19
19
  modal/_tunnel.pyi,sha256=rvC7USR2BcKkbZIeCJXwf7-UfGE-LPLjKsGNiK7Lxa4,13366
20
20
  modal/_type_manager.py,sha256=DWjgmjYJuOagw2erin506UUbG2H5UzZCFEekS-7hmfA,9087
21
21
  modal/_watcher.py,sha256=K6LYnlmSGQB4tWWI9JADv-tvSvQ1j522FwT71B51CX8,3584
22
- modal/app.py,sha256=W-24GIIBTKBqBWF6n4aB_heUhCb8d4KJfGF8oWteZn8,52454
23
- modal/app.pyi,sha256=6wlYK9nNp0GuHXyUxGmcZ6J92EjsWS-KK6KRuejqXEY,49718
22
+ modal/app.py,sha256=tXbc6EO7ZCxphspVuqIOHOr-OKqL7De8vChDTYEAElg,54516
23
+ modal/app.pyi,sha256=AUV5Rp8qQrZJTP2waoKHFY7rYgsXNMYibMcCAQKuSeo,50544
24
24
  modal/billing.py,sha256=zmQ3bcCJlwa4KD1IA_QgdWpm1pn13c-7qfy79iEauYI,195
25
25
  modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
26
26
  modal/client.py,sha256=kyAIVB3Ay-XKJizQ_1ufUFB__EagV0MLmHJpyYyJ7J0,18636
27
- modal/client.pyi,sha256=FGi7I3pZlYeqgO-K6BAnw70jdm-Zb3cxkuMFCACHIJY,15829
27
+ modal/client.pyi,sha256=xJmCcXA-PosLDtwke2D3FfNQrdt1jVNJ_jGodXMBA8Y,15829
28
28
  modal/cloud_bucket_mount.py,sha256=I2GRXYhOWLIz2kJZjXu75jAm9EJkBNcutGc6jR2ReUw,5928
29
29
  modal/cloud_bucket_mount.pyi,sha256=VuUOipMIHqFXMkD-3g2bsoqpSxV5qswlFHDOqPQzYAo,7405
30
- modal/cls.py,sha256=_Dzu8Tjjp_XukJxfBioAeHf03egKQYuKj7SrHJiK6CE,40676
30
+ modal/cls.py,sha256=ZxzivE3fNci4-A5uyBYNAzXMXtdqDg3gnYvgbdy5fhg,40384
31
31
  modal/cls.pyi,sha256=jJsDPFoqzM4ht-V-e-xEJKJ5TINLF0fYtoBm_UeAW5Y,27281
32
32
  modal/config.py,sha256=ZvQtKR4vACdvV-sDI-CtGepUGNMpQucwhgkGZ3k6gc0,12869
33
33
  modal/container_process.py,sha256=Mutkl7sg_WR5zP4oJiWSC-3UdYRqp0zdKi1shZbi-bk,6996
@@ -41,7 +41,7 @@ modal/file_io.py,sha256=OSKr77TujcXGJW1iikzYiHckLSmv07QBgBHcxxYEkoI,21456
41
41
  modal/file_io.pyi,sha256=xtO6Glf_BFwDE7QiQQo24QqcMf_Vv-iz7WojcGVlLBU,15932
42
42
  modal/file_pattern_matcher.py,sha256=A_Kdkej6q7YQyhM_2-BvpFmPqJ0oHb54B6yf9VqvPVE,8116
43
43
  modal/functions.py,sha256=kcNHvqeGBxPI7Cgd57NIBBghkfbeFJzXO44WW0jSmao,325
44
- modal/functions.pyi,sha256=Z6VuukLrjASAgf0kV9I6c09WvP_b2gCujX6f9j2bBaw,37988
44
+ modal/functions.pyi,sha256=CMwApS396tdElFrjnV6RuL2DTCz4C3jYzYoq1y_LPUQ,37988
45
45
  modal/gpu.py,sha256=Fe5ORvVPDIstSq1xjmM6OoNgLYFWvogP9r5BgmD3hYg,6769
46
46
  modal/image.py,sha256=HDkOnhIAN8g63a8LTN4J5SjC9ciReFQQJIxTS2z5KFM,107216
47
47
  modal/image.pyi,sha256=dMvMwAuvWkNN2BRYJFijkEy2m_xtEXgCKK0T7FVldsc,77514
@@ -64,8 +64,8 @@ modal/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
64
64
  modal/queue.py,sha256=aaH3SNWeh_HjEyVUtFiN345v0GJFoucJ6aNDrDggWZQ,25775
65
65
  modal/queue.pyi,sha256=mFu7GFFVFNLU9VZshnfekEsb-ABgpjdhJ07KXHvdv3A,37256
66
66
  modal/retries.py,sha256=IvNLDM0f_GLUDD5VgEDoN09C88yoxSrCquinAuxT1Sc,5205
67
- modal/runner.py,sha256=ZqXk12TyiuyZQXlDhSTXKylg-G0_I0uK6fzdJ_qFV7o,25182
68
- modal/runner.pyi,sha256=vUDRKqLz09QvZsaCH1gTG_iujewj-eGxxb6-VmN6eAw,8531
67
+ modal/runner.py,sha256=b_oG8LU8pT3NByfhwLJfYJ0RdNbnlBd-Zcds-J1JRYU,25197
68
+ modal/runner.pyi,sha256=DV3Z7h0owgRyOu9W5KU5O3UbRftX99KGrZQId91fpsU,8671
69
69
  modal/running_app.py,sha256=v61mapYNV1-O-Uaho5EfJlryMLvIT9We0amUOSvSGx8,1188
70
70
  modal/sandbox.py,sha256=R1WOFDTmg_SiG7NIEbzQMeTYHwLvX9r3j9lpINhIufc,45970
71
71
  modal/sandbox.pyi,sha256=elVE1xEy_ZhD009oNPnCwlZi4tK-RUb1qAoxkVteG9E,50713
@@ -155,7 +155,7 @@ modal/experimental/__init__.py,sha256=9gkVuDmu3m4TlKoU3MzEtTOemUSs8EEOWba40s7Aa0
155
155
  modal/experimental/flash.py,sha256=C4sef08rARYFllsgtqukFmYL18SZW0_JpMS0BejDcUs,28552
156
156
  modal/experimental/flash.pyi,sha256=vV_OQhtdrPn8SW0XrBK-aLLHHIvxAzLzwFbWrke-m74,15463
157
157
  modal/experimental/ipython.py,sha256=TrCfmol9LGsRZMeDoeMPx3Hv3BFqQhYnmD_iH0pqdhk,2904
158
- modal-1.2.1.dev6.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
158
+ modal-1.2.1.dev8.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
159
159
  modal_docs/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,28
160
160
  modal_docs/gen_cli_docs.py,sha256=c1yfBS_x--gL5bs0N4ihMwqwX8l3IBWSkBAKNNIi6bQ,3801
161
161
  modal_docs/gen_reference_docs.py,sha256=d_CQUGQ0rfw28u75I2mov9AlS773z9rG40-yq5o7g2U,6359
@@ -183,10 +183,10 @@ modal_proto/task_command_router_pb2.py,sha256=_pD2ZpU0bNzhwBdzmLoLyLtAtftI_Agxwn
183
183
  modal_proto/task_command_router_pb2.pyi,sha256=EyDgXPLr7alqjXYERV8w_MPuO404x0uCppmSkrfE9IE,14589
184
184
  modal_proto/task_command_router_pb2_grpc.py,sha256=uEQ0HdrCp8v-9bB5yIic9muA8spCShLHY6Bz9cCgOUE,10114
185
185
  modal_proto/task_command_router_pb2_grpc.pyi,sha256=s3Yxsrawdj4nr8vqQqsAxyX6ilWaGbdECy425KKbLIA,3301
186
- modal_version/__init__.py,sha256=LY_wuei6F9T8FfZ_2nkSI70w3GrMT-NIf59vhLq1kV8,120
186
+ modal_version/__init__.py,sha256=BGT-zG4jbs-VPlDeSA13G_diAPSM3dNIBl_5lJOh-4w,120
187
187
  modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
188
- modal-1.2.1.dev6.dist-info/METADATA,sha256=c1rDfKMZkhrvkyQicFPbv9evf5wuXLUX5BzW6R_YHwc,2483
189
- modal-1.2.1.dev6.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
190
- modal-1.2.1.dev6.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
191
- modal-1.2.1.dev6.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
192
- modal-1.2.1.dev6.dist-info/RECORD,,
188
+ modal-1.2.1.dev8.dist-info/METADATA,sha256=gfahBhON2zAnVUbV8XvOE3cuXb3nukFnV9mOuVwiWBs,2483
189
+ modal-1.2.1.dev8.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
190
+ modal-1.2.1.dev8.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
191
+ modal-1.2.1.dev8.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
192
+ modal-1.2.1.dev8.dist-info/RECORD,,
modal_version/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # Copyright Modal Labs 2025
2
2
  """Supplies the current version of the modal client library."""
3
3
 
4
- __version__ = "1.2.1.dev6"
4
+ __version__ = "1.2.1.dev8"