modal 1.1.2.dev11__py3-none-any.whl → 1.1.2.dev13__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/secret.py CHANGED
@@ -6,6 +6,7 @@ from typing import Optional, Union
6
6
 
7
7
  from google.protobuf.message import Message
8
8
  from grpclib import GRPCError, Status
9
+ from synchronicity import classproperty
9
10
 
10
11
  from modal_proto import api_pb2
11
12
 
@@ -16,7 +17,7 @@ from ._utils.async_utils import synchronize_api
16
17
  from ._utils.deprecation import deprecation_warning, warn_if_passing_namespace
17
18
  from ._utils.grpc_utils import retry_transient_errors
18
19
  from ._utils.name_utils import check_object_name
19
- from ._utils.time_utils import timestamp_to_localized_dt
20
+ from ._utils.time_utils import as_timestamp, timestamp_to_localized_dt
20
21
  from .client import _Client
21
22
  from .exception import InvalidError, NotFoundError
22
23
 
@@ -35,6 +36,68 @@ class SecretInfo:
35
36
  created_by: Optional[str]
36
37
 
37
38
 
39
+ class _SecretManager:
40
+ """Namespace with methods for managing named Secret objects."""
41
+
42
+ @staticmethod
43
+ async def list(
44
+ *,
45
+ max_objects: Optional[int] = None, # Limit requests to this size
46
+ created_before: Optional[Union[datetime, str]] = None, # Limit based on creation date
47
+ environment_name: str = "", # Uses active environment if not specified
48
+ client: Optional[_Client] = None, # Optional client with Modal credentials
49
+ ) -> list["_Secret"]:
50
+ """Return a list of hydrated Secret objects.
51
+
52
+ **Examples:**
53
+
54
+ ```python
55
+ secrets = modal.Secret.objects.list()
56
+ print([s.name for s in secrets])
57
+ ```
58
+
59
+ Secrets will be retreived from the active environment, or another one can be specified:
60
+
61
+ ```python notest
62
+ dev_secrets = modal.Secret.objects.list(environment_name="dev")
63
+ ```
64
+
65
+ By default, all named Secrets are returned, newest to oldest. It's also possible to limit the
66
+ number of results and to filter by creation date:
67
+
68
+ ```python
69
+ secrets = modal.Secret.objects.list(max_objects=10, created_before="2025-01-01")
70
+ ```
71
+
72
+ """
73
+ client = await _Client.from_env() if client is None else client
74
+ if max_objects is not None and max_objects < 0:
75
+ raise InvalidError("max_objects cannot be negative")
76
+
77
+ items: list[api_pb2.SecretListItem] = []
78
+
79
+ async def retrieve_page(created_before: float) -> bool:
80
+ max_page_size = 100 if max_objects is None else min(100, max_objects - len(items))
81
+ pagination = api_pb2.ListPagination(max_objects=max_page_size, created_before=created_before)
82
+ req = api_pb2.SecretListRequest(environment_name=environment_name, pagination=pagination)
83
+ resp = await retry_transient_errors(client.stub.SecretList, req)
84
+ items.extend(resp.items)
85
+ finished = (len(resp.items) < max_page_size) or (max_objects is not None and len(items) >= max_objects)
86
+ return finished
87
+
88
+ finished = await retrieve_page(as_timestamp(created_before))
89
+ while True:
90
+ if finished:
91
+ break
92
+ finished = await retrieve_page(items[-1].metadata.creation_info.created_at)
93
+
94
+ secrets = [_Secret._new_hydrated(item.secret_id, client, item.metadata, is_another_app=True) for item in items]
95
+ return secrets[:max_objects] if max_objects is not None else secrets
96
+
97
+
98
+ SecretManager = synchronize_api(_SecretManager)
99
+
100
+
38
101
  class _Secret(_Object, type_prefix="st"):
39
102
  """Secrets provide a dictionary of environment variables for images.
40
103
 
@@ -47,6 +110,10 @@ class _Secret(_Object, type_prefix="st"):
47
110
 
48
111
  _metadata: Optional[api_pb2.SecretMetadata] = None
49
112
 
113
+ @classproperty
114
+ def objects(cls) -> _SecretManager:
115
+ return _SecretManager
116
+
50
117
  @property
51
118
  def name(self) -> Optional[str]:
52
119
  return self._name
modal/secret.pyi CHANGED
@@ -4,6 +4,7 @@ import modal._object
4
4
  import modal.client
5
5
  import modal.object
6
6
  import modal_proto.api_pb2
7
+ import synchronicity
7
8
  import typing
8
9
  import typing_extensions
9
10
 
@@ -28,6 +29,115 @@ class SecretInfo:
28
29
  """Return self==value."""
29
30
  ...
30
31
 
32
+ class _SecretManager:
33
+ """Namespace with methods for managing named Secret objects."""
34
+ @staticmethod
35
+ async def list(
36
+ *,
37
+ max_objects: typing.Optional[int] = None,
38
+ created_before: typing.Union[datetime.datetime, str, None] = None,
39
+ environment_name: str = "",
40
+ client: typing.Optional[modal.client._Client] = None,
41
+ ) -> list[_Secret]:
42
+ """Return a list of hydrated Secret objects.
43
+
44
+ **Examples:**
45
+
46
+ ```python
47
+ secrets = modal.Secret.objects.list()
48
+ print([s.name for s in secrets])
49
+ ```
50
+
51
+ Secrets will be retreived from the active environment, or another one can be specified:
52
+
53
+ ```python notest
54
+ dev_secrets = modal.Secret.objects.list(environment_name="dev")
55
+ ```
56
+
57
+ By default, all named Secrets are returned, newest to oldest. It's also possible to limit the
58
+ number of results and to filter by creation date:
59
+
60
+ ```python
61
+ secrets = modal.Secret.objects.list(max_objects=10, created_before="2025-01-01")
62
+ ```
63
+ """
64
+ ...
65
+
66
+ class SecretManager:
67
+ """Namespace with methods for managing named Secret objects."""
68
+ def __init__(self, /, *args, **kwargs):
69
+ """Initialize self. See help(type(self)) for accurate signature."""
70
+ ...
71
+
72
+ class __list_spec(typing_extensions.Protocol):
73
+ def __call__(
74
+ self,
75
+ /,
76
+ *,
77
+ max_objects: typing.Optional[int] = None,
78
+ created_before: typing.Union[datetime.datetime, str, None] = None,
79
+ environment_name: str = "",
80
+ client: typing.Optional[modal.client.Client] = None,
81
+ ) -> list[Secret]:
82
+ """Return a list of hydrated Secret objects.
83
+
84
+ **Examples:**
85
+
86
+ ```python
87
+ secrets = modal.Secret.objects.list()
88
+ print([s.name for s in secrets])
89
+ ```
90
+
91
+ Secrets will be retreived from the active environment, or another one can be specified:
92
+
93
+ ```python notest
94
+ dev_secrets = modal.Secret.objects.list(environment_name="dev")
95
+ ```
96
+
97
+ By default, all named Secrets are returned, newest to oldest. It's also possible to limit the
98
+ number of results and to filter by creation date:
99
+
100
+ ```python
101
+ secrets = modal.Secret.objects.list(max_objects=10, created_before="2025-01-01")
102
+ ```
103
+ """
104
+ ...
105
+
106
+ async def aio(
107
+ self,
108
+ /,
109
+ *,
110
+ max_objects: typing.Optional[int] = None,
111
+ created_before: typing.Union[datetime.datetime, str, None] = None,
112
+ environment_name: str = "",
113
+ client: typing.Optional[modal.client.Client] = None,
114
+ ) -> list[Secret]:
115
+ """Return a list of hydrated Secret objects.
116
+
117
+ **Examples:**
118
+
119
+ ```python
120
+ secrets = modal.Secret.objects.list()
121
+ print([s.name for s in secrets])
122
+ ```
123
+
124
+ Secrets will be retreived from the active environment, or another one can be specified:
125
+
126
+ ```python notest
127
+ dev_secrets = modal.Secret.objects.list(environment_name="dev")
128
+ ```
129
+
130
+ By default, all named Secrets are returned, newest to oldest. It's also possible to limit the
131
+ number of results and to filter by creation date:
132
+
133
+ ```python
134
+ secrets = modal.Secret.objects.list(max_objects=10, created_before="2025-01-01")
135
+ ```
136
+ """
137
+ ...
138
+
139
+ list: __list_spec
140
+
31
141
  class _Secret(modal._object._Object):
32
142
  """Secrets provide a dictionary of environment variables for images.
33
143
 
@@ -40,6 +150,8 @@ class _Secret(modal._object._Object):
40
150
 
41
151
  _metadata: typing.Optional[modal_proto.api_pb2.SecretMetadata]
42
152
 
153
+ @synchronicity.classproperty
154
+ def objects(cls) -> _SecretManager: ...
43
155
  @property
44
156
  def name(self) -> typing.Optional[str]: ...
45
157
  def _hydrate_metadata(self, metadata: typing.Optional[google.protobuf.message.Message]): ...
@@ -157,6 +269,8 @@ class Secret(modal.object.Object):
157
269
  """mdmd:hidden"""
158
270
  ...
159
271
 
272
+ @synchronicity.classproperty
273
+ def objects(cls) -> SecretManager: ...
160
274
  @property
161
275
  def name(self) -> typing.Optional[str]: ...
162
276
  def _hydrate_metadata(self, metadata: typing.Optional[google.protobuf.message.Message]): ...
modal/volume.py CHANGED
@@ -25,6 +25,7 @@ from typing import (
25
25
 
26
26
  from google.protobuf.message import Message
27
27
  from grpclib import GRPCError, Status
28
+ from synchronicity import classproperty
28
29
  from synchronicity.async_wrap import asynccontextmanager
29
30
 
30
31
  import modal.exception
@@ -32,7 +33,13 @@ import modal_proto.api_pb2
32
33
  from modal.exception import InvalidError, VolumeUploadTimeoutError
33
34
  from modal_proto import api_pb2
34
35
 
35
- from ._object import EPHEMERAL_OBJECT_HEARTBEAT_SLEEP, _get_environment_name, _Object, live_method, live_method_gen
36
+ from ._object import (
37
+ EPHEMERAL_OBJECT_HEARTBEAT_SLEEP,
38
+ _get_environment_name,
39
+ _Object,
40
+ live_method,
41
+ live_method_gen,
42
+ )
36
43
  from ._resolver import Resolver
37
44
  from ._utils.async_utils import (
38
45
  TaskContext,
@@ -55,7 +62,7 @@ from ._utils.deprecation import deprecation_warning, warn_if_passing_namespace
55
62
  from ._utils.grpc_utils import retry_transient_errors
56
63
  from ._utils.http_utils import ClientSessionRegistry
57
64
  from ._utils.name_utils import check_object_name
58
- from ._utils.time_utils import timestamp_to_localized_dt
65
+ from ._utils.time_utils import as_timestamp, timestamp_to_localized_dt
59
66
  from .client import _Client
60
67
  from .config import logger
61
68
 
@@ -106,6 +113,68 @@ class VolumeInfo:
106
113
  created_by: Optional[str]
107
114
 
108
115
 
116
+ class _VolumeManager:
117
+ """Namespace with methods for managing named Volume objects."""
118
+
119
+ @staticmethod
120
+ async def list(
121
+ *,
122
+ max_objects: Optional[int] = None, # Limit requests to this size
123
+ created_before: Optional[Union[datetime, str]] = None, # Limit based on creation date
124
+ environment_name: str = "", # Uses active environment if not specified
125
+ client: Optional[_Client] = None, # Optional client with Modal credentials
126
+ ) -> list["_Volume"]:
127
+ """Return a list of hydrated Volume objects.
128
+
129
+ **Examples:**
130
+
131
+ ```python
132
+ volumes = modal.Volume.objects.list()
133
+ print([v.name for v in volumes])
134
+ ```
135
+
136
+ Volumes will be retreived from the active environment, or another one can be specified:
137
+
138
+ ```python notest
139
+ dev_volumes = modal.Volume.objects.list(environment_name="dev")
140
+ ```
141
+
142
+ By default, all named Volumes are returned, newest to oldest. It's also possible to limit the
143
+ number of results and to filter by creation date:
144
+
145
+ ```python
146
+ volumes = modal.Volume.objects.list(max_objects=10, created_before="2025-01-01")
147
+ ```
148
+
149
+ """
150
+ client = await _Client.from_env() if client is None else client
151
+ if max_objects is not None and max_objects < 0:
152
+ raise InvalidError("max_objects cannot be negative")
153
+
154
+ items: list[api_pb2.VolumeListItem] = []
155
+
156
+ async def retrieve_page(created_before: float) -> bool:
157
+ max_page_size = 100 if max_objects is None else min(100, max_objects - len(items))
158
+ pagination = api_pb2.ListPagination(max_objects=max_page_size, created_before=created_before)
159
+ req = api_pb2.VolumeListRequest(environment_name=environment_name, pagination=pagination)
160
+ resp = await retry_transient_errors(client.stub.VolumeList, req)
161
+ items.extend(resp.items)
162
+ finished = (len(resp.items) < max_page_size) or (max_objects is not None and len(items) >= max_objects)
163
+ return finished
164
+
165
+ finished = await retrieve_page(as_timestamp(created_before))
166
+ while True:
167
+ if finished:
168
+ break
169
+ finished = await retrieve_page(items[-1].metadata.creation_info.created_at)
170
+
171
+ volumes = [_Volume._new_hydrated(item.volume_id, client, item.metadata, is_another_app=True) for item in items]
172
+ return volumes[:max_objects] if max_objects is not None else volumes
173
+
174
+
175
+ VolumeManager = synchronize_api(_VolumeManager)
176
+
177
+
109
178
  class _Volume(_Object, type_prefix="vo"):
110
179
  """A writeable volume that can be used to share files between one or more Modal functions.
111
180
 
@@ -152,6 +221,14 @@ class _Volume(_Object, type_prefix="vo"):
152
221
  _metadata: "typing.Optional[api_pb2.VolumeMetadata]"
153
222
  _read_only: bool = False
154
223
 
224
+ @classproperty
225
+ def objects(cls) -> _VolumeManager:
226
+ return _VolumeManager
227
+
228
+ @property
229
+ def name(self) -> Optional[str]:
230
+ return self._name
231
+
155
232
  def read_only(self) -> "_Volume":
156
233
  """Configure Volume to mount as read-only.
157
234
 
@@ -181,10 +258,6 @@ class _Volume(_Object, type_prefix="vo"):
181
258
  obj = _Volume._from_loader(_load, "Volume()", hydrate_lazily=True, deps=lambda: [self])
182
259
  return obj
183
260
 
184
- @property
185
- def name(self) -> Optional[str]:
186
- return self._name
187
-
188
261
  def _hydrate_metadata(self, metadata: Optional[Message]):
189
262
  if metadata:
190
263
  assert isinstance(metadata, api_pb2.VolumeMetadata)
modal/volume.pyi CHANGED
@@ -10,6 +10,7 @@ import modal.client
10
10
  import modal.object
11
11
  import modal_proto.api_pb2
12
12
  import pathlib
13
+ import synchronicity
13
14
  import synchronicity.combined_types
14
15
  import typing
15
16
  import typing_extensions
@@ -79,6 +80,115 @@ class VolumeInfo:
79
80
  """Return self==value."""
80
81
  ...
81
82
 
83
+ class _VolumeManager:
84
+ """Namespace with methods for managing named Volume objects."""
85
+ @staticmethod
86
+ async def list(
87
+ *,
88
+ max_objects: typing.Optional[int] = None,
89
+ created_before: typing.Union[datetime.datetime, str, None] = None,
90
+ environment_name: str = "",
91
+ client: typing.Optional[modal.client._Client] = None,
92
+ ) -> list[_Volume]:
93
+ """Return a list of hydrated Volume objects.
94
+
95
+ **Examples:**
96
+
97
+ ```python
98
+ volumes = modal.Volume.objects.list()
99
+ print([v.name for v in volumes])
100
+ ```
101
+
102
+ Volumes will be retreived from the active environment, or another one can be specified:
103
+
104
+ ```python notest
105
+ dev_volumes = modal.Volume.objects.list(environment_name="dev")
106
+ ```
107
+
108
+ By default, all named Volumes are returned, newest to oldest. It's also possible to limit the
109
+ number of results and to filter by creation date:
110
+
111
+ ```python
112
+ volumes = modal.Volume.objects.list(max_objects=10, created_before="2025-01-01")
113
+ ```
114
+ """
115
+ ...
116
+
117
+ class VolumeManager:
118
+ """Namespace with methods for managing named Volume objects."""
119
+ def __init__(self, /, *args, **kwargs):
120
+ """Initialize self. See help(type(self)) for accurate signature."""
121
+ ...
122
+
123
+ class __list_spec(typing_extensions.Protocol):
124
+ def __call__(
125
+ self,
126
+ /,
127
+ *,
128
+ max_objects: typing.Optional[int] = None,
129
+ created_before: typing.Union[datetime.datetime, str, None] = None,
130
+ environment_name: str = "",
131
+ client: typing.Optional[modal.client.Client] = None,
132
+ ) -> list[Volume]:
133
+ """Return a list of hydrated Volume objects.
134
+
135
+ **Examples:**
136
+
137
+ ```python
138
+ volumes = modal.Volume.objects.list()
139
+ print([v.name for v in volumes])
140
+ ```
141
+
142
+ Volumes will be retreived from the active environment, or another one can be specified:
143
+
144
+ ```python notest
145
+ dev_volumes = modal.Volume.objects.list(environment_name="dev")
146
+ ```
147
+
148
+ By default, all named Volumes are returned, newest to oldest. It's also possible to limit the
149
+ number of results and to filter by creation date:
150
+
151
+ ```python
152
+ volumes = modal.Volume.objects.list(max_objects=10, created_before="2025-01-01")
153
+ ```
154
+ """
155
+ ...
156
+
157
+ async def aio(
158
+ self,
159
+ /,
160
+ *,
161
+ max_objects: typing.Optional[int] = None,
162
+ created_before: typing.Union[datetime.datetime, str, None] = None,
163
+ environment_name: str = "",
164
+ client: typing.Optional[modal.client.Client] = None,
165
+ ) -> list[Volume]:
166
+ """Return a list of hydrated Volume objects.
167
+
168
+ **Examples:**
169
+
170
+ ```python
171
+ volumes = modal.Volume.objects.list()
172
+ print([v.name for v in volumes])
173
+ ```
174
+
175
+ Volumes will be retreived from the active environment, or another one can be specified:
176
+
177
+ ```python notest
178
+ dev_volumes = modal.Volume.objects.list(environment_name="dev")
179
+ ```
180
+
181
+ By default, all named Volumes are returned, newest to oldest. It's also possible to limit the
182
+ number of results and to filter by creation date:
183
+
184
+ ```python
185
+ volumes = modal.Volume.objects.list(max_objects=10, created_before="2025-01-01")
186
+ ```
187
+ """
188
+ ...
189
+
190
+ list: __list_spec
191
+
82
192
  class _Volume(modal._object._Object):
83
193
  """A writeable volume that can be used to share files between one or more Modal functions.
84
194
 
@@ -125,6 +235,10 @@ class _Volume(modal._object._Object):
125
235
  _metadata: typing.Optional[modal_proto.api_pb2.VolumeMetadata]
126
236
  _read_only: bool
127
237
 
238
+ @synchronicity.classproperty
239
+ def objects(cls) -> _VolumeManager: ...
240
+ @property
241
+ def name(self) -> typing.Optional[str]: ...
128
242
  def read_only(self) -> _Volume:
129
243
  """Configure Volume to mount as read-only.
130
244
 
@@ -148,8 +262,6 @@ class _Volume(modal._object._Object):
148
262
  """
149
263
  ...
150
264
 
151
- @property
152
- def name(self) -> typing.Optional[str]: ...
153
265
  def _hydrate_metadata(self, metadata: typing.Optional[google.protobuf.message.Message]): ...
154
266
  def _get_metadata(self) -> typing.Optional[google.protobuf.message.Message]: ...
155
267
  async def _get_lock(self): ...
@@ -429,6 +541,10 @@ class Volume(modal.object.Object):
429
541
  """mdmd:hidden"""
430
542
  ...
431
543
 
544
+ @synchronicity.classproperty
545
+ def objects(cls) -> VolumeManager: ...
546
+ @property
547
+ def name(self) -> typing.Optional[str]: ...
432
548
  def read_only(self) -> Volume:
433
549
  """Configure Volume to mount as read-only.
434
550
 
@@ -452,8 +568,6 @@ class Volume(modal.object.Object):
452
568
  """
453
569
  ...
454
570
 
455
- @property
456
- def name(self) -> typing.Optional[str]: ...
457
571
  def _hydrate_metadata(self, metadata: typing.Optional[google.protobuf.message.Message]): ...
458
572
  def _get_metadata(self) -> typing.Optional[google.protobuf.message.Message]: ...
459
573
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.1.2.dev11
3
+ Version: 1.1.2.dev13
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -22,7 +22,7 @@ Requires-Dist: click~=8.1
22
22
  Requires-Dist: grpclib<0.4.9,>=0.4.7
23
23
  Requires-Dist: protobuf!=4.24.0,<7.0,>=3.19
24
24
  Requires-Dist: rich>=12.0.0
25
- Requires-Dist: synchronicity~=0.10.1
25
+ Requires-Dist: synchronicity~=0.10.2
26
26
  Requires-Dist: toml
27
27
  Requires-Dist: typer>=0.9
28
28
  Requires-Dist: types-certifi