modAL 1.0.1__tar.gz → 1.0.1.dev0__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.
Files changed (184) hide show
  1. {modal-1.0.1 → modal-1.0.1.dev0}/PKG-INFO +1 -1
  2. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_output.py +6 -15
  3. {modal-1.0.1 → modal-1.0.1.dev0}/modal/cli/app.py +2 -4
  4. {modal-1.0.1 → modal-1.0.1.dev0}/modal/cli/cluster.py +1 -2
  5. {modal-1.0.1 → modal-1.0.1.dev0}/modal/cli/container.py +1 -2
  6. {modal-1.0.1 → modal-1.0.1.dev0}/modal/cli/dict.py +1 -2
  7. {modal-1.0.1 → modal-1.0.1.dev0}/modal/cli/network_file_system.py +1 -2
  8. {modal-1.0.1 → modal-1.0.1.dev0}/modal/cli/queues.py +1 -2
  9. {modal-1.0.1 → modal-1.0.1.dev0}/modal/cli/secret.py +1 -2
  10. {modal-1.0.1 → modal-1.0.1.dev0}/modal/cli/utils.py +15 -5
  11. {modal-1.0.1 → modal-1.0.1.dev0}/modal/cli/volume.py +3 -4
  12. {modal-1.0.1 → modal-1.0.1.dev0}/modal/client.py +2 -3
  13. {modal-1.0.1 → modal-1.0.1.dev0}/modal/client.pyi +10 -2
  14. {modal-1.0.1 → modal-1.0.1.dev0}/modal/functions.pyi +6 -6
  15. {modal-1.0.1 → modal-1.0.1.dev0}/modal/image.py +0 -3
  16. {modal-1.0.1 → modal-1.0.1.dev0}/modal/image.pyi +0 -2
  17. {modal-1.0.1 → modal-1.0.1.dev0}/modal/sandbox.py +4 -1
  18. {modal-1.0.1 → modal-1.0.1.dev0}/modal/snapshot.py +3 -9
  19. {modal-1.0.1 → modal-1.0.1.dev0}/modal.egg-info/PKG-INFO +1 -1
  20. {modal-1.0.1 → modal-1.0.1.dev0}/modal.egg-info/SOURCES.txt +0 -1
  21. {modal-1.0.1 → modal-1.0.1.dev0}/modal_version/__init__.py +1 -1
  22. modal-1.0.1/modal/_utils/time_utils.py +0 -15
  23. {modal-1.0.1 → modal-1.0.1.dev0}/LICENSE +0 -0
  24. {modal-1.0.1 → modal-1.0.1.dev0}/README.md +0 -0
  25. {modal-1.0.1 → modal-1.0.1.dev0}/modal/__init__.py +0 -0
  26. {modal-1.0.1 → modal-1.0.1.dev0}/modal/__main__.py +0 -0
  27. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_clustered_functions.py +0 -0
  28. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_clustered_functions.pyi +0 -0
  29. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_container_entrypoint.py +0 -0
  30. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_functions.py +0 -0
  31. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_ipython.py +0 -0
  32. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_location.py +0 -0
  33. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_object.py +0 -0
  34. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_partial_function.py +0 -0
  35. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_pty.py +0 -0
  36. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_resolver.py +0 -0
  37. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_resources.py +0 -0
  38. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_runtime/__init__.py +0 -0
  39. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_runtime/asgi.py +0 -0
  40. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_runtime/container_io_manager.py +0 -0
  41. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_runtime/container_io_manager.pyi +0 -0
  42. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_runtime/execution_context.py +0 -0
  43. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_runtime/execution_context.pyi +0 -0
  44. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  45. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_runtime/telemetry.py +0 -0
  46. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_runtime/user_code_imports.py +0 -0
  47. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_serialization.py +0 -0
  48. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_traceback.py +0 -0
  49. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_tunnel.py +0 -0
  50. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_tunnel.pyi +0 -0
  51. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_type_manager.py +0 -0
  52. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_utils/__init__.py +0 -0
  53. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_utils/app_utils.py +0 -0
  54. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_utils/async_utils.py +0 -0
  55. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_utils/blob_utils.py +0 -0
  56. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_utils/bytes_io_segment_payload.py +0 -0
  57. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_utils/deprecation.py +0 -0
  58. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_utils/docker_utils.py +0 -0
  59. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_utils/function_utils.py +0 -0
  60. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_utils/git_utils.py +0 -0
  61. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_utils/grpc_testing.py +0 -0
  62. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_utils/grpc_utils.py +0 -0
  63. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_utils/hash_utils.py +0 -0
  64. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_utils/http_utils.py +0 -0
  65. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_utils/jwt_utils.py +0 -0
  66. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_utils/logger.py +0 -0
  67. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_utils/mount_utils.py +0 -0
  68. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_utils/name_utils.py +0 -0
  69. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_utils/package_utils.py +0 -0
  70. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_utils/pattern_utils.py +0 -0
  71. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_utils/rand_pb_testing.py +0 -0
  72. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_utils/shell_utils.py +0 -0
  73. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_vendor/__init__.py +0 -0
  74. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  75. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_vendor/cloudpickle.py +0 -0
  76. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_vendor/tblib.py +0 -0
  77. {modal-1.0.1 → modal-1.0.1.dev0}/modal/_watcher.py +0 -0
  78. {modal-1.0.1 → modal-1.0.1.dev0}/modal/app.py +0 -0
  79. {modal-1.0.1 → modal-1.0.1.dev0}/modal/app.pyi +0 -0
  80. {modal-1.0.1 → modal-1.0.1.dev0}/modal/call_graph.py +0 -0
  81. {modal-1.0.1 → modal-1.0.1.dev0}/modal/cli/__init__.py +0 -0
  82. {modal-1.0.1 → modal-1.0.1.dev0}/modal/cli/_download.py +0 -0
  83. {modal-1.0.1 → modal-1.0.1.dev0}/modal/cli/_traceback.py +0 -0
  84. {modal-1.0.1 → modal-1.0.1.dev0}/modal/cli/config.py +0 -0
  85. {modal-1.0.1 → modal-1.0.1.dev0}/modal/cli/entry_point.py +0 -0
  86. {modal-1.0.1 → modal-1.0.1.dev0}/modal/cli/environment.py +0 -0
  87. {modal-1.0.1 → modal-1.0.1.dev0}/modal/cli/import_refs.py +0 -0
  88. {modal-1.0.1 → modal-1.0.1.dev0}/modal/cli/launch.py +0 -0
  89. {modal-1.0.1 → modal-1.0.1.dev0}/modal/cli/profile.py +0 -0
  90. {modal-1.0.1 → modal-1.0.1.dev0}/modal/cli/programs/__init__.py +0 -0
  91. {modal-1.0.1 → modal-1.0.1.dev0}/modal/cli/programs/run_jupyter.py +0 -0
  92. {modal-1.0.1 → modal-1.0.1.dev0}/modal/cli/programs/vscode.py +0 -0
  93. {modal-1.0.1 → modal-1.0.1.dev0}/modal/cli/run.py +0 -0
  94. {modal-1.0.1 → modal-1.0.1.dev0}/modal/cli/token.py +0 -0
  95. {modal-1.0.1 → modal-1.0.1.dev0}/modal/cloud_bucket_mount.py +0 -0
  96. {modal-1.0.1 → modal-1.0.1.dev0}/modal/cloud_bucket_mount.pyi +0 -0
  97. {modal-1.0.1 → modal-1.0.1.dev0}/modal/cls.py +0 -0
  98. {modal-1.0.1 → modal-1.0.1.dev0}/modal/cls.pyi +0 -0
  99. {modal-1.0.1 → modal-1.0.1.dev0}/modal/config.py +0 -0
  100. {modal-1.0.1 → modal-1.0.1.dev0}/modal/container_process.py +0 -0
  101. {modal-1.0.1 → modal-1.0.1.dev0}/modal/container_process.pyi +0 -0
  102. {modal-1.0.1 → modal-1.0.1.dev0}/modal/dict.py +0 -0
  103. {modal-1.0.1 → modal-1.0.1.dev0}/modal/dict.pyi +0 -0
  104. {modal-1.0.1 → modal-1.0.1.dev0}/modal/environments.py +0 -0
  105. {modal-1.0.1 → modal-1.0.1.dev0}/modal/environments.pyi +0 -0
  106. {modal-1.0.1 → modal-1.0.1.dev0}/modal/exception.py +0 -0
  107. {modal-1.0.1 → modal-1.0.1.dev0}/modal/experimental/__init__.py +0 -0
  108. {modal-1.0.1 → modal-1.0.1.dev0}/modal/experimental/ipython.py +0 -0
  109. {modal-1.0.1 → modal-1.0.1.dev0}/modal/file_io.py +0 -0
  110. {modal-1.0.1 → modal-1.0.1.dev0}/modal/file_io.pyi +0 -0
  111. {modal-1.0.1 → modal-1.0.1.dev0}/modal/file_pattern_matcher.py +0 -0
  112. {modal-1.0.1 → modal-1.0.1.dev0}/modal/functions.py +0 -0
  113. {modal-1.0.1 → modal-1.0.1.dev0}/modal/gpu.py +0 -0
  114. {modal-1.0.1 → modal-1.0.1.dev0}/modal/io_streams.py +0 -0
  115. {modal-1.0.1 → modal-1.0.1.dev0}/modal/io_streams.pyi +0 -0
  116. {modal-1.0.1 → modal-1.0.1.dev0}/modal/mount.py +0 -0
  117. {modal-1.0.1 → modal-1.0.1.dev0}/modal/mount.pyi +0 -0
  118. {modal-1.0.1 → modal-1.0.1.dev0}/modal/network_file_system.py +0 -0
  119. {modal-1.0.1 → modal-1.0.1.dev0}/modal/network_file_system.pyi +0 -0
  120. {modal-1.0.1 → modal-1.0.1.dev0}/modal/object.py +0 -0
  121. {modal-1.0.1 → modal-1.0.1.dev0}/modal/object.pyi +0 -0
  122. {modal-1.0.1 → modal-1.0.1.dev0}/modal/output.py +0 -0
  123. {modal-1.0.1 → modal-1.0.1.dev0}/modal/parallel_map.py +0 -0
  124. {modal-1.0.1 → modal-1.0.1.dev0}/modal/parallel_map.pyi +0 -0
  125. {modal-1.0.1 → modal-1.0.1.dev0}/modal/partial_function.py +0 -0
  126. {modal-1.0.1 → modal-1.0.1.dev0}/modal/partial_function.pyi +0 -0
  127. {modal-1.0.1 → modal-1.0.1.dev0}/modal/proxy.py +0 -0
  128. {modal-1.0.1 → modal-1.0.1.dev0}/modal/proxy.pyi +0 -0
  129. {modal-1.0.1 → modal-1.0.1.dev0}/modal/py.typed +0 -0
  130. {modal-1.0.1 → modal-1.0.1.dev0}/modal/queue.py +0 -0
  131. {modal-1.0.1 → modal-1.0.1.dev0}/modal/queue.pyi +0 -0
  132. {modal-1.0.1 → modal-1.0.1.dev0}/modal/requirements/2023.12.312.txt +0 -0
  133. {modal-1.0.1 → modal-1.0.1.dev0}/modal/requirements/2023.12.txt +0 -0
  134. {modal-1.0.1 → modal-1.0.1.dev0}/modal/requirements/2024.04.txt +0 -0
  135. {modal-1.0.1 → modal-1.0.1.dev0}/modal/requirements/2024.10.txt +0 -0
  136. {modal-1.0.1 → modal-1.0.1.dev0}/modal/requirements/PREVIEW.txt +0 -0
  137. {modal-1.0.1 → modal-1.0.1.dev0}/modal/requirements/README.md +0 -0
  138. {modal-1.0.1 → modal-1.0.1.dev0}/modal/requirements/base-images.json +0 -0
  139. {modal-1.0.1 → modal-1.0.1.dev0}/modal/retries.py +0 -0
  140. {modal-1.0.1 → modal-1.0.1.dev0}/modal/runner.py +0 -0
  141. {modal-1.0.1 → modal-1.0.1.dev0}/modal/runner.pyi +0 -0
  142. {modal-1.0.1 → modal-1.0.1.dev0}/modal/running_app.py +0 -0
  143. {modal-1.0.1 → modal-1.0.1.dev0}/modal/sandbox.pyi +0 -0
  144. {modal-1.0.1 → modal-1.0.1.dev0}/modal/schedule.py +0 -0
  145. {modal-1.0.1 → modal-1.0.1.dev0}/modal/scheduler_placement.py +0 -0
  146. {modal-1.0.1 → modal-1.0.1.dev0}/modal/secret.py +0 -0
  147. {modal-1.0.1 → modal-1.0.1.dev0}/modal/secret.pyi +0 -0
  148. {modal-1.0.1 → modal-1.0.1.dev0}/modal/serving.py +0 -0
  149. {modal-1.0.1 → modal-1.0.1.dev0}/modal/serving.pyi +0 -0
  150. {modal-1.0.1 → modal-1.0.1.dev0}/modal/snapshot.pyi +0 -0
  151. {modal-1.0.1 → modal-1.0.1.dev0}/modal/stream_type.py +0 -0
  152. {modal-1.0.1 → modal-1.0.1.dev0}/modal/token_flow.py +0 -0
  153. {modal-1.0.1 → modal-1.0.1.dev0}/modal/token_flow.pyi +0 -0
  154. {modal-1.0.1 → modal-1.0.1.dev0}/modal/volume.py +0 -0
  155. {modal-1.0.1 → modal-1.0.1.dev0}/modal/volume.pyi +0 -0
  156. {modal-1.0.1 → modal-1.0.1.dev0}/modal.egg-info/dependency_links.txt +0 -0
  157. {modal-1.0.1 → modal-1.0.1.dev0}/modal.egg-info/entry_points.txt +0 -0
  158. {modal-1.0.1 → modal-1.0.1.dev0}/modal.egg-info/requires.txt +0 -0
  159. {modal-1.0.1 → modal-1.0.1.dev0}/modal.egg-info/top_level.txt +0 -0
  160. {modal-1.0.1 → modal-1.0.1.dev0}/modal_docs/__init__.py +0 -0
  161. {modal-1.0.1 → modal-1.0.1.dev0}/modal_docs/gen_cli_docs.py +0 -0
  162. {modal-1.0.1 → modal-1.0.1.dev0}/modal_docs/gen_reference_docs.py +0 -0
  163. {modal-1.0.1 → modal-1.0.1.dev0}/modal_docs/mdmd/__init__.py +0 -0
  164. {modal-1.0.1 → modal-1.0.1.dev0}/modal_docs/mdmd/mdmd.py +0 -0
  165. {modal-1.0.1 → modal-1.0.1.dev0}/modal_docs/mdmd/signatures.py +0 -0
  166. {modal-1.0.1 → modal-1.0.1.dev0}/modal_proto/__init__.py +0 -0
  167. {modal-1.0.1 → modal-1.0.1.dev0}/modal_proto/api.proto +0 -0
  168. {modal-1.0.1 → modal-1.0.1.dev0}/modal_proto/api_grpc.py +0 -0
  169. {modal-1.0.1 → modal-1.0.1.dev0}/modal_proto/api_pb2.py +0 -0
  170. {modal-1.0.1 → modal-1.0.1.dev0}/modal_proto/api_pb2.pyi +0 -0
  171. {modal-1.0.1 → modal-1.0.1.dev0}/modal_proto/api_pb2_grpc.py +0 -0
  172. {modal-1.0.1 → modal-1.0.1.dev0}/modal_proto/api_pb2_grpc.pyi +0 -0
  173. {modal-1.0.1 → modal-1.0.1.dev0}/modal_proto/modal_api_grpc.py +0 -0
  174. {modal-1.0.1 → modal-1.0.1.dev0}/modal_proto/modal_options_grpc.py +0 -0
  175. {modal-1.0.1 → modal-1.0.1.dev0}/modal_proto/options.proto +0 -0
  176. {modal-1.0.1 → modal-1.0.1.dev0}/modal_proto/options_grpc.py +0 -0
  177. {modal-1.0.1 → modal-1.0.1.dev0}/modal_proto/options_pb2.py +0 -0
  178. {modal-1.0.1 → modal-1.0.1.dev0}/modal_proto/options_pb2.pyi +0 -0
  179. {modal-1.0.1 → modal-1.0.1.dev0}/modal_proto/options_pb2_grpc.py +0 -0
  180. {modal-1.0.1 → modal-1.0.1.dev0}/modal_proto/options_pb2_grpc.pyi +0 -0
  181. {modal-1.0.1 → modal-1.0.1.dev0}/modal_proto/py.typed +0 -0
  182. {modal-1.0.1 → modal-1.0.1.dev0}/modal_version/__main__.py +0 -0
  183. {modal-1.0.1 → modal-1.0.1.dev0}/pyproject.toml +0 -0
  184. {modal-1.0.1 → modal-1.0.1.dev0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.0.1
3
+ Version: 1.0.1.dev0
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -32,7 +32,6 @@ from rich.progress import (
32
32
  from rich.spinner import Spinner
33
33
  from rich.text import Text
34
34
 
35
- from modal._utils.time_utils import timestamp_to_local
36
35
  from modal_proto import api_pb2
37
36
 
38
37
  from ._utils.grpc_utils import RETRYABLE_GRPC_STATUS_CODES, retry_transient_errors
@@ -82,27 +81,22 @@ def download_progress_bar() -> Progress:
82
81
  )
83
82
 
84
83
 
85
- class LineBufferedOutput:
84
+ class LineBufferedOutput(io.StringIO):
86
85
  """Output stream that buffers lines and passes them to a callback."""
87
86
 
88
87
  LINE_REGEX = re.compile("(\r\n|\r|\n)")
89
88
 
90
- def __init__(self, callback: Callable[[str], None], show_timestamps: bool):
89
+ def __init__(self, callback: Callable[[str], None]):
91
90
  self._callback = callback
92
91
  self._buf = ""
93
- self._show_timestamps = show_timestamps
94
92
 
95
- def write(self, log: api_pb2.TaskLogs):
96
- chunks = self.LINE_REGEX.split(self._buf + log.data)
93
+ def write(self, data: str):
94
+ chunks = self.LINE_REGEX.split(self._buf + data)
97
95
 
98
96
  # re.split("(<exp>)") returns the matched groups, and also the separators.
99
97
  # e.g. re.split("(+)", "a+b") returns ["a", "+", "b"].
100
98
  # This means that chunks is guaranteed to be odd in length.
101
99
 
102
- if self._show_timestamps:
103
- for i in range(0, len(chunks) - 1, 2):
104
- chunks[i] = f"{timestamp_to_local(log.timestamp)} {chunks[i]}"
105
-
106
100
  completed_lines = "".join(chunks[:-1])
107
101
  remainder = chunks[-1]
108
102
 
@@ -142,14 +136,12 @@ class OutputManager:
142
136
  _app_page_url: str | None
143
137
  _show_image_logs: bool
144
138
  _status_spinner_live: Live | None
145
- _show_timestamps: bool
146
139
 
147
140
  def __init__(
148
141
  self,
149
142
  *,
150
143
  stdout: io.TextIOWrapper | None = None,
151
144
  status_spinner_text: str = "Running app...",
152
- show_timestamps: bool = False,
153
145
  ):
154
146
  self._stdout = stdout or sys.stdout
155
147
  self._console = Console(file=stdout, highlight=False)
@@ -164,7 +156,6 @@ class OutputManager:
164
156
  self._app_page_url = None
165
157
  self._show_image_logs = False
166
158
  self._status_spinner_live = None
167
- self._show_timestamps = show_timestamps
168
159
 
169
160
  @classmethod
170
161
  def disable(cls):
@@ -364,9 +355,9 @@ class OutputManager:
364
355
  async def put_log_content(self, log: api_pb2.TaskLogs):
365
356
  stream = self._line_buffers.get(log.file_descriptor)
366
357
  if stream is None:
367
- stream = LineBufferedOutput(functools.partial(self._print_log, log.file_descriptor), self._show_timestamps)
358
+ stream = LineBufferedOutput(functools.partial(self._print_log, log.file_descriptor))
368
359
  self._line_buffers[log.file_descriptor] = stream
369
- stream.write(log)
360
+ stream.write(log.data)
370
361
 
371
362
  def flush_lines(self):
372
363
  for stream in self._line_buffers.values():
@@ -15,8 +15,7 @@ from modal.client import _Client
15
15
  from modal.environments import ensure_env
16
16
  from modal_proto import api_pb2
17
17
 
18
- from .._utils.time_utils import timestamp_to_local
19
- from .utils import ENV_OPTION, display_table, get_app_id_from_name, stream_app_logs
18
+ from .utils import ENV_OPTION, display_table, get_app_id_from_name, stream_app_logs, timestamp_to_local
20
19
 
21
20
  APP_IDENTIFIER = Argument("", help="App name or ID")
22
21
  NAME_OPTION = typer.Option("", "-n", "--name", help="Deprecated: Pass App name as a positional argument")
@@ -85,7 +84,6 @@ def logs(
85
84
  app_identifier: str = APP_IDENTIFIER,
86
85
  *,
87
86
  env: Optional[str] = ENV_OPTION,
88
- timestamps: bool = typer.Option(False, "--timestamps", help="Show timestamps for each log line"),
89
87
  ):
90
88
  """Show App logs, streaming while active.
91
89
 
@@ -105,7 +103,7 @@ def logs(
105
103
 
106
104
  """
107
105
  app_id = get_app_id(app_identifier, env)
108
- stream_app_logs(app_id, show_timestamps=timestamps)
106
+ stream_app_logs(app_id)
109
107
 
110
108
 
111
109
  @app_cli.command("rollback", no_args_is_help=True, context_settings={"ignore_unknown_options": True})
@@ -8,8 +8,7 @@ from rich.text import Text
8
8
  from modal._object import _get_environment_name
9
9
  from modal._pty import get_pty_info
10
10
  from modal._utils.async_utils import synchronizer
11
- from modal._utils.time_utils import timestamp_to_local
12
- from modal.cli.utils import ENV_OPTION, display_table, is_tty
11
+ from modal.cli.utils import ENV_OPTION, display_table, is_tty, timestamp_to_local
13
12
  from modal.client import _Client
14
13
  from modal.config import config
15
14
  from modal.container_process import _ContainerProcess
@@ -8,8 +8,7 @@ from modal._object import _get_environment_name
8
8
  from modal._pty import get_pty_info
9
9
  from modal._utils.async_utils import synchronizer
10
10
  from modal._utils.grpc_utils import retry_transient_errors
11
- from modal._utils.time_utils import timestamp_to_local
12
- from modal.cli.utils import ENV_OPTION, display_table, is_tty, stream_app_logs
11
+ from modal.cli.utils import ENV_OPTION, display_table, is_tty, stream_app_logs, timestamp_to_local
13
12
  from modal.client import _Client
14
13
  from modal.config import config
15
14
  from modal.container_process import _ContainerProcess
@@ -8,8 +8,7 @@ from typer import Argument, Option, Typer
8
8
  from modal._resolver import Resolver
9
9
  from modal._utils.async_utils import synchronizer
10
10
  from modal._utils.grpc_utils import retry_transient_errors
11
- from modal._utils.time_utils import timestamp_to_local
12
- from modal.cli.utils import ENV_OPTION, YES_OPTION, display_table
11
+ from modal.cli.utils import ENV_OPTION, YES_OPTION, display_table, timestamp_to_local
13
12
  from modal.client import _Client
14
13
  from modal.dict import _Dict
15
14
  from modal.environments import ensure_env
@@ -17,9 +17,8 @@ from modal._location import display_location
17
17
  from modal._output import OutputManager, ProgressHandler
18
18
  from modal._utils.async_utils import synchronizer
19
19
  from modal._utils.grpc_utils import retry_transient_errors
20
- from modal._utils.time_utils import timestamp_to_local
21
20
  from modal.cli._download import _volume_download
22
- from modal.cli.utils import ENV_OPTION, YES_OPTION, display_table
21
+ from modal.cli.utils import ENV_OPTION, YES_OPTION, display_table, timestamp_to_local
23
22
  from modal.client import _Client
24
23
  from modal.environments import ensure_env
25
24
  from modal.network_file_system import _NetworkFileSystem
@@ -8,8 +8,7 @@ from typer import Argument, Option, Typer
8
8
  from modal._resolver import Resolver
9
9
  from modal._utils.async_utils import synchronizer
10
10
  from modal._utils.grpc_utils import retry_transient_errors
11
- from modal._utils.time_utils import timestamp_to_local
12
- from modal.cli.utils import ENV_OPTION, YES_OPTION, display_table
11
+ from modal.cli.utils import ENV_OPTION, YES_OPTION, display_table, timestamp_to_local
13
12
  from modal.client import _Client
14
13
  from modal.environments import ensure_env
15
14
  from modal.queue import _Queue
@@ -13,8 +13,7 @@ from typer import Argument
13
13
 
14
14
  from modal._utils.async_utils import synchronizer
15
15
  from modal._utils.grpc_utils import retry_transient_errors
16
- from modal._utils.time_utils import timestamp_to_local
17
- from modal.cli.utils import ENV_OPTION, YES_OPTION, display_table
16
+ from modal.cli.utils import ENV_OPTION, YES_OPTION, display_table, timestamp_to_local
18
17
  from modal.client import _Client
19
18
  from modal.environments import ensure_env
20
19
  from modal.secret import _Secret
@@ -1,6 +1,7 @@
1
1
  # Copyright Modal Labs 2022
2
2
  import asyncio
3
3
  from collections.abc import Sequence
4
+ from datetime import datetime
4
5
  from json import dumps
5
6
  from typing import Optional, Union
6
7
 
@@ -22,13 +23,10 @@ from ..exception import NotFoundError
22
23
 
23
24
  @synchronizer.create_blocking
24
25
  async def stream_app_logs(
25
- app_id: Optional[str] = None,
26
- task_id: Optional[str] = None,
27
- app_logs_url: Optional[str] = None,
28
- show_timestamps: bool = False,
26
+ app_id: Optional[str] = None, task_id: Optional[str] = None, app_logs_url: Optional[str] = None
29
27
  ):
30
28
  client = await _Client.from_env()
31
- output_mgr = OutputManager(status_spinner_text=f"Tailing logs for {app_id}", show_timestamps=show_timestamps)
29
+ output_mgr = OutputManager(status_spinner_text=f"Tailing logs for {app_id}")
32
30
  try:
33
31
  with output_mgr.show_status_spinner():
34
32
  await get_app_logs_loop(client, output_mgr, app_id=app_id, task_id=task_id, app_logs_url=app_logs_url)
@@ -63,6 +61,18 @@ async def get_app_id_from_name(name: str, env: Optional[str], client: Optional[_
63
61
  return resp.app_id
64
62
 
65
63
 
64
+ def timestamp_to_local(ts: float, isotz: bool = True) -> str:
65
+ if ts > 0:
66
+ locale_tz = datetime.now().astimezone().tzinfo
67
+ dt = datetime.fromtimestamp(ts, tz=locale_tz)
68
+ if isotz:
69
+ return dt.isoformat(sep=" ", timespec="seconds")
70
+ else:
71
+ return f"{datetime.strftime(dt, '%Y-%m-%d %H:%M')} {locale_tz.tzname(dt)}"
72
+ else:
73
+ return None
74
+
75
+
66
76
  def _plain(text: Union[Text, str]) -> str:
67
77
  return text.plain if isinstance(text, Text) else text
68
78
 
@@ -15,9 +15,8 @@ import modal
15
15
  from modal._output import OutputManager, ProgressHandler
16
16
  from modal._utils.async_utils import synchronizer
17
17
  from modal._utils.grpc_utils import retry_transient_errors
18
- from modal._utils.time_utils import timestamp_to_local
19
18
  from modal.cli._download import _volume_download
20
- from modal.cli.utils import ENV_OPTION, YES_OPTION, display_table
19
+ from modal.cli.utils import ENV_OPTION, YES_OPTION, display_table, timestamp_to_local
21
20
  from modal.client import _Client
22
21
  from modal.environments import ensure_env
23
22
  from modal.volume import _AbstractVolumeUploadContextManager, _Volume
@@ -204,7 +203,7 @@ async def put(
204
203
  vol.object_id,
205
204
  vol._client,
206
205
  progress_cb=progress_handler.progress,
207
- force=force,
206
+ force=force
208
207
  ) as batch:
209
208
  batch.put_directory(local_path, remote_path)
210
209
  except FileExistsError as exc:
@@ -220,7 +219,7 @@ async def put(
220
219
  vol.object_id,
221
220
  vol._client,
222
221
  progress_cb=progress_handler.progress,
223
- force=force,
222
+ force=force
224
223
  ) as batch:
225
224
  batch.put_file(local_path, remote_path)
226
225
 
@@ -3,7 +3,6 @@ import asyncio
3
3
  import os
4
4
  import platform
5
5
  import sys
6
- import urllib.parse
7
6
  import warnings
8
7
  from collections.abc import AsyncGenerator, AsyncIterator, Collection, Mapping
9
8
  from typing import (
@@ -51,8 +50,8 @@ def _get_metadata(client_type: int, credentials: Optional[tuple[str, str]], vers
51
50
  "x-modal-client-version": version,
52
51
  "x-modal-client-type": str(client_type),
53
52
  "x-modal-python-version": python_version,
54
- "x-modal-node": urllib.parse.quote(platform.node()),
55
- "x-modal-platform": urllib.parse.quote(platform_str),
53
+ "x-modal-node": platform.node(),
54
+ "x-modal-platform": platform_str,
56
55
  }
57
56
  if credentials and client_type == api_pb2.CLIENT_TYPE_CLIENT:
58
57
  token_id, token_secret = credentials
@@ -27,7 +27,11 @@ class _Client:
27
27
  _snapshotted: bool
28
28
 
29
29
  def __init__(
30
- self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "1.0.1"
30
+ self,
31
+ server_url: str,
32
+ client_type: int,
33
+ credentials: typing.Optional[tuple[str, str]],
34
+ version: str = "1.0.1.dev0",
31
35
  ): ...
32
36
  def is_closed(self) -> bool: ...
33
37
  @property
@@ -86,7 +90,11 @@ class Client:
86
90
  _snapshotted: bool
87
91
 
88
92
  def __init__(
89
- self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "1.0.1"
93
+ self,
94
+ server_url: str,
95
+ client_type: int,
96
+ credentials: typing.Optional[tuple[str, str]],
97
+ version: str = "1.0.1.dev0",
90
98
  ): ...
91
99
  def is_closed(self) -> bool: ...
92
100
  @property
@@ -227,11 +227,11 @@ class Function(
227
227
 
228
228
  _call_generator: ___call_generator_spec[typing_extensions.Self]
229
229
 
230
- class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
230
+ class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
231
231
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER: ...
232
232
  async def aio(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER: ...
233
233
 
234
- remote: __remote_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
234
+ remote: __remote_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
235
235
 
236
236
  class __remote_gen_spec(typing_extensions.Protocol[SUPERSELF]):
237
237
  def __call__(self, /, *args, **kwargs) -> typing.Generator[typing.Any, None, None]: ...
@@ -246,12 +246,12 @@ class Function(
246
246
  self, *args: modal._functions.P.args, **kwargs: modal._functions.P.kwargs
247
247
  ) -> modal._functions.OriginalReturnType: ...
248
248
 
249
- class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
249
+ class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
250
250
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
251
251
  async def aio(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
252
252
 
253
253
  _experimental_spawn: ___experimental_spawn_spec[
254
- modal._functions.P, modal._functions.ReturnType, typing_extensions.Self
254
+ modal._functions.ReturnType, modal._functions.P, typing_extensions.Self
255
255
  ]
256
256
 
257
257
  class ___spawn_map_inner_spec(typing_extensions.Protocol[P_INNER, SUPERSELF]):
@@ -260,11 +260,11 @@ class Function(
260
260
 
261
261
  _spawn_map_inner: ___spawn_map_inner_spec[modal._functions.P, typing_extensions.Self]
262
262
 
263
- class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
263
+ class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
264
264
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
265
265
  async def aio(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
266
266
 
267
- spawn: __spawn_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
267
+ spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
268
268
 
269
269
  def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]: ...
270
270
 
@@ -423,9 +423,6 @@ class _Image(_Object, type_prefix="im"):
423
423
  self._deferred_mounts = other._deferred_mounts
424
424
  self._added_python_source_set = other._added_python_source_set
425
425
 
426
- def _get_metadata(self) -> Optional[Message]:
427
- return self._metadata
428
-
429
426
  def _hydrate_metadata(self, metadata: Optional[Message]):
430
427
  env_image_id = config.get("image_id") # set as an env var in containers
431
428
  if env_image_id == self.object_id:
@@ -96,7 +96,6 @@ class _Image(modal._object._Object):
96
96
 
97
97
  def _initialize_from_empty(self): ...
98
98
  def _initialize_from_other(self, other: _Image): ...
99
- def _get_metadata(self) -> typing.Optional[google.protobuf.message.Message]: ...
100
99
  def _hydrate_metadata(self, metadata: typing.Optional[google.protobuf.message.Message]): ...
101
100
  def _add_mount_layer_or_copy(self, mount: modal.mount._Mount, copy: bool = False): ...
102
101
  @property
@@ -353,7 +352,6 @@ class Image(modal.object.Object):
353
352
  def __init__(self, *args, **kwargs): ...
354
353
  def _initialize_from_empty(self): ...
355
354
  def _initialize_from_other(self, other: Image): ...
356
- def _get_metadata(self) -> typing.Optional[google.protobuf.message.Message]: ...
357
355
  def _hydrate_metadata(self, metadata: typing.Optional[google.protobuf.message.Message]): ...
358
356
  def _add_mount_layer_or_copy(self, mount: modal.mount.Mount, copy: bool = False): ...
359
357
  @property
@@ -738,7 +738,10 @@ class _Sandbox(_Object, type_prefix="sb"):
738
738
  recursive: Optional[bool] = None,
739
739
  timeout: Optional[int] = None,
740
740
  ) -> AsyncIterator[FileWatchEvent]:
741
- """Watch a file or directory in the Sandbox for changes."""
741
+ """Watch a file or directory in the Sandbox for changes.
742
+
743
+ See the [guide](/docs/guide/sandbox-files#file-watching) for usage information.
744
+ """
742
745
  task_id = await self._get_task_id()
743
746
  async for event in _FileIO.watch(path, self._client, task_id, filter, recursive, timeout):
744
747
  yield event
@@ -11,19 +11,13 @@ from .client import _Client
11
11
 
12
12
 
13
13
  class _SandboxSnapshot(_Object, type_prefix="sn"):
14
- """
15
- > Sandbox memory snapshots are in **early preview**.
16
-
17
- A `SandboxSnapshot` object lets you interact with a stored Sandbox snapshot that was created by calling
18
- `._experimental_snapshot()` on a Sandbox instance. This includes both the filesystem and memory state of
19
- the original Sandbox at the time the snapshot was taken.
14
+ """A `SandboxSnapshot` object lets you interact with a stored Sandbox snapshot that was created by calling
15
+ .snapshot() on a Sandbox instance. This includes both the filesystem and memory state of the original Sandbox at the
16
+ time the snapshot was taken.
20
17
  """
21
18
 
22
19
  @staticmethod
23
20
  async def from_id(sandbox_snapshot_id: str, client: Optional[_Client] = None):
24
- """
25
- Construct a `SandboxSnapshot` object from a sandbox snapshot ID.
26
- """
27
21
  if client is None:
28
22
  client = await _Client.from_env()
29
23
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.0.1
3
+ Version: 1.0.1.dev0
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -119,7 +119,6 @@ modal/_utils/package_utils.py
119
119
  modal/_utils/pattern_utils.py
120
120
  modal/_utils/rand_pb_testing.py
121
121
  modal/_utils/shell_utils.py
122
- modal/_utils/time_utils.py
123
122
  modal/_vendor/__init__.py
124
123
  modal/_vendor/a2wsgi_wsgi.py
125
124
  modal/_vendor/cloudpickle.py
@@ -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.0.1"
4
+ __version__ = "1.0.1.dev0"
@@ -1,15 +0,0 @@
1
- # Copyright Modal Labs 2025
2
- from datetime import datetime
3
- from typing import Optional
4
-
5
-
6
- def timestamp_to_local(ts: float, isotz: bool = True) -> Optional[str]:
7
- if ts > 0:
8
- locale_tz = datetime.now().astimezone().tzinfo
9
- dt = datetime.fromtimestamp(ts, tz=locale_tz)
10
- if isotz:
11
- return dt.isoformat(sep=" ", timespec="seconds")
12
- else:
13
- return f"{datetime.strftime(dt, '%Y-%m-%d %H:%M')} {locale_tz.tzname(dt)}"
14
- else:
15
- return None
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes