modal 1.1.1.dev27__tar.gz → 1.1.1.dev28__tar.gz

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.

Files changed (188) hide show
  1. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/PKG-INFO +1 -1
  2. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_object.py +9 -1
  3. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/client.pyi +2 -2
  4. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/dict.py +45 -2
  5. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/dict.pyi +55 -0
  6. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/functions.pyi +6 -6
  7. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/object.pyi +4 -0
  8. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/queue.py +44 -2
  9. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/queue.pyi +53 -0
  10. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/secret.py +44 -2
  11. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/secret.pyi +55 -0
  12. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/volume.py +48 -18
  13. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/volume.pyi +50 -8
  14. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal.egg-info/PKG-INFO +1 -1
  15. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal_version/__init__.py +1 -1
  16. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/LICENSE +0 -0
  17. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/README.md +0 -0
  18. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/__init__.py +0 -0
  19. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/__main__.py +0 -0
  20. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_clustered_functions.py +0 -0
  21. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_clustered_functions.pyi +0 -0
  22. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_container_entrypoint.py +0 -0
  23. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_functions.py +0 -0
  24. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_ipython.py +0 -0
  25. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_location.py +0 -0
  26. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_output.py +0 -0
  27. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_partial_function.py +0 -0
  28. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_pty.py +0 -0
  29. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_resolver.py +0 -0
  30. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_resources.py +0 -0
  31. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_runtime/__init__.py +0 -0
  32. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_runtime/asgi.py +0 -0
  33. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_runtime/container_io_manager.py +0 -0
  34. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_runtime/container_io_manager.pyi +0 -0
  35. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_runtime/execution_context.py +0 -0
  36. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_runtime/execution_context.pyi +0 -0
  37. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  38. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_runtime/telemetry.py +0 -0
  39. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_runtime/user_code_imports.py +0 -0
  40. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_serialization.py +0 -0
  41. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_traceback.py +0 -0
  42. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_tunnel.py +0 -0
  43. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_tunnel.pyi +0 -0
  44. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_type_manager.py +0 -0
  45. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_utils/__init__.py +0 -0
  46. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_utils/app_utils.py +0 -0
  47. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_utils/async_utils.py +0 -0
  48. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_utils/auth_token_manager.py +0 -0
  49. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_utils/blob_utils.py +0 -0
  50. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_utils/bytes_io_segment_payload.py +0 -0
  51. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_utils/deprecation.py +0 -0
  52. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_utils/docker_utils.py +0 -0
  53. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_utils/function_utils.py +0 -0
  54. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_utils/git_utils.py +0 -0
  55. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_utils/grpc_testing.py +0 -0
  56. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_utils/grpc_utils.py +0 -0
  57. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_utils/hash_utils.py +0 -0
  58. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_utils/http_utils.py +0 -0
  59. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_utils/jwt_utils.py +0 -0
  60. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_utils/logger.py +0 -0
  61. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_utils/mount_utils.py +0 -0
  62. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_utils/name_utils.py +0 -0
  63. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_utils/package_utils.py +0 -0
  64. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_utils/pattern_utils.py +0 -0
  65. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_utils/rand_pb_testing.py +0 -0
  66. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_utils/shell_utils.py +0 -0
  67. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_utils/time_utils.py +0 -0
  68. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_vendor/__init__.py +0 -0
  69. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  70. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_vendor/cloudpickle.py +0 -0
  71. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_vendor/tblib.py +0 -0
  72. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/_watcher.py +0 -0
  73. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/app.py +0 -0
  74. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/app.pyi +0 -0
  75. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/builder/2023.12.312.txt +0 -0
  76. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/builder/2023.12.txt +0 -0
  77. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/builder/2024.04.txt +0 -0
  78. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/builder/2024.10.txt +0 -0
  79. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/builder/2025.06.txt +0 -0
  80. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/builder/PREVIEW.txt +0 -0
  81. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/builder/README.md +0 -0
  82. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/builder/base-images.json +0 -0
  83. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/call_graph.py +0 -0
  84. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/cli/__init__.py +0 -0
  85. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/cli/_download.py +0 -0
  86. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/cli/_traceback.py +0 -0
  87. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/cli/app.py +0 -0
  88. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/cli/cluster.py +0 -0
  89. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/cli/config.py +0 -0
  90. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/cli/container.py +0 -0
  91. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/cli/dict.py +0 -0
  92. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/cli/entry_point.py +0 -0
  93. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/cli/environment.py +0 -0
  94. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/cli/import_refs.py +0 -0
  95. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/cli/launch.py +0 -0
  96. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/cli/network_file_system.py +0 -0
  97. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/cli/profile.py +0 -0
  98. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/cli/programs/__init__.py +0 -0
  99. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/cli/programs/run_jupyter.py +0 -0
  100. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/cli/programs/vscode.py +0 -0
  101. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/cli/queues.py +0 -0
  102. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/cli/run.py +0 -0
  103. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/cli/secret.py +0 -0
  104. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/cli/token.py +0 -0
  105. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/cli/utils.py +0 -0
  106. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/cli/volume.py +0 -0
  107. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/client.py +0 -0
  108. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/cloud_bucket_mount.py +0 -0
  109. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/cloud_bucket_mount.pyi +0 -0
  110. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/cls.py +0 -0
  111. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/cls.pyi +0 -0
  112. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/config.py +0 -0
  113. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/container_process.py +0 -0
  114. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/container_process.pyi +0 -0
  115. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/environments.py +0 -0
  116. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/environments.pyi +0 -0
  117. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/exception.py +0 -0
  118. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/experimental/__init__.py +0 -0
  119. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/experimental/flash.py +0 -0
  120. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/experimental/flash.pyi +0 -0
  121. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/experimental/ipython.py +0 -0
  122. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/file_io.py +0 -0
  123. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/file_io.pyi +0 -0
  124. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/file_pattern_matcher.py +0 -0
  125. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/functions.py +0 -0
  126. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/gpu.py +0 -0
  127. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/image.py +0 -0
  128. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/image.pyi +0 -0
  129. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/io_streams.py +0 -0
  130. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/io_streams.pyi +0 -0
  131. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/mount.py +0 -0
  132. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/mount.pyi +0 -0
  133. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/network_file_system.py +0 -0
  134. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/network_file_system.pyi +0 -0
  135. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/object.py +0 -0
  136. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/output.py +0 -0
  137. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/parallel_map.py +0 -0
  138. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/parallel_map.pyi +0 -0
  139. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/partial_function.py +0 -0
  140. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/partial_function.pyi +0 -0
  141. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/proxy.py +0 -0
  142. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/proxy.pyi +0 -0
  143. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/py.typed +0 -0
  144. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/retries.py +0 -0
  145. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/runner.py +0 -0
  146. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/runner.pyi +0 -0
  147. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/running_app.py +0 -0
  148. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/sandbox.py +0 -0
  149. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/sandbox.pyi +0 -0
  150. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/schedule.py +0 -0
  151. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/scheduler_placement.py +0 -0
  152. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/serving.py +0 -0
  153. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/serving.pyi +0 -0
  154. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/snapshot.py +0 -0
  155. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/snapshot.pyi +0 -0
  156. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/stream_type.py +0 -0
  157. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/token_flow.py +0 -0
  158. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal/token_flow.pyi +0 -0
  159. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal.egg-info/SOURCES.txt +0 -0
  160. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal.egg-info/dependency_links.txt +0 -0
  161. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal.egg-info/entry_points.txt +0 -0
  162. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal.egg-info/requires.txt +0 -0
  163. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal.egg-info/top_level.txt +0 -0
  164. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal_docs/__init__.py +0 -0
  165. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal_docs/gen_cli_docs.py +0 -0
  166. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal_docs/gen_reference_docs.py +0 -0
  167. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal_docs/mdmd/__init__.py +0 -0
  168. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal_docs/mdmd/mdmd.py +0 -0
  169. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal_docs/mdmd/signatures.py +0 -0
  170. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal_proto/__init__.py +0 -0
  171. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal_proto/api.proto +0 -0
  172. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal_proto/api_grpc.py +0 -0
  173. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal_proto/api_pb2.py +0 -0
  174. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal_proto/api_pb2.pyi +0 -0
  175. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal_proto/api_pb2_grpc.py +0 -0
  176. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal_proto/api_pb2_grpc.pyi +0 -0
  177. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal_proto/modal_api_grpc.py +0 -0
  178. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal_proto/modal_options_grpc.py +0 -0
  179. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal_proto/options.proto +0 -0
  180. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal_proto/options_grpc.py +0 -0
  181. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal_proto/options_pb2.py +0 -0
  182. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal_proto/options_pb2.pyi +0 -0
  183. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal_proto/options_pb2_grpc.py +0 -0
  184. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal_proto/options_pb2_grpc.pyi +0 -0
  185. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal_proto/py.typed +0 -0
  186. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/modal_version/__main__.py +0 -0
  187. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/pyproject.toml +0 -0
  188. {modal-1.1.1.dev27 → modal-1.1.1.dev28}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.1.1.dev27
3
+ Version: 1.1.1.dev28
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -48,6 +48,10 @@ class _Object:
48
48
  _is_hydrated: bool
49
49
  _is_rehydrated: bool
50
50
 
51
+ # Not all object subclasses have a meaningful "name" concept
52
+ # So whether they expose this is a matter of having a name property
53
+ _name: Optional[str]
54
+
51
55
  @classmethod
52
56
  def __init_subclass__(cls, type_prefix: Optional[str] = None):
53
57
  super().__init_subclass__()
@@ -68,6 +72,7 @@ class _Object:
68
72
  hydrate_lazily: bool = False,
69
73
  deps: Optional[Callable[..., Sequence["_Object"]]] = None,
70
74
  deduplication_key: Optional[Callable[[], Awaitable[Hashable]]] = None,
75
+ name: Optional[str] = None,
71
76
  ):
72
77
  self._local_uuid = str(uuid.uuid4())
73
78
  self._load = load
@@ -83,6 +88,8 @@ class _Object:
83
88
  self._is_hydrated = False
84
89
  self._is_rehydrated = False
85
90
 
91
+ self._name = name
92
+
86
93
  self._initialize_from_empty()
87
94
 
88
95
  def _unhydrate(self):
@@ -163,10 +170,11 @@ class _Object:
163
170
  hydrate_lazily: bool = False,
164
171
  deps: Optional[Callable[..., Sequence["_Object"]]] = None,
165
172
  deduplication_key: Optional[Callable[[], Awaitable[Hashable]]] = None,
173
+ name: Optional[str] = None,
166
174
  ):
167
175
  # TODO(erikbern): flip the order of the two first arguments
168
176
  obj = _Object.__new__(cls)
169
- obj._init(rep, load, is_another_app, preload, hydrate_lazily, deps, deduplication_key)
177
+ obj._init(rep, load, is_another_app, preload, hydrate_lazily, deps, deduplication_key, name)
170
178
  return obj
171
179
 
172
180
  @staticmethod
@@ -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.1.dev27",
36
+ version: str = "1.1.1.dev28",
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.1.dev27",
167
+ version: str = "1.1.1.dev28",
168
168
  ):
169
169
  """mdmd:hidden
170
170
  The Modal client object is not intended to be instantiated directly by users.
@@ -1,7 +1,10 @@
1
1
  # Copyright Modal Labs 2022
2
2
  from collections.abc import AsyncIterator, Mapping
3
+ from dataclasses import dataclass
4
+ from datetime import datetime
3
5
  from typing import Any, Optional
4
6
 
7
+ from google.protobuf.message import Message
5
8
  from grpclib import GRPCError
6
9
  from synchronicity.async_wrap import asynccontextmanager
7
10
 
@@ -23,6 +26,18 @@ def _serialize_dict(data):
23
26
  return [api_pb2.DictEntry(key=serialize(k), value=serialize(v)) for k, v in data.items()]
24
27
 
25
28
 
29
+ @dataclass
30
+ class DictInfo:
31
+ """Information about the Dict object."""
32
+
33
+ # This dataclass should be limited to information that is unchanging over the lifetime of the Dict,
34
+ # since it is transmitted from the server when the object is hydrated and could be stale when accessed.
35
+
36
+ name: Optional[str]
37
+ created_at: datetime
38
+ created_by: Optional[str]
39
+
40
+
26
41
  class _Dict(_Object, type_prefix="di"):
27
42
  """Distributed dictionary for storage in Modal apps.
28
43
 
@@ -65,12 +80,29 @@ class _Dict(_Object, type_prefix="di"):
65
80
  For more examples, see the [guide](https://modal.com/docs/guide/dicts-and-queues#modal-dicts).
66
81
  """
67
82
 
83
+ _name: Optional[str] = None
84
+ _metadata: Optional[api_pb2.DictMetadata] = None
85
+
68
86
  def __init__(self, data={}):
69
87
  """mdmd:hidden"""
70
88
  raise RuntimeError(
71
89
  "`Dict(...)` constructor is not allowed. Please use `Dict.from_name` or `Dict.ephemeral` instead"
72
90
  )
73
91
 
92
+ @property
93
+ def name(self) -> Optional[str]:
94
+ return self._name
95
+
96
+ def _hydrate_metadata(self, metadata: Optional[Message]):
97
+ if metadata:
98
+ assert isinstance(metadata, api_pb2.DictMetadata)
99
+ self._metadata = metadata
100
+ self._name = metadata.name
101
+
102
+ def _get_metadata(self) -> api_pb2.DictMetadata:
103
+ assert self._metadata
104
+ return self._metadata
105
+
74
106
  @classmethod
75
107
  @asynccontextmanager
76
108
  async def ephemeral(
@@ -112,7 +144,7 @@ class _Dict(_Object, type_prefix="di"):
112
144
  async with TaskContext() as tc:
113
145
  request = api_pb2.DictHeartbeatRequest(dict_id=response.dict_id)
114
146
  tc.infinite_loop(lambda: client.stub.DictHeartbeat(request), sleep=_heartbeat_sleep)
115
- yield cls._new_hydrated(response.dict_id, client, None, is_another_app=True)
147
+ yield cls._new_hydrated(response.dict_id, client, response.metadata, is_another_app=True)
116
148
 
117
149
  @staticmethod
118
150
  def from_name(
@@ -155,7 +187,7 @@ class _Dict(_Object, type_prefix="di"):
155
187
  logger.debug(f"Created dict with id {response.dict_id}")
156
188
  self._hydrate(response.dict_id, resolver.client, response.metadata)
157
189
 
158
- return _Dict._from_loader(_load, "Dict()", is_another_app=True, hydrate_lazily=True)
190
+ return _Dict._from_loader(_load, "Dict()", is_another_app=True, hydrate_lazily=True, name=name)
159
191
 
160
192
  @staticmethod
161
193
  async def lookup(
@@ -209,6 +241,17 @@ class _Dict(_Object, type_prefix="di"):
209
241
  req = api_pb2.DictDeleteRequest(dict_id=obj.object_id)
210
242
  await retry_transient_errors(obj._client.stub.DictDelete, req)
211
243
 
244
+ @live_method
245
+ async def info(self) -> DictInfo:
246
+ """Return information about the Dict object."""
247
+ metadata = self._get_metadata()
248
+ creation_info = metadata.creation_info
249
+ return DictInfo(
250
+ name=metadata.name or None,
251
+ created_at=datetime.fromtimestamp(creation_info.created_at),
252
+ created_by=creation_info.created_by or None,
253
+ )
254
+
212
255
  @live_method
213
256
  async def clear(self) -> None:
214
257
  """Remove all items from the Dict."""
@@ -1,13 +1,37 @@
1
1
  import collections.abc
2
+ import datetime
3
+ import google.protobuf.message
2
4
  import modal._object
3
5
  import modal.client
4
6
  import modal.object
7
+ import modal_proto.api_pb2
5
8
  import synchronicity.combined_types
6
9
  import typing
7
10
  import typing_extensions
8
11
 
9
12
  def _serialize_dict(data): ...
10
13
 
14
+ class DictInfo:
15
+ """Information about the Dict object."""
16
+
17
+ name: typing.Optional[str]
18
+ created_at: datetime.datetime
19
+ created_by: typing.Optional[str]
20
+
21
+ def __init__(
22
+ self, name: typing.Optional[str], created_at: datetime.datetime, created_by: typing.Optional[str]
23
+ ) -> None:
24
+ """Initialize self. See help(type(self)) for accurate signature."""
25
+ ...
26
+
27
+ def __repr__(self):
28
+ """Return repr(self)."""
29
+ ...
30
+
31
+ def __eq__(self, other):
32
+ """Return self==value."""
33
+ ...
34
+
11
35
  class _Dict(modal._object._Object):
12
36
  """Distributed dictionary for storage in Modal apps.
13
37
 
@@ -49,10 +73,18 @@ class _Dict(modal._object._Object):
49
73
 
50
74
  For more examples, see the [guide](https://modal.com/docs/guide/dicts-and-queues#modal-dicts).
51
75
  """
76
+
77
+ _name: typing.Optional[str]
78
+ _metadata: typing.Optional[modal_proto.api_pb2.DictMetadata]
79
+
52
80
  def __init__(self, data={}):
53
81
  """mdmd:hidden"""
54
82
  ...
55
83
 
84
+ @property
85
+ def name(self) -> typing.Optional[str]: ...
86
+ def _hydrate_metadata(self, metadata: typing.Optional[google.protobuf.message.Message]): ...
87
+ def _get_metadata(self) -> modal_proto.api_pb2.DictMetadata: ...
56
88
  @classmethod
57
89
  def ephemeral(
58
90
  cls: type[_Dict],
@@ -131,6 +163,10 @@ class _Dict(modal._object._Object):
131
163
  client: typing.Optional[modal.client._Client] = None,
132
164
  environment_name: typing.Optional[str] = None,
133
165
  ): ...
166
+ async def info(self) -> DictInfo:
167
+ """Return information about the Dict object."""
168
+ ...
169
+
134
170
  async def clear(self) -> None:
135
171
  """Remove all items from the Dict."""
136
172
  ...
@@ -264,10 +300,18 @@ class Dict(modal.object.Object):
264
300
 
265
301
  For more examples, see the [guide](https://modal.com/docs/guide/dicts-and-queues#modal-dicts).
266
302
  """
303
+
304
+ _name: typing.Optional[str]
305
+ _metadata: typing.Optional[modal_proto.api_pb2.DictMetadata]
306
+
267
307
  def __init__(self, data={}):
268
308
  """mdmd:hidden"""
269
309
  ...
270
310
 
311
+ @property
312
+ def name(self) -> typing.Optional[str]: ...
313
+ def _hydrate_metadata(self, metadata: typing.Optional[google.protobuf.message.Message]): ...
314
+ def _get_metadata(self) -> modal_proto.api_pb2.DictMetadata: ...
271
315
  @classmethod
272
316
  def ephemeral(
273
317
  cls: type[Dict],
@@ -388,6 +432,17 @@ class Dict(modal.object.Object):
388
432
 
389
433
  delete: __delete_spec
390
434
 
435
+ class __info_spec(typing_extensions.Protocol[SUPERSELF]):
436
+ def __call__(self, /) -> DictInfo:
437
+ """Return information about the Dict object."""
438
+ ...
439
+
440
+ async def aio(self, /) -> DictInfo:
441
+ """Return information about the Dict object."""
442
+ ...
443
+
444
+ info: __info_spec[typing_extensions.Self]
445
+
391
446
  class __clear_spec(typing_extensions.Protocol[SUPERSELF]):
392
447
  def __call__(self, /) -> None:
393
448
  """Remove all items from the Dict."""
@@ -428,7 +428,7 @@ class Function(
428
428
 
429
429
  _call_generator: ___call_generator_spec[typing_extensions.Self]
430
430
 
431
- class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
431
+ class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
432
432
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER:
433
433
  """Calls the function remotely, executing it with the given arguments and returning the execution's result."""
434
434
  ...
@@ -437,7 +437,7 @@ class Function(
437
437
  """Calls the function remotely, executing it with the given arguments and returning the execution's result."""
438
438
  ...
439
439
 
440
- remote: __remote_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
440
+ remote: __remote_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
441
441
 
442
442
  class __remote_gen_spec(typing_extensions.Protocol[SUPERSELF]):
443
443
  def __call__(self, /, *args, **kwargs) -> typing.Generator[typing.Any, None, None]:
@@ -464,7 +464,7 @@ class Function(
464
464
  """
465
465
  ...
466
466
 
467
- class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
467
+ class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
468
468
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
469
469
  """[Experimental] Calls the function with the given arguments, without waiting for the results.
470
470
 
@@ -488,7 +488,7 @@ class Function(
488
488
  ...
489
489
 
490
490
  _experimental_spawn: ___experimental_spawn_spec[
491
- modal._functions.P, modal._functions.ReturnType, typing_extensions.Self
491
+ modal._functions.ReturnType, modal._functions.P, typing_extensions.Self
492
492
  ]
493
493
 
494
494
  class ___spawn_map_inner_spec(typing_extensions.Protocol[P_INNER, SUPERSELF]):
@@ -497,7 +497,7 @@ class Function(
497
497
 
498
498
  _spawn_map_inner: ___spawn_map_inner_spec[modal._functions.P, typing_extensions.Self]
499
499
 
500
- class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
500
+ class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
501
501
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
502
502
  """Calls the function with the given arguments, without waiting for the results.
503
503
 
@@ -518,7 +518,7 @@ class Function(
518
518
  """
519
519
  ...
520
520
 
521
- spawn: __spawn_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
521
+ spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
522
522
 
523
523
  def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]:
524
524
  """Return the inner Python object wrapped by this Modal Function."""
@@ -31,6 +31,7 @@ class Object:
31
31
  _client: typing.Optional[modal.client.Client]
32
32
  _is_hydrated: bool
33
33
  _is_rehydrated: bool
34
+ _name: typing.Optional[str]
34
35
 
35
36
  def __init__(self, *args, **kwargs):
36
37
  """mdmd:hidden"""
@@ -54,6 +55,7 @@ class Object:
54
55
  hydrate_lazily: bool = False,
55
56
  deps: typing.Optional[collections.abc.Callable[..., collections.abc.Sequence[Object]]] = None,
56
57
  deduplication_key: typing.Optional[collections.abc.Callable[[], collections.abc.Hashable]] = None,
58
+ name: typing.Optional[str] = None,
57
59
  ): ...
58
60
  def aio(
59
61
  self,
@@ -75,6 +77,7 @@ class Object:
75
77
  deduplication_key: typing.Optional[
76
78
  collections.abc.Callable[[], collections.abc.Awaitable[collections.abc.Hashable]]
77
79
  ] = None,
80
+ name: typing.Optional[str] = None,
78
81
  ): ...
79
82
 
80
83
  _init: ___init_spec[typing_extensions.Self]
@@ -107,6 +110,7 @@ class Object:
107
110
  hydrate_lazily: bool = False,
108
111
  deps: typing.Optional[collections.abc.Callable[..., collections.abc.Sequence[Object]]] = None,
109
112
  deduplication_key: typing.Optional[collections.abc.Callable[[], collections.abc.Hashable]] = None,
113
+ name: typing.Optional[str] = None,
110
114
  ): ...
111
115
  @staticmethod
112
116
  def _get_type_from_id(object_id: str) -> type[Object]: ...
@@ -3,8 +3,11 @@ import queue # The system library
3
3
  import time
4
4
  import warnings
5
5
  from collections.abc import AsyncGenerator, AsyncIterator
6
+ from dataclasses import dataclass
7
+ from datetime import datetime
6
8
  from typing import Any, Optional
7
9
 
10
+ from google.protobuf.message import Message
8
11
  from grpclib import GRPCError, Status
9
12
  from synchronicity.async_wrap import asynccontextmanager
10
13
 
@@ -21,6 +24,18 @@ from .client import _Client
21
24
  from .exception import InvalidError, RequestSizeError
22
25
 
23
26
 
27
+ @dataclass
28
+ class QueueInfo:
29
+ """Information about the Queue object."""
30
+
31
+ # This dataclass should be limited to information that is unchanging over the lifetime of the Queue,
32
+ # since it is transmitted from the server when the object is hydrated and could be stale when accessed.
33
+
34
+ name: Optional[str]
35
+ created_at: datetime
36
+ created_by: Optional[str]
37
+
38
+
24
39
  class _Queue(_Object, type_prefix="qu"):
25
40
  """Distributed, FIFO queue for data flow in Modal apps.
26
41
 
@@ -94,10 +109,26 @@ class _Queue(_Object, type_prefix="qu"):
94
109
  Partition keys must be non-empty and must not exceed 64 bytes.
95
110
  """
96
111
 
112
+ _metadata: Optional[api_pb2.QueueMetadata] = None
113
+
97
114
  def __init__(self):
98
115
  """mdmd:hidden"""
99
116
  raise RuntimeError("Queue() is not allowed. Please use `Queue.from_name(...)` or `Queue.ephemeral()` instead.")
100
117
 
118
+ @property
119
+ def name(self) -> Optional[str]:
120
+ return self._name
121
+
122
+ def _hydrate_metadata(self, metadata: Optional[Message]):
123
+ if metadata:
124
+ assert isinstance(metadata, api_pb2.QueueMetadata)
125
+ self._metadata = metadata
126
+ self._name = metadata.name
127
+
128
+ def _get_metadata(self) -> api_pb2.QueueMetadata:
129
+ assert self._metadata
130
+ return self._metadata
131
+
101
132
  @staticmethod
102
133
  def validate_partition_key(partition: Optional[str]) -> bytes:
103
134
  if partition is not None:
@@ -142,7 +173,7 @@ class _Queue(_Object, type_prefix="qu"):
142
173
  async with TaskContext() as tc:
143
174
  request = api_pb2.QueueHeartbeatRequest(queue_id=response.queue_id)
144
175
  tc.infinite_loop(lambda: client.stub.QueueHeartbeat(request), sleep=_heartbeat_sleep)
145
- yield cls._new_hydrated(response.queue_id, client, None, is_another_app=True)
176
+ yield cls._new_hydrated(response.queue_id, client, response.metadata, is_another_app=True)
146
177
 
147
178
  @staticmethod
148
179
  def from_name(
@@ -175,7 +206,7 @@ class _Queue(_Object, type_prefix="qu"):
175
206
  response = await resolver.client.stub.QueueGetOrCreate(req)
176
207
  self._hydrate(response.queue_id, resolver.client, response.metadata)
177
208
 
178
- return _Queue._from_loader(_load, "Queue()", is_another_app=True, hydrate_lazily=True)
209
+ return _Queue._from_loader(_load, "Queue()", is_another_app=True, hydrate_lazily=True, name=name)
179
210
 
180
211
  @staticmethod
181
212
  async def lookup(
@@ -222,6 +253,17 @@ class _Queue(_Object, type_prefix="qu"):
222
253
  req = api_pb2.QueueDeleteRequest(queue_id=obj.object_id)
223
254
  await retry_transient_errors(obj._client.stub.QueueDelete, req)
224
255
 
256
+ @live_method
257
+ async def info(self) -> QueueInfo:
258
+ """Return information about the Queue object."""
259
+ metadata = self._get_metadata()
260
+ creation_info = metadata.creation_info
261
+ return QueueInfo(
262
+ name=metadata.name or None,
263
+ created_at=datetime.fromtimestamp(creation_info.created_at),
264
+ created_by=creation_info.created_by or None,
265
+ )
266
+
225
267
  async def _get_nonblocking(self, partition: Optional[str], n_values: int) -> list[Any]:
226
268
  request = api_pb2.QueueGetRequest(
227
269
  queue_id=self.object_id,
@@ -1,11 +1,35 @@
1
1
  import collections.abc
2
+ import datetime
3
+ import google.protobuf.message
2
4
  import modal._object
3
5
  import modal.client
4
6
  import modal.object
7
+ import modal_proto.api_pb2
5
8
  import synchronicity.combined_types
6
9
  import typing
7
10
  import typing_extensions
8
11
 
12
+ class QueueInfo:
13
+ """Information about the Queue object."""
14
+
15
+ name: typing.Optional[str]
16
+ created_at: datetime.datetime
17
+ created_by: typing.Optional[str]
18
+
19
+ def __init__(
20
+ self, name: typing.Optional[str], created_at: datetime.datetime, created_by: typing.Optional[str]
21
+ ) -> None:
22
+ """Initialize self. See help(type(self)) for accurate signature."""
23
+ ...
24
+
25
+ def __repr__(self):
26
+ """Return repr(self)."""
27
+ ...
28
+
29
+ def __eq__(self, other):
30
+ """Return self==value."""
31
+ ...
32
+
9
33
  class _Queue(modal._object._Object):
10
34
  """Distributed, FIFO queue for data flow in Modal apps.
11
35
 
@@ -78,10 +102,17 @@ class _Queue(modal._object._Object):
78
102
 
79
103
  Partition keys must be non-empty and must not exceed 64 bytes.
80
104
  """
105
+
106
+ _metadata: typing.Optional[modal_proto.api_pb2.QueueMetadata]
107
+
81
108
  def __init__(self):
82
109
  """mdmd:hidden"""
83
110
  ...
84
111
 
112
+ @property
113
+ def name(self) -> typing.Optional[str]: ...
114
+ def _hydrate_metadata(self, metadata: typing.Optional[google.protobuf.message.Message]): ...
115
+ def _get_metadata(self) -> modal_proto.api_pb2.QueueMetadata: ...
85
116
  @staticmethod
86
117
  def validate_partition_key(partition: typing.Optional[str]) -> bytes: ...
87
118
  @classmethod
@@ -155,6 +186,10 @@ class _Queue(modal._object._Object):
155
186
  client: typing.Optional[modal.client._Client] = None,
156
187
  environment_name: typing.Optional[str] = None,
157
188
  ): ...
189
+ async def info(self) -> QueueInfo:
190
+ """Return information about the Queue object."""
191
+ ...
192
+
158
193
  async def _get_nonblocking(self, partition: typing.Optional[str], n_values: int) -> list[typing.Any]: ...
159
194
  async def _get_blocking(
160
195
  self, partition: typing.Optional[str], timeout: typing.Optional[float], n_values: int
@@ -335,10 +370,17 @@ class Queue(modal.object.Object):
335
370
 
336
371
  Partition keys must be non-empty and must not exceed 64 bytes.
337
372
  """
373
+
374
+ _metadata: typing.Optional[modal_proto.api_pb2.QueueMetadata]
375
+
338
376
  def __init__(self):
339
377
  """mdmd:hidden"""
340
378
  ...
341
379
 
380
+ @property
381
+ def name(self) -> typing.Optional[str]: ...
382
+ def _hydrate_metadata(self, metadata: typing.Optional[google.protobuf.message.Message]): ...
383
+ def _get_metadata(self) -> modal_proto.api_pb2.QueueMetadata: ...
342
384
  @staticmethod
343
385
  def validate_partition_key(partition: typing.Optional[str]) -> bytes: ...
344
386
  @classmethod
@@ -453,6 +495,17 @@ class Queue(modal.object.Object):
453
495
 
454
496
  delete: __delete_spec
455
497
 
498
+ class __info_spec(typing_extensions.Protocol[SUPERSELF]):
499
+ def __call__(self, /) -> QueueInfo:
500
+ """Return information about the Queue object."""
501
+ ...
502
+
503
+ async def aio(self, /) -> QueueInfo:
504
+ """Return information about the Queue object."""
505
+ ...
506
+
507
+ info: __info_spec[typing_extensions.Self]
508
+
456
509
  class ___get_nonblocking_spec(typing_extensions.Protocol[SUPERSELF]):
457
510
  def __call__(self, /, partition: typing.Optional[str], n_values: int) -> list[typing.Any]: ...
458
511
  async def aio(self, /, partition: typing.Optional[str], n_values: int) -> list[typing.Any]: ...
@@ -1,12 +1,15 @@
1
1
  # Copyright Modal Labs 2022
2
2
  import os
3
+ from dataclasses import dataclass
4
+ from datetime import datetime
3
5
  from typing import Optional, Union
4
6
 
7
+ from google.protobuf.message import Message
5
8
  from grpclib import GRPCError, Status
6
9
 
7
10
  from modal_proto import api_pb2
8
11
 
9
- from ._object import _get_environment_name, _Object
12
+ from ._object import _get_environment_name, _Object, live_method
10
13
  from ._resolver import Resolver
11
14
  from ._runtime.execution_context import is_local
12
15
  from ._utils.async_utils import synchronize_api
@@ -19,6 +22,18 @@ from .exception import InvalidError, NotFoundError
19
22
  ENV_DICT_WRONG_TYPE_ERR = "the env_dict argument to Secret has to be a dict[str, Union[str, None]]"
20
23
 
21
24
 
25
+ @dataclass
26
+ class SecretInfo:
27
+ """Information about the Secret object."""
28
+
29
+ # This dataclass should be limited to information that is unchanging over the lifetime of the Secret,
30
+ # since it is transmitted from the server when the object is hydrated and could be stale when accessed.
31
+
32
+ name: Optional[str]
33
+ created_at: datetime
34
+ created_by: Optional[str]
35
+
36
+
22
37
  class _Secret(_Object, type_prefix="st"):
23
38
  """Secrets provide a dictionary of environment variables for images.
24
39
 
@@ -29,6 +44,22 @@ class _Secret(_Object, type_prefix="st"):
29
44
  See [the secrets guide page](https://modal.com/docs/guide/secrets) for more information.
30
45
  """
31
46
 
47
+ _metadata: Optional[api_pb2.SecretMetadata] = None
48
+
49
+ @property
50
+ def name(self) -> Optional[str]:
51
+ return self._name
52
+
53
+ def _hydrate_metadata(self, metadata: Optional[Message]):
54
+ if metadata:
55
+ assert isinstance(metadata, api_pb2.SecretMetadata)
56
+ self._metadata = metadata
57
+ self._name = metadata.name
58
+
59
+ def _get_metadata(self) -> api_pb2.SecretMetadata:
60
+ assert self._metadata
61
+ return self._metadata
62
+
32
63
  @staticmethod
33
64
  def from_dict(
34
65
  env_dict: dict[
@@ -202,7 +233,7 @@ class _Secret(_Object, type_prefix="st"):
202
233
  raise
203
234
  self._hydrate(response.secret_id, resolver.client, response.metadata)
204
235
 
205
- return _Secret._from_loader(_load, "Secret()", hydrate_lazily=True)
236
+ return _Secret._from_loader(_load, "Secret()", hydrate_lazily=True, name=name)
206
237
 
207
238
  @staticmethod
208
239
  async def lookup(
@@ -261,5 +292,16 @@ class _Secret(_Object, type_prefix="st"):
261
292
  resp = await retry_transient_errors(client.stub.SecretGetOrCreate, request)
262
293
  return resp.secret_id
263
294
 
295
+ @live_method
296
+ async def info(self) -> SecretInfo:
297
+ """Return information about the Secret object."""
298
+ metadata = self._get_metadata()
299
+ creation_info = metadata.creation_info
300
+ return SecretInfo(
301
+ name=metadata.name or None,
302
+ created_at=datetime.fromtimestamp(creation_info.created_at),
303
+ created_by=creation_info.created_by or None,
304
+ )
305
+
264
306
 
265
307
  Secret = synchronize_api(_Secret)