modal 0.74.41__py3-none-any.whl → 0.74.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.
@@ -4,7 +4,6 @@ import os
4
4
 
5
5
  from modal._runtime.user_code_imports import (
6
6
  Service,
7
- get_active_app_fallback,
8
7
  import_class_service,
9
8
  import_single_function_service,
10
9
  )
@@ -441,16 +440,9 @@ def main(container_args: api_pb2.ContainerArguments, client: Client):
441
440
  function_def,
442
441
  ser_usr_cls,
443
442
  ser_fun,
444
- param_args,
445
- param_kwargs,
446
443
  )
447
444
 
448
- # If the cls/function decorator was applied in local scope, but the app is global, we can look it up
449
- if service.app is not None:
450
- active_app = service.app
451
- else:
452
- # if the app can't be inferred by the imported function, use name-based fallback
453
- active_app = get_active_app_fallback(function_def)
445
+ active_app = service.app
454
446
 
455
447
  if function_def.pty_info.pty_type == api_pb2.PTYInfo.PTY_TYPE_SHELL:
456
448
  # Concurrency and batching doesn't apply for `modal shell`.
@@ -18,8 +18,9 @@ from modal.exception import ExecutionError, InvalidError
18
18
  from modal_proto import api_pb2
19
19
 
20
20
  if typing.TYPE_CHECKING:
21
+ import modal._functions
22
+ import modal._partial_function
21
23
  import modal.app
22
- import modal.partial_function
23
24
  from modal._runtime.asgi import LifespanManager
24
25
 
25
26
 
@@ -41,7 +42,7 @@ class Service(metaclass=ABCMeta):
41
42
  """
42
43
 
43
44
  user_cls_instance: Any
44
- app: Optional["modal.app._App"]
45
+ app: "modal.app._App"
45
46
  service_deps: Optional[Sequence["modal._object._Object"]]
46
47
 
47
48
  @abstractmethod
@@ -93,7 +94,7 @@ def construct_webhook_callable(
93
94
  @dataclass
94
95
  class ImportedFunction(Service):
95
96
  user_cls_instance: Any
96
- app: Optional["modal.app._App"]
97
+ app: modal.app._App
97
98
  service_deps: Optional[Sequence["modal._object._Object"]]
98
99
 
99
100
  _user_defined_callable: Callable[..., Any]
@@ -136,7 +137,7 @@ class ImportedFunction(Service):
136
137
  @dataclass
137
138
  class ImportedClass(Service):
138
139
  user_cls_instance: Any
139
- app: Optional["modal.app._App"]
140
+ app: "modal.app._App"
140
141
  service_deps: Optional[Sequence["modal._object._Object"]]
141
142
 
142
143
  _partial_functions: dict[str, "modal._partial_function._PartialFunction"]
@@ -147,6 +148,7 @@ class ImportedClass(Service):
147
148
  finalized_functions = {}
148
149
  for method_name, _partial in self._partial_functions.items():
149
150
  user_func = _partial.raw_f
151
+ assert user_func
150
152
  # Check this property before we turn it into a method (overriden by webhooks)
151
153
  is_async = get_is_async(user_func)
152
154
  # Use the function definition for whether this is a generator (overriden by webhooks)
@@ -160,7 +162,7 @@ class ImportedClass(Service):
160
162
  finalized_function = FinalizedFunction(
161
163
  callable=bound_func,
162
164
  is_async=is_async,
163
- is_generator=is_generator,
165
+ is_generator=bool(is_generator),
164
166
  data_format=api_pb2.DATA_FORMAT_PICKLE,
165
167
  )
166
168
  else:
@@ -178,7 +180,7 @@ class ImportedClass(Service):
178
180
  return finalized_functions
179
181
 
180
182
 
181
- def get_user_class_instance(_cls: modal.cls._Cls, args: tuple, kwargs: dict[str, Any]) -> typing.Any:
183
+ def get_user_class_instance(_cls: modal.cls._Cls, args: tuple[Any, ...], kwargs: dict[str, Any]) -> typing.Any:
182
184
  """Returns instance of the underlying class to be used as the `self`
183
185
 
184
186
  For the time being, this is an instance of the underlying user defined type, with
@@ -187,7 +189,7 @@ def get_user_class_instance(_cls: modal.cls._Cls, args: tuple, kwargs: dict[str,
187
189
 
188
190
  TODO: Could possibly change this to use an Obj to clean up the data model? would invalidate isinstance checks though
189
191
  """
190
- cls = synchronizer._translate_out(_cls) # ugly
192
+ cls = typing.cast(modal.cls.Cls, synchronizer._translate_out(_cls)) # ugly
191
193
  modal_obj: modal.cls.Obj = cls(*args, **kwargs)
192
194
  modal_obj._entered = True # ugly but prevents .local() from triggering additional enter-logic
193
195
  # TODO: unify lifecycle logic between .local() and container_entrypoint
@@ -197,10 +199,8 @@ def get_user_class_instance(_cls: modal.cls._Cls, args: tuple, kwargs: dict[str,
197
199
 
198
200
  def import_single_function_service(
199
201
  function_def: api_pb2.Function,
200
- ser_cls, # used only for @build functions
201
- ser_fun,
202
- cls_args, # used only for @build functions
203
- cls_kwargs, # used only for @build functions
202
+ ser_cls: Optional[type], # used only for @build functions
203
+ ser_fun: Optional[Callable[..., Any]],
204
204
  ) -> Service:
205
205
  """Imports a function dynamically, and locates the app.
206
206
 
@@ -226,11 +226,15 @@ def import_single_function_service(
226
226
  """
227
227
  user_defined_callable: Callable
228
228
  service_deps: Optional[Sequence["modal._object._Object"]] = None
229
- active_app: Optional[modal.app._App] = None
229
+ active_app: modal.app._App
230
+
231
+ user_cls_or_cls: typing.Union[None, type, modal.cls.Cls]
232
+ user_cls_instance = None
230
233
 
231
234
  if ser_fun is not None:
232
235
  # This is a serialized function we already fetched from the server
233
- cls, user_defined_callable = ser_cls, ser_fun
236
+ user_cls_or_cls, user_defined_callable = ser_cls, ser_fun
237
+ active_app = get_active_app_fallback(function_def)
234
238
  else:
235
239
  # Load the module dynamically
236
240
  module = importlib.import_module(function_def.module_name)
@@ -242,44 +246,53 @@ def import_single_function_service(
242
246
  parts = qual_name.split(".")
243
247
  if len(parts) == 1:
244
248
  # This is a function
245
- cls = None
249
+ user_cls_or_cls = None
246
250
  f = getattr(module, qual_name)
247
251
  if isinstance(f, Function):
248
- function = synchronizer._translate_in(f)
249
- service_deps = function.deps(only_explicit_mounts=True)
250
- user_defined_callable = function.get_raw_f()
251
- active_app = function._app
252
+ _function: modal._functions._Function[Any, Any, Any] = synchronizer._translate_in(f) # type: ignore
253
+ service_deps = _function.deps(only_explicit_mounts=True)
254
+ user_defined_callable = _function.get_raw_f()
255
+ assert _function._app # app should always be set on a decorated function
256
+ active_app = _function._app
252
257
  else:
253
258
  user_defined_callable = f
259
+ active_app = get_active_app_fallback(function_def)
260
+
254
261
  elif len(parts) == 2:
255
262
  # This path should only be triggered by @build class builder methods and can be removed
256
263
  # once @build is deprecated.
257
264
  assert not function_def.use_method_name # new "placeholder methods" should not be invoked directly!
258
265
  assert function_def.is_builder_function
259
266
  cls_name, fun_name = parts
260
- cls = getattr(module, cls_name)
261
- if isinstance(cls, modal.cls.Cls):
267
+ user_cls_or_cls = getattr(module, cls_name)
268
+ if isinstance(user_cls_or_cls, modal.cls.Cls):
262
269
  # The cls decorator is in global scope
263
- _cls = synchronizer._translate_in(cls)
270
+ _cls = typing.cast(modal.cls._Cls, synchronizer._translate_in(user_cls_or_cls))
264
271
  user_defined_callable = _cls._callables[fun_name]
265
272
  # Intentionally not including these, since @build functions don't actually
266
273
  # forward the information from their parent class.
267
274
  # service_deps = _cls._get_class_service_function().deps(only_explicit_mounts=True)
275
+ assert _cls._app
268
276
  active_app = _cls._app
269
277
  else:
270
278
  # This is non-decorated class
271
- user_defined_callable = getattr(cls, fun_name)
279
+ user_defined_callable = getattr(user_cls_or_cls, fun_name) # unbound method
280
+ active_app = get_active_app_fallback(function_def)
272
281
  else:
273
282
  raise InvalidError(f"Invalid function qualname {qual_name}")
274
283
 
275
284
  # Instantiate the class if it's defined
276
- if cls:
277
- # This code is only used for @build methods on classes
278
- user_cls_instance = get_user_class_instance(cls, cls_args, cls_kwargs)
279
- # Bind the function to the instance as self (using the descriptor protocol!)
285
+ if user_cls_or_cls:
286
+ if isinstance(user_cls_or_cls, modal.cls.Cls):
287
+ # This code is only used for @build methods on classes
288
+ _cls = typing.cast(modal.cls._Cls, user_cls_or_cls)
289
+ user_cls_instance = get_user_class_instance(_cls, (), {})
290
+ # Bind the unbound method to the instance as self (using the descriptor protocol!)
291
+ else:
292
+ # serialized=True or "undecorated"
293
+ user_cls_instance = user_cls_or_cls()
294
+
280
295
  user_defined_callable = user_defined_callable.__get__(user_cls_instance)
281
- else:
282
- user_cls_instance = None
283
296
 
284
297
  return ImportedFunction(
285
298
  user_cls_instance,
@@ -331,7 +344,7 @@ def import_class_service(
331
344
  cls_or_user_cls = getattr(module, cls_name)
332
345
 
333
346
  if isinstance(cls_or_user_cls, modal.cls.Cls):
334
- _cls = synchronizer._translate_in(cls_or_user_cls)
347
+ _cls = typing.cast(modal.cls._Cls, synchronizer._translate_in(cls_or_user_cls))
335
348
  class_service_function: _Function = _cls._get_class_service_function()
336
349
  service_deps = class_service_function.deps(only_explicit_mounts=True)
337
350
  active_app = class_service_function.app
@@ -339,7 +352,7 @@ def import_class_service(
339
352
  # Undecorated user class (serialized or local scope-decoration).
340
353
  service_deps = None # we can't infer service deps for now
341
354
  active_app = get_active_app_fallback(function_def)
342
- _client: "modal.client._Client" = synchronizer._translate_in(client)
355
+ _client = typing.cast("modal.client._Client", synchronizer._translate_in(client))
343
356
  _service_function: modal._functions._Function[Any, Any, Any] = modal._functions._Function._new_hydrated(
344
357
  service_function_hydration_data.object_id,
345
358
  _client,
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.74.41"
30
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.74.43"
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.74.41"
88
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.74.43"
89
89
  ): ...
90
90
  def is_closed(self) -> bool: ...
91
91
  @property
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 0.74.41
3
+ Version: 0.74.43
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -2,7 +2,7 @@ modal/__init__.py,sha256=7wz1AT_bpWJJEzXsAo3QMb7i87y7UGXwfneb0bGDhRg,2502
2
2
  modal/__main__.py,sha256=sTJcc9EbDuCKSwg3tL6ZckFw9WWdlkXW8mId1IvJCNc,2846
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=DymOImhc3uGRkIq_qXmBsEbWMB4EBMpfuXzz2S4BcGg,29404
5
+ modal/_container_entrypoint.py,sha256=2Zx9O_EMJg0H77EdnC2vGKs6uFMWwbP1NLFf-qYmWmU,28962
6
6
  modal/_functions.py,sha256=R6L5cWIy2Lls6O6_taAIalUCigXwm1VU5Kb98Qffxr0,74233
7
7
  modal/_ipython.py,sha256=TW1fkVOmZL3YYqdS2YlM1hqpf654Yf8ZyybHdBnlhSw,301
8
8
  modal/_location.py,sha256=joiX-0ZeutEUDTrrqLF1GHXCdVLF-rHzstocbMcd_-k,366
@@ -22,7 +22,7 @@ modal/app.py,sha256=r-9vVU1lrR1CWtJEo60fuaianvxY_oOXZyv1Qx1DEkI,51231
22
22
  modal/app.pyi,sha256=0QNtnUpAFbOPcbwCt119ge7OmoBqMFw5SajLgdE5eOw,28600
23
23
  modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
24
24
  modal/client.py,sha256=U-YKSw0n7J1ZLREt9cbEJCtmHe5YoPKFxl0xlkan2yc,15565
25
- modal/client.pyi,sha256=Y0yN-3N4IasGRptKzyjNu7HCELCsVwuG3ZDMmuSq6p0,7593
25
+ modal/client.pyi,sha256=rPfz_p9AUkyobZGg5_yGRjA1RFzdHk97BrdExJ_57KE,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=pfEKqzNldQbtoM45CApRhSd8jJ7hD6o-83SLX8DTXXk,33549
@@ -88,7 +88,7 @@ modal/_runtime/execution_context.py,sha256=73Y5zH_o-MhVCrkJXakYVlFkKqCa2CWvqoHjO
88
88
  modal/_runtime/execution_context.pyi,sha256=TAxQq7uLj7i9r9XbXgFZiSVBWxObFWN-rkssS0I7Vkk,661
89
89
  modal/_runtime/gpu_memory_snapshot.py,sha256=tA3m1d1cwnmHpvpCeN_WijDd6n8byn7LWlpicbIxiOI,3144
90
90
  modal/_runtime/telemetry.py,sha256=T1RoAGyjBDr1swiM6pPsGRSITm7LI5FDK18oNXxY08U,5163
91
- modal/_runtime/user_code_imports.py,sha256=kAv37Pl1TmGKduv0Kozum0xNTD42bDLloSIsT7zf84o,16884
91
+ modal/_runtime/user_code_imports.py,sha256=78wJyleqY2RVibqcpbDQyfWVBVT9BjyHPeoV9WdwV5Y,17720
92
92
  modal/_utils/__init__.py,sha256=waLjl5c6IPDhSsdWAm9Bji4e2PVxamYABKAze6CHVXY,28
93
93
  modal/_utils/app_utils.py,sha256=88BT4TPLWfYAQwKTHcyzNQRHg8n9B-QE2UyJs96iV-0,108
94
94
  modal/_utils/async_utils.py,sha256=b2TJyKY1Hq7df7M-fo3qlFM95mGdo3dCuqRPPcV5hsE,27445
@@ -145,7 +145,7 @@ modal/requirements/2024.10.txt,sha256=qD-5cVIVM9wXesJ6JC89Ew-3m2KjEElUz3jaw_MddR
145
145
  modal/requirements/PREVIEW.txt,sha256=qD-5cVIVM9wXesJ6JC89Ew-3m2KjEElUz3jaw_MddRo,296
146
146
  modal/requirements/README.md,sha256=9tK76KP0Uph7O0M5oUgsSwEZDj5y-dcUPsnpR0Sc-Ik,854
147
147
  modal/requirements/base-images.json,sha256=57vMSqzMbLBxw5tFWSaMiIkkVEps4JfX5PAtXGnkS4U,740
148
- modal-0.74.41.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
148
+ modal-0.74.43.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
149
149
  modal_docs/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,28
150
150
  modal_docs/gen_cli_docs.py,sha256=c1yfBS_x--gL5bs0N4ihMwqwX8l3IBWSkBAKNNIi6bQ,3801
151
151
  modal_docs/gen_reference_docs.py,sha256=d_CQUGQ0rfw28u75I2mov9AlS773z9rG40-yq5o7g2U,6359
@@ -153,13 +153,13 @@ modal_docs/mdmd/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,2
153
153
  modal_docs/mdmd/mdmd.py,sha256=Irx49MCCTlBOP4FBdLR--JrpA3-WhsVeriq0LGgsRic,6232
154
154
  modal_docs/mdmd/signatures.py,sha256=XJaZrK7Mdepk5fdX51A8uENiLFNil85Ud0d4MH8H5f0,3218
155
155
  modal_proto/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
156
- modal_proto/api.proto,sha256=eD4A8icYJFeunpUiDqra14EmUnosttBtDnrrfdxGeBw,93783
157
- modal_proto/api_grpc.py,sha256=LZ_rylBFJ79c5-dYOcyebPjRGrCKNO3j2K7giw9P9-g,113653
158
- modal_proto/api_pb2.py,sha256=NEV1ZmTOfpBuCgJ4F6eSZKQkBHXPaHowdJtCs0OCek0,330916
159
- modal_proto/api_pb2.pyi,sha256=JP8s6JHac1Cz2kpJaBNk6K_bPoel1j50uJYco6lV7Co,450054
160
- modal_proto/api_pb2_grpc.py,sha256=QaApxS-KExaYpQU0kdnoxXPrERZk_b4lRZKY_V4mbq0,245789
161
- modal_proto/api_pb2_grpc.pyi,sha256=6obh-mFStQnsXcUHzTI_SXuED9mhwcKnfIt_urSZkJw,57552
162
- modal_proto/modal_api_grpc.py,sha256=r7qGJdW69v5pXca2ySm6gqTGYG_pFPWBno_4XGMkbpU,15136
156
+ modal_proto/api.proto,sha256=PsfT4IqkQdVqsF6o_S5mM9Yb1JpniqcRkCzgcn3h16c,94226
157
+ modal_proto/api_grpc.py,sha256=kTSfs9yI_YtxkPuATpSQ1_Nvd7TtjK03H61RupX1bpM,114421
158
+ modal_proto/api_pb2.py,sha256=eckMxlyef8RCBDGPqFl4W7Q7pkvHnU0qF3tN9i_5z-M,332381
159
+ modal_proto/api_pb2.pyi,sha256=4siyIFPPqBL-JgfTfZkImKGA-A98-T7WRQvNeMexKdM,451890
160
+ modal_proto/api_pb2_grpc.py,sha256=-KPQMzXmTYwgF23_tGpODCVK79iOdV1sRsw5mN-byMw,247448
161
+ modal_proto/api_pb2_grpc.pyi,sha256=ls1qcby7goTrlE6BluSWpo73cW_ajvh3rOe41azMBWM,57929
162
+ modal_proto/modal_api_grpc.py,sha256=X-sgFt2CYE5cahn41gc0oDNfK0sWIOs7AlmHUWKMB1k,15234
163
163
  modal_proto/modal_options_grpc.py,sha256=qJ1cuwA54oRqrdTyPTbvfhFZYd9HhJKK5UCwt523r3Y,120
164
164
  modal_proto/options.proto,sha256=zp9h5r61ivsp0XwEWwNBsVqNTbRA1VSY_UtN7sEcHtE,549
165
165
  modal_proto/options_grpc.py,sha256=M18X3d-8F_cNYSVM3I25dUTO5rZ0rd-vCCfynfh13Nc,125
@@ -170,9 +170,9 @@ modal_proto/options_pb2_grpc.pyi,sha256=CImmhxHsYnF09iENPoe8S4J-n93jtgUYD2JPAc0y
170
170
  modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
171
171
  modal_version/__init__.py,sha256=m94xZNWIjH8oUtJk4l9xfovzDJede2o7X-q0MHVECtM,470
172
172
  modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
173
- modal_version/_version_generated.py,sha256=m83LTb2g-PJjADQjIdFLiMqVHWVmJ-AsmauNkBXU9jA,149
174
- modal-0.74.41.dist-info/METADATA,sha256=n4fQoRxKwb_edXA7eyb5tiGyOQcsuLo0igQKdcQ8yGE,2451
175
- modal-0.74.41.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
176
- modal-0.74.41.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
177
- modal-0.74.41.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
178
- modal-0.74.41.dist-info/RECORD,,
173
+ modal_version/_version_generated.py,sha256=2hJCmnOxsmCyxzg5ap4nElf3c9LDoeofF0nLLlnsOGw,149
174
+ modal-0.74.43.dist-info/METADATA,sha256=lWjSSt9FvZhtmQQVZkKRNBjRGF_BDnGSJGdQgL2neps,2451
175
+ modal-0.74.43.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
176
+ modal-0.74.43.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
177
+ modal-0.74.43.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
178
+ modal-0.74.43.dist-info/RECORD,,
modal_proto/api.proto CHANGED
@@ -2887,6 +2887,20 @@ message VolumeDeleteRequest {
2887
2887
  string environment_name = 2 [deprecated=true];
2888
2888
  }
2889
2889
 
2890
+ message VolumeGetFile2Request {
2891
+ string volume_id = 1;
2892
+ string path = 2;
2893
+ uint64 start = 3;
2894
+ uint64 len = 4; // 0 is interpreted as 'read to end'
2895
+ }
2896
+
2897
+ message VolumeGetFile2Response {
2898
+ repeated string get_urls = 1;
2899
+ uint64 size = 2; // total file size
2900
+ uint64 start = 3; // file position of first byte returned
2901
+ uint64 len = 4; // number of bytes returned
2902
+ }
2903
+
2890
2904
  message VolumeGetFileRequest {
2891
2905
  string volume_id = 1;
2892
2906
  string path = 2;
@@ -3273,6 +3287,7 @@ service ModalClient {
3273
3287
  rpc VolumeCopyFiles(VolumeCopyFilesRequest) returns (google.protobuf.Empty);
3274
3288
  rpc VolumeDelete(VolumeDeleteRequest) returns (google.protobuf.Empty);
3275
3289
  rpc VolumeGetFile(VolumeGetFileRequest) returns (VolumeGetFileResponse);
3290
+ rpc VolumeGetFile2(VolumeGetFile2Request) returns (VolumeGetFile2Response);
3276
3291
  rpc VolumeGetOrCreate(VolumeGetOrCreateRequest) returns (VolumeGetOrCreateResponse);
3277
3292
  rpc VolumeHeartbeat(VolumeHeartbeatRequest) returns (google.protobuf.Empty);
3278
3293
  rpc VolumeList(VolumeListRequest) returns (VolumeListResponse);
modal_proto/api_grpc.py CHANGED
@@ -570,6 +570,10 @@ class ModalClientBase(abc.ABC):
570
570
  async def VolumeGetFile(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.VolumeGetFileRequest, modal_proto.api_pb2.VolumeGetFileResponse]') -> None:
571
571
  pass
572
572
 
573
+ @abc.abstractmethod
574
+ async def VolumeGetFile2(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.VolumeGetFile2Request, modal_proto.api_pb2.VolumeGetFile2Response]') -> None:
575
+ pass
576
+
573
577
  @abc.abstractmethod
574
578
  async def VolumeGetOrCreate(self, stream: 'grpclib.server.Stream[modal_proto.api_pb2.VolumeGetOrCreateRequest, modal_proto.api_pb2.VolumeGetOrCreateResponse]') -> None:
575
579
  pass
@@ -1440,6 +1444,12 @@ class ModalClientBase(abc.ABC):
1440
1444
  modal_proto.api_pb2.VolumeGetFileRequest,
1441
1445
  modal_proto.api_pb2.VolumeGetFileResponse,
1442
1446
  ),
1447
+ '/modal.client.ModalClient/VolumeGetFile2': grpclib.const.Handler(
1448
+ self.VolumeGetFile2,
1449
+ grpclib.const.Cardinality.UNARY_UNARY,
1450
+ modal_proto.api_pb2.VolumeGetFile2Request,
1451
+ modal_proto.api_pb2.VolumeGetFile2Response,
1452
+ ),
1443
1453
  '/modal.client.ModalClient/VolumeGetOrCreate': grpclib.const.Handler(
1444
1454
  self.VolumeGetOrCreate,
1445
1455
  grpclib.const.Cardinality.UNARY_UNARY,
@@ -2334,6 +2344,12 @@ class ModalClientStub:
2334
2344
  modal_proto.api_pb2.VolumeGetFileRequest,
2335
2345
  modal_proto.api_pb2.VolumeGetFileResponse,
2336
2346
  )
2347
+ self.VolumeGetFile2 = grpclib.client.UnaryUnaryMethod(
2348
+ channel,
2349
+ '/modal.client.ModalClient/VolumeGetFile2',
2350
+ modal_proto.api_pb2.VolumeGetFile2Request,
2351
+ modal_proto.api_pb2.VolumeGetFile2Response,
2352
+ )
2337
2353
  self.VolumeGetOrCreate = grpclib.client.UnaryUnaryMethod(
2338
2354
  channel,
2339
2355
  '/modal.client.ModalClient/VolumeGetOrCreate',