modal 1.1.5.dev77__py3-none-any.whl → 1.1.5.dev78__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.

Potentially problematic release.


This version of modal might be problematic. Click here for more details.

@@ -1,5 +1,6 @@
1
1
  # Copyright Modal Labs 2022
2
2
  import re
3
+ from collections.abc import Mapping
3
4
 
4
5
  from ..exception import InvalidError
5
6
 
@@ -37,7 +38,7 @@ def is_valid_tag(tag: str, max_length: int = 50) -> bool:
37
38
  return bool(re.match(pattern, tag))
38
39
 
39
40
 
40
- def check_tag_dict(tags: dict[str, str]) -> dict[str, str]:
41
+ def check_tag_dict(tags: Mapping[str, str]) -> None:
41
42
  rules = (
42
43
  "\n\nTags may contain only alphanumeric characters, dashes, periods, or underscores, "
43
44
  "and must be 63 characters or less."
@@ -49,8 +50,6 @@ def check_tag_dict(tags: dict[str, str]) -> dict[str, str]:
49
50
  if not is_valid_tag(value, max_length):
50
51
  raise InvalidError(f"Invalid tag value: {value!r}.{rules}")
51
52
 
52
- return tags
53
-
54
53
 
55
54
  def check_object_name(name: str, object_type: str) -> None:
56
55
  message = (
modal/app.py CHANGED
@@ -1,7 +1,7 @@
1
1
  # Copyright Modal Labs 2022
2
2
  import inspect
3
3
  import typing
4
- from collections.abc import AsyncGenerator, Collection, Coroutine, Sequence
4
+ from collections.abc import AsyncGenerator, Collection, Coroutine, Mapping, Sequence
5
5
  from pathlib import PurePosixPath
6
6
  from textwrap import dedent
7
7
  from typing import (
@@ -191,9 +191,12 @@ class _App:
191
191
  if name is not None and not isinstance(name, str):
192
192
  raise InvalidError("Invalid value for `name`: Must be string.")
193
193
 
194
+ if tags is not None:
195
+ check_tag_dict(tags)
196
+
194
197
  self._name = name
195
198
  self._description = name
196
- self._tags = check_tag_dict(tags or {})
199
+ self._tags = tags or {}
197
200
  self._include_source_default = include_source
198
201
 
199
202
  check_sequence(secrets, _Secret, "`secrets=` has to be a list or tuple of `modal.Secret` objects")
@@ -1088,6 +1091,41 @@ class _App:
1088
1091
 
1089
1092
  return self
1090
1093
 
1094
+ async def set_tags(self, tags: Mapping[str, str], *, client: Optional[_Client] = None) -> None:
1095
+ """Attach key-value metadata to the App.
1096
+
1097
+ Tag metadata can be used to add organization-specific context to the App and can be
1098
+ included in billing reports and other informational APIs. Tags can also be set in
1099
+ the App constructor.
1100
+
1101
+ Any tags set on the App before calling this method will be removed if they are not
1102
+ included in the argument (i.e., this method does not have `.update()` semantics).
1103
+
1104
+ """
1105
+ # Note that we are requiring the App to be "running" before we set the tags.
1106
+ # Alternatively, we could hold onto the tags (i.e. in `self._tags`) and then pass
1107
+ # then up when AppPublish gets called. I'm not certain we want to support it, though.
1108
+ # It might not be obvious to users that `.set_tags()` is eager and has immediate effect
1109
+ # when the App is running, but lazy (and potentially ignored) otherwise. There would be
1110
+ # other complications, like what do you do with any tags set in the constructor, and
1111
+ # what should `.get_tags()` do when it's called before the App is running?
1112
+ if self._app_id is None:
1113
+ raise InvalidError("`App.set_tags` cannot be called before the App is running.")
1114
+ check_tag_dict(tags)
1115
+ req = api_pb2.AppSetTagsRequest(app_id=self._app_id, tags=tags)
1116
+
1117
+ client = client or self._client or await _Client.from_env()
1118
+ await retry_transient_errors(client.stub.AppSetTags, req)
1119
+
1120
+ async def get_tags(self, *, client: Optional[_Client] = None) -> dict[str, str]:
1121
+ """Get the tags that are currently attached to the App."""
1122
+ if self._app_id is None:
1123
+ raise InvalidError("`App.get_tags` cannot be called before the App is running.")
1124
+ req = api_pb2.AppGetTagsRequest(app_id=self._app_id)
1125
+ client = client or self._client or await _Client.from_env()
1126
+ resp = await retry_transient_errors(client.stub.AppGetTags, req)
1127
+ return dict(resp.tags)
1128
+
1091
1129
  async def _logs(self, client: Optional[_Client] = None) -> AsyncGenerator[str, None]:
1092
1130
  """Stream logs from the app.
1093
1131
 
modal/app.pyi CHANGED
@@ -521,6 +521,24 @@ class _App:
521
521
  """
522
522
  ...
523
523
 
524
+ async def set_tags(
525
+ self, tags: collections.abc.Mapping[str, str], *, client: typing.Optional[modal.client._Client] = None
526
+ ) -> None:
527
+ """Attach key-value metadata to the App.
528
+
529
+ Tag metadata can be used to add organization-specific context to the App and can be
530
+ included in billing reports and other informational APIs. Tags can also be set in
531
+ the App constructor.
532
+
533
+ Any tags set on the App before calling this method will be removed if they are not
534
+ included in the argument (i.e., this method does not have `.update()` semantics).
535
+ """
536
+ ...
537
+
538
+ async def get_tags(self, *, client: typing.Optional[modal.client._Client] = None) -> dict[str, str]:
539
+ """Get the tags that are currently attached to the App."""
540
+ ...
541
+
524
542
  def _logs(self, client: typing.Optional[modal.client._Client] = None) -> collections.abc.AsyncGenerator[str, None]:
525
543
  """Stream logs from the app.
526
544
 
@@ -1133,6 +1151,48 @@ class App:
1133
1151
  """
1134
1152
  ...
1135
1153
 
1154
+ class __set_tags_spec(typing_extensions.Protocol[SUPERSELF]):
1155
+ def __call__(
1156
+ self, /, tags: collections.abc.Mapping[str, str], *, client: typing.Optional[modal.client.Client] = None
1157
+ ) -> None:
1158
+ """Attach key-value metadata to the App.
1159
+
1160
+ Tag metadata can be used to add organization-specific context to the App and can be
1161
+ included in billing reports and other informational APIs. Tags can also be set in
1162
+ the App constructor.
1163
+
1164
+ Any tags set on the App before calling this method will be removed if they are not
1165
+ included in the argument (i.e., this method does not have `.update()` semantics).
1166
+ """
1167
+ ...
1168
+
1169
+ async def aio(
1170
+ self, /, tags: collections.abc.Mapping[str, str], *, client: typing.Optional[modal.client.Client] = None
1171
+ ) -> None:
1172
+ """Attach key-value metadata to the App.
1173
+
1174
+ Tag metadata can be used to add organization-specific context to the App and can be
1175
+ included in billing reports and other informational APIs. Tags can also be set in
1176
+ the App constructor.
1177
+
1178
+ Any tags set on the App before calling this method will be removed if they are not
1179
+ included in the argument (i.e., this method does not have `.update()` semantics).
1180
+ """
1181
+ ...
1182
+
1183
+ set_tags: __set_tags_spec[typing_extensions.Self]
1184
+
1185
+ class __get_tags_spec(typing_extensions.Protocol[SUPERSELF]):
1186
+ def __call__(self, /, *, client: typing.Optional[modal.client.Client] = None) -> dict[str, str]:
1187
+ """Get the tags that are currently attached to the App."""
1188
+ ...
1189
+
1190
+ async def aio(self, /, *, client: typing.Optional[modal.client.Client] = None) -> dict[str, str]:
1191
+ """Get the tags that are currently attached to the App."""
1192
+ ...
1193
+
1194
+ get_tags: __get_tags_spec[typing_extensions.Self]
1195
+
1136
1196
  class ___logs_spec(typing_extensions.Protocol[SUPERSELF]):
1137
1197
  def __call__(self, /, client: typing.Optional[modal.client.Client] = None) -> typing.Generator[str, None, None]:
1138
1198
  """Stream logs from the app.
modal/client.pyi CHANGED
@@ -33,7 +33,7 @@ class _Client:
33
33
  server_url: str,
34
34
  client_type: int,
35
35
  credentials: typing.Optional[tuple[str, str]],
36
- version: str = "1.1.5.dev77",
36
+ version: str = "1.1.5.dev78",
37
37
  ):
38
38
  """mdmd:hidden
39
39
  The Modal client object is not intended to be instantiated directly by users.
@@ -164,7 +164,7 @@ class Client:
164
164
  server_url: str,
165
165
  client_type: int,
166
166
  credentials: typing.Optional[tuple[str, str]],
167
- version: str = "1.1.5.dev77",
167
+ version: str = "1.1.5.dev78",
168
168
  ):
169
169
  """mdmd:hidden
170
170
  The Modal client object is not intended to be instantiated directly by users.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.1.5.dev77
3
+ Version: 1.1.5.dev78
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -18,11 +18,11 @@ modal/_tunnel.py,sha256=zTBxBiuH1O22tS1OliAJdIsSmaZS8PlnifS_6S5z-mk,6320
18
18
  modal/_tunnel.pyi,sha256=rvC7USR2BcKkbZIeCJXwf7-UfGE-LPLjKsGNiK7Lxa4,13366
19
19
  modal/_type_manager.py,sha256=DWjgmjYJuOagw2erin506UUbG2H5UzZCFEekS-7hmfA,9087
20
20
  modal/_watcher.py,sha256=K6LYnlmSGQB4tWWI9JADv-tvSvQ1j522FwT71B51CX8,3584
21
- modal/app.py,sha256=zjRWTevpIIzAELw2O_xG9NJ-SkFRCcPpDUZ7msTrk7A,49097
22
- modal/app.pyi,sha256=4QyptueFsCNOq0veUh0kJEWxRThiEHjALJ-9kC4ig8g,44455
21
+ modal/app.py,sha256=0yiON6FWi3fP9dD4B06DfxMLvp8tyE3ISPq0joX-HuE,51222
22
+ modal/app.pyi,sha256=bJyY4iOdG7M3b1-viRM-VHC4JyJuR3IoBgjqf9UjkFg,47204
23
23
  modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
24
24
  modal/client.py,sha256=kyAIVB3Ay-XKJizQ_1ufUFB__EagV0MLmHJpyYyJ7J0,18636
25
- modal/client.pyi,sha256=A_chd3EwhK-AoDR9lrJFn9C32uAd2WoJflrVrh87gyw,15831
25
+ modal/client.pyi,sha256=bRLTEAC_EXw_8pgqMh-H0tgMaAHo4ixLRfJJa9xjBlA,15831
26
26
  modal/cloud_bucket_mount.py,sha256=I2GRXYhOWLIz2kJZjXu75jAm9EJkBNcutGc6jR2ReUw,5928
27
27
  modal/cloud_bucket_mount.pyi,sha256=VuUOipMIHqFXMkD-3g2bsoqpSxV5qswlFHDOqPQzYAo,7405
28
28
  modal/cls.py,sha256=IZG9gLlssbhTgIn6iSEmBSKkbbkst3skASMae-59FII,40239
@@ -106,7 +106,7 @@ modal/_utils/http_utils.py,sha256=yeTFsXYr0rYMEhB7vBP7audG9Uc7OLhzKBANFDZWVt0,24
106
106
  modal/_utils/jwt_utils.py,sha256=fxH9plyrbAemTbjSsQtzIdDXE9QXxvMC4DiUZ16G0aA,1360
107
107
  modal/_utils/logger.py,sha256=NgbMKFT9chYYt_TU01DdIior5ByYr2gZtrWIk1SFRLc,1782
108
108
  modal/_utils/mount_utils.py,sha256=gGCgIlWwYiJbUtgFY2GJcWYismYvazbMAeUOgf7NhFQ,3205
109
- modal/_utils/name_utils.py,sha256=ZmO5Mt_GNwswSfK5k799YG_VM6b1sjN8AqEWDNPZAKo,2505
109
+ modal/_utils/name_utils.py,sha256=CIQL0Y4z47tq0rfNL2Kdo5dxsGrxoN2hR9Ko0HapeE4,2517
110
110
  modal/_utils/package_utils.py,sha256=LcL2olGN4xaUzu2Tbv-C-Ft9Qp6bsLxEfETOAVd-mjU,2073
111
111
  modal/_utils/pattern_utils.py,sha256=ZUffaECfe2iYBhH6cvCB-0-UWhmEBTZEl_TwG_So3ag,6714
112
112
  modal/_utils/rand_pb_testing.py,sha256=mmVPk1rZldHwHZx0DnHTuHQlRLAiiAYdxjwEJpxvT9c,3900
@@ -153,7 +153,7 @@ modal/experimental/__init__.py,sha256=9gkVuDmu3m4TlKoU3MzEtTOemUSs8EEOWba40s7Aa0
153
153
  modal/experimental/flash.py,sha256=C4sef08rARYFllsgtqukFmYL18SZW0_JpMS0BejDcUs,28552
154
154
  modal/experimental/flash.pyi,sha256=vV_OQhtdrPn8SW0XrBK-aLLHHIvxAzLzwFbWrke-m74,15463
155
155
  modal/experimental/ipython.py,sha256=TrCfmol9LGsRZMeDoeMPx3Hv3BFqQhYnmD_iH0pqdhk,2904
156
- modal-1.1.5.dev77.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
156
+ modal-1.1.5.dev78.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
157
157
  modal_docs/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,28
158
158
  modal_docs/gen_cli_docs.py,sha256=c1yfBS_x--gL5bs0N4ihMwqwX8l3IBWSkBAKNNIi6bQ,3801
159
159
  modal_docs/gen_reference_docs.py,sha256=d_CQUGQ0rfw28u75I2mov9AlS773z9rG40-yq5o7g2U,6359
@@ -182,10 +182,10 @@ modal_proto/sandbox_router_pb2.py,sha256=INd9izYaIYqllESQt4MSv2Rj9Hf5bMjAvtCc9b4
182
182
  modal_proto/sandbox_router_pb2.pyi,sha256=YCK0WnCgRos3-p7t4USQQ7x6WAuM278yeQX2IeU5mLg,13295
183
183
  modal_proto/sandbox_router_pb2_grpc.py,sha256=zonC5flvCwxeZYJPENj1IJo2Mr0J58DpoC1_8IdPYik,8243
184
184
  modal_proto/sandbox_router_pb2_grpc.pyi,sha256=4QgCB9b7_ykvH8YD-hfnogVH9CLyHVDC5QNb03l4_X8,2735
185
- modal_version/__init__.py,sha256=JE4P8flX0W_TOA9B7kyt3vpv-G3xzwi-ztMkzANXmuM,121
185
+ modal_version/__init__.py,sha256=pKVma3bQcpqr2Yci6ujfQYJPvhqZ3zAQLdBB6HaXMZk,121
186
186
  modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
187
- modal-1.1.5.dev77.dist-info/METADATA,sha256=3EW-K63Vcb9xTN1mPdouC0WaGJtwZFP7Z_3W7-0RpO0,2481
188
- modal-1.1.5.dev77.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
189
- modal-1.1.5.dev77.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
190
- modal-1.1.5.dev77.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
191
- modal-1.1.5.dev77.dist-info/RECORD,,
187
+ modal-1.1.5.dev78.dist-info/METADATA,sha256=rw1d0l5XOAGgCc24QrL_fS3IPxylytBDk6QuBUHf8Ug,2481
188
+ modal-1.1.5.dev78.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
189
+ modal-1.1.5.dev78.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
190
+ modal-1.1.5.dev78.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
191
+ modal-1.1.5.dev78.dist-info/RECORD,,
modal_version/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # Copyright Modal Labs 2025
2
2
  """Supplies the current version of the modal client library."""
3
3
 
4
- __version__ = "1.1.5.dev77"
4
+ __version__ = "1.1.5.dev78"