modal 0.73.137__py3-none-any.whl → 0.73.139__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/client.pyi CHANGED
@@ -31,7 +31,7 @@ class _Client:
31
31
  server_url: str,
32
32
  client_type: int,
33
33
  credentials: typing.Optional[tuple[str, str]],
34
- version: str = "0.73.137",
34
+ version: str = "0.73.139",
35
35
  ): ...
36
36
  def is_closed(self) -> bool: ...
37
37
  @property
@@ -93,7 +93,7 @@ class Client:
93
93
  server_url: str,
94
94
  client_type: int,
95
95
  credentials: typing.Optional[tuple[str, str]],
96
- version: str = "0.73.137",
96
+ version: str = "0.73.139",
97
97
  ): ...
98
98
  def is_closed(self) -> bool: ...
99
99
  @property
@@ -1,17 +1,17 @@
1
- # Copyright Modal Labs 2022
1
+ # Copyright Modal Labs 2025
2
2
  from dataclasses import dataclass
3
3
  from typing import Any, Callable, Optional
4
4
 
5
5
  from modal_proto import api_pb2
6
6
 
7
- from ._clustered_functions import ClusterInfo, get_cluster_info as _get_cluster_info
8
- from ._functions import _Function
9
- from ._object import _get_environment_name
10
- from ._partial_function import _PartialFunction, _PartialFunctionFlags
11
- from ._runtime.container_io_manager import _ContainerIOManager
12
- from ._utils.async_utils import synchronizer
13
- from .client import _Client
14
- from .exception import InvalidError
7
+ from .._clustered_functions import ClusterInfo, get_cluster_info as _get_cluster_info
8
+ from .._functions import _Function
9
+ from .._object import _get_environment_name
10
+ from .._partial_function import _PartialFunction, _PartialFunctionFlags
11
+ from .._runtime.container_io_manager import _ContainerIOManager
12
+ from .._utils.async_utils import synchronizer
13
+ from ..client import _Client
14
+ from ..exception import InvalidError
15
15
 
16
16
 
17
17
  def stop_fetching_inputs():
@@ -0,0 +1,80 @@
1
+ # Copyright Modal Labs 2025
2
+
3
+ """This module provides Jupyter/IPython extensions for Modal.
4
+
5
+ Use in a notebook with `%load_ext modal.experimental.ipython`.
6
+ """
7
+
8
+ from IPython.core.magic import Magics, line_magic, magics_class
9
+
10
+ from ..cls import Cls
11
+ from ..exception import NotFoundError
12
+ from ..functions import Function
13
+
14
+
15
+ @magics_class
16
+ class ModalMagics(Magics):
17
+ @line_magic
18
+ def modal(self, line):
19
+ """Lookup a deployed Modal Function or Class.
20
+
21
+ **Example:**
22
+
23
+ ```python notest
24
+ %modal from main/my-app import my_function, MyClass as Foo
25
+
26
+ # Now you can call my_function() and Foo from your notebook.
27
+ my_function.remote()
28
+ Foo().my_method.remote()
29
+ ```
30
+ """
31
+ line = line.strip()
32
+ if not line.startswith("from "):
33
+ print("Invalid syntax. Use: %modal from <env>/<app> import <function|Class>[, <function|Class> [as alias]]")
34
+ return
35
+
36
+ # Remove the initial "from "
37
+ line_without_from = line[5:]
38
+ env_app_part, sep, import_part = line_without_from.partition(" import ")
39
+ if not sep:
40
+ print("Invalid syntax. Missing 'import' keyword.")
41
+ return
42
+
43
+ # Parse environment and app from "env/app"
44
+ if "/" not in env_app_part:
45
+ print("Invalid app specification. Expected format: <env>/<app>")
46
+ return
47
+ environment, app = env_app_part.split("/", 1)
48
+
49
+ # Parse the import items (multiple imports separated by commas)
50
+ import_items = [item.strip() for item in import_part.split(",")]
51
+ for item in import_items:
52
+ if not item:
53
+ continue
54
+ parts = item.split()
55
+ # Expect either "Model" or "Model as alias"
56
+ if len(parts) == 0:
57
+ continue
58
+ model_name = parts[0]
59
+ alias = model_name
60
+ if len(parts) == 3 and parts[1] == "as":
61
+ alias = parts[2]
62
+ elif len(parts) > 1:
63
+ print(f"Invalid syntax in import item: {item!r}. Expected format: <function|Class> [as alias]")
64
+ return
65
+
66
+ # Try to load using Function; if not found, fallback to Cls
67
+ try:
68
+ obj: Function | Cls = Function.from_name(app, model_name, environment_name=environment)
69
+ obj.hydrate()
70
+ except NotFoundError:
71
+ obj = Cls.from_name(app, model_name, environment_name=environment)
72
+ obj.hydrate()
73
+
74
+ # Set the loaded object in the notebook namespace
75
+ self.shell.user_ns[alias] = obj # type: ignore
76
+ print(f"Loaded {alias!r} from environment {environment!r} and app {app!r}.")
77
+
78
+
79
+ def load_ipython_extension(ipython):
80
+ ipython.register_magics(ModalMagics)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: modal
3
- Version: 0.73.137
3
+ Version: 0.73.139
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -23,7 +23,7 @@ modal/app.py,sha256=NKH7Cw1M6eyyrMXFbhWfdo3uRd28-8kv0Pcw56kPiPU,47312
23
23
  modal/app.pyi,sha256=pUEqciyGZ446sc_QoG8XcQ_oc6oU-U4dqjkxjhgOX98,26968
24
24
  modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
25
25
  modal/client.py,sha256=j9D3hNis1lfhnz9lVFGgJgowbH3PaGUzNKgHPWYG778,15372
26
- modal/client.pyi,sha256=1ZQ2rcA03XgJu14y_BblgYqApkRWfIJ4fyxhKevqEE0,7661
26
+ modal/client.pyi,sha256=TG0L4EXdgzkaOWg71l4k5r1jG8KPqFV0QpTitHcsqug,7661
27
27
  modal/cloud_bucket_mount.py,sha256=YOe9nnvSr4ZbeCn587d7_VhE9IioZYRvF9VYQTQux08,5914
28
28
  modal/cloud_bucket_mount.pyi,sha256=30T3K1a89l6wzmEJ_J9iWv9SknoGqaZDx59Xs-ZQcmk,1607
29
29
  modal/cls.py,sha256=PJimWA9q_sbQJNLbYy7fzjZGBm_hdfXuuZ7O_pKLXdk,31586
@@ -36,8 +36,6 @@ modal/dict.pyi,sha256=kKb0Kc6RUabtQ5Hwslg_vwL_OIrwIAJ2NXrJTepTtp4,7684
36
36
  modal/environments.py,sha256=mrOaS9hiIQijGWJYIgVKQnwC-kONhWHm1GqoK_9G75E,6924
37
37
  modal/environments.pyi,sha256=JvSroVOIXDIILL40Z5G4HyY16bmih2YMWMvWL-SFTwo,3373
38
38
  modal/exception.py,sha256=4JyO-SACaLNDe2QC48EjsK8GMkZ8AgEurZ8j1YdRu8E,5263
39
- modal/experimental.py,sha256=e625Ekpo2HtYkk6ZltM_XYcI9xhLxic8_7Na91PbdUg,4017
40
- modal/experimental.pyi,sha256=24tIYu_w9RLwFrz1cIsgYuqmDCtV8eg6-bQNz3zjhDo,939
41
39
  modal/file_io.py,sha256=lcMs_E9Xfm0YX1t9U2wNIBPnqHRxmImqjLW1GHqVmyg,20945
42
40
  modal/file_io.pyi,sha256=NTRft1tbPSWf9TlWVeZmTlgB5AZ_Zhu2srWIrWr7brk,9445
43
41
  modal/file_pattern_matcher.py,sha256=trosX-Bp7dOubudN1bLLhRAoidWy1TcoaR4Pv8CedWw,6497
@@ -139,8 +137,8 @@ modal/cli/volume.py,sha256=c2IuVNO2yJVaXmZkRh3xwQmznlRTgFoJr_BIzzqtVv0,10251
139
137
  modal/cli/programs/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,28
140
138
  modal/cli/programs/run_jupyter.py,sha256=MX6YQ6zRyRk1xo8tYZFiGam0p5KETwax81L6TpaS9I0,2778
141
139
  modal/cli/programs/vscode.py,sha256=kfvhZQ4bJwtVm3MgC1V7AlygZOlKT1a33alr_uwrewA,3473
142
- modal/extensions/__init__.py,sha256=waLjl5c6IPDhSsdWAm9Bji4e2PVxamYABKAze6CHVXY,28
143
- modal/extensions/ipython.py,sha256=Xvzy-A7cvwMSDa9p4c4CEMLOX2_Xsg9DkM1J9uyu7jc,983
140
+ modal/experimental/__init__.py,sha256=6VY98HfgoQU-65ELJ4Re_PB3VBCjDMZywuK1oOvljkg,4025
141
+ modal/experimental/ipython.py,sha256=epLUZeDSdE226TH_tU3igRKCiVuQi99mUOrIJ4SemOE,2792
144
142
  modal/requirements/2023.12.312.txt,sha256=zWWUVgVQ92GXBKNYYr2-5vn9rlnXcmkqlwlX5u1eTYw,400
145
143
  modal/requirements/2023.12.txt,sha256=OjsbXFkCSdkzzryZP82Q73osr5wxQ6EUzmGcK7twfkA,502
146
144
  modal/requirements/2024.04.txt,sha256=6NnrbIE-mflwMyKyQ0tsWeY8XFE1kSW9oE8DVDoD8QU,544
@@ -172,10 +170,10 @@ modal_proto/options_pb2_grpc.pyi,sha256=CImmhxHsYnF09iENPoe8S4J-n93jtgUYD2JPAc0y
172
170
  modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
173
171
  modal_version/__init__.py,sha256=wiJQ53c-OMs0Xf1UeXOxQ7FwlV1VzIjnX6o-pRYZ_Pk,470
174
172
  modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
175
- modal_version/_version_generated.py,sha256=QHBoomBMWDUt_CBZ4xPiJHj48d0DlYKga1FmdAlwlmA,150
176
- modal-0.73.137.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
177
- modal-0.73.137.dist-info/METADATA,sha256=u53-kr6xBoh7hkSjCViAn4LrIhwD5mOz-krXfGpVpmw,2453
178
- modal-0.73.137.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
179
- modal-0.73.137.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
180
- modal-0.73.137.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
181
- modal-0.73.137.dist-info/RECORD,,
173
+ modal_version/_version_generated.py,sha256=BkJksRs5yv4FbMkmb2um70eKKARGICQ_YaprEcfYt-8,150
174
+ modal-0.73.139.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
175
+ modal-0.73.139.dist-info/METADATA,sha256=7tqvXIUSis4JTajabvNaHK6X96IWdkeUrBYRREaKTHM,2453
176
+ modal-0.73.139.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
177
+ modal-0.73.139.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
178
+ modal-0.73.139.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
179
+ modal-0.73.139.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  # Copyright Modal Labs 2025
2
2
 
3
3
  # Note: Reset this value to -1 whenever you make a minor `0.X` release of the client.
4
- build_number = 137 # git: 83f489c
4
+ build_number = 139 # git: 69e1f51
modal/experimental.pyi DELETED
@@ -1,29 +0,0 @@
1
- import modal._clustered_functions
2
- import modal.client
3
- import typing
4
- import typing_extensions
5
-
6
- def stop_fetching_inputs(): ...
7
- def get_local_input_concurrency(): ...
8
- def set_local_input_concurrency(concurrency: int): ...
9
- def clustered(size: int, broadcast: bool = True): ...
10
- def get_cluster_info() -> modal._clustered_functions.ClusterInfo: ...
11
-
12
- class AppInfo:
13
- app_id: str
14
- name: str
15
- containers: int
16
-
17
- def __init__(self, app_id: str, name: str, containers: int) -> None: ...
18
- def __repr__(self): ...
19
- def __eq__(self, other): ...
20
-
21
- class __list_deployed_apps_spec(typing_extensions.Protocol):
22
- def __call__(
23
- self, environment_name: str = "", client: typing.Optional[modal.client.Client] = None
24
- ) -> list[AppInfo]: ...
25
- async def aio(
26
- self, environment_name: str = "", client: typing.Optional[modal.client.Client] = None
27
- ) -> list[AppInfo]: ...
28
-
29
- list_deployed_apps: __list_deployed_apps_spec
@@ -1 +0,0 @@
1
- # Copyright Modal Labs 2022
@@ -1,41 +0,0 @@
1
- # Copyright Modal Labs 2022
2
- import atexit
3
- import logging
4
- import sys
5
- from typing import Any
6
-
7
- from modal import App
8
- from modal._utils.async_utils import run_coro_blocking
9
- from modal.config import config, logger
10
-
11
- app_ctx: Any
12
-
13
-
14
- def load_ipython_extension(ipython):
15
- global app_ctx
16
-
17
- # Set logger for notebook sys.stdout
18
- logger.addHandler(logging.StreamHandler(stream=sys.stdout))
19
- logger.setLevel(config["loglevel"])
20
-
21
- # Create an app and provide it in the IPython app
22
- app = App()
23
- ipython.push({"app": app})
24
-
25
- app_ctx = app.run()
26
-
27
- # Notebooks have an event loop present, but we want this function
28
- # to be blocking. This is fairly hacky.
29
- run_coro_blocking(app_ctx.__aenter__())
30
-
31
- def exit_app():
32
- print("Exiting modal app")
33
- run_coro_blocking(app_ctx.__aexit__(None, None, None))
34
-
35
- atexit.register(exit_app)
36
-
37
-
38
- def unload_ipython_extension(ipython):
39
- global app_ctx
40
-
41
- run_coro_blocking(app_ctx.__aexit__(None, None, None))