modal 0.73.47__py3-none-any.whl → 0.73.49__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/_functions.py +1 -0
- modal/client.pyi +2 -2
- modal/functions.pyi +6 -6
- {modal-0.73.47.dist-info → modal-0.73.49.dist-info}/METADATA +1 -1
- {modal-0.73.47.dist-info → modal-0.73.49.dist-info}/RECORD +16 -13
- modal_docs/mdmd/__init__.py +1 -0
- modal_docs/mdmd/mdmd.py +195 -0
- modal_docs/mdmd/signatures.py +76 -0
- modal_proto/api.proto +3 -0
- modal_proto/api_pb2.py +504 -504
- modal_proto/api_pb2.pyi +7 -2
- modal_version/_version_generated.py +1 -1
- {modal-0.73.47.dist-info → modal-0.73.49.dist-info}/LICENSE +0 -0
- {modal-0.73.47.dist-info → modal-0.73.49.dist-info}/WHEEL +0 -0
- {modal-0.73.47.dist-info → modal-0.73.49.dist-info}/entry_points.txt +0 -0
- {modal-0.73.47.dist-info → modal-0.73.49.dist-info}/top_level.txt +0 -0
modal/_functions.py
CHANGED
@@ -862,6 +862,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
|
|
862
862
|
warm_pool_size=function_definition.warm_pool_size,
|
863
863
|
concurrency_limit=function_definition.concurrency_limit,
|
864
864
|
task_idle_timeout_secs=function_definition.task_idle_timeout_secs,
|
865
|
+
autoscaler_settings=function_definition.autoscaler_settings,
|
865
866
|
worker_id=function_definition.worker_id,
|
866
867
|
timeout_secs=function_definition.timeout_secs,
|
867
868
|
web_url=function_definition.web_url,
|
modal/client.pyi
CHANGED
@@ -27,7 +27,7 @@ class _Client:
|
|
27
27
|
_snapshotted: bool
|
28
28
|
|
29
29
|
def __init__(
|
30
|
-
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.73.
|
30
|
+
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.73.49"
|
31
31
|
): ...
|
32
32
|
def is_closed(self) -> bool: ...
|
33
33
|
@property
|
@@ -85,7 +85,7 @@ class Client:
|
|
85
85
|
_snapshotted: bool
|
86
86
|
|
87
87
|
def __init__(
|
88
|
-
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.73.
|
88
|
+
self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.73.49"
|
89
89
|
): ...
|
90
90
|
def is_closed(self) -> bool: ...
|
91
91
|
@property
|
modal/functions.pyi
CHANGED
@@ -200,11 +200,11 @@ class Function(
|
|
200
200
|
|
201
201
|
_call_generator_nowait: ___call_generator_nowait_spec[typing_extensions.Self]
|
202
202
|
|
203
|
-
class __remote_spec(typing_extensions.Protocol[
|
203
|
+
class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
|
204
204
|
def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER: ...
|
205
205
|
async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER: ...
|
206
206
|
|
207
|
-
remote: __remote_spec[modal._functions.
|
207
|
+
remote: __remote_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
|
208
208
|
|
209
209
|
class __remote_gen_spec(typing_extensions.Protocol[SUPERSELF]):
|
210
210
|
def __call__(self, *args, **kwargs) -> typing.Generator[typing.Any, None, None]: ...
|
@@ -219,19 +219,19 @@ class Function(
|
|
219
219
|
self, *args: modal._functions.P.args, **kwargs: modal._functions.P.kwargs
|
220
220
|
) -> modal._functions.OriginalReturnType: ...
|
221
221
|
|
222
|
-
class ___experimental_spawn_spec(typing_extensions.Protocol[
|
222
|
+
class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
|
223
223
|
def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
|
224
224
|
async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
|
225
225
|
|
226
226
|
_experimental_spawn: ___experimental_spawn_spec[
|
227
|
-
modal._functions.
|
227
|
+
modal._functions.P, modal._functions.ReturnType, typing_extensions.Self
|
228
228
|
]
|
229
229
|
|
230
|
-
class __spawn_spec(typing_extensions.Protocol[
|
230
|
+
class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
|
231
231
|
def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
|
232
232
|
async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
|
233
233
|
|
234
|
-
spawn: __spawn_spec[modal._functions.
|
234
|
+
spawn: __spawn_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
|
235
235
|
|
236
236
|
def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]: ...
|
237
237
|
|
@@ -3,7 +3,7 @@ modal/__main__.py,sha256=CgIjP8m1xJjjd4AXc-delmR6LdBCZclw2A_V38CFIio,2870
|
|
3
3
|
modal/_clustered_functions.py,sha256=kTf-9YBXY88NutC1akI-gCbvf01RhMPCw-zoOI_YIUE,2700
|
4
4
|
modal/_clustered_functions.pyi,sha256=vllkegc99A0jrUOWa8mdlSbdp6uz36TsHhGxysAOpaQ,771
|
5
5
|
modal/_container_entrypoint.py,sha256=YtfJ852XUDtAWBD-yVs99zy933-VBEKQyIngEj36Qcw,29286
|
6
|
-
modal/_functions.py,sha256=
|
6
|
+
modal/_functions.py,sha256=VY9SfEWbcw-kzkALDlg7yHxGlCVVuEaAahH6jAuaNHk,72450
|
7
7
|
modal/_ipython.py,sha256=TW1fkVOmZL3YYqdS2YlM1hqpf654Yf8ZyybHdBnlhSw,301
|
8
8
|
modal/_location.py,sha256=joiX-0ZeutEUDTrrqLF1GHXCdVLF-rHzstocbMcd_-k,366
|
9
9
|
modal/_object.py,sha256=ItQcsMNkz9Y3kdTsvfNarbW-paJ2qabDyQ7njaqY0XI,11359
|
@@ -22,7 +22,7 @@ modal/app.py,sha256=rCOPD51gVyow8muyaqMuV65qfTnAZKf_w1OCZdSF_6o,44636
|
|
22
22
|
modal/app.pyi,sha256=0MMCgskIL4r3eq8oBcfm2lLyeao2gXjS3iXaIfmaJ-o,25959
|
23
23
|
modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
|
24
24
|
modal/client.py,sha256=8SQawr7P1PNUCq1UmJMUQXG2jIo4Nmdcs311XqrNLRE,15276
|
25
|
-
modal/client.pyi,sha256=
|
25
|
+
modal/client.pyi,sha256=X9G_7tXQ8SbERaND370QtmRBIrwVIS8Bd2MYsgTLIiA,7593
|
26
26
|
modal/cloud_bucket_mount.py,sha256=YOe9nnvSr4ZbeCn587d7_VhE9IioZYRvF9VYQTQux08,5914
|
27
27
|
modal/cloud_bucket_mount.pyi,sha256=30T3K1a89l6wzmEJ_J9iWv9SknoGqaZDx59Xs-ZQcmk,1607
|
28
28
|
modal/cls.py,sha256=nJhJr-YuttOmdNWxtMHcBV-q6iQmY5qXkEy9yO43clY,31130
|
@@ -41,7 +41,7 @@ modal/file_io.py,sha256=lcMs_E9Xfm0YX1t9U2wNIBPnqHRxmImqjLW1GHqVmyg,20945
|
|
41
41
|
modal/file_io.pyi,sha256=NTRft1tbPSWf9TlWVeZmTlgB5AZ_Zhu2srWIrWr7brk,9445
|
42
42
|
modal/file_pattern_matcher.py,sha256=trosX-Bp7dOubudN1bLLhRAoidWy1TcoaR4Pv8CedWw,6497
|
43
43
|
modal/functions.py,sha256=kcNHvqeGBxPI7Cgd57NIBBghkfbeFJzXO44WW0jSmao,325
|
44
|
-
modal/functions.pyi,sha256=
|
44
|
+
modal/functions.pyi,sha256=R_I4Foho4htNCFhYc0YKhWoS_jLqZNOrNfTrAA-Clew,14395
|
45
45
|
modal/gpu.py,sha256=Kbhs_u49FaC2Zi0TjCdrpstpRtT5eZgecynmQi5IZVE,6752
|
46
46
|
modal/image.py,sha256=EtYt7_Rjgi71gt_SutqF8KSjnzWgP5P1Z7XsV-eIoFw,91470
|
47
47
|
modal/image.pyi,sha256=Oc2ndYHSdQTcRpZKHSfTdj-m_2oQAsnc2EWTthbNn-s,26380
|
@@ -147,11 +147,14 @@ modal/requirements/base-images.json,sha256=kLNo5Sqmnhp9H6Hr9IcaGJFrRaRg1yfuepUWk
|
|
147
147
|
modal_docs/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,28
|
148
148
|
modal_docs/gen_cli_docs.py,sha256=c1yfBS_x--gL5bs0N4ihMwqwX8l3IBWSkBAKNNIi6bQ,3801
|
149
149
|
modal_docs/gen_reference_docs.py,sha256=aDcUSSDtAAZ4eeFWyroeIg2TOzyRoYcic-d9Zh9TdLY,6656
|
150
|
+
modal_docs/mdmd/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,28
|
151
|
+
modal_docs/mdmd/mdmd.py,sha256=Irx49MCCTlBOP4FBdLR--JrpA3-WhsVeriq0LGgsRic,6232
|
152
|
+
modal_docs/mdmd/signatures.py,sha256=XJaZrK7Mdepk5fdX51A8uENiLFNil85Ud0d4MH8H5f0,3218
|
150
153
|
modal_proto/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
|
151
|
-
modal_proto/api.proto,sha256=
|
154
|
+
modal_proto/api.proto,sha256=wyTg0T4hrf2Txdo5byoqAEE2bZNYJkR6fmu3kakW3y8,86809
|
152
155
|
modal_proto/api_grpc.py,sha256=FYGqDegM_w_qxdtlxum8k31mDibKoMvmNxv_p9cKdKs,109056
|
153
|
-
modal_proto/api_pb2.py,sha256=
|
154
|
-
modal_proto/api_pb2.pyi,sha256=
|
156
|
+
modal_proto/api_pb2.py,sha256=oEcC56Tib2HIEsbfCVXcEKg397BuhbvWPZEqjS-tDzU,312448
|
157
|
+
modal_proto/api_pb2.pyi,sha256=sZiDbbk4b6XU1SzqnxkN4RslzGN-hCXjErZDlW94SpY,421094
|
155
158
|
modal_proto/api_pb2_grpc.py,sha256=DNp0Et5i_Ey4dKx_1o1LRtYhyWYyT0NzTcAY4EcHn-c,235765
|
156
159
|
modal_proto/api_pb2_grpc.pyi,sha256=RI6tWC3L8EIN4-izFSEGPPJl5Ta0lXPNuHUJaWAr35s,54892
|
157
160
|
modal_proto/modal_api_grpc.py,sha256=UG8WJU81afrWPwItWB4Ag64E9EpyREMpBbAVGVEYJiM,14550
|
@@ -165,10 +168,10 @@ modal_proto/options_pb2_grpc.pyi,sha256=CImmhxHsYnF09iENPoe8S4J-n93jtgUYD2JPAc0y
|
|
165
168
|
modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
166
169
|
modal_version/__init__.py,sha256=wiJQ53c-OMs0Xf1UeXOxQ7FwlV1VzIjnX6o-pRYZ_Pk,470
|
167
170
|
modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
|
168
|
-
modal_version/_version_generated.py,sha256=
|
169
|
-
modal-0.73.
|
170
|
-
modal-0.73.
|
171
|
-
modal-0.73.
|
172
|
-
modal-0.73.
|
173
|
-
modal-0.73.
|
174
|
-
modal-0.73.
|
171
|
+
modal_version/_version_generated.py,sha256=Yb0Ldsxsrgh9H8bk0NkkH3wEnZKBKauFeqsEXN2nc2Q,149
|
172
|
+
modal-0.73.49.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
|
173
|
+
modal-0.73.49.dist-info/METADATA,sha256=uNlUtlN9-k4kkC-1AVgeFXNivMrum5Y2fkHsq4ayhVc,2452
|
174
|
+
modal-0.73.49.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
175
|
+
modal-0.73.49.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
|
176
|
+
modal-0.73.49.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
|
177
|
+
modal-0.73.49.dist-info/RECORD,,
|
@@ -0,0 +1 @@
|
|
1
|
+
# Copyright Modal Labs 2023
|
modal_docs/mdmd/mdmd.py
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
# Copyright Modal Labs 2023
|
2
|
+
"""mdmd - MoDal MarkDown"""
|
3
|
+
|
4
|
+
import inspect
|
5
|
+
import warnings
|
6
|
+
from enum import Enum, EnumMeta
|
7
|
+
from types import ModuleType
|
8
|
+
from typing import Callable
|
9
|
+
|
10
|
+
import synchronicity.synchronizer
|
11
|
+
|
12
|
+
from .signatures import get_signature
|
13
|
+
|
14
|
+
|
15
|
+
def format_docstring(docstring: str):
|
16
|
+
if docstring is None:
|
17
|
+
docstring = ""
|
18
|
+
else:
|
19
|
+
docstring = inspect.cleandoc(docstring)
|
20
|
+
|
21
|
+
if docstring and not docstring.endswith("\n"):
|
22
|
+
docstring += "\n"
|
23
|
+
|
24
|
+
return docstring
|
25
|
+
|
26
|
+
|
27
|
+
def function_str(name: str, func):
|
28
|
+
signature = get_signature(name, func)
|
29
|
+
decl = f"""```python
|
30
|
+
{signature}
|
31
|
+
```\n\n"""
|
32
|
+
docstring = format_docstring(func.__doc__)
|
33
|
+
return decl + docstring
|
34
|
+
|
35
|
+
|
36
|
+
def class_str(name, obj, title_level="##"):
|
37
|
+
def qual_name(cls):
|
38
|
+
if cls.__module__ == "builtins":
|
39
|
+
return cls.__name__
|
40
|
+
return f"{cls.__module__}.{cls.__name__}"
|
41
|
+
|
42
|
+
bases = [qual_name(b) for b in obj.__bases__]
|
43
|
+
bases_str = f"({', '.join(bases)})" if bases else ""
|
44
|
+
decl = f"""```python
|
45
|
+
class {name}{bases_str}
|
46
|
+
```\n\n"""
|
47
|
+
parts = [decl]
|
48
|
+
docstring = format_docstring(obj.__doc__)
|
49
|
+
|
50
|
+
if isinstance(obj, EnumMeta) and not docstring:
|
51
|
+
# Python 3.11 removed the docstring from enums
|
52
|
+
docstring = "An enumeration.\n"
|
53
|
+
|
54
|
+
if docstring:
|
55
|
+
parts.append(docstring + "\n")
|
56
|
+
|
57
|
+
if isinstance(obj, EnumMeta):
|
58
|
+
enum_vals = "\n".join(f"* `{k}`" for k in obj.__members__.keys())
|
59
|
+
parts.append(f"The possible values are:\n\n{enum_vals}\n")
|
60
|
+
|
61
|
+
else:
|
62
|
+
init = inspect.unwrap(obj.__init__)
|
63
|
+
|
64
|
+
if (inspect.isfunction(init) or inspect.ismethod(init)) and not object_is_private("constructor", init):
|
65
|
+
parts.append(function_str("__init__", init))
|
66
|
+
|
67
|
+
member_title_level = title_level + "#"
|
68
|
+
|
69
|
+
entries = {}
|
70
|
+
|
71
|
+
def rec_update_attributes(cls):
|
72
|
+
# first bases, then class itself
|
73
|
+
for base_cls in cls.__bases__:
|
74
|
+
rec_update_attributes(base_cls)
|
75
|
+
entries.update(cls.__dict__)
|
76
|
+
|
77
|
+
rec_update_attributes(obj)
|
78
|
+
|
79
|
+
for member_name, member in entries.items():
|
80
|
+
if isinstance(member, classmethod) or isinstance(member, staticmethod):
|
81
|
+
# get the original function definition instead of the descriptor object
|
82
|
+
member = getattr(obj, member_name)
|
83
|
+
elif isinstance(member, property):
|
84
|
+
member = member.fget
|
85
|
+
elif isinstance(member, (synchronicity.synchronizer.FunctionWithAio, synchronicity.synchronizer.MethodWithAio)):
|
86
|
+
member = member._func
|
87
|
+
|
88
|
+
if object_is_private(member_name, member):
|
89
|
+
continue
|
90
|
+
|
91
|
+
if callable(member):
|
92
|
+
parts.append(f"{member_title_level} {member_name}\n\n")
|
93
|
+
parts.append(function_str(member_name, member))
|
94
|
+
|
95
|
+
return "".join(parts)
|
96
|
+
|
97
|
+
|
98
|
+
def module_str(header, module, title_level="#", filter_items: Callable[[ModuleType, str], bool] = None):
|
99
|
+
header = [f"{title_level} {header}\n\n"]
|
100
|
+
docstring = format_docstring(module.__doc__)
|
101
|
+
if docstring:
|
102
|
+
header.append(docstring + "\n")
|
103
|
+
|
104
|
+
object_docs = []
|
105
|
+
member_title_level = title_level + "#"
|
106
|
+
for qual_name, name, item in module_items(module, filter_items):
|
107
|
+
try:
|
108
|
+
if hasattr(item, "__wrapped__"):
|
109
|
+
item = item.__wrapped__
|
110
|
+
except KeyError:
|
111
|
+
pass
|
112
|
+
except:
|
113
|
+
print("failed on", qual_name, name, item)
|
114
|
+
raise
|
115
|
+
if inspect.isclass(item):
|
116
|
+
classdoc = class_str(name, item, title_level=member_title_level)
|
117
|
+
object_docs.append(f"{member_title_level} {qual_name}\n\n")
|
118
|
+
object_docs.append(classdoc)
|
119
|
+
elif callable(item):
|
120
|
+
funcdoc = function_str(name, item)
|
121
|
+
object_docs.append(f"{member_title_level} {qual_name}\n\n")
|
122
|
+
object_docs.append(funcdoc)
|
123
|
+
else:
|
124
|
+
item_doc = getattr(module, f"__doc__{name}", None)
|
125
|
+
if item_doc:
|
126
|
+
# variable documentation
|
127
|
+
object_docs.append(f"{member_title_level} {qual_name}\n\n")
|
128
|
+
object_docs.append(item_doc)
|
129
|
+
else:
|
130
|
+
warnings.warn(f"Not sure how to document: {name} ({item}")
|
131
|
+
|
132
|
+
if object_docs:
|
133
|
+
return "".join(header + object_docs)
|
134
|
+
return ""
|
135
|
+
|
136
|
+
|
137
|
+
def object_is_private(name, obj):
|
138
|
+
docstring = inspect.getdoc(obj)
|
139
|
+
if docstring is None:
|
140
|
+
docstring = ""
|
141
|
+
module = getattr(obj, "__module__", None) # obj is class
|
142
|
+
if not module:
|
143
|
+
cls = getattr(obj, "__class__", None) # obj is instance
|
144
|
+
if cls:
|
145
|
+
module = getattr(cls, "__module__", None)
|
146
|
+
if module == "builtins":
|
147
|
+
return True
|
148
|
+
|
149
|
+
if docstring.lstrip().startswith("mdmd:hidden") or name.startswith("_"):
|
150
|
+
return True
|
151
|
+
|
152
|
+
return False
|
153
|
+
|
154
|
+
|
155
|
+
def default_filter(module, item_name):
|
156
|
+
"""Include non-private objects defined in the module itself"""
|
157
|
+
item = getattr(module, item_name)
|
158
|
+
if object_is_private(item_name, item) or inspect.ismodule(item):
|
159
|
+
return False
|
160
|
+
member_module = getattr(item, "__module__", type(item).__module__)
|
161
|
+
return member_module == module.__name__
|
162
|
+
|
163
|
+
|
164
|
+
def package_filter(module_prefix: str):
|
165
|
+
"""Include non-private objects defined in any module with the prefix `module_prefix`"""
|
166
|
+
|
167
|
+
def return_filter(module, item_name):
|
168
|
+
item = getattr(module, item_name)
|
169
|
+
if object_is_private(item_name, item) or inspect.ismodule(item):
|
170
|
+
return False
|
171
|
+
member_module = getattr(item, "__module__", type(item).__module__)
|
172
|
+
return member_module.startswith(module_prefix)
|
173
|
+
|
174
|
+
return return_filter
|
175
|
+
|
176
|
+
|
177
|
+
def module_items(module, filter_items: Callable[[ModuleType, str], bool] = None):
|
178
|
+
"""Returns filtered members of module"""
|
179
|
+
if filter_items is None:
|
180
|
+
# default filter is to only include classes and functions declared (or whose type is declared) in the file
|
181
|
+
filter_items = default_filter
|
182
|
+
|
183
|
+
for member_name, member in inspect.getmembers(module):
|
184
|
+
# only modal items
|
185
|
+
if not filter_items(module, member_name):
|
186
|
+
continue
|
187
|
+
|
188
|
+
qual_name = f"{module.__name__}.{member_name}"
|
189
|
+
yield qual_name, member_name, member
|
190
|
+
|
191
|
+
|
192
|
+
class Category(Enum):
|
193
|
+
FUNCTION = "function"
|
194
|
+
CLASS = "class"
|
195
|
+
MODULE = "module"
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# Copyright Modal Labs 2023
|
2
|
+
import ast
|
3
|
+
import inspect
|
4
|
+
import re
|
5
|
+
import textwrap
|
6
|
+
import warnings
|
7
|
+
|
8
|
+
from synchronicity.synchronizer import FunctionWithAio
|
9
|
+
|
10
|
+
|
11
|
+
def _signature_from_ast(func) -> tuple[str, str]:
|
12
|
+
"""Get function signature, including decorators and comments, from source code
|
13
|
+
|
14
|
+
Traverses functools.wraps-wrappings to get source of underlying function.
|
15
|
+
|
16
|
+
Has the advantage over inspect.signature that it can get decorators, default arguments and comments verbatim
|
17
|
+
from the function definition.
|
18
|
+
"""
|
19
|
+
src = inspect.getsource(func)
|
20
|
+
src = textwrap.dedent(src)
|
21
|
+
|
22
|
+
def get_source_segment(src, fromline, fromcol, toline, tocol) -> str:
|
23
|
+
lines = src.split("\n")
|
24
|
+
lines = lines[fromline - 1 : toline]
|
25
|
+
lines[-1] = lines[-1][:tocol]
|
26
|
+
lines[0] = lines[0][fromcol:]
|
27
|
+
return "\n".join(lines)
|
28
|
+
|
29
|
+
tree = ast.parse(src)
|
30
|
+
func_def = list(ast.iter_child_nodes(tree))[0]
|
31
|
+
assert isinstance(func_def, (ast.FunctionDef, ast.AsyncFunctionDef))
|
32
|
+
decorator_starts = [(item.lineno, item.col_offset - 1) for item in func_def.decorator_list]
|
33
|
+
declaration_start = min([(func_def.lineno, func_def.col_offset)] + decorator_starts)
|
34
|
+
body_start = min((item.lineno, item.col_offset) for item in func_def.body)
|
35
|
+
|
36
|
+
return (
|
37
|
+
func_def.name,
|
38
|
+
get_source_segment(src, declaration_start[0], declaration_start[1], body_start[0], body_start[1] - 1).strip(),
|
39
|
+
)
|
40
|
+
|
41
|
+
|
42
|
+
def get_signature(name, callable) -> str:
|
43
|
+
"""A problem with using *only* this method is that the wrapping method signature will not be respected.
|
44
|
+
TODO: use source parsing *only* to extract default arguments, comments (and possibly decorators) and "merge"
|
45
|
+
that definition with the outer-most definition."""
|
46
|
+
|
47
|
+
if not (inspect.isfunction(callable) or inspect.ismethod(callable) or isinstance(callable, FunctionWithAio)):
|
48
|
+
assert hasattr(callable, "__call__")
|
49
|
+
callable = callable.__call__
|
50
|
+
|
51
|
+
try:
|
52
|
+
original_name, definition_source = _signature_from_ast(callable)
|
53
|
+
except Exception:
|
54
|
+
warnings.warn(f"Could not get source signature for {name}. Using fallback.")
|
55
|
+
original_name = name
|
56
|
+
definition_source = f"def {name}{inspect.signature(callable)}"
|
57
|
+
|
58
|
+
if original_name != name:
|
59
|
+
# ugly name and definition replacement hack when needed
|
60
|
+
definition_source = definition_source.replace(f"def {original_name}", f"def {name}")
|
61
|
+
|
62
|
+
if (
|
63
|
+
"async def" in definition_source
|
64
|
+
and not inspect.iscoroutinefunction(callable)
|
65
|
+
and not inspect.isasyncgenfunction(callable)
|
66
|
+
):
|
67
|
+
# hack to "reset" signature to a blocking one if the underlying source definition is async
|
68
|
+
# but the wrapper function isn't (like when synchronicity wraps an async function as a blocking one)
|
69
|
+
definition_source = definition_source.replace("async def", "def")
|
70
|
+
definition_source = definition_source.replace("asynccontextmanager", "contextmanager")
|
71
|
+
definition_source = definition_source.replace("AsyncIterator", "Iterator")
|
72
|
+
|
73
|
+
# remove any synchronicity-internal decorators
|
74
|
+
definition_source, _ = re.subn(r"^\s*@synchronizer\..*\n", "", definition_source)
|
75
|
+
|
76
|
+
return definition_source
|
modal_proto/api.proto
CHANGED
@@ -1435,6 +1435,9 @@ message FunctionData {
|
|
1435
1435
|
bool untrusted = 27; // If set, the function will be run in an untrusted environment.
|
1436
1436
|
bool snapshot_debug = 28; // For internal debugging use only.
|
1437
1437
|
bool runtime_perf_record = 29; // For internal debugging use only.
|
1438
|
+
|
1439
|
+
AutoscalerSettings autoscaler_settings = 31; // Bundle of parameters related to autoscaling
|
1440
|
+
|
1438
1441
|
}
|
1439
1442
|
|
1440
1443
|
message FunctionExtended {
|