modal 0.66.44__py3-none-any.whl → 0.66.46__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/app.py CHANGED
@@ -177,7 +177,8 @@ class _App:
177
177
 
178
178
  _name: Optional[str]
179
179
  _description: Optional[str]
180
- _indexed_objects: Dict[str, _Object]
180
+ _functions: Dict[str, _Function]
181
+ _classes: Dict[str, _Cls]
181
182
 
182
183
  _image: Optional[_Image]
183
184
  _mounts: Sequence[_Mount]
@@ -223,7 +224,8 @@ class _App:
223
224
  if image is not None and not isinstance(image, _Image):
224
225
  raise InvalidError("image has to be a modal Image or AioImage object")
225
226
 
226
- self._indexed_objects = {}
227
+ self._functions = {}
228
+ self._classes = {}
227
229
  self._image = image
228
230
  self._mounts = mounts
229
231
  self._secrets = secrets
@@ -312,6 +314,7 @@ class _App:
312
314
  raise InvalidError(f"App attribute `{key}` with value {value!r} is not a valid Modal object")
313
315
 
314
316
  def _add_object(self, tag, obj):
317
+ # TODO(erikbern): replace this with _add_function and _add_class
315
318
  if self._running_app:
316
319
  # If this is inside a container, then objects can be defined after app initialization.
317
320
  # So we may have to initialize objects once they get bound to the app.
@@ -320,7 +323,12 @@ class _App:
320
323
  metadata: Message = self._running_app.object_handle_metadata[object_id]
321
324
  obj._hydrate(object_id, self._client, metadata)
322
325
 
323
- self._indexed_objects[tag] = obj
326
+ if isinstance(obj, _Function):
327
+ self._functions[tag] = obj
328
+ elif isinstance(obj, _Cls):
329
+ self._classes[tag] = obj
330
+ else:
331
+ raise RuntimeError(f"Expected `obj` to be a _Function or _Cls (got {type(obj)}")
324
332
 
325
333
  def __getitem__(self, tag: str):
326
334
  deprecation_error((2024, 3, 25), _app_attr_error)
@@ -334,7 +342,7 @@ class _App:
334
342
  if tag.startswith("__"):
335
343
  # Hacky way to avoid certain issues, e.g. pickle will try to look this up
336
344
  raise AttributeError(f"App has no member {tag}")
337
- if tag not in self._indexed_objects:
345
+ if tag not in self._functions or tag not in self._classes:
338
346
  # Primarily to make hasattr work
339
347
  raise AttributeError(f"App has no member {tag}")
340
348
  deprecation_error((2024, 3, 25), _app_attr_error)
@@ -360,7 +368,9 @@ class _App:
360
368
 
361
369
  def _uncreate_all_objects(self):
362
370
  # TODO(erikbern): this doesn't unhydrate objects that aren't tagged
363
- for obj in self._indexed_objects.values():
371
+ for obj in self._functions.values():
372
+ obj._unhydrate()
373
+ for obj in self._classes.values():
364
374
  obj._unhydrate()
365
375
 
366
376
  @asynccontextmanager
@@ -459,18 +469,17 @@ class _App:
459
469
  return [m for m in all_mounts if m.is_local()]
460
470
 
461
471
  def _add_function(self, function: _Function, is_web_endpoint: bool):
462
- if function.tag in self._indexed_objects:
463
- old_function = self._indexed_objects[function.tag]
464
- if isinstance(old_function, _Function):
465
- if not is_notebook():
466
- logger.warning(
467
- f"Warning: Tag '{function.tag}' collision!"
468
- " Overriding existing function "
469
- f"[{old_function._info.module_name}].{old_function._info.function_name}"
470
- f" with new function [{function._info.module_name}].{function._info.function_name}"
471
- )
472
- else:
473
- logger.warning(f"Warning: tag {function.tag} exists but is overridden by function")
472
+ if function.tag in self._functions:
473
+ if not is_notebook():
474
+ old_function: _Function = self._functions[function.tag]
475
+ logger.warning(
476
+ f"Warning: Tag '{function.tag}' collision!"
477
+ " Overriding existing function "
478
+ f"[{old_function._info.module_name}].{old_function._info.function_name}"
479
+ f" with new function [{function._info.module_name}].{function._info.function_name}"
480
+ )
481
+ if function.tag in self._classes:
482
+ logger.warning(f"Warning: tag {function.tag} exists but is overridden by function")
474
483
 
475
484
  self._add_object(function.tag, function)
476
485
  if is_web_endpoint:
@@ -484,21 +493,22 @@ class _App:
484
493
  _App._container_app = running_app
485
494
 
486
495
  # Hydrate objects on app
496
+ indexed_objects = dict(**self._functions, **self._classes)
487
497
  for tag, object_id in running_app.tag_to_object_id.items():
488
- if tag in self._indexed_objects:
489
- obj = self._indexed_objects[tag]
498
+ if tag in indexed_objects:
499
+ obj = indexed_objects[tag]
490
500
  handle_metadata = running_app.object_handle_metadata[object_id]
491
501
  obj._hydrate(object_id, client, handle_metadata)
492
502
 
493
503
  @property
494
504
  def registered_functions(self) -> Dict[str, _Function]:
495
505
  """All modal.Function objects registered on the app."""
496
- return {tag: obj for tag, obj in self._indexed_objects.items() if isinstance(obj, _Function)}
506
+ return self._functions
497
507
 
498
508
  @property
499
509
  def registered_classes(self) -> Dict[str, _Function]:
500
510
  """All modal.Cls objects registered on the app."""
501
- return {tag: obj for tag, obj in self._indexed_objects.items() if isinstance(obj, _Cls)}
511
+ return self._classes
502
512
 
503
513
  @property
504
514
  def registered_entrypoints(self) -> Dict[str, _LocalEntrypoint]:
@@ -507,7 +517,11 @@ class _App:
507
517
 
508
518
  @property
509
519
  def indexed_objects(self) -> Dict[str, _Object]:
510
- return self._indexed_objects
520
+ deprecation_warning(
521
+ (2024, 11, 25),
522
+ "`app.indexed_objects` is deprecated! Use `app.registered_functions` or `app.registered_classes` instead.",
523
+ )
524
+ return dict(**self._functions, **self._classes)
511
525
 
512
526
  @property
513
527
  def registered_web_endpoints(self) -> List[str]:
@@ -1002,8 +1016,9 @@ class _App:
1002
1016
  bar.remote()
1003
1017
  ```
1004
1018
  """
1005
- for tag, object in other_app._indexed_objects.items():
1006
- existing_object = self._indexed_objects.get(tag)
1019
+ indexed_objects = dict(**other_app._functions, **other_app._classes)
1020
+ for tag, object in indexed_objects.items():
1021
+ existing_object = indexed_objects.get(tag)
1007
1022
  if existing_object and existing_object != object:
1008
1023
  logger.warning(
1009
1024
  f"Named app object {tag} with existing value {existing_object} is being "
modal/app.pyi CHANGED
@@ -76,7 +76,8 @@ class _App:
76
76
  _container_app: typing.ClassVar[typing.Optional[modal.running_app.RunningApp]]
77
77
  _name: typing.Optional[str]
78
78
  _description: typing.Optional[str]
79
- _indexed_objects: typing.Dict[str, modal.object._Object]
79
+ _functions: typing.Dict[str, modal.functions._Function]
80
+ _classes: typing.Dict[str, modal.cls._Cls]
80
81
  _image: typing.Optional[modal.image._Image]
81
82
  _mounts: typing.Sequence[modal.mount._Mount]
82
83
  _secrets: typing.Sequence[modal.secret._Secret]
@@ -270,7 +271,8 @@ class App:
270
271
  _container_app: typing.ClassVar[typing.Optional[modal.running_app.RunningApp]]
271
272
  _name: typing.Optional[str]
272
273
  _description: typing.Optional[str]
273
- _indexed_objects: typing.Dict[str, modal.object.Object]
274
+ _functions: typing.Dict[str, modal.functions.Function]
275
+ _classes: typing.Dict[str, modal.cls.Cls]
274
276
  _image: typing.Optional[modal.image.Image]
275
277
  _mounts: typing.Sequence[modal.mount.Mount]
276
278
  _secrets: typing.Sequence[modal.secret.Secret]
modal/cli/import_refs.py CHANGED
@@ -154,7 +154,7 @@ Registered functions and local entrypoints on the selected app are:
154
154
  # entrypoint is in entrypoint registry, for now
155
155
  return app.registered_entrypoints[function_name]
156
156
 
157
- function = app.indexed_objects[function_name] # functions are in blueprint
157
+ function = app.registered_functions[function_name]
158
158
  assert isinstance(function, Function)
159
159
  return function
160
160
 
modal/cli/launch.py CHANGED
@@ -25,7 +25,7 @@ launch_cli = Typer(
25
25
  )
26
26
 
27
27
 
28
- def _launch_program(name: str, filename: str, args: Dict[str, Any]) -> None:
28
+ def _launch_program(name: str, filename: str, detach: bool, args: Dict[str, Any]) -> None:
29
29
  os.environ["MODAL_LAUNCH_ARGS"] = json.dumps(args)
30
30
 
31
31
  program_path = str(Path(__file__).parent / "programs" / filename)
@@ -37,7 +37,7 @@ def _launch_program(name: str, filename: str, args: Dict[str, Any]) -> None:
37
37
  func = entrypoint.info.raw_f
38
38
  isasync = inspect.iscoroutinefunction(func)
39
39
  with enable_output():
40
- with run_app(app):
40
+ with run_app(app, detach=detach):
41
41
  try:
42
42
  if isasync:
43
43
  asyncio.run(func())
@@ -57,6 +57,7 @@ def jupyter(
57
57
  add_python: Optional[str] = "3.11",
58
58
  mount: Optional[str] = None, # Create a `modal.Mount` from a local directory.
59
59
  volume: Optional[str] = None, # Attach a persisted `modal.Volume` by name (creating if missing).
60
+ detach: bool = False, # Run the app in "detached" mode to persist after local client disconnects
60
61
  ):
61
62
  args = {
62
63
  "cpu": cpu,
@@ -68,7 +69,7 @@ def jupyter(
68
69
  "mount": mount,
69
70
  "volume": volume,
70
71
  }
71
- _launch_program("jupyter", "run_jupyter.py", args)
72
+ _launch_program("jupyter", "run_jupyter.py", detach, args)
72
73
 
73
74
 
74
75
  @launch_cli.command(name="vscode", help="Start Visual Studio Code on Modal.")
@@ -79,6 +80,7 @@ def vscode(
79
80
  timeout: int = 3600,
80
81
  mount: Optional[str] = None, # Create a `modal.Mount` from a local directory.
81
82
  volume: Optional[str] = None, # Attach a persisted `modal.Volume` by name (creating if missing).
83
+ detach: bool = False, # Run the app in "detached" mode to persist after local client disconnects
82
84
  ):
83
85
  args = {
84
86
  "cpu": cpu,
@@ -88,4 +90,4 @@ def vscode(
88
90
  "mount": mount,
89
91
  "volume": volume,
90
92
  }
91
- _launch_program("vscode", "vscode.py", args)
93
+ _launch_program("vscode", "vscode.py", detach, args)
modal/cli/run.py CHANGED
@@ -136,7 +136,7 @@ def _get_clean_app_description(func_ref: str) -> str:
136
136
 
137
137
 
138
138
  def _get_click_command_for_function(app: App, function_tag):
139
- function = app.indexed_objects[function_tag]
139
+ function = app.registered_functions[function_tag]
140
140
  assert isinstance(function, Function)
141
141
  function = typing.cast(Function, function)
142
142
  if function.is_generator:
@@ -147,7 +147,7 @@ def _get_click_command_for_function(app: App, function_tag):
147
147
  method_name: Optional[str] = None
148
148
  if function.info.user_cls is not None:
149
149
  class_name, method_name = function_tag.rsplit(".", 1)
150
- cls = typing.cast(Cls, app.indexed_objects[class_name])
150
+ cls = typing.cast(Cls, app.registered_classes[class_name])
151
151
  cls_signature = _get_signature(function.info.user_cls)
152
152
  fun_signature = _get_signature(function.info.raw_f, is_method=True)
153
153
  signature = dict(**cls_signature, **fun_signature) # Pool all arguments
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.44",
34
+ version: str = "0.66.46",
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.44",
93
+ version: str = "0.66.46",
94
94
  ): ...
95
95
  def is_closed(self) -> bool: ...
96
96
  @property
modal/runner.py CHANGED
@@ -327,11 +327,13 @@ async def _run_app(
327
327
  )
328
328
 
329
329
  try:
330
+ indexed_objects = dict(**app._functions, **app._classes) # TODO(erikbern): remove
331
+
330
332
  # Create all members
331
- await _create_all_objects(client, running_app, app._indexed_objects, environment_name)
333
+ await _create_all_objects(client, running_app, indexed_objects, environment_name)
332
334
 
333
335
  # Publish the app
334
- await _publish_app(client, running_app, app_state, app._indexed_objects)
336
+ await _publish_app(client, running_app, app_state, indexed_objects)
335
337
  except asyncio.CancelledError as e:
336
338
  # this typically happens on sigint/ctrl-C during setup (the KeyboardInterrupt happens in the main thread)
337
339
  if output_mgr := _get_output_manager():
@@ -424,16 +426,18 @@ async def _serve_update(
424
426
  try:
425
427
  running_app: RunningApp = await _init_local_app_existing(client, existing_app_id, environment_name)
426
428
 
429
+ indexed_objects = dict(**app._functions, **app._classes) # TODO(erikbern): remove
430
+
427
431
  # Create objects
428
432
  await _create_all_objects(
429
433
  client,
430
434
  running_app,
431
- app._indexed_objects,
435
+ indexed_objects,
432
436
  environment_name,
433
437
  )
434
438
 
435
439
  # Publish the updated app
436
- await _publish_app(client, running_app, api_pb2.APP_STATE_UNSPECIFIED, app._indexed_objects)
440
+ await _publish_app(client, running_app, api_pb2.APP_STATE_UNSPECIFIED, indexed_objects)
437
441
 
438
442
  # Communicate to the parent process
439
443
  is_ready.set()
@@ -521,17 +525,19 @@ async def _deploy_app(
521
525
 
522
526
  tc.infinite_loop(heartbeat, sleep=HEARTBEAT_INTERVAL)
523
527
 
528
+ indexed_objects = dict(**app._functions, **app._classes) # TODO(erikbern): remove
529
+
524
530
  try:
525
531
  # Create all members
526
532
  await _create_all_objects(
527
533
  client,
528
534
  running_app,
529
- app._indexed_objects,
535
+ indexed_objects,
530
536
  environment_name=environment_name,
531
537
  )
532
538
 
533
539
  app_url, warnings = await _publish_app(
534
- client, running_app, api_pb2.APP_STATE_DEPLOYED, app._indexed_objects, name, tag
540
+ client, running_app, api_pb2.APP_STATE_DEPLOYED, indexed_objects, name, tag
535
541
  )
536
542
  except Exception as e:
537
543
  # Note that AppClientDisconnect only stops the app if it's still initializing, and is a no-op otherwise.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: modal
3
- Version: 0.66.44
3
+ Version: 0.66.46
4
4
  Summary: Python client library for Modal
5
5
  Author: Modal Labs
6
6
  Author-email: support@modal.com
@@ -15,11 +15,11 @@ modal/_traceback.py,sha256=1yNp1Dqq4qRIQp8idDp5PEqjwH4eA8MNI0FhFkCOFgo,4408
15
15
  modal/_tunnel.py,sha256=SVmQxGbV7dcLwyY9eB2PIWmXw8QQmcKg2ppTcRQgZrU,6283
16
16
  modal/_tunnel.pyi,sha256=SA_Q0UGB-D9skFjv6CqlTnCEWU67a2xJxfwVdXtar3Y,1259
17
17
  modal/_watcher.py,sha256=STlDe73R7IS33a_GMW2HnDc3hCDKLdsBfMxRpVh-flA,3581
18
- modal/app.py,sha256=QEBK8qYSrux36oi3iS3msBQmcUOS1r4s2nengzzynjQ,44658
19
- modal/app.pyi,sha256=wHwBIDqkUb2CQzYVhxZafJ8xZ17TZ-8y-cRyOeZsEm0,25182
18
+ modal/app.py,sha256=ZQux8ZGLblIWbKHn7s15mucx97EwbjJso9WKRTYYOf0,45208
19
+ modal/app.pyi,sha256=sX2BXX_178lp8O_GvwZqsxDdxQi1j3DjNfthMvlMlJU,25273
20
20
  modal/call_graph.py,sha256=l-Wi6vM8aosCdHTWegcCyGeVJGFdZ_fzlCmbRVPBXFI,2593
21
21
  modal/client.py,sha256=4SpWb4n0nolITR36kADZl1tYLOg6avukmzZU56UQjCo,16385
22
- modal/client.pyi,sha256=G8PEZZNj243tqHX-HnfOm5hyUADo34xQEiOLCpPVVxU,7372
22
+ modal/client.pyi,sha256=N5m93MVzaU_kPJTpeQL6E_PaSXqWVEKqpK1a-cSbrd8,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
25
  modal/cls.py,sha256=apKnBOHKYEpBiMC8mRvHtCDJl1g0vP0tG1r8mUZ1yH0,24684
@@ -57,7 +57,7 @@ modal/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
57
  modal/queue.py,sha256=qCBE-V2QRl_taSDUR5bDJL91J0A8xwbTxfEU3taA4Es,18338
58
58
  modal/queue.pyi,sha256=t5yhDqXRtexy7AVraCexPyT6Xo3QA_H5OxVe_JLzTps,9999
59
59
  modal/retries.py,sha256=z4dYXdksUcjkefM3vGLkhCQ_m_TUPLJgC4uSYDzWSOU,3750
60
- modal/runner.py,sha256=ZHHuPQ130pZzHdm8vOVQx6db4FiEg3SheDNyShVn9Jg,23805
60
+ modal/runner.py,sha256=oCyAB0cPqHi9fzkbmnccwhCpUYFXMiy0_zjomMnCzDI,24055
61
61
  modal/runner.pyi,sha256=b2qoID4HO-ww6Q0jdboR9iCTxVWTzGiC2taIx7kA-U0,5135
62
62
  modal/running_app.py,sha256=AhWWCB16_k5R80oQxEVSNrmRq3fVInUCxtXKrAvcEPQ,566
63
63
  modal/sandbox.py,sha256=_7_sqTrEiC2zFo1XN7UCHA1L9NFXj6Kb6xu6Ecfancg,24878
@@ -108,12 +108,12 @@ modal/cli/container.py,sha256=LGrF9iz8D3PGst6IUl0VB1Y1LJ0BWLrNRNFxWa4z-tg,3199
108
108
  modal/cli/dict.py,sha256=lIEl6uxygFt3omC-oF6tHUxnFjVhy4d0InC_kZrlkvM,4454
109
109
  modal/cli/entry_point.py,sha256=aaNxFAqZcmtSjwzkYIA_Ba9CkL4cL4_i2gy5VjoXxkM,4228
110
110
  modal/cli/environment.py,sha256=eq8Rixbo8u-nJPvtGwW4-I1lXZPnevsFEv65WlSxFXY,4362
111
- modal/cli/import_refs.py,sha256=0sYZLcgcnor_CECq-7yX3WBs1W55nz5y65sbysxxKzY,9267
112
- modal/cli/launch.py,sha256=aY1fXxZyGn1Ih0lAzuAvzpXP6_OxvVCoZCgCIyV9Vos,2692
111
+ modal/cli/import_refs.py,sha256=wVJFeKeQinNk7NAm3ixVEal-7x2Nf-Ryv8IO6kpjaZ8,9242
112
+ modal/cli/launch.py,sha256=FgZ0L-e3dLl9vRJ_IVHfSRUzCbmdyS8-u_abC42tTDo,2941
113
113
  modal/cli/network_file_system.py,sha256=p_o3wu8rh2tjHXJYrjaad__pD8hv93ypeDtfSY2fSEU,7527
114
114
  modal/cli/profile.py,sha256=s4jCYHwriOorEFCKxeGZoSWX8rXTR_hDTNFZhOA565s,3109
115
115
  modal/cli/queues.py,sha256=mJ44A319sPIrysH3A0HCIz4Or0jFey6miiuQKZoEQxo,4493
116
- modal/cli/run.py,sha256=xwO3NvXRDvoQ9v_9NfRYvMdXW2Ag7W-mkOfnwNWVCoI,16288
116
+ modal/cli/run.py,sha256=cQKxmvwh6gcp53-BA7LtD8TDKb7gSducN73K0IDS4ok,16296
117
117
  modal/cli/secret.py,sha256=GWz425Fhdftb2hDljQzO2NS1NY5ogg298Uu-e0JAQWs,4211
118
118
  modal/cli/token.py,sha256=mxSgOWakXG6N71hQb1ko61XAR9ZGkTMZD-Txn7gmTac,1924
119
119
  modal/cli/utils.py,sha256=59-cqBHSg00oFMRHtRbFZZnoIJfW6w9Gfno63XfNpt4,3633
@@ -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=K3KTopYH17C5MMGzy3fUGtpOXRNNR7V0OY19WIBopyw,149
163
- modal-0.66.44.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
164
- modal-0.66.44.dist-info/METADATA,sha256=YzAinNyCi4yGXbE_WKgrgchbMkTiWxI0ttvOlgIDkYE,2329
165
- modal-0.66.44.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
166
- modal-0.66.44.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
167
- modal-0.66.44.dist-info/top_level.txt,sha256=1nvYbOSIKcmU50fNrpnQnrrOpj269ei3LzgB6j9xGqg,64
168
- modal-0.66.44.dist-info/RECORD,,
162
+ modal_version/_version_generated.py,sha256=N8mpIXKbt7EuKIx6aQ9-LmDKBDCvLeqLUivzvv2sN_M,149
163
+ modal-0.66.46.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
164
+ modal-0.66.46.dist-info/METADATA,sha256=1WlWKqR60QmEs2zSJO-ued9eR3bd-9-kDL_2oHrZVPw,2329
165
+ modal-0.66.46.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
166
+ modal-0.66.46.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
167
+ modal-0.66.46.dist-info/top_level.txt,sha256=1nvYbOSIKcmU50fNrpnQnrrOpj269ei3LzgB6j9xGqg,64
168
+ modal-0.66.46.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  # Copyright Modal Labs 2024
2
2
 
3
3
  # Note: Reset this value to -1 whenever you make a minor `0.X` release of the client.
4
- build_number = 44 # git: 10a2cbc
4
+ build_number = 46 # git: 406b5f3