modal 0.66.41__py3-none-any.whl → 0.66.43__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 +1 -1
- modal/_runtime/user_code_imports.py +1 -1
- modal/_utils/grpc_testing.py +33 -26
- modal/client.pyi +2 -2
- modal/cls.py +26 -19
- modal/cls.pyi +4 -4
- modal/functions.py +32 -29
- modal/functions.pyi +7 -11
- modal/partial_function.py +1 -1
- {modal-0.66.41.dist-info → modal-0.66.43.dist-info}/METADATA +1 -1
- {modal-0.66.41.dist-info → modal-0.66.43.dist-info}/RECORD +16 -16
- modal_version/_version_generated.py +1 -1
- {modal-0.66.41.dist-info → modal-0.66.43.dist-info}/LICENSE +0 -0
- {modal-0.66.41.dist-info → modal-0.66.43.dist-info}/WHEEL +0 -0
- {modal-0.66.41.dist-info → modal-0.66.43.dist-info}/entry_points.txt +0 -0
- {modal-0.66.41.dist-info → modal-0.66.43.dist-info}/top_level.txt +0 -0
modal/_container_entrypoint.py
CHANGED
@@ -499,7 +499,7 @@ def main(container_args: api_pb2.ContainerArguments, client: Client):
|
|
499
499
|
call_lifecycle_functions(event_loop, container_io_manager, list(pre_snapshot_methods.values()))
|
500
500
|
|
501
501
|
# If this container is being used to create a checkpoint, checkpoint the container after
|
502
|
-
# global imports and
|
502
|
+
# global imports and initialization. Checkpointed containers run from this point onwards.
|
503
503
|
if is_snapshotting_function:
|
504
504
|
container_io_manager.memory_snapshot()
|
505
505
|
|
@@ -197,7 +197,7 @@ def get_user_class_instance(
|
|
197
197
|
modal_obj: modal.cls.Obj = cls(*args, **kwargs)
|
198
198
|
modal_obj.entered = True # ugly but prevents .local() from triggering additional enter-logic
|
199
199
|
# TODO: unify lifecycle logic between .local() and container_entrypoint
|
200
|
-
user_cls_instance = modal_obj.
|
200
|
+
user_cls_instance = modal_obj._cached_user_cls_instance()
|
201
201
|
else:
|
202
202
|
# undecorated class (non-global decoration or serialized)
|
203
203
|
user_cls_instance = cls(*args, **kwargs)
|
modal/_utils/grpc_testing.py
CHANGED
@@ -52,7 +52,7 @@ def patch_mock_servicer(cls):
|
|
52
52
|
ctx = InterceptionContext()
|
53
53
|
servicer.interception_context = ctx
|
54
54
|
yield ctx
|
55
|
-
ctx.
|
55
|
+
ctx._assert_responses_consumed()
|
56
56
|
servicer.interception_context = None
|
57
57
|
|
58
58
|
cls.intercept = intercept
|
@@ -64,7 +64,7 @@ def patch_mock_servicer(cls):
|
|
64
64
|
ctx = servicer_self.interception_context
|
65
65
|
if ctx:
|
66
66
|
intercepted_stream = await InterceptedStream(ctx, method_name, stream).initialize()
|
67
|
-
custom_responder = ctx.
|
67
|
+
custom_responder = ctx._next_custom_responder(method_name, intercepted_stream.request_message)
|
68
68
|
if custom_responder:
|
69
69
|
return await custom_responder(servicer_self, intercepted_stream)
|
70
70
|
else:
|
@@ -105,19 +105,23 @@ class InterceptionContext:
|
|
105
105
|
self.custom_responses: Dict[str, List[Tuple[Callable[[Any], bool], List[Any]]]] = defaultdict(list)
|
106
106
|
self.custom_defaults: Dict[str, Callable[["MockClientServicer", grpclib.server.Stream], Awaitable[None]]] = {}
|
107
107
|
|
108
|
-
def add_recv(self, method_name: str, msg):
|
109
|
-
self.calls.append((method_name, msg))
|
110
|
-
|
111
108
|
def add_response(
|
112
109
|
self, method_name: str, first_payload, *, request_filter: Callable[[Any], bool] = lambda req: True
|
113
110
|
):
|
114
|
-
|
111
|
+
"""Adds one response payload to an expected queue of responses for a method.
|
112
|
+
|
113
|
+
These responses will be used once each instead of calling the MockServicer's
|
114
|
+
implementation of the method.
|
115
|
+
|
116
|
+
The interception context will throw an exception on exit if not all of the added
|
117
|
+
responses have been consumed.
|
118
|
+
"""
|
115
119
|
self.custom_responses[method_name].append((request_filter, [first_payload]))
|
116
120
|
|
117
121
|
def set_responder(
|
118
122
|
self, method_name: str, responder: Callable[["MockClientServicer", grpclib.server.Stream], Awaitable[None]]
|
119
123
|
):
|
120
|
-
"""Replace the default responder
|
124
|
+
"""Replace the default responder from the MockClientServicer with a custom implementation
|
121
125
|
|
122
126
|
```python notest
|
123
127
|
def custom_responder(servicer, stream):
|
@@ -128,11 +132,28 @@ class InterceptionContext:
|
|
128
132
|
ctx.set_responder("SomeMethod", custom_responder)
|
129
133
|
```
|
130
134
|
|
131
|
-
Responses added via `.add_response()` take precedence
|
135
|
+
Responses added via `.add_response()` take precedence over the use of this replacement
|
132
136
|
"""
|
133
137
|
self.custom_defaults[method_name] = responder
|
134
138
|
|
135
|
-
def
|
139
|
+
def pop_request(self, method_name):
|
140
|
+
# fast forward to the next request of type method_name
|
141
|
+
# dropping any preceding requests if there is a match
|
142
|
+
# returns the payload of the request
|
143
|
+
for i, (_method_name, msg) in enumerate(self.calls):
|
144
|
+
if _method_name == method_name:
|
145
|
+
self.calls = self.calls[i + 1 :]
|
146
|
+
return msg
|
147
|
+
|
148
|
+
raise KeyError(f"No message of that type in call list: {self.calls}")
|
149
|
+
|
150
|
+
def get_requests(self, method_name: str) -> List[Any]:
|
151
|
+
return [msg for _method_name, msg in self.calls if _method_name == method_name]
|
152
|
+
|
153
|
+
def _add_recv(self, method_name: str, msg):
|
154
|
+
self.calls.append((method_name, msg))
|
155
|
+
|
156
|
+
def _next_custom_responder(self, method_name, request):
|
136
157
|
method_responses = self.custom_responses[method_name]
|
137
158
|
for i, (request_filter, response_messages) in enumerate(method_responses):
|
138
159
|
try:
|
@@ -159,7 +180,7 @@ class InterceptionContext:
|
|
159
180
|
|
160
181
|
return responder
|
161
182
|
|
162
|
-
def
|
183
|
+
def _assert_responses_consumed(self):
|
163
184
|
unconsumed = []
|
164
185
|
for method_name, queued_responses in self.custom_responses.items():
|
165
186
|
unconsumed += [method_name] * len(queued_responses)
|
@@ -167,23 +188,9 @@ class InterceptionContext:
|
|
167
188
|
if unconsumed:
|
168
189
|
raise ResponseNotConsumed(unconsumed)
|
169
190
|
|
170
|
-
def pop_request(self, method_name):
|
171
|
-
# fast forward to the next request of type method_name
|
172
|
-
# dropping any preceding requests if there is a match
|
173
|
-
# returns the payload of the request
|
174
|
-
for i, (_method_name, msg) in enumerate(self.calls):
|
175
|
-
if _method_name == method_name:
|
176
|
-
self.calls = self.calls[i + 1 :]
|
177
|
-
return msg
|
178
|
-
|
179
|
-
raise KeyError(f"No message of that type in call list: {self.calls}")
|
180
|
-
|
181
|
-
def get_requests(self, method_name: str) -> List[Any]:
|
182
|
-
return [msg for _method_name, msg in self.calls if _method_name == method_name]
|
183
|
-
|
184
191
|
|
185
192
|
class InterceptedStream:
|
186
|
-
def __init__(self, interception_context, method_name, stream):
|
193
|
+
def __init__(self, interception_context: InterceptionContext, method_name: str, stream):
|
187
194
|
self.interception_context = interception_context
|
188
195
|
self.method_name = method_name
|
189
196
|
self.stream = stream
|
@@ -200,7 +207,7 @@ class InterceptedStream:
|
|
200
207
|
return ret
|
201
208
|
|
202
209
|
msg = await self.stream.recv_message()
|
203
|
-
self.interception_context.
|
210
|
+
self.interception_context._add_recv(self.method_name, msg)
|
204
211
|
return msg
|
205
212
|
|
206
213
|
async def send_message(self, msg):
|
modal/client.pyi
CHANGED
@@ -31,7 +31,7 @@ class _Client:
|
|
31
31
|
server_url: str,
|
32
32
|
client_type: int,
|
33
33
|
credentials: typing.Optional[typing.Tuple[str, str]],
|
34
|
-
version: str = "0.66.
|
34
|
+
version: str = "0.66.43",
|
35
35
|
): ...
|
36
36
|
def is_closed(self) -> bool: ...
|
37
37
|
@property
|
@@ -90,7 +90,7 @@ class Client:
|
|
90
90
|
server_url: str,
|
91
91
|
client_type: int,
|
92
92
|
credentials: typing.Optional[typing.Tuple[str, str]],
|
93
|
-
version: str = "0.66.
|
93
|
+
version: str = "0.66.43",
|
94
94
|
): ...
|
95
95
|
def is_closed(self) -> bool: ...
|
96
96
|
@property
|
modal/cls.py
CHANGED
@@ -113,7 +113,7 @@ class _Obj:
|
|
113
113
|
method = self._instance_service_function._bind_instance_method(class_bound_method)
|
114
114
|
self._method_functions[method_name] = method
|
115
115
|
else:
|
116
|
-
# <v0.63 classes - bind each individual method to the new parameters
|
116
|
+
# looked up <v0.63 classes - bind each individual method to the new parameters
|
117
117
|
self._instance_service_function = None
|
118
118
|
for method_name, class_bound_method in classbound_methods.items():
|
119
119
|
method = class_bound_method._bind_parameters(self, from_other_workspace, options, args, kwargs)
|
@@ -125,12 +125,14 @@ class _Obj:
|
|
125
125
|
self._user_cls = user_cls
|
126
126
|
self._construction_args = (args, kwargs) # used for lazy construction in case of explicit constructors
|
127
127
|
|
128
|
-
def
|
128
|
+
def _new_user_cls_instance(self):
|
129
129
|
args, kwargs = self._construction_args
|
130
130
|
if not _use_annotation_parameters(self._user_cls):
|
131
131
|
# TODO(elias): deprecate this code path eventually
|
132
132
|
user_cls_instance = self._user_cls(*args, **kwargs)
|
133
133
|
else:
|
134
|
+
# ignore constructor (assumes there is no custom constructor,
|
135
|
+
# which is guaranteed by _use_annotation_parameters)
|
134
136
|
# set the attributes on the class corresponding to annotations
|
135
137
|
# with = parameter() specifications
|
136
138
|
sig = _get_class_constructor_signature(self._user_cls)
|
@@ -139,6 +141,7 @@ class _Obj:
|
|
139
141
|
user_cls_instance = self._user_cls.__new__(self._user_cls) # new instance without running __init__
|
140
142
|
user_cls_instance.__dict__.update(bound_vars.arguments)
|
141
143
|
|
144
|
+
# TODO: always use Obj instances instead of making modifications to user cls
|
142
145
|
user_cls_instance._modal_functions = self._method_functions # Needed for PartialFunction.__get__
|
143
146
|
return user_cls_instance
|
144
147
|
|
@@ -163,10 +166,12 @@ class _Obj:
|
|
163
166
|
)
|
164
167
|
await self._instance_service_function.keep_warm(warm_pool_size)
|
165
168
|
|
166
|
-
def
|
167
|
-
"""
|
169
|
+
def _cached_user_cls_instance(self):
|
170
|
+
"""Get or construct the local object
|
171
|
+
|
172
|
+
Used for .local() calls and getting attributes of classes"""
|
168
173
|
if not self._user_cls_instance:
|
169
|
-
self._user_cls_instance = self.
|
174
|
+
self._user_cls_instance = self._new_user_cls_instance() # Instantiate object
|
170
175
|
|
171
176
|
return self._user_cls_instance
|
172
177
|
|
@@ -196,7 +201,7 @@ class _Obj:
|
|
196
201
|
@synchronizer.nowrap
|
197
202
|
async def aenter(self):
|
198
203
|
if not self.entered:
|
199
|
-
user_cls_instance = self.
|
204
|
+
user_cls_instance = self._cached_user_cls_instance()
|
200
205
|
if hasattr(user_cls_instance, "__aenter__"):
|
201
206
|
await user_cls_instance.__aenter__()
|
202
207
|
elif hasattr(user_cls_instance, "__enter__"):
|
@@ -205,20 +210,22 @@ class _Obj:
|
|
205
210
|
|
206
211
|
def __getattr__(self, k):
|
207
212
|
if k in self._method_functions:
|
208
|
-
#
|
209
|
-
#
|
210
|
-
#
|
213
|
+
# If we know the user is accessing a *method* and not another attribute,
|
214
|
+
# we don't have to create an instance of the user class yet.
|
215
|
+
# This is because it might just be a call to `.remote()` on it which
|
216
|
+
# doesn't require a local instance.
|
217
|
+
# As long as we have the service function or params, we can do remote calls
|
218
|
+
# without calling the constructor of the class in the calling context.
|
211
219
|
return self._method_functions[k]
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
raise AttributeError(k)
|
220
|
+
|
221
|
+
# if it's *not* a method, it *might* be an attribute of the class,
|
222
|
+
# so we construct it and proxy the attribute
|
223
|
+
# TODO: To get lazy loading (from_name) of classes to work, we need to avoid
|
224
|
+
# this path, otherwise local initialization will happen regardless if user
|
225
|
+
# only runs .remote(), since we don't know methods for the class until we
|
226
|
+
# load it
|
227
|
+
user_cls_instance = self._cached_user_cls_instance()
|
228
|
+
return getattr(user_cls_instance, k)
|
222
229
|
|
223
230
|
|
224
231
|
Obj = synchronize_api(_Obj)
|
modal/cls.pyi
CHANGED
@@ -37,9 +37,9 @@ class _Obj:
|
|
37
37
|
args,
|
38
38
|
kwargs,
|
39
39
|
): ...
|
40
|
-
def
|
40
|
+
def _new_user_cls_instance(self): ...
|
41
41
|
async def keep_warm(self, warm_pool_size: int) -> None: ...
|
42
|
-
def
|
42
|
+
def _cached_user_cls_instance(self): ...
|
43
43
|
def enter(self): ...
|
44
44
|
@property
|
45
45
|
def entered(self): ...
|
@@ -66,7 +66,7 @@ class Obj:
|
|
66
66
|
kwargs,
|
67
67
|
): ...
|
68
68
|
def _uses_common_service_function(self): ...
|
69
|
-
def
|
69
|
+
def _new_user_cls_instance(self): ...
|
70
70
|
|
71
71
|
class __keep_warm_spec(typing_extensions.Protocol):
|
72
72
|
def __call__(self, warm_pool_size: int) -> None: ...
|
@@ -74,7 +74,7 @@ class Obj:
|
|
74
74
|
|
75
75
|
keep_warm: __keep_warm_spec
|
76
76
|
|
77
|
-
def
|
77
|
+
def _cached_user_cls_instance(self): ...
|
78
78
|
def enter(self): ...
|
79
79
|
@property
|
80
80
|
def entered(self): ...
|
modal/functions.py
CHANGED
@@ -314,7 +314,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
314
314
|
_tag: str
|
315
315
|
_raw_f: Callable[..., Any]
|
316
316
|
_build_args: dict
|
317
|
-
|
317
|
+
|
318
318
|
_is_generator: Optional[bool] = None
|
319
319
|
_cluster_size: Optional[int] = None
|
320
320
|
|
@@ -323,10 +323,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
323
323
|
_use_function_id: str # The function to invoke
|
324
324
|
_use_method_name: str = ""
|
325
325
|
|
326
|
-
# TODO (elias): remove _parent. In case of instance functions, and methods bound on those,
|
327
|
-
# this references the parent class-function and is used to infer the client for lazy-loaded methods
|
328
|
-
_parent: Optional["_Function"] = None
|
329
|
-
|
330
326
|
_class_parameter_info: Optional["api_pb2.ClassParameterInfo"] = None
|
331
327
|
_method_handle_metadata: Optional[Dict[str, "api_pb2.FunctionHandleMetadata"]] = None
|
332
328
|
|
@@ -511,7 +507,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
511
507
|
fun._info = class_bound_method._info
|
512
508
|
fun._obj = instance_service_function._obj
|
513
509
|
fun._is_method = True
|
514
|
-
fun._parent = instance_service_function._parent
|
515
510
|
fun._app = class_bound_method._app
|
516
511
|
fun._spec = class_bound_method._spec
|
517
512
|
return fun
|
@@ -1019,27 +1014,37 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
1019
1014
|
Binds a class-function to a specific instance of (init params, options) or a new workspace
|
1020
1015
|
"""
|
1021
1016
|
|
1022
|
-
|
1023
|
-
|
1017
|
+
# In some cases, reuse the base function, i.e. not create new clones of each method or the "service function"
|
1018
|
+
can_use_parent = len(args) + len(kwargs) == 0 and not from_other_workspace and options is None
|
1019
|
+
parent = self
|
1020
|
+
|
1021
|
+
async def _load(param_bound_func: _Function, resolver: Resolver, existing_object_id: Optional[str]):
|
1022
|
+
if parent is None:
|
1024
1023
|
raise ExecutionError("Can't find the parent class' service function")
|
1025
1024
|
try:
|
1026
|
-
identity = f"{
|
1025
|
+
identity = f"{parent.info.function_name} class service function"
|
1027
1026
|
except Exception:
|
1028
1027
|
# Can't always look up the function name that way, so fall back to generic message
|
1029
1028
|
identity = "class service function for a parameterized class"
|
1030
|
-
if not
|
1031
|
-
if
|
1029
|
+
if not parent.is_hydrated:
|
1030
|
+
if parent.app._running_app is None:
|
1032
1031
|
reason = ", because the App it is defined on is not running"
|
1033
1032
|
else:
|
1034
1033
|
reason = ""
|
1035
1034
|
raise ExecutionError(
|
1036
1035
|
f"The {identity} has not been hydrated with the metadata it needs to run on Modal{reason}."
|
1037
1036
|
)
|
1038
|
-
|
1037
|
+
|
1038
|
+
assert parent._client.stub
|
1039
|
+
|
1040
|
+
if can_use_parent:
|
1041
|
+
# We can end up here if parent wasn't hydrated when class was instantiated, but has been since.
|
1042
|
+
param_bound_func._hydrate_from_other(parent)
|
1043
|
+
return
|
1044
|
+
|
1039
1045
|
if (
|
1040
|
-
|
1041
|
-
and
|
1042
|
-
== api_pb2.ClassParameterInfo.PARAM_SERIALIZATION_FORMAT_PROTO
|
1046
|
+
parent._class_parameter_info
|
1047
|
+
and parent._class_parameter_info.format == api_pb2.ClassParameterInfo.PARAM_SERIALIZATION_FORMAT_PROTO
|
1043
1048
|
):
|
1044
1049
|
if args:
|
1045
1050
|
# TODO(elias) - We could potentially support positional args as well, if we want to?
|
@@ -1047,34 +1052,30 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
1047
1052
|
"Can't use positional arguments with modal.parameter-based synthetic constructors.\n"
|
1048
1053
|
"Use (<parameter_name>=value) keyword arguments when constructing classes instead."
|
1049
1054
|
)
|
1050
|
-
serialized_params = serialize_proto_params(kwargs,
|
1055
|
+
serialized_params = serialize_proto_params(kwargs, parent._class_parameter_info.schema)
|
1051
1056
|
else:
|
1052
1057
|
serialized_params = serialize((args, kwargs))
|
1053
1058
|
environment_name = _get_environment_name(None, resolver)
|
1054
|
-
assert
|
1059
|
+
assert parent is not None
|
1055
1060
|
req = api_pb2.FunctionBindParamsRequest(
|
1056
|
-
function_id=
|
1061
|
+
function_id=parent._object_id,
|
1057
1062
|
serialized_params=serialized_params,
|
1058
1063
|
function_options=options,
|
1059
1064
|
environment_name=environment_name
|
1060
1065
|
or "", # TODO: investigate shouldn't environment name always be specified here?
|
1061
1066
|
)
|
1062
1067
|
|
1063
|
-
response = await retry_transient_errors(
|
1064
|
-
|
1068
|
+
response = await retry_transient_errors(parent._client.stub.FunctionBindParams, req)
|
1069
|
+
param_bound_func._hydrate(response.bound_function_id, parent._client, response.handle_metadata)
|
1065
1070
|
|
1066
1071
|
fun: _Function = _Function._from_loader(_load, "Function(parametrized)", hydrate_lazily=True)
|
1067
1072
|
|
1068
|
-
|
1069
|
-
|
1070
|
-
|
1071
|
-
# Edge case that lets us hydrate all objects right away
|
1072
|
-
# if the instance didn't use explicit constructor arguments
|
1073
|
-
fun._hydrate_from_other(self)
|
1073
|
+
if can_use_parent and parent.is_hydrated:
|
1074
|
+
# skip the resolver altogether:
|
1075
|
+
fun._hydrate_from_other(parent)
|
1074
1076
|
|
1075
1077
|
fun._info = self._info
|
1076
1078
|
fun._obj = obj
|
1077
|
-
fun._parent = self
|
1078
1079
|
return fun
|
1079
1080
|
|
1080
1081
|
@live_method
|
@@ -1265,8 +1266,10 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
1265
1266
|
+ f"or call it locally: {self._function_name}.local()"
|
1266
1267
|
)
|
1267
1268
|
|
1269
|
+
# TODO (live_method on properties is not great, since it could be blocking the event loop from async contexts)
|
1268
1270
|
@property
|
1269
|
-
|
1271
|
+
@live_method
|
1272
|
+
async def web_url(self) -> str:
|
1270
1273
|
"""URL of a Function running as a web endpoint."""
|
1271
1274
|
if not self._web_url:
|
1272
1275
|
raise ValueError(
|
@@ -1439,7 +1442,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
1439
1442
|
return fun(*args, **kwargs)
|
1440
1443
|
else:
|
1441
1444
|
# This is a method on a class, so bind the self to the function
|
1442
|
-
user_cls_instance = obj.
|
1445
|
+
user_cls_instance = obj._cached_user_cls_instance()
|
1443
1446
|
|
1444
1447
|
fun = info.raw_f.__get__(user_cls_instance)
|
1445
1448
|
|
modal/functions.pyi
CHANGED
@@ -120,12 +120,10 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], modal.object.
|
|
120
120
|
_tag: str
|
121
121
|
_raw_f: typing.Callable[..., typing.Any]
|
122
122
|
_build_args: dict
|
123
|
-
_can_use_base_function: bool
|
124
123
|
_is_generator: typing.Optional[bool]
|
125
124
|
_cluster_size: typing.Optional[int]
|
126
125
|
_use_function_id: str
|
127
126
|
_use_method_name: str
|
128
|
-
_parent: typing.Optional[_Function]
|
129
127
|
_class_parameter_info: typing.Optional[modal_proto.api_pb2.ClassParameterInfo]
|
130
128
|
_method_handle_metadata: typing.Optional[typing.Dict[str, modal_proto.api_pb2.FunctionHandleMetadata]]
|
131
129
|
|
@@ -218,7 +216,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], modal.object.
|
|
218
216
|
def _get_metadata(self): ...
|
219
217
|
def _check_no_web_url(self, fn_name: str): ...
|
220
218
|
@property
|
221
|
-
def web_url(self) -> str: ...
|
219
|
+
async def web_url(self) -> str: ...
|
222
220
|
@property
|
223
221
|
def is_generator(self) -> bool: ...
|
224
222
|
@property
|
@@ -296,12 +294,10 @@ class Function(typing.Generic[P, ReturnType, OriginalReturnType], modal.object.O
|
|
296
294
|
_tag: str
|
297
295
|
_raw_f: typing.Callable[..., typing.Any]
|
298
296
|
_build_args: dict
|
299
|
-
_can_use_base_function: bool
|
300
297
|
_is_generator: typing.Optional[bool]
|
301
298
|
_cluster_size: typing.Optional[int]
|
302
299
|
_use_function_id: str
|
303
300
|
_use_method_name: str
|
304
|
-
_parent: typing.Optional[Function]
|
305
301
|
_class_parameter_info: typing.Optional[modal_proto.api_pb2.ClassParameterInfo]
|
306
302
|
_method_handle_metadata: typing.Optional[typing.Dict[str, modal_proto.api_pb2.FunctionHandleMetadata]]
|
307
303
|
|
@@ -450,11 +446,11 @@ class Function(typing.Generic[P, ReturnType, OriginalReturnType], modal.object.O
|
|
450
446
|
|
451
447
|
_call_generator_nowait: ___call_generator_nowait_spec
|
452
448
|
|
453
|
-
class __remote_spec(typing_extensions.Protocol[
|
449
|
+
class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER]):
|
454
450
|
def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER: ...
|
455
451
|
async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER: ...
|
456
452
|
|
457
|
-
remote: __remote_spec[
|
453
|
+
remote: __remote_spec[P, ReturnType]
|
458
454
|
|
459
455
|
class __remote_gen_spec(typing_extensions.Protocol):
|
460
456
|
def __call__(self, *args, **kwargs) -> typing.Generator[typing.Any, None, None]: ...
|
@@ -466,17 +462,17 @@ class Function(typing.Generic[P, ReturnType, OriginalReturnType], modal.object.O
|
|
466
462
|
def _get_obj(self) -> typing.Optional[modal.cls.Obj]: ...
|
467
463
|
def local(self, *args: P.args, **kwargs: P.kwargs) -> OriginalReturnType: ...
|
468
464
|
|
469
|
-
class ___experimental_spawn_spec(typing_extensions.Protocol[
|
465
|
+
class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER]):
|
470
466
|
def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
|
471
467
|
async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
|
472
468
|
|
473
|
-
_experimental_spawn: ___experimental_spawn_spec[
|
469
|
+
_experimental_spawn: ___experimental_spawn_spec[P, ReturnType]
|
474
470
|
|
475
|
-
class __spawn_spec(typing_extensions.Protocol[
|
471
|
+
class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER]):
|
476
472
|
def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
|
477
473
|
async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
|
478
474
|
|
479
|
-
spawn: __spawn_spec[
|
475
|
+
spawn: __spawn_spec[P, ReturnType]
|
480
476
|
|
481
477
|
def get_raw_f(self) -> typing.Callable[..., typing.Any]: ...
|
482
478
|
|
modal/partial_function.py
CHANGED
@@ -91,7 +91,7 @@ class _PartialFunction(typing.Generic[P, ReturnType, OriginalReturnType]):
|
|
91
91
|
if obj: # accessing the method on an instance of a class, e.g. `MyClass().fun``
|
92
92
|
if hasattr(obj, "_modal_functions"):
|
93
93
|
# This happens inside "local" user methods when they refer to other methods,
|
94
|
-
# e.g. Foo().parent_method()
|
94
|
+
# e.g. Foo().parent_method.remote() calling self.other_method.remote()
|
95
95
|
return getattr(obj, "_modal_functions")[k]
|
96
96
|
else:
|
97
97
|
# special edge case: referencing a method of an instance of an
|
@@ -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=58spAmCbOk8eyjFKW-H21W-FGyYiJmfpEMK4BnTEUrk,2706
|
4
4
|
modal/_clustered_functions.pyi,sha256=UQ7DHiQFI3A2Z8kC3gltNHJkQfvgvPuSbFsse9lSPkw,785
|
5
|
-
modal/_container_entrypoint.py,sha256=
|
5
|
+
modal/_container_entrypoint.py,sha256=qP7TRT92_wKlxR-XuLg1FzJ7w4oknlrWDlp68XE0CwQ,28670
|
6
6
|
modal/_ipython.py,sha256=HF_DYy0e0qM9WnGDmTY30s1RxzGya9GeORCauCEpRaE,450
|
7
7
|
modal/_location.py,sha256=S3lSxIU3h9HkWpkJ3Pwo0pqjIOSB1fjeSgUsY3x7eec,1202
|
8
8
|
modal/_output.py,sha256=SMaLrf1btBzHTV_tH5NzA8ZTWNJh5J0b31iG3sQU8_4,25494
|
@@ -19,11 +19,11 @@ modal/app.py,sha256=QEBK8qYSrux36oi3iS3msBQmcUOS1r4s2nengzzynjQ,44658
|
|
19
19
|
modal/app.pyi,sha256=wHwBIDqkUb2CQzYVhxZafJ8xZ17TZ-8y-cRyOeZsEm0,25182
|
20
20
|
modal/call_graph.py,sha256=l-Wi6vM8aosCdHTWegcCyGeVJGFdZ_fzlCmbRVPBXFI,2593
|
21
21
|
modal/client.py,sha256=4SpWb4n0nolITR36kADZl1tYLOg6avukmzZU56UQjCo,16385
|
22
|
-
modal/client.pyi,sha256=
|
22
|
+
modal/client.pyi,sha256=rygeOYppvKVoM_DviDsgkMWVmNq1dWUFcr2PZEX7rmg,7372
|
23
23
|
modal/cloud_bucket_mount.py,sha256=eWQhCtMIczpokjfTZEgNBCGO_s5ft46PqTSLfKBykq4,5748
|
24
24
|
modal/cloud_bucket_mount.pyi,sha256=tTF7M4FR9bTA30cFkz8qq3ZTlFL19NHU_36e_5GgAGA,1424
|
25
|
-
modal/cls.py,sha256=
|
26
|
-
modal/cls.pyi,sha256=
|
25
|
+
modal/cls.py,sha256=apKnBOHKYEpBiMC8mRvHtCDJl1g0vP0tG1r8mUZ1yH0,24684
|
26
|
+
modal/cls.pyi,sha256=om3xQuHTMu7q_32BDKJjAz4oP_7Z5JWV27kyDKTPFI8,8332
|
27
27
|
modal/config.py,sha256=oVmvclQ2Qlt-VmL3wEp8DgDrnTPh_K5UBEYrSXv4C4s,10928
|
28
28
|
modal/container_process.py,sha256=c_jBPtyPeSxbIcbLfs_FzTrt-1eErtRSnsfxkDozFoY,5589
|
29
29
|
modal/container_process.pyi,sha256=k2kClwaSzz11eci1pzFZgCm-ptXapHAyHTOENorlazA,2594
|
@@ -33,8 +33,8 @@ modal/environments.py,sha256=KwKdrWfSnz2XF5eYXZyd0kdac1x1PVw2uxPYsGy8cys,6517
|
|
33
33
|
modal/environments.pyi,sha256=oScvFAclF55-tL9UioLIL_SPBwgy_9O-BBvJ-PLbRgY,3542
|
34
34
|
modal/exception.py,sha256=K-czk1oK8wFvK8snWrytXSByo2WNb9SJAlgBVPGWZBs,6417
|
35
35
|
modal/experimental.py,sha256=jFuNbwrNHos47viMB9q-cHJSvf2RDxDdoEcss9plaZE,2302
|
36
|
-
modal/functions.py,sha256=
|
37
|
-
modal/functions.pyi,sha256=
|
36
|
+
modal/functions.py,sha256=BxccB-3a1migZQ6JA6iiHZJQ7WQ-jYpmg9DEZoTxzcc,71639
|
37
|
+
modal/functions.pyi,sha256=0JsvWf9vvj2fMSNIHoXmL9FS7sWoRU4hU6911PyqEls,24800
|
38
38
|
modal/gpu.py,sha256=r4rL6uH3UJIQthzYvfWauXNyh01WqCPtKZCmmSX1fd4,6881
|
39
39
|
modal/image.py,sha256=j-NH8pLWk4jd5UOGD4y6W7DHWoeb3rG_VR7zPLSqj-Q,78927
|
40
40
|
modal/image.pyi,sha256=QEjjnl4ZSmqt7toHww5ZbhL2Re5qaFGgH7qADcJS_vA,24493
|
@@ -49,7 +49,7 @@ modal/object.pyi,sha256=cwWg93H4rBk9evt1itLZAZXH5wUMyTJBZ_ADazgfjGg,8465
|
|
49
49
|
modal/output.py,sha256=FtPR7yvjZMgdSKD_KYkIcwYgCOiV9EKYjaj7K55Hjvg,1940
|
50
50
|
modal/parallel_map.py,sha256=lf8Wer6FAf8-dYqPqoL45cz7FYDU66-TF-h5CO2Kf5Q,16052
|
51
51
|
modal/parallel_map.pyi,sha256=pOhT0P3DDYlwLx0fR3PTsecA7DI8uOdXC1N8i-ZkyOY,2328
|
52
|
-
modal/partial_function.py,sha256=
|
52
|
+
modal/partial_function.py,sha256=xkEqMPG0rnP_BUDqGikerv6uiWxDkwZdIFSdoHMDm2A,28216
|
53
53
|
modal/partial_function.pyi,sha256=BqKN7It5QLxS2yhhhDX0RgI8EyNMPBD6Duk21kN_fvA,8908
|
54
54
|
modal/proxy.py,sha256=ZrOsuQP7dSZFq1OrIxalNnt0Zvsnp1h86Th679sSL40,1417
|
55
55
|
modal/proxy.pyi,sha256=UvygdOYneLTuoDY6hVaMNCyZ947Tmx93IdLjErUqkvM,368
|
@@ -78,13 +78,13 @@ modal/_runtime/asgi.py,sha256=WoAwIiGKpk089MOca3_iA73h36v0uBuoPx0-87ajIDY,19843
|
|
78
78
|
modal/_runtime/container_io_manager.py,sha256=_MEhwyCSYeCaPQnztPxkm0anRXa3CPcwIKi403N53uo,44120
|
79
79
|
modal/_runtime/execution_context.py,sha256=cXEVY4wEK-oZJVJptyj1ZplZvVQ1HDzFsyHxhaY4o8M,2718
|
80
80
|
modal/_runtime/telemetry.py,sha256=3NbrfwYH6mvDckzdTppymmda2lQKX2oHGc2JwdFZdUc,5191
|
81
|
-
modal/_runtime/user_code_imports.py,sha256=
|
81
|
+
modal/_runtime/user_code_imports.py,sha256=2COhqA77zwbP__-DWiDHEScHM-Go3CmI-AlKvT_oBOU,14545
|
82
82
|
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=3H4pBC4DW6rCA6hgRux6FMxGqPgHM-G-BTs7KXZiWz4,23958
|
85
85
|
modal/_utils/blob_utils.py,sha256=pAY22w0oVc6ujGfI7La7HPUMOf42FehIapuhSDeeqEs,15835
|
86
86
|
modal/_utils/function_utils.py,sha256=28mxgg_-7JF1DDiNnp3iNVsFQkOzMFjORsetdvZZTHU,24475
|
87
|
-
modal/_utils/grpc_testing.py,sha256=
|
87
|
+
modal/_utils/grpc_testing.py,sha256=LOzWygTdHINzV-o_Ajbl7sOFbUQFoonP0iKpsJjA_nc,8301
|
88
88
|
modal/_utils/grpc_utils.py,sha256=tM1Q32VOU2WG733IfVHTLZdiyCe8Ga0f0Dx0iDLLy_8,7724
|
89
89
|
modal/_utils/hash_utils.py,sha256=HefF7zPQPxFxyx3fpz-AdSm4QsHZNNvgL9-iQHY-_F4,1790
|
90
90
|
modal/_utils/http_utils.py,sha256=VKXYNPJtrSwZ1ttcXVGQUWmn8cLAXiOTv05g2ac3GbU,2179
|
@@ -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=UnAuHBPuPSstqgdCOx0SBVdfhpeJnMlY_oxEbu44Izg,470
|
161
161
|
modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
|
162
|
-
modal_version/_version_generated.py,sha256=
|
163
|
-
modal-0.66.
|
164
|
-
modal-0.66.
|
165
|
-
modal-0.66.
|
166
|
-
modal-0.66.
|
167
|
-
modal-0.66.
|
168
|
-
modal-0.66.
|
162
|
+
modal_version/_version_generated.py,sha256=7WGR5zgAcB_J0SICoQ3twGcle4mHokidCuBdi-SVPYE,149
|
163
|
+
modal-0.66.43.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
|
164
|
+
modal-0.66.43.dist-info/METADATA,sha256=KMqp2KMs325ypwcMgvoCDdfV1-QnMjkjJ8uxAQ07DIw,2329
|
165
|
+
modal-0.66.43.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
|
166
|
+
modal-0.66.43.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
|
167
|
+
modal-0.66.43.dist-info/top_level.txt,sha256=1nvYbOSIKcmU50fNrpnQnrrOpj269ei3LzgB6j9xGqg,64
|
168
|
+
modal-0.66.43.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|