Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove usage of method placeholder functions #2364

Merged
merged 180 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from 178 commits
Commits
Show all changes
180 commits
Select commit Hold shift + click to select a range
d4c2782
Miscellaneous cleanup of @app.cls code
devennavani Oct 20, 2024
bc850a1
More cleanup
devennavani Oct 20, 2024
1511cc9
Add comment
devennavani Oct 20, 2024
bc52e38
More cleanup
devennavani Oct 20, 2024
a0e5bc0
Add class method webhook configs to definition of class service function
devennavani Oct 20, 2024
04f5496
Merge branch 'main' into deven/add_method_webhook_configs_to_class_fu…
devennavani Oct 21, 2024
dcb9a8b
Merge branch 'main' into deven/add_method_webhook_configs_to_class_fu…
devennavani Oct 21, 2024
b4e736b
Merge branch 'main' into deven/add_method_webhook_configs_to_class_fu…
devennavani Oct 21, 2024
035b1bc
fix merge coflict
devennavani Oct 25, 2024
c0ff217
wip
devennavani Oct 25, 2024
d6d1142
Merge branch 'main' into deven/add_method_webhook_configs_to_class_fu…
devennavani Oct 25, 2024
5414a36
stage
devennavani Oct 25, 2024
bc6bd79
wip
devennavani Oct 27, 2024
bd90f9f
wip
devennavani Oct 27, 2024
b3aa81d
fix bugs
devennavani Oct 27, 2024
033dff7
bug fix
devennavani Oct 27, 2024
283c958
FunctionType fixes
devennavani Oct 27, 2024
f0de58e
code cleanup
devennavani Oct 27, 2024
08afd9a
protoc changes
devennavani Oct 27, 2024
f95b8a3
fixes
devennavani Oct 28, 2024
916eb9f
fixes
devennavani Oct 28, 2024
29f0cf6
Merge branch 'main' into deven/add_method_webhook_configs_to_class_fu…
devennavani Oct 28, 2024
59b7668
functions cleanup
devennavani Oct 28, 2024
91d4b4c
Merge branch 'main' into deven/add_method_webhook_configs_to_class_fu…
devennavani Oct 28, 2024
1d81bb8
Merge branch 'main' into deven/add_method_webhook_configs_to_class_fu…
devennavani Oct 28, 2024
2c45527
CopyFrom fix
devennavani Oct 28, 2024
0e56501
fixes from testing
devennavani Oct 28, 2024
79df7dd
Merge branch 'main' into deven/add_method_webhook_configs_to_class_fu…
devennavani Oct 29, 2024
80e751d
Fix Cls.lookup
devennavani Oct 29, 2024
2c62b8f
Merge branch 'main' into deven/add_method_webhook_configs_to_class_fu…
devennavani Oct 29, 2024
0d2e96e
proto changes for class_get_handle_metadata to handle old clients
devennavani Oct 29, 2024
3d6f120
remove extraneous space
devennavani Oct 29, 2024
f0aad3f
add method_definitions_set to function proto
devennavani Oct 29, 2024
735792f
Include only_class_function in requests
devennavani Oct 29, 2024
18711a6
Merge branch 'main' into deven/add_method_webhook_configs_to_class_fu…
devennavani Oct 30, 2024
17b2eb3
Merge branch 'main' into deven/add_method_webhook_configs_to_class_fu…
devennavani Oct 30, 2024
4473f4d
Merge branch 'main' into deven/add_method_webhook_configs_to_class_fu…
devennavani Oct 30, 2024
4f0b2b1
support lookup from new clients
devennavani Oct 30, 2024
42d99ad
Merge branch 'main' into deven/add_method_webhook_configs_to_class_fu…
devennavani Oct 31, 2024
6fcb498
Support new client looking up 0.62 class
devennavani Oct 31, 2024
9e8c2a9
Merge branch 'main' into deven/add_method_webhook_configs_to_class_fu…
devennavani Oct 31, 2024
7215fba
obj init cleanup
devennavani Oct 31, 2024
8b6fe60
Merge branch 'main' into deven/add_method_webhook_configs_to_class_fu…
devennavani Oct 31, 2024
223aae2
Merge branch 'main' into deven/add_method_webhook_configs_to_class_fu…
devennavani Nov 1, 2024
f089aa9
Merge branch 'main' into deven/add_method_webhook_configs_to_class_fu…
devennavani Nov 1, 2024
656b999
Merge branch 'main' into deven/add_method_webhook_configs_to_class_fu…
devennavani Nov 1, 2024
c38f050
Fix merge coflicts
devennavani Nov 4, 2024
1b02192
Remove duplicate MessageDefinition
devennavani Nov 4, 2024
5748499
Fix merge conflicts
devennavani Nov 5, 2024
55cd50d
Merge branch 'main' into deven/add_method_webhook_configs_to_class_fu…
devennavani Nov 6, 2024
248b446
Merge branch 'main' into deven/add_method_webhook_configs_to_class_fu…
devennavani Nov 6, 2024
c63621f
Make tag parameter non-optional in from_name and lookup for Function …
devennavani Nov 6, 2024
2f287d9
Merge branch 'deven/make_tag_parameter_non_optional' into deven/add_m…
devennavani Nov 6, 2024
d714065
fix merge conflict
devennavani Nov 8, 2024
7ff4d7a
fix merge conflict
devennavani Nov 12, 2024
8b99874
fix merge conflict
devennavani Nov 13, 2024
289d05b
Add util to get api_pb2.Function.FunctionType from is_generator
devennavani Nov 13, 2024
e291110
fix merge conflicts
devennavani Nov 13, 2024
1fd397a
fix type check
devennavani Nov 13, 2024
2cf1dfb
fix type check
devennavani Nov 13, 2024
6820379
is_generator can be None
devennavani Nov 13, 2024
8603419
add quotes
devennavani Nov 13, 2024
4f03e92
fix merge conflict
devennavani Nov 13, 2024
f766f0e
fix merge conflicts
devennavani Nov 14, 2024
1571203
cleanup
devennavani Nov 14, 2024
32ee93a
Send method definitions in FunctionPrecreateRequest
devennavani Nov 14, 2024
5197215
Can't pass null webhook_config to CopyFrom
devennavani Nov 14, 2024
bcdf8eb
fix typo
devennavani Nov 14, 2024
2d21c34
fix merge conflicts
devennavani Nov 14, 2024
e354099
Add _method_functions attribute to _Function
devennavani Nov 14, 2024
5cab92a
fix unbound error
devennavani Nov 14, 2024
2e1cce6
update comment
devennavani Nov 14, 2024
27fa0f9
fix merge conflicts
devennavani Nov 14, 2024
d16ccc7
remove bind_method_old
devennavani Nov 14, 2024
ec89499
cleanup
devennavani Nov 14, 2024
57e1151
Merge branch 'main' into deven/add_method_functions_to_function
devennavani Nov 14, 2024
9fe8165
Hydrate _method_functions on _Function
devennavani Nov 14, 2024
bd6c83a
fix merge conflict
devennavani Nov 14, 2024
effea0d
Merge branch 'deven/send_method_definitions_in_function_precreate' in…
devennavani Nov 14, 2024
1898632
Merge branch 'deven/add_method_functions_to_function' into deven/hydr…
devennavani Nov 14, 2024
62494b9
Merge branch 'main' into deven/send_method_definitions_in_function_pr…
devennavani Nov 15, 2024
5b8ae61
Update mock FunctionPrecreate
devennavani Nov 15, 2024
1a157b6
Merge branch 'deven/send_method_definitions_in_function_precreate' in…
devennavani Nov 15, 2024
01949c7
fix merge conflict
devennavani Nov 15, 2024
9498022
Merge branch 'deven/add_method_functions_to_function' into deven/hydr…
devennavani Nov 15, 2024
64f3811
fix merge conflicts
devennavani Nov 15, 2024
7fe211f
Merge branch 'main' into deven/hydrate_method_functions
devennavani Nov 15, 2024
1b8bda0
Merge branch 'deven/hydrate_method_functions' into deven/add_method_w…
devennavani Nov 15, 2024
239a23d
Populate FunctionHandleMetadata.method_handle_metadata in _Function _…
devennavani Nov 15, 2024
0157938
fix merge conflicts
devennavani Nov 15, 2024
a11f647
fix type checks
devennavani Nov 15, 2024
5f55097
Merge branch 'deven/update_function_get_metadata' into deven/add_meth…
devennavani Nov 15, 2024
5979da6
Merge branch 'main' into deven/update_function_get_metadata
devennavani Nov 15, 2024
0fe42a3
Merge branch 'deven/update_function_get_metadata' into deven/add_meth…
devennavani Nov 15, 2024
8c1bb09
Merge branch 'main' into deven/add_method_webhook_configs_to_class_fu…
devennavani Nov 15, 2024
1157219
Remove extraneous changes
devennavani Nov 15, 2024
611ed93
cleanup
devennavani Nov 15, 2024
bf7dac8
Merge branch 'main' into deven/add_method_webhook_configs_to_class_fu…
devennavani Nov 15, 2024
4502e2b
Add _is_web_endpoint to _Function
devennavani Nov 15, 2024
b9cb233
fix merge conflicts
devennavani Nov 15, 2024
6dc8a72
dont modify _add_function
devennavani Nov 15, 2024
e6c6c9f
Merge branch 'deven/add_is_web_endpoint_to_function' into deven/add_m…
devennavani Nov 15, 2024
c260a93
add web endpoints for methods
devennavani Nov 15, 2024
3886d51
fix bug in mock FunctionPrecreate
devennavani Nov 15, 2024
d003af1
fix method_functions lookup for build function
devennavani Nov 17, 2024
7f6d475
fix tests
devennavani Nov 17, 2024
d664055
more fixes
devennavani Nov 18, 2024
1e62bd9
fix issue
devennavani Nov 18, 2024
4080e57
override properly
devennavani Nov 18, 2024
22eb509
fix parameter name
devennavani Nov 18, 2024
544f5bc
more fixes
devennavani Nov 18, 2024
5d50cd1
more test fixes
devennavani Nov 18, 2024
9d2b9f2
more fixes
devennavani Nov 18, 2024
b7be1ac
Merge branch 'main' into deven/add_is_web_endpoint_to_function
devennavani Nov 18, 2024
f90b22d
Have _Function override _Object _hydrate method
devennavani Nov 18, 2024
022d640
Merge branch 'deven/have_function_override_hydrate' into deven/add_me…
devennavani Nov 18, 2024
0978ed4
Merge branch 'main' into deven/add_method_webhook_configs_to_class_fu…
devennavani Nov 18, 2024
d1e7d8a
corrections
devennavani Nov 18, 2024
8a7c9f2
more fixes
devennavani Nov 18, 2024
7685d63
cleanup
devennavani Nov 18, 2024
4efafa1
more cleanup
devennavani Nov 18, 2024
8400fb1
cleanup
devennavani Nov 18, 2024
f4ff8cd
cleanup
devennavani Nov 18, 2024
2627cb3
fix type error
devennavani Nov 18, 2024
85699d3
values() -> items()
devennavani Nov 18, 2024
7271966
Merge branch 'main' into deven/add_method_webhook_configs_to_class_fu…
devennavani Nov 18, 2024
cc7496e
Fix mock FunctionPrecreate
devennavani Nov 18, 2024
978b034
fix FunctionCreate
devennavani Nov 18, 2024
a5c93b9
fix test_web_cls
devennavani Nov 18, 2024
106c711
another fix
devennavani Nov 18, 2024
504be62
Bring mock FunctionCreate to parity with server
devennavani Nov 18, 2024
f859f51
fix merge conflcit
devennavani Nov 18, 2024
527d912
Add method_handle_metadata attr to _Function
devennavani Nov 18, 2024
77fe7ac
Merge branch 'deven/add_method_handle_metadata_attr_to_function' into…
devennavani Nov 18, 2024
c0df225
Remove _method_functions from _Function
devennavani Nov 18, 2024
b9be7ad
Merge branch 'deven/remove_method_functions_from_function' into deven…
devennavani Nov 18, 2024
f3a819c
remove redundant line
devennavani Nov 18, 2024
cf185a3
Merge branch 'main' into deven/add_method_handle_metadata_attr_to_fun…
devennavani Nov 18, 2024
1cc2bce
Merge branch 'deven/add_method_handle_metadata_attr_to_function' into…
devennavani Nov 18, 2024
d262999
Merge branch 'deven/remove_method_functions_from_function' into deven…
devennavani Nov 18, 2024
f5ff857
fix merge conflict
devennavani Nov 18, 2024
6efc9b4
Merge branch 'deven/remove_method_functions_from_function' of https:/…
devennavani Nov 18, 2024
cd851a2
Merge branch 'deven/remove_method_functions_from_function' into deven…
devennavani Nov 18, 2024
66b7d1c
Merge branch 'main' into deven/add_method_webhook_configs_to_class_fu…
devennavani Nov 18, 2024
49acb6c
fix modal shell maybe
devennavani Nov 18, 2024
28c98d1
fix cli tests
devennavani Nov 18, 2024
049e148
undo some unnecessary formatting
devennavani Nov 18, 2024
b4dbed2
undo more unnecessary formatting
devennavani Nov 18, 2024
5960950
fix type check
devennavani Nov 18, 2024
da6453c
fix tests
devennavani Nov 18, 2024
d628942
Merge branch 'main' into deven/add_method_webhook_configs_to_class_fu…
devennavani Nov 19, 2024
f43f426
Bump minor number
devennavani Nov 19, 2024
0c6dd03
fix bug in cls hydrate metadata
devennavani Nov 19, 2024
1b2680c
fix merge conflict
devennavani Nov 20, 2024
ef52754
dont set use_function_id on instance service function method function
devennavani Nov 20, 2024
32f2485
Merge branch 'main' into deven/add_method_webhook_configs_to_class_fu…
devennavani Nov 20, 2024
971823e
Merge branch 'main' into deven/add_method_webhook_configs_to_class_fu…
devennavani Nov 20, 2024
139d63a
remove print statement
devennavani Nov 20, 2024
eaecbdc
Merge branch 'main' into deven/add_method_webhook_configs_to_class_fu…
devennavani Nov 20, 2024
6ff7335
url display changes
devennavani Nov 21, 2024
46b654a
use arrow
devennavani Nov 21, 2024
f0ef776
fix merge conflict
devennavani Nov 21, 2024
16fde81
undo some changes
devennavani Nov 21, 2024
7508f4d
Show urls for modal domains and custom domains for web endpoint metho…
devennavani Nov 21, 2024
3885f94
fix merge conflict"
devennavani Nov 21, 2024
2c89765
Merge branch 'main' into deven/show_urls_for_new_style_class_methods
devennavani Nov 21, 2024
33fcc06
Merge branch 'main' into deven/show_urls_for_new_style_class_methods
devennavani Nov 21, 2024
2766df5
Merge branch 'deven/show_urls_for_new_style_class_methods' into deven…
devennavani Nov 21, 2024
5176514
Merge branch 'main' into deven/add_method_webhook_configs_to_class_fu…
devennavani Nov 21, 2024
24bf4d4
Merge branch 'main' into deven/add_method_webhook_configs_to_class_fu…
devennavani Nov 21, 2024
ce79aea
bug fix
devennavani Nov 21, 2024
e3cb65d
Merge branch 'main' into deven/add_method_webhook_configs_to_class_fu…
devennavani Nov 22, 2024
fa9554c
add test coverage
devennavani Nov 23, 2024
720aa55
edit build_number
devennavani Nov 23, 2024
1b4d2f0
fix merge conflict
devennavani Nov 25, 2024
19e63fc
fix merge conflict
devennavani Nov 26, 2024
818d3ff
merge conflict
devennavani Nov 27, 2024
0cf5156
bump version generated
devennavani Nov 27, 2024
407b723
fix build number
devennavani Nov 27, 2024
1d606b6
address nits
devennavani Nov 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion modal/_runtime/container_io_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ async def _dynamic_concurrency_loop(self):
await asyncio.sleep(DYNAMIC_CONCURRENCY_INTERVAL_SECS)

async def get_app_objects(self) -> RunningApp:
req = api_pb2.AppGetObjectsRequest(app_id=self.app_id, include_unindexed=True)
req = api_pb2.AppGetObjectsRequest(app_id=self.app_id, include_unindexed=True, only_class_function=True)
devennavani marked this conversation as resolved.
Show resolved Hide resolved
resp = await retry_transient_errors(self._client.stub.AppGetObjects, req)
logger.debug(f"AppGetObjects received {len(resp.items)} objects for app {self.app_id}")

Expand Down
2 changes: 1 addition & 1 deletion modal/cli/import_refs.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ def get_by_object_path(obj: Any, obj_path: str) -> Optional[Any]:
def _infer_function_or_help(
app: App, module, accept_local_entrypoint: bool, accept_webhook: bool
) -> Union[Function, LocalEntrypoint]:
function_choices = set(tag for tag, func in app.registered_functions.items() if not func.info.is_service_class())
function_choices = set(tag for tag in app.registered_functions.keys())
devennavani marked this conversation as resolved.
Show resolved Hide resolved
if not accept_webhook:
function_choices -= set(app.registered_web_endpoints)
if accept_local_entrypoint:
Expand Down
21 changes: 17 additions & 4 deletions modal/cli/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,20 +136,33 @@ def _get_clean_app_description(func_ref: str) -> str:


def _get_click_command_for_function(app: App, function_tag):
function = app.registered_functions[function_tag]
function = app.registered_functions.get(function_tag, None)
devennavani marked this conversation as resolved.
Show resolved Hide resolved
if not function or (isinstance(function, Function) and function.info.user_cls is not None):
# This is either a function_tag for a class method function (e.g MyClass.foo) or a function tag for a
# class service function (MyClass.*)
class_name, method_name = function_tag.rsplit(".", 1)
if not function:
function = app.registered_functions.get(f"{class_name}.*")
assert isinstance(function, Function)
function = typing.cast(Function, function)
if function.is_generator:
raise InvalidError("`modal run` is not supported for generator functions")

signature: Dict[str, ParameterMetadata]
cls: Optional[Cls] = None
method_name: Optional[str] = None
if function.info.user_cls is not None:
class_name, method_name = function_tag.rsplit(".", 1)
cls = typing.cast(Cls, app.registered_classes[class_name])
cls_signature = _get_signature(function.info.user_cls)
fun_signature = _get_signature(function.info.raw_f, is_method=True)
if method_name == "*":
method_names = list(cls._get_partial_functions().keys())
if len(method_names) == 1:
method_name = method_names[0]
else:
class_name = function.info.user_cls.__name__
raise click.UsageError(
f"Please specify a specific method of {class_name} to run, e.g. `modal run foo.py::MyClass.bar`" # noqa: E501
)
fun_signature = _get_signature(getattr(cls, method_name).info.raw_f, is_method=True)
signature = dict(**cls_signature, **fun_signature) # Pool all arguments
# TODO(erikbern): assert there's no overlap?
else:
Expand Down
89 changes: 49 additions & 40 deletions modal/cls.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ class _Cls(_Object, type_prefix="cs"):
_class_service_function: Optional[
_Function
] # The _Function serving *all* methods of the class, used for version >=v0.63
_method_functions: Dict[str, _Function] # Placeholder _Functions for each method
_method_functions: Dict[str, _Function] = None # Placeholder _Functions for each method
devennavani marked this conversation as resolved.
Show resolved Hide resolved
_options: Optional[api_pb2.FunctionOptions]
_callables: Dict[str, Callable[..., Any]]
_from_other_workspace: Optional[bool] # Functions require FunctionBindParams before invocation.
Expand All @@ -253,7 +253,6 @@ class _Cls(_Object, type_prefix="cs"):
def _initialize_from_empty(self):
self._user_cls = None
self._class_service_function = None
self._method_functions = {}
self._options = None
self._callables = {}
self._from_other_workspace = None
Expand All @@ -273,27 +272,49 @@ def _get_partial_functions(self) -> Dict[str, _PartialFunction]:

def _hydrate_metadata(self, metadata: Message):
assert isinstance(metadata, api_pb2.ClassHandleMetadata)

for method in metadata.methods:
if method.function_name in self._method_functions:
# This happens when the class is loaded locally
# since each function will already be a loaded dependency _Function
self._method_functions[method.function_name]._hydrate(
method.function_id, self._client, method.function_handle_metadata
)
if (
self._class_service_function
and self._class_service_function._method_handle_metadata
and len(self._class_service_function._method_handle_metadata)
):
# The class only has a class service service function and no method placeholders.
devennavani marked this conversation as resolved.
Show resolved Hide resolved
if self._method_functions:
# We're here when the Cls is loaded locally (e.g. _Cls.from_local) so the _method_functions mapping is
# populated with (un-hydrated) _Function objects
for (
method_name,
method_handle_metadata,
) in self._class_service_function._method_handle_metadata.items():
self._method_functions[method_name]._hydrate(
self._class_service_function.object_id, self._client, method_handle_metadata
)
else:
# We're here when the function is loaded remotely (e.g. _Cls.from_name)
self._method_functions = {}
for (
method_name,
method_handle_metadata,
) in self._class_service_function._method_handle_metadata.items():
self._method_functions[method_name] = _Function._new_hydrated(
self._class_service_function.object_id, self._client, method_handle_metadata
)
elif self._class_service_function:
# A class with a class service function and method placeholder functions
self._method_functions = {}
for method in metadata.methods:
self._method_functions[method.function_name] = _Function._new_hydrated(
self._class_service_function.object_id, self._client, method.function_handle_metadata
)
else:
# pre 0.63 class that does not have a class service function and only method functions
self._method_functions = {}
for method in metadata.methods:
self._method_functions[method.function_name] = _Function._new_hydrated(
method.function_id, self._client, method.function_handle_metadata
)

def _get_metadata(self) -> api_pb2.ClassHandleMetadata:
devennavani marked this conversation as resolved.
Show resolved Hide resolved
class_handle_metadata = api_pb2.ClassHandleMetadata()
for f_name, f in self._method_functions.items():
class_handle_metadata.methods.append(
api_pb2.ClassMethod(
function_name=f_name, function_id=f.object_id, function_handle_metadata=f._get_metadata()
)
)
return class_handle_metadata

@staticmethod
Expand Down Expand Up @@ -327,56 +348,43 @@ def from_local(user_cls, app: "modal.app._App", class_service_function: _Functio
# validate signature
_Cls.validate_construction_mechanism(user_cls)

functions: Dict[str, _Function] = {}
method_functions: Dict[str, _Function] = {}
partial_functions: Dict[str, _PartialFunction] = _find_partial_methods_for_user_cls(
user_cls, _PartialFunctionFlags.FUNCTION
)

for method_name, partial_function in partial_functions.items():
method_function = class_service_function._bind_method_old(user_cls, method_name, partial_function)
app._add_function(method_function, is_web_endpoint=partial_function.webhook_config is not None)
method_function = class_service_function._bind_method(user_cls, method_name, partial_function)
if partial_function.webhook_config is not None:
app._web_endpoints.append(method_function.tag)
partial_function.wrapped = True
functions[method_name] = method_function
method_functions[method_name] = method_function

# Disable the warning that these are not wrapped
for partial_function in _find_partial_methods_for_user_cls(user_cls, ~_PartialFunctionFlags.FUNCTION).values():
partial_function.wrapped = True

# Get all callables
callables: Dict[str, Callable] = {
k: pf.raw_f for k, pf in _find_partial_methods_for_user_cls(user_cls, ~_PartialFunctionFlags(0)).items()
k: pf.raw_f for k, pf in _find_partial_methods_for_user_cls(user_cls, _PartialFunctionFlags.all()).items()
}

def _deps() -> List[_Function]:
return [class_service_function] + list(functions.values())
return [class_service_function]

async def _load(self: "_Cls", resolver: Resolver, existing_object_id: Optional[str]):
req = api_pb2.ClassCreateRequest(app_id=resolver.app_id, existing_class_id=existing_object_id)
for f_name, f in self._method_functions.items():
req.methods.append(
api_pb2.ClassMethod(
function_name=f_name, function_id=f.object_id, function_handle_metadata=f._get_metadata()
)
)
req = api_pb2.ClassCreateRequest(
app_id=resolver.app_id, existing_class_id=existing_object_id, only_class_function=True
)
resp = await resolver.client.stub.ClassCreate(req)
# Even though we already have the function_handle_metadata for this method locally,
# The RPC is going to replace it with function_handle_metadata derived from the server.
# We need to overwrite the definition_id sent back from the server here with the definition_id
# previously stored in function metadata, which may have been sent back from FunctionCreate.
# The problem is that this metadata propagates back and overwrites the metadata on the Function
# object itself. This is really messy. Maybe better to exclusively populate the method metadata
# from the function metadata we already have locally? Really a lot to clean up here...
devennavani marked this conversation as resolved.
Show resolved Hide resolved
for method in resp.handle_metadata.methods:
f_metadata = self._method_functions[method.function_name]._get_metadata()
method.function_handle_metadata.definition_id = f_metadata.definition_id
self._hydrate(resp.class_id, resolver.client, resp.handle_metadata)

rep = f"Cls({user_cls.__name__})"
cls: _Cls = _Cls._from_loader(_load, rep, deps=_deps)
cls._app = app
cls._user_cls = user_cls
cls._class_service_function = class_service_function
cls._method_functions = functions
cls._method_functions = method_functions
cls._callables = callables
cls._from_other_workspace = False
return cls
Expand Down Expand Up @@ -415,6 +423,7 @@ async def _load_remote(obj: _Object, resolver: Resolver, existing_object_id: Opt
environment_name=_environment_name,
lookup_published=workspace is not None,
workspace_name=workspace,
only_class_function=True,
)
try:
response = await retry_transient_errors(resolver.client.stub.ClassGet, request)
Expand Down
102 changes: 4 additions & 98 deletions modal/functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ async def create(
function_call_invocation_type: "api_pb2.FunctionCallInvocationType.ValueType",
) -> "_Invocation":
assert client.stub
function_id = function._invocation_function_id()
function_id = function.object_id
item = await _create_input(args, kwargs, client, method_name=function._use_method_name)

request = api_pb2.FunctionMapRequest(
Expand Down Expand Up @@ -319,8 +319,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
_cluster_size: Optional[int] = None

# when this is the method of a class/object function, invocation of this function
# should be using another function id and supply the method name in the FunctionInput:
_use_function_id: str # The function to invoke
# should supply the method name in the FunctionInput:
_use_method_name: str = ""

_class_parameter_info: Optional["api_pb2.ClassParameterInfo"] = None
Expand Down Expand Up @@ -360,94 +359,6 @@ def _bind_method(
fun._is_method = True
return fun

def _bind_method_old(
devennavani marked this conversation as resolved.
Show resolved Hide resolved
self,
user_cls,
method_name: str,
partial_function: "modal.partial_function._PartialFunction",
):
"""mdmd:hidden

Creates a function placeholder function that binds a specific method name to
this function for use when invoking the function.

Should only be used on "class service functions". For "instance service functions",
we don't create an actual backend function, and instead do client-side "fake-hydration"
only, see _bind_instance_method.

"""
class_service_function = self
assert class_service_function._info # has to be a local function to be able to "bind" it
assert not class_service_function._is_method # should not be used on an already bound method placeholder
assert not class_service_function._obj # should only be used on base function / class service function
full_name = f"{user_cls.__name__}.{method_name}"
function_type = get_function_type(partial_function.is_generator)

async def _load(method_bound_function: "_Function", resolver: Resolver, existing_object_id: Optional[str]):
function_definition = api_pb2.Function(
function_name=full_name,
webhook_config=partial_function.webhook_config,
function_type=function_type,
is_method=True,
use_function_id=class_service_function.object_id,
use_method_name=method_name,
batch_max_size=partial_function.batch_max_size or 0,
batch_linger_ms=partial_function.batch_wait_ms or 0,
)
assert resolver.app_id
request = api_pb2.FunctionCreateRequest(
app_id=resolver.app_id,
function=function_definition,
# method_bound_function.object_id usually gets set by preload
existing_function_id=existing_object_id or method_bound_function.object_id or "",
defer_updates=True,
)
assert resolver.client.stub is not None # client should be connected when load is called
with FunctionCreationStatus(resolver, full_name) as function_creation_status:
response = await resolver.client.stub.FunctionCreate(request)
method_bound_function._hydrate(
response.function_id,
resolver.client,
response.handle_metadata,
)
function_creation_status.set_response(response)

async def _preload(method_bound_function: "_Function", resolver: Resolver, existing_object_id: Optional[str]):
if class_service_function._use_method_name:
raise ExecutionError(f"Can't bind method to already bound {class_service_function}")
assert resolver.app_id
req = api_pb2.FunctionPrecreateRequest(
app_id=resolver.app_id,
function_name=full_name,
function_type=function_type,
webhook_config=partial_function.webhook_config,
use_function_id=class_service_function.object_id,
use_method_name=method_name,
existing_function_id=existing_object_id or "",
)
assert resolver.client.stub # client should be connected at this point
response = await retry_transient_errors(resolver.client.stub.FunctionPrecreate, req)
method_bound_function._hydrate(response.function_id, resolver.client, response.handle_metadata)

def _deps():
return [class_service_function]

rep = f"Method({full_name})"

fun = _Function._from_loader(_load, rep, preload=_preload, deps=_deps)
fun._tag = full_name
fun._raw_f = partial_function.raw_f
fun._info = FunctionInfo(
partial_function.raw_f, user_cls=user_cls, serialized=class_service_function.info.is_serialized()
) # needed for .local()
fun._use_method_name = method_name
fun._app = class_service_function._app
fun._is_generator = partial_function.is_generator
fun._cluster_size = partial_function.cluster_size
fun._spec = class_service_function._spec
fun._is_method = True
return fun

def _bind_instance_method(self, class_bound_method: "_Function"):
"""mdmd:hidden

Expand Down Expand Up @@ -475,7 +386,6 @@ def hydrate_from_instance_service_function(method_placeholder_fun):
method_placeholder_fun._is_generator = class_bound_method._is_generator
method_placeholder_fun._cluster_size = class_bound_method._cluster_size
method_placeholder_fun._use_method_name = method_name
method_placeholder_fun._use_function_id = instance_service_function.object_id
method_placeholder_fun._is_method = True

async def _load(fun: "_Function", resolver: Resolver, existing_object_id: Optional[str]):
Expand Down Expand Up @@ -848,6 +758,8 @@ async def _load(self: _Function, resolver: Resolver, existing_object_id: Optiona
class_serialized=class_serialized or b"",
function_type=function_type,
webhook_config=webhook_config,
method_definitions=method_definitions,
method_definitions_set=True,
shared_volume_mounts=network_file_system_mount_protos(
validated_network_file_systems, allow_cross_region_volumes
),
Expand Down Expand Up @@ -1224,7 +1136,6 @@ def _initialize_from_empty(self):
self._web_url = None
self._function_name = None
self._info = None
self._use_function_id = ""
self._serve_mounts = frozenset()

def _hydrate_metadata(self, metadata: Optional[Message]):
Expand All @@ -1234,15 +1145,11 @@ def _hydrate_metadata(self, metadata: Optional[Message]):
self._web_url = metadata.web_url
self._function_name = metadata.function_name
self._is_method = metadata.is_method
self._use_function_id = metadata.use_function_id
self._use_method_name = metadata.use_method_name
self._class_parameter_info = metadata.class_parameter_info
self._method_handle_metadata = dict(metadata.method_handle_metadata)
self._definition_id = metadata.definition_id

def _invocation_function_id(self) -> str:
return self._use_function_id or self.object_id

def _get_metadata(self):
# Overridden concrete implementation of base class method
assert self._function_name, f"Function name must be set before metadata can be retrieved for {self}"
Expand All @@ -1251,7 +1158,6 @@ def _get_metadata(self):
function_type=get_function_type(self._is_generator),
web_url=self._web_url or "",
use_method_name=self._use_method_name,
use_function_id=self._use_function_id,
is_method=self._is_method,
class_parameter_info=self._class_parameter_info,
definition_id=self._definition_id,
Expand Down
Loading
Loading