modal 0.67.22__py3-none-any.whl → 0.67.33__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/_container_entrypoint.py +3 -1
- modal/_utils/function_utils.py +6 -0
- modal/client.pyi +2 -2
- modal/cls.py +68 -15
- modal/cls.pyi +5 -6
- modal/functions.py +1 -64
- modal/functions.pyi +6 -10
- modal/image.py +13 -8
- modal/image.pyi +2 -2
- {modal-0.67.22.dist-info → modal-0.67.33.dist-info}/METADATA +1 -1
- {modal-0.67.22.dist-info → modal-0.67.33.dist-info}/RECORD +23 -23
- modal_proto/api.proto +2 -0
- modal_proto/api_grpc.py +16 -0
- modal_proto/api_pb2.py +658 -658
- modal_proto/api_pb2.pyi +4 -1
- modal_proto/api_pb2_grpc.py +33 -0
- modal_proto/api_pb2_grpc.pyi +10 -0
- modal_proto/modal_api_grpc.py +1 -0
- modal_version/_version_generated.py +1 -1
- {modal-0.67.22.dist-info → modal-0.67.33.dist-info}/LICENSE +0 -0
- {modal-0.67.22.dist-info → modal-0.67.33.dist-info}/WHEEL +0 -0
- {modal-0.67.22.dist-info → modal-0.67.33.dist-info}/entry_points.txt +0 -0
- {modal-0.67.22.dist-info → modal-0.67.33.dist-info}/top_level.txt +0 -0
modal/_container_entrypoint.py
CHANGED
@@ -478,7 +478,9 @@ def main(container_args: api_pb2.ContainerArguments, client: Client):
|
|
478
478
|
if len(service.code_deps) != len(dep_object_ids):
|
479
479
|
raise ExecutionError(
|
480
480
|
f"Function has {len(service.code_deps)} dependencies"
|
481
|
-
f" but container got {len(dep_object_ids)} object ids
|
481
|
+
f" but container got {len(dep_object_ids)} object ids.\n"
|
482
|
+
f"Code deps: {service.code_deps}\n"
|
483
|
+
f"Object ids: {dep_object_ids}"
|
482
484
|
)
|
483
485
|
for object_id, obj in zip(dep_object_ids, service.code_deps):
|
484
486
|
metadata: Message = container_app.object_handle_metadata[object_id]
|
modal/_utils/function_utils.py
CHANGED
@@ -248,7 +248,13 @@ class FunctionInfo:
|
|
248
248
|
def get_globals(self) -> dict[str, Any]:
|
249
249
|
from .._vendor.cloudpickle import _extract_code_globals
|
250
250
|
|
251
|
+
if self.raw_f is None:
|
252
|
+
return {}
|
253
|
+
|
251
254
|
func = self.raw_f
|
255
|
+
while hasattr(func, "__wrapped__") and func is not func.__wrapped__:
|
256
|
+
# Unwrap functions decorated using functools.wrapped (potentially multiple times)
|
257
|
+
func = func.__wrapped__
|
252
258
|
f_globals_ref = _extract_code_globals(func.__code__)
|
253
259
|
f_globals = {k: func.__globals__[k] for k in f_globals_ref if k in func.__globals__}
|
254
260
|
return f_globals
|
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.67.
|
29
|
+
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.67.33"
|
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.67.
|
84
|
+
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.67.33"
|
85
85
|
): ...
|
86
86
|
def is_closed(self) -> bool: ...
|
87
87
|
@property
|
modal/cls.py
CHANGED
@@ -72,6 +72,68 @@ def _get_class_constructor_signature(user_cls: type) -> inspect.Signature:
|
|
72
72
|
return inspect.Signature(constructor_parameters)
|
73
73
|
|
74
74
|
|
75
|
+
def _bind_instance_method(service_function: _Function, class_bound_method: _Function):
|
76
|
+
"""mdmd:hidden
|
77
|
+
|
78
|
+
Binds an "instance service function" to a specific method.
|
79
|
+
This "dummy" _Function gets no unique object_id and isn't backend-backed at the moment, since all
|
80
|
+
it does it forward invocations to the underlying instance_service_function with the specified method,
|
81
|
+
and we don't support web_config for parameterized methods at the moment.
|
82
|
+
"""
|
83
|
+
# TODO(elias): refactor to not use `_from_loader()` as a crutch for lazy-loading the
|
84
|
+
# underlying instance_service_function. It's currently used in order to take advantage
|
85
|
+
# of resolver logic and get "chained" resolution of lazy loads, even though this thin
|
86
|
+
# object itself doesn't need any "loading"
|
87
|
+
assert service_function._obj
|
88
|
+
method_name = class_bound_method._use_method_name
|
89
|
+
full_function_name = f"{class_bound_method._function_name}[parameterized]"
|
90
|
+
|
91
|
+
def hydrate_from_instance_service_function(method_placeholder_fun):
|
92
|
+
method_placeholder_fun._hydrate_from_other(service_function)
|
93
|
+
method_placeholder_fun._obj = service_function._obj
|
94
|
+
method_placeholder_fun._web_url = (
|
95
|
+
class_bound_method._web_url
|
96
|
+
) # TODO: this shouldn't be set when actual parameters are used
|
97
|
+
method_placeholder_fun._function_name = full_function_name
|
98
|
+
method_placeholder_fun._is_generator = class_bound_method._is_generator
|
99
|
+
method_placeholder_fun._cluster_size = class_bound_method._cluster_size
|
100
|
+
method_placeholder_fun._use_method_name = method_name
|
101
|
+
method_placeholder_fun._is_method = True
|
102
|
+
|
103
|
+
async def _load(fun: "_Function", resolver: Resolver, existing_object_id: Optional[str]):
|
104
|
+
# there is currently no actual loading logic executed to create each method on
|
105
|
+
# the *parameterized* instance of a class - it uses the parameter-bound service-function
|
106
|
+
# for the instance. This load method just makes sure to set all attributes after the
|
107
|
+
# `service_function` has been loaded (it's in the `_deps`)
|
108
|
+
hydrate_from_instance_service_function(fun)
|
109
|
+
|
110
|
+
def _deps():
|
111
|
+
if service_function.is_hydrated:
|
112
|
+
# without this check, the common service_function will be reloaded by all methods
|
113
|
+
# TODO(elias): Investigate if we can fix this multi-loader in the resolver - feels like a bug?
|
114
|
+
return []
|
115
|
+
return [service_function]
|
116
|
+
|
117
|
+
rep = f"Method({full_function_name})"
|
118
|
+
|
119
|
+
fun = _Function._from_loader(
|
120
|
+
_load,
|
121
|
+
rep,
|
122
|
+
deps=_deps,
|
123
|
+
hydrate_lazily=True,
|
124
|
+
)
|
125
|
+
if service_function.is_hydrated:
|
126
|
+
# Eager hydration (skip load) if the instance service function is already loaded
|
127
|
+
hydrate_from_instance_service_function(fun)
|
128
|
+
|
129
|
+
fun._info = class_bound_method._info
|
130
|
+
fun._obj = service_function._obj
|
131
|
+
fun._is_method = True
|
132
|
+
fun._app = class_bound_method._app
|
133
|
+
fun._spec = class_bound_method._spec
|
134
|
+
return fun
|
135
|
+
|
136
|
+
|
75
137
|
class _Obj:
|
76
138
|
"""An instance of a `Cls`, i.e. `Cls("foo", 42)` returns an `Obj`.
|
77
139
|
|
@@ -90,10 +152,9 @@ class _Obj:
|
|
90
152
|
|
91
153
|
def __init__(
|
92
154
|
self,
|
93
|
-
user_cls: type,
|
155
|
+
user_cls: Optional[type], # this would be None in case of lookups
|
94
156
|
class_service_function: Optional[_Function], # only None for <v0.63 classes
|
95
157
|
classbound_methods: dict[str, _Function],
|
96
|
-
from_other_workspace: bool,
|
97
158
|
options: Optional[api_pb2.FunctionOptions],
|
98
159
|
args,
|
99
160
|
kwargs,
|
@@ -107,17 +168,15 @@ class _Obj:
|
|
107
168
|
if class_service_function:
|
108
169
|
# >= v0.63 classes
|
109
170
|
# first create the singular object function used by all methods on this parameterization
|
110
|
-
self._instance_service_function = class_service_function._bind_parameters(
|
111
|
-
self, from_other_workspace, options, args, kwargs
|
112
|
-
)
|
171
|
+
self._instance_service_function = class_service_function._bind_parameters(self, options, args, kwargs)
|
113
172
|
for method_name, class_bound_method in classbound_methods.items():
|
114
|
-
method = self._instance_service_function
|
173
|
+
method = _bind_instance_method(self._instance_service_function, class_bound_method)
|
115
174
|
self._method_functions[method_name] = method
|
116
175
|
else:
|
117
176
|
# looked up <v0.63 classes - bind each individual method to the new parameters
|
118
177
|
self._instance_service_function = None
|
119
178
|
for method_name, class_bound_method in classbound_methods.items():
|
120
|
-
method = class_bound_method._bind_parameters(self,
|
179
|
+
method = class_bound_method._bind_parameters(self, options, args, kwargs)
|
121
180
|
self._method_functions[method_name] = method
|
122
181
|
|
123
182
|
# Used for construction local object lazily
|
@@ -247,7 +306,6 @@ class _Cls(_Object, type_prefix="cs"):
|
|
247
306
|
_method_functions: Optional[dict[str, _Function]] = None # Placeholder _Functions for each method
|
248
307
|
_options: Optional[api_pb2.FunctionOptions]
|
249
308
|
_callables: dict[str, Callable[..., Any]]
|
250
|
-
_from_other_workspace: Optional[bool] # Functions require FunctionBindParams before invocation.
|
251
309
|
_app: Optional["modal.app._App"] = None # not set for lookups
|
252
310
|
|
253
311
|
def _initialize_from_empty(self):
|
@@ -255,7 +313,6 @@ class _Cls(_Object, type_prefix="cs"):
|
|
255
313
|
self._class_service_function = None
|
256
314
|
self._options = None
|
257
315
|
self._callables = {}
|
258
|
-
self._from_other_workspace = None
|
259
316
|
|
260
317
|
def _initialize_from_other(self, other: "_Cls"):
|
261
318
|
self._user_cls = other._user_cls
|
@@ -263,7 +320,6 @@ class _Cls(_Object, type_prefix="cs"):
|
|
263
320
|
self._method_functions = other._method_functions
|
264
321
|
self._options = other._options
|
265
322
|
self._callables = other._callables
|
266
|
-
self._from_other_workspace = other._from_other_workspace
|
267
323
|
|
268
324
|
def _get_partial_functions(self) -> dict[str, _PartialFunction]:
|
269
325
|
if not self._user_cls:
|
@@ -277,7 +333,7 @@ class _Cls(_Object, type_prefix="cs"):
|
|
277
333
|
and self._class_service_function._method_handle_metadata
|
278
334
|
and len(self._class_service_function._method_handle_metadata)
|
279
335
|
):
|
280
|
-
# The class only has a class service
|
336
|
+
# The class only has a class service function and no method placeholders (v0.67+)
|
281
337
|
if self._method_functions:
|
282
338
|
# We're here when the Cls is loaded locally (e.g. _Cls.from_local) so the _method_functions mapping is
|
283
339
|
# populated with (un-hydrated) _Function objects
|
@@ -298,7 +354,7 @@ class _Cls(_Object, type_prefix="cs"):
|
|
298
354
|
self._method_functions[method_name] = _Function._new_hydrated(
|
299
355
|
self._class_service_function.object_id, self._client, method_handle_metadata
|
300
356
|
)
|
301
|
-
elif self._class_service_function:
|
357
|
+
elif self._class_service_function and self._class_service_function.object_id:
|
302
358
|
# A class with a class service function and method placeholder functions
|
303
359
|
self._method_functions = {}
|
304
360
|
for method in metadata.methods:
|
@@ -382,7 +438,6 @@ class _Cls(_Object, type_prefix="cs"):
|
|
382
438
|
cls._class_service_function = class_service_function
|
383
439
|
cls._method_functions = method_functions
|
384
440
|
cls._callables = callables
|
385
|
-
cls._from_other_workspace = False
|
386
441
|
return cls
|
387
442
|
|
388
443
|
def _uses_common_service_function(self):
|
@@ -449,7 +504,6 @@ class _Cls(_Object, type_prefix="cs"):
|
|
449
504
|
|
450
505
|
rep = f"Ref({app_name})"
|
451
506
|
cls = cls._from_loader(_load_remote, rep, is_another_app=True)
|
452
|
-
cls._from_other_workspace = bool(workspace is not None)
|
453
507
|
return cls
|
454
508
|
|
455
509
|
def with_options(
|
@@ -543,7 +597,6 @@ class _Cls(_Object, type_prefix="cs"):
|
|
543
597
|
self._user_cls,
|
544
598
|
self._class_service_function,
|
545
599
|
self._method_functions,
|
546
|
-
self._from_other_workspace,
|
547
600
|
self._options,
|
548
601
|
args,
|
549
602
|
kwargs,
|
modal/cls.pyi
CHANGED
@@ -19,6 +19,9 @@ T = typing.TypeVar("T")
|
|
19
19
|
|
20
20
|
def _use_annotation_parameters(user_cls) -> bool: ...
|
21
21
|
def _get_class_constructor_signature(user_cls: type) -> inspect.Signature: ...
|
22
|
+
def _bind_instance_method(
|
23
|
+
service_function: modal.functions._Function, class_bound_method: modal.functions._Function
|
24
|
+
): ...
|
22
25
|
|
23
26
|
class _Obj:
|
24
27
|
_functions: dict[str, modal.functions._Function]
|
@@ -30,10 +33,9 @@ class _Obj:
|
|
30
33
|
def _uses_common_service_function(self): ...
|
31
34
|
def __init__(
|
32
35
|
self,
|
33
|
-
user_cls: type,
|
36
|
+
user_cls: typing.Optional[type],
|
34
37
|
class_service_function: typing.Optional[modal.functions._Function],
|
35
38
|
classbound_methods: dict[str, modal.functions._Function],
|
36
|
-
from_other_workspace: bool,
|
37
39
|
options: typing.Optional[modal_proto.api_pb2.FunctionOptions],
|
38
40
|
args,
|
39
41
|
kwargs,
|
@@ -58,10 +60,9 @@ class Obj:
|
|
58
60
|
|
59
61
|
def __init__(
|
60
62
|
self,
|
61
|
-
user_cls: type,
|
63
|
+
user_cls: typing.Optional[type],
|
62
64
|
class_service_function: typing.Optional[modal.functions.Function],
|
63
65
|
classbound_methods: dict[str, modal.functions.Function],
|
64
|
-
from_other_workspace: bool,
|
65
66
|
options: typing.Optional[modal_proto.api_pb2.FunctionOptions],
|
66
67
|
args,
|
67
68
|
kwargs,
|
@@ -90,7 +91,6 @@ class _Cls(modal.object._Object):
|
|
90
91
|
_method_functions: typing.Optional[dict[str, modal.functions._Function]]
|
91
92
|
_options: typing.Optional[modal_proto.api_pb2.FunctionOptions]
|
92
93
|
_callables: dict[str, typing.Callable[..., typing.Any]]
|
93
|
-
_from_other_workspace: typing.Optional[bool]
|
94
94
|
_app: typing.Optional[modal.app._App]
|
95
95
|
|
96
96
|
def _initialize_from_empty(self): ...
|
@@ -142,7 +142,6 @@ class Cls(modal.object.Object):
|
|
142
142
|
_method_functions: typing.Optional[dict[str, modal.functions.Function]]
|
143
143
|
_options: typing.Optional[modal_proto.api_pb2.FunctionOptions]
|
144
144
|
_callables: dict[str, typing.Callable[..., typing.Any]]
|
145
|
-
_from_other_workspace: typing.Optional[bool]
|
146
145
|
_app: typing.Optional[modal.app.App]
|
147
146
|
|
148
147
|
def __init__(self, *args, **kwargs): ...
|
modal/functions.py
CHANGED
@@ -424,68 +424,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
424
424
|
fun._is_method = True
|
425
425
|
return fun
|
426
426
|
|
427
|
-
def _bind_instance_method(self, class_bound_method: "_Function"):
|
428
|
-
"""mdmd:hidden
|
429
|
-
|
430
|
-
Binds an "instance service function" to a specific method.
|
431
|
-
This "dummy" _Function gets no unique object_id and isn't backend-backed at the moment, since all
|
432
|
-
it does it forward invocations to the underlying instance_service_function with the specified method,
|
433
|
-
and we don't support web_config for parameterized methods at the moment.
|
434
|
-
"""
|
435
|
-
# TODO(elias): refactor to not use `_from_loader()` as a crutch for lazy-loading the
|
436
|
-
# underlying instance_service_function. It's currently used in order to take advantage
|
437
|
-
# of resolver logic and get "chained" resolution of lazy loads, even though this thin
|
438
|
-
# object itself doesn't need any "loading"
|
439
|
-
instance_service_function = self
|
440
|
-
assert instance_service_function._obj
|
441
|
-
method_name = class_bound_method._use_method_name
|
442
|
-
full_function_name = f"{class_bound_method._function_name}[parameterized]"
|
443
|
-
|
444
|
-
def hydrate_from_instance_service_function(method_placeholder_fun):
|
445
|
-
method_placeholder_fun._hydrate_from_other(instance_service_function)
|
446
|
-
method_placeholder_fun._obj = instance_service_function._obj
|
447
|
-
method_placeholder_fun._web_url = (
|
448
|
-
class_bound_method._web_url
|
449
|
-
) # TODO: this shouldn't be set when actual parameters are used
|
450
|
-
method_placeholder_fun._function_name = full_function_name
|
451
|
-
method_placeholder_fun._is_generator = class_bound_method._is_generator
|
452
|
-
method_placeholder_fun._cluster_size = class_bound_method._cluster_size
|
453
|
-
method_placeholder_fun._use_method_name = method_name
|
454
|
-
method_placeholder_fun._is_method = True
|
455
|
-
|
456
|
-
async def _load(fun: "_Function", resolver: Resolver, existing_object_id: Optional[str]):
|
457
|
-
# there is currently no actual loading logic executed to create each method on
|
458
|
-
# the *parameterized* instance of a class - it uses the parameter-bound service-function
|
459
|
-
# for the instance. This load method just makes sure to set all attributes after the
|
460
|
-
# `instance_service_function` has been loaded (it's in the `_deps`)
|
461
|
-
hydrate_from_instance_service_function(fun)
|
462
|
-
|
463
|
-
def _deps():
|
464
|
-
if instance_service_function.is_hydrated:
|
465
|
-
# without this check, the common instance_service_function will be reloaded by all methods
|
466
|
-
# TODO(elias): Investigate if we can fix this multi-loader in the resolver - feels like a bug?
|
467
|
-
return []
|
468
|
-
return [instance_service_function]
|
469
|
-
|
470
|
-
rep = f"Method({full_function_name})"
|
471
|
-
|
472
|
-
fun = _Function._from_loader(
|
473
|
-
_load,
|
474
|
-
rep,
|
475
|
-
deps=_deps,
|
476
|
-
hydrate_lazily=True,
|
477
|
-
)
|
478
|
-
if instance_service_function.is_hydrated:
|
479
|
-
# Eager hydration (skip load) if the instance service function is already loaded
|
480
|
-
hydrate_from_instance_service_function(fun)
|
481
|
-
|
482
|
-
fun._info = class_bound_method._info
|
483
|
-
fun._obj = instance_service_function._obj
|
484
|
-
fun._is_method = True
|
485
|
-
fun._app = class_bound_method._app
|
486
|
-
fun._spec = class_bound_method._spec
|
487
|
-
return fun
|
488
|
-
|
489
427
|
@staticmethod
|
490
428
|
def from_args(
|
491
429
|
info: FunctionInfo,
|
@@ -982,7 +920,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
982
920
|
def _bind_parameters(
|
983
921
|
self,
|
984
922
|
obj: "modal.cls._Obj",
|
985
|
-
from_other_workspace: bool,
|
986
923
|
options: Optional[api_pb2.FunctionOptions],
|
987
924
|
args: Sized,
|
988
925
|
kwargs: dict[str, Any],
|
@@ -993,7 +930,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
993
930
|
"""
|
994
931
|
|
995
932
|
# In some cases, reuse the base function, i.e. not create new clones of each method or the "service function"
|
996
|
-
can_use_parent = len(args) + len(kwargs) == 0 and
|
933
|
+
can_use_parent = len(args) + len(kwargs) == 0 and options is None
|
997
934
|
parent = self
|
998
935
|
|
999
936
|
async def _load(param_bound_func: _Function, resolver: Resolver, existing_object_id: Optional[str]):
|
modal/functions.pyi
CHANGED
@@ -150,7 +150,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], modal.object.
|
|
150
150
|
_method_handle_metadata: typing.Optional[dict[str, modal_proto.api_pb2.FunctionHandleMetadata]]
|
151
151
|
|
152
152
|
def _bind_method(self, user_cls, method_name: str, partial_function: modal.partial_function._PartialFunction): ...
|
153
|
-
def _bind_instance_method(self, class_bound_method: _Function): ...
|
154
153
|
@staticmethod
|
155
154
|
def from_args(
|
156
155
|
info: modal._utils.function_utils.FunctionInfo,
|
@@ -200,7 +199,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], modal.object.
|
|
200
199
|
def _bind_parameters(
|
201
200
|
self,
|
202
201
|
obj: modal.cls._Obj,
|
203
|
-
from_other_workspace: bool,
|
204
202
|
options: typing.Optional[modal_proto.api_pb2.FunctionOptions],
|
205
203
|
args: collections.abc.Sized,
|
206
204
|
kwargs: dict[str, typing.Any],
|
@@ -320,7 +318,6 @@ class Function(typing.Generic[P, ReturnType, OriginalReturnType], modal.object.O
|
|
320
318
|
|
321
319
|
def __init__(self, *args, **kwargs): ...
|
322
320
|
def _bind_method(self, user_cls, method_name: str, partial_function: modal.partial_function.PartialFunction): ...
|
323
|
-
def _bind_instance_method(self, class_bound_method: Function): ...
|
324
321
|
@staticmethod
|
325
322
|
def from_args(
|
326
323
|
info: modal._utils.function_utils.FunctionInfo,
|
@@ -370,7 +367,6 @@ class Function(typing.Generic[P, ReturnType, OriginalReturnType], modal.object.O
|
|
370
367
|
def _bind_parameters(
|
371
368
|
self,
|
372
369
|
obj: modal.cls.Obj,
|
373
|
-
from_other_workspace: bool,
|
374
370
|
options: typing.Optional[modal_proto.api_pb2.FunctionOptions],
|
375
371
|
args: collections.abc.Sized,
|
376
372
|
kwargs: dict[str, typing.Any],
|
@@ -459,11 +455,11 @@ class Function(typing.Generic[P, ReturnType, OriginalReturnType], modal.object.O
|
|
459
455
|
|
460
456
|
_call_generator_nowait: ___call_generator_nowait_spec
|
461
457
|
|
462
|
-
class __remote_spec(typing_extensions.Protocol[
|
458
|
+
class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER]):
|
463
459
|
def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER: ...
|
464
460
|
async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER: ...
|
465
461
|
|
466
|
-
remote: __remote_spec[
|
462
|
+
remote: __remote_spec[P, ReturnType]
|
467
463
|
|
468
464
|
class __remote_gen_spec(typing_extensions.Protocol):
|
469
465
|
def __call__(self, *args, **kwargs) -> typing.Generator[typing.Any, None, None]: ...
|
@@ -475,17 +471,17 @@ class Function(typing.Generic[P, ReturnType, OriginalReturnType], modal.object.O
|
|
475
471
|
def _get_obj(self) -> typing.Optional[modal.cls.Obj]: ...
|
476
472
|
def local(self, *args: P.args, **kwargs: P.kwargs) -> OriginalReturnType: ...
|
477
473
|
|
478
|
-
class ___experimental_spawn_spec(typing_extensions.Protocol[
|
474
|
+
class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER]):
|
479
475
|
def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
|
480
476
|
async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
|
481
477
|
|
482
|
-
_experimental_spawn: ___experimental_spawn_spec[
|
478
|
+
_experimental_spawn: ___experimental_spawn_spec[P, ReturnType]
|
483
479
|
|
484
|
-
class __spawn_spec(typing_extensions.Protocol[
|
480
|
+
class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER]):
|
485
481
|
def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
|
486
482
|
async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
|
487
483
|
|
488
|
-
spawn: __spawn_spec[
|
484
|
+
spawn: __spawn_spec[P, ReturnType]
|
489
485
|
|
490
486
|
def get_raw_f(self) -> typing.Callable[..., typing.Any]: ...
|
491
487
|
|
modal/image.py
CHANGED
@@ -677,13 +677,13 @@ class _Image(_Object, type_prefix="im"):
|
|
677
677
|
context_mount=mount,
|
678
678
|
)
|
679
679
|
|
680
|
-
def
|
681
|
-
"""Adds Python
|
680
|
+
def add_local_python_source(self, *modules: str, copy: bool = False) -> "_Image":
|
681
|
+
"""Adds locally available Python packages/modules to containers
|
682
682
|
|
683
|
-
Adds all files from the specified Python
|
683
|
+
Adds all files from the specified Python package or module to containers running the Image.
|
684
684
|
|
685
685
|
Packages are added to the `/root` directory of containers, which is on the `PYTHONPATH`
|
686
|
-
of any executed Modal Functions.
|
686
|
+
of any executed Modal Functions, enabling import of the module by that name.
|
687
687
|
|
688
688
|
By default (`copy=False`), the files are added to containers on startup and are not built into the actual Image,
|
689
689
|
which speeds up deployment.
|
@@ -693,9 +693,14 @@ class _Image(_Object, type_prefix="im"):
|
|
693
693
|
required if you want to run additional build steps after this one.
|
694
694
|
|
695
695
|
**Note:** This excludes all dot-prefixed subdirectories or files and all `.pyc`/`__pycache__` files.
|
696
|
-
To add full directories with finer control, use `.add_local_dir()` instead
|
696
|
+
To add full directories with finer control, use `.add_local_dir()` instead and specify `/root` as
|
697
|
+
the destination directory.
|
697
698
|
"""
|
698
|
-
|
699
|
+
|
700
|
+
def only_py_files(filename):
|
701
|
+
return filename.endswith(".py")
|
702
|
+
|
703
|
+
mount = _Mount.from_local_python_packages(*modules, condition=only_py_files)
|
699
704
|
return self._add_mount_layer_or_copy(mount, copy=copy)
|
700
705
|
|
701
706
|
def copy_local_dir(self, local_path: Union[str, Path], remote_path: Union[str, Path] = ".") -> "_Image":
|
@@ -1005,8 +1010,8 @@ class _Image(_Object, type_prefix="im"):
|
|
1005
1010
|
If not provided as argument the path to the lockfile is inferred. However, the
|
1006
1011
|
file has to exist, unless `ignore_lockfile` is set to `True`.
|
1007
1012
|
|
1008
|
-
Note that the root project of the poetry project is not installed,
|
1009
|
-
|
1013
|
+
Note that the root project of the poetry project is not installed, only the dependencies.
|
1014
|
+
For including local python source files see `add_local_python_source`
|
1010
1015
|
"""
|
1011
1016
|
|
1012
1017
|
def build_dockerfile(version: ImageBuilderVersion) -> DockerfileSpec:
|
modal/image.pyi
CHANGED
@@ -115,7 +115,7 @@ class _Image(modal.object._Object):
|
|
115
115
|
def copy_local_file(
|
116
116
|
self, local_path: typing.Union[str, pathlib.Path], remote_path: typing.Union[str, pathlib.Path] = "./"
|
117
117
|
) -> _Image: ...
|
118
|
-
def
|
118
|
+
def add_local_python_source(self, *module_names: str, copy: bool = False) -> _Image: ...
|
119
119
|
def copy_local_dir(
|
120
120
|
self, local_path: typing.Union[str, pathlib.Path], remote_path: typing.Union[str, pathlib.Path] = "."
|
121
121
|
) -> _Image: ...
|
@@ -372,7 +372,7 @@ class Image(modal.object.Object):
|
|
372
372
|
def copy_local_file(
|
373
373
|
self, local_path: typing.Union[str, pathlib.Path], remote_path: typing.Union[str, pathlib.Path] = "./"
|
374
374
|
) -> Image: ...
|
375
|
-
def
|
375
|
+
def add_local_python_source(self, *module_names: str, copy: bool = False) -> Image: ...
|
376
376
|
def copy_local_dir(
|
377
377
|
self, local_path: typing.Union[str, pathlib.Path], remote_path: typing.Union[str, pathlib.Path] = "."
|
378
378
|
) -> Image: ...
|
@@ -2,7 +2,7 @@ modal/__init__.py,sha256=Yn8zS7Jxl5uZjPM331Pc4FdSmp9Rt6VdE7TiE4ZKRc8,2151
|
|
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=
|
5
|
+
modal/_container_entrypoint.py,sha256=Tae8hAbgN62HM9Q_UFuCyyswfdh1rOey2zzXkbG4Cns,28795
|
6
6
|
modal/_ipython.py,sha256=HF_DYy0e0qM9WnGDmTY30s1RxzGya9GeORCauCEpRaE,450
|
7
7
|
modal/_location.py,sha256=S3lSxIU3h9HkWpkJ3Pwo0pqjIOSB1fjeSgUsY3x7eec,1202
|
8
8
|
modal/_output.py,sha256=0fWX_KQwhER--U81ys16CL-pA5A-LN20C0EZjElKGJQ,25410
|
@@ -19,11 +19,11 @@ modal/app.py,sha256=EJ7FUN6rWnSwLJoYJh8nmKg_t-8hdN8_rt0OrkP7JvQ,46084
|
|
19
19
|
modal/app.pyi,sha256=BE5SlR5tRECuc6-e2lUuOknDdov3zxgZ4N0AsLb5ZVQ,25270
|
20
20
|
modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
|
21
21
|
modal/client.py,sha256=VMg_aIuo_LOEe2ttxBHEND3PLhTp5lo-onH4wELhIyY,16375
|
22
|
-
modal/client.pyi,sha256=
|
22
|
+
modal/client.pyi,sha256=Xu0iGZiUWQoXYK4tgi8QgxTK38UPWHz-rS3_oDFwFYQ,7354
|
23
23
|
modal/cloud_bucket_mount.py,sha256=G7T7jWLD0QkmrfKR75mSTwdUZ2xNfj7pkVqb4ipmxmI,5735
|
24
24
|
modal/cloud_bucket_mount.pyi,sha256=CEi7vrH3kDUF4LAy4qP6tfImy2UJuFRcRbsgRNM1wo8,1403
|
25
|
-
modal/cls.py,sha256=
|
26
|
-
modal/cls.pyi,sha256=
|
25
|
+
modal/cls.py,sha256=OJqzj_V-n1g48BY_4Jg_BOTQdftEEl4kTWN0X4FOOdg,27378
|
26
|
+
modal/cls.pyi,sha256=47jaIT06fz8PSUrs-MaNn6r03PHsAyUGsKuK5e9RMhQ,8140
|
27
27
|
modal/config.py,sha256=1KhNJkjYsJkX1V8RPPdRYPlM2HE-ZZs0JVSxbiXjmrw,11010
|
28
28
|
modal/container_process.py,sha256=YRCKjn56oqTtGjtLxpl_KSkOhYrcRitgF3LOI6o14Q4,5759
|
29
29
|
modal/container_process.pyi,sha256=k2kClwaSzz11eci1pzFZgCm-ptXapHAyHTOENorlazA,2594
|
@@ -33,11 +33,11 @@ modal/environments.py,sha256=5cgA-zbm6ngKLsRA19zSOgtgo9-BarJK3FJK0BiF2Lo,6505
|
|
33
33
|
modal/environments.pyi,sha256=XalNpiPkAtHWAvOU2Cotq0ozmtl-Jv0FDsR8h9mr27Q,3521
|
34
34
|
modal/exception.py,sha256=EBkdWVved2XEPsXaoPRu56xfxFFHL9iuqvUsdj42WDA,6392
|
35
35
|
modal/experimental.py,sha256=jFuNbwrNHos47viMB9q-cHJSvf2RDxDdoEcss9plaZE,2302
|
36
|
-
modal/functions.py,sha256=
|
37
|
-
modal/functions.pyi,sha256=
|
36
|
+
modal/functions.py,sha256=Lteg8dMa8ly72-RM1ozxeGQ500pdeFyJgtflVwp3U7Q,66629
|
37
|
+
modal/functions.pyi,sha256=3Jupx4QKaqQ9p9XQSG4zDjB3W-87EYkFg55w2QuSQ8M,24892
|
38
38
|
modal/gpu.py,sha256=r4rL6uH3UJIQthzYvfWauXNyh01WqCPtKZCmmSX1fd4,6881
|
39
|
-
modal/image.py,sha256=
|
40
|
-
modal/image.pyi,sha256=
|
39
|
+
modal/image.py,sha256=cQ6WP1xHXZT_nY8z3aEFiGwKzrTV0yxi3Ab8JzF91eo,79653
|
40
|
+
modal/image.pyi,sha256=PIKH6JBA4L5TfdJrQu3pm2ykyIITmiP920TpP8cdyQA,24585
|
41
41
|
modal/io_streams.py,sha256=YfKAlWQAxzPCHE0-wVlAlX5vldrpfKMdr9ggL0c5VJo,15063
|
42
42
|
modal/io_streams.pyi,sha256=bCCVSxkMcosYd8O3PQDDwJw7TQ8JEcnYonLJ5t27TQs,4804
|
43
43
|
modal/mount.py,sha256=liaid5p42o0OKnzoocJJ_oCovDVderk3-JuCTa5pqtA,27656
|
@@ -83,7 +83,7 @@ modal/_utils/__init__.py,sha256=waLjl5c6IPDhSsdWAm9Bji4e2PVxamYABKAze6CHVXY,28
|
|
83
83
|
modal/_utils/app_utils.py,sha256=88BT4TPLWfYAQwKTHcyzNQRHg8n9B-QE2UyJs96iV-0,108
|
84
84
|
modal/_utils/async_utils.py,sha256=9ubwMkwiDB4gzOYG2jL9j7Fs-5dxHjcifZe3r7JRg-k,25091
|
85
85
|
modal/_utils/blob_utils.py,sha256=0k_qUpO5GHnz538wjRhyRw4NdJ5O322N7QSilIu32jw,16601
|
86
|
-
modal/_utils/function_utils.py,sha256=
|
86
|
+
modal/_utils/function_utils.py,sha256=GV-mq6sSGXQIX5PcExYWJMaWY9YLjChjsiQjg-oPvm8,24902
|
87
87
|
modal/_utils/grpc_testing.py,sha256=iqM9n5M0cWUUIIWNaEDer_pIfPnzXdZBO4L8FVbNepQ,8309
|
88
88
|
modal/_utils/grpc_utils.py,sha256=PPB5ay-vXencXNIWPVw5modr3EH7gfq2QPcO5YJ1lMU,7737
|
89
89
|
modal/_utils/hash_utils.py,sha256=xKwSI1eQobyWNdJwyLO59eB241LOXmQ6QvtzJsyIMcw,1784
|
@@ -142,13 +142,13 @@ modal_global_objects/mounts/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0
|
|
142
142
|
modal_global_objects/mounts/modal_client_package.py,sha256=W0E_yShsRojPzWm6LtIQqNVolapdnrZkm2hVEQuZK_4,767
|
143
143
|
modal_global_objects/mounts/python_standalone.py,sha256=SL_riIxpd8mP4i4CLDCWiFFNj0Ltknm9c_UIGfX5d60,1836
|
144
144
|
modal_proto/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
|
145
|
-
modal_proto/api.proto,sha256=
|
146
|
-
modal_proto/api_grpc.py,sha256=
|
147
|
-
modal_proto/api_pb2.py,sha256=
|
148
|
-
modal_proto/api_pb2.pyi,sha256=
|
149
|
-
modal_proto/api_pb2_grpc.py,sha256=
|
150
|
-
modal_proto/api_pb2_grpc.pyi,sha256=
|
151
|
-
modal_proto/modal_api_grpc.py,sha256=
|
145
|
+
modal_proto/api.proto,sha256=0AWqw9MDQvRrQTZI2xa8fVRcbs2lJG1DDt42ijBaJeY,78122
|
146
|
+
modal_proto/api_grpc.py,sha256=HvNN-zBJUs6z-a1T9Jn2ECs-Fs7RnkJ4FVzPTH6xU98,100519
|
147
|
+
modal_proto/api_pb2.py,sha256=hTsHAAKqmUxctck2FbpmOX4QTeF5pkNR_4BDYkrCxjk,284470
|
148
|
+
modal_proto/api_pb2.pyi,sha256=n9SMpTlIG3qhNI6XBc3gez5OeSzuaRfs4jkfu8WMIRw,380221
|
149
|
+
modal_proto/api_pb2_grpc.py,sha256=VFMzQ9MYmPg1XbTnmdjyw3MKhcPcqpBXtsQvtTn1uDI,217438
|
150
|
+
modal_proto/api_pb2_grpc.pyi,sha256=1o_3hja-2Y0iAlZDxnIXgahZQuaM6ruWLuc_2YoFuls,50691
|
151
|
+
modal_proto/modal_api_grpc.py,sha256=qUVht8AH8avTG2fm7Gw4pLwbgQTfqVgF5s_17HcrGKk,13438
|
152
152
|
modal_proto/modal_options_grpc.py,sha256=qJ1cuwA54oRqrdTyPTbvfhFZYd9HhJKK5UCwt523r3Y,120
|
153
153
|
modal_proto/options.proto,sha256=a-siq4swVbZPfaFRXAipRZzGP2bq8OsdUvjlyzAeodQ,488
|
154
154
|
modal_proto/options_grpc.py,sha256=M18X3d-8F_cNYSVM3I25dUTO5rZ0rd-vCCfynfh13Nc,125
|
@@ -159,10 +159,10 @@ modal_proto/options_pb2_grpc.pyi,sha256=CImmhxHsYnF09iENPoe8S4J-n93jtgUYD2JPAc0y
|
|
159
159
|
modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
160
160
|
modal_version/__init__.py,sha256=3IY-AWLH55r35_mQXIaut0jrJvoPuf1NZJBQQfSbPuo,470
|
161
161
|
modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
|
162
|
-
modal_version/_version_generated.py,sha256=
|
163
|
-
modal-0.67.
|
164
|
-
modal-0.67.
|
165
|
-
modal-0.67.
|
166
|
-
modal-0.67.
|
167
|
-
modal-0.67.
|
168
|
-
modal-0.67.
|
162
|
+
modal_version/_version_generated.py,sha256=1ZSIr835IEHi57LCELaWz0cnuPoKmFPQ6RSNQ5d-jAI,149
|
163
|
+
modal-0.67.33.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
|
164
|
+
modal-0.67.33.dist-info/METADATA,sha256=_5CGgZs2lnu_9I4Q5SRSIA9FYgHkOoRBOO_uOxtLMmE,2329
|
165
|
+
modal-0.67.33.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
|
166
|
+
modal-0.67.33.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
|
167
|
+
modal-0.67.33.dist-info/top_level.txt,sha256=1nvYbOSIKcmU50fNrpnQnrrOpj269ei3LzgB6j9xGqg,64
|
168
|
+
modal-0.67.33.dist-info/RECORD,,
|
modal_proto/api.proto
CHANGED
@@ -589,6 +589,7 @@ message CheckpointInfo {
|
|
589
589
|
string runtime_fingerprint = 4;
|
590
590
|
int64 size = 5;
|
591
591
|
bool checksum_is_file_index = 6;
|
592
|
+
string original_task_id = 7;
|
592
593
|
}
|
593
594
|
|
594
595
|
message ClassCreateRequest {
|
@@ -2674,6 +2675,7 @@ service ModalClient {
|
|
2674
2675
|
rpc ContainerFilesystemExec(ContainerFilesystemExecRequest) returns (ContainerFilesystemExecResponse);
|
2675
2676
|
rpc ContainerFilesystemExecGetOutput(ContainerFilesystemExecGetOutputRequest) returns (stream FilesystemRuntimeOutputBatch);
|
2676
2677
|
rpc ContainerHeartbeat(ContainerHeartbeatRequest) returns (ContainerHeartbeatResponse);
|
2678
|
+
rpc ContainerHello(google.protobuf.Empty) returns (google.protobuf.Empty);
|
2677
2679
|
rpc ContainerLog(ContainerLogRequest) returns (google.protobuf.Empty);
|
2678
2680
|
rpc ContainerStop(ContainerStopRequest) returns (ContainerStopResponse);
|
2679
2681
|
|
modal_proto/api_grpc.py
CHANGED
@@ -129,6 +129,10 @@ class ModalClientBase(abc.ABC):
|
|
129
129
|
async def ContainerHeartbeat(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.ContainerHeartbeatRequest, modal_proto.api_pb2.ContainerHeartbeatResponse]') -> None:
|
130
130
|
pass
|
131
131
|
|
132
|
+
@abc.abstractmethod
|
133
|
+
async def ContainerHello(self, stream: 'grpclib.server.Stream[google.protobuf.empty_pb2.Empty, google.protobuf.empty_pb2.Empty]') -> None:
|
134
|
+
pass
|
135
|
+
|
132
136
|
@abc.abstractmethod
|
133
137
|
async def ContainerLog(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.ContainerLogRequest, google.protobuf.empty_pb2.Empty]') -> None:
|
134
138
|
pass
|
@@ -707,6 +711,12 @@ class ModalClientBase(abc.ABC):
|
|
707
711
|
modal_proto.api_pb2.ContainerHeartbeatRequest,
|
708
712
|
modal_proto.api_pb2.ContainerHeartbeatResponse,
|
709
713
|
),
|
714
|
+
'/modal.client.ModalClient/ContainerHello': grpclib.const.Handler(
|
715
|
+
self.ContainerHello,
|
716
|
+
grpclib.const.Cardinality.UNARY_UNARY,
|
717
|
+
google.protobuf.empty_pb2.Empty,
|
718
|
+
google.protobuf.empty_pb2.Empty,
|
719
|
+
),
|
710
720
|
'/modal.client.ModalClient/ContainerLog': grpclib.const.Handler(
|
711
721
|
self.ContainerLog,
|
712
722
|
grpclib.const.Cardinality.UNARY_UNARY,
|
@@ -1493,6 +1503,12 @@ class ModalClientStub:
|
|
1493
1503
|
modal_proto.api_pb2.ContainerHeartbeatRequest,
|
1494
1504
|
modal_proto.api_pb2.ContainerHeartbeatResponse,
|
1495
1505
|
)
|
1506
|
+
self.ContainerHello = grpclib.client.UnaryUnaryMethod(
|
1507
|
+
channel,
|
1508
|
+
'/modal.client.ModalClient/ContainerHello',
|
1509
|
+
google.protobuf.empty_pb2.Empty,
|
1510
|
+
google.protobuf.empty_pb2.Empty,
|
1511
|
+
)
|
1496
1512
|
self.ContainerLog = grpclib.client.UnaryUnaryMethod(
|
1497
1513
|
channel,
|
1498
1514
|
'/modal.client.ModalClient/ContainerLog',
|