modal 0.72.4__py3-none-any.whl → 0.72.48__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.
Files changed (73) hide show
  1. modal/_container_entrypoint.py +5 -10
  2. modal/_object.py +297 -0
  3. modal/_resolver.py +7 -5
  4. modal/_runtime/container_io_manager.py +0 -11
  5. modal/_runtime/user_code_imports.py +7 -7
  6. modal/_serialization.py +4 -3
  7. modal/_tunnel.py +1 -1
  8. modal/app.py +14 -61
  9. modal/app.pyi +25 -25
  10. modal/cli/app.py +3 -2
  11. modal/cli/container.py +1 -1
  12. modal/cli/import_refs.py +185 -113
  13. modal/cli/launch.py +10 -5
  14. modal/cli/programs/run_jupyter.py +2 -2
  15. modal/cli/programs/vscode.py +3 -3
  16. modal/cli/run.py +134 -68
  17. modal/client.py +1 -0
  18. modal/client.pyi +18 -14
  19. modal/cloud_bucket_mount.py +4 -0
  20. modal/cloud_bucket_mount.pyi +4 -0
  21. modal/cls.py +33 -5
  22. modal/cls.pyi +20 -5
  23. modal/container_process.pyi +8 -6
  24. modal/dict.py +1 -1
  25. modal/dict.pyi +32 -29
  26. modal/environments.py +1 -1
  27. modal/environments.pyi +2 -1
  28. modal/experimental.py +47 -11
  29. modal/experimental.pyi +29 -0
  30. modal/file_io.pyi +30 -28
  31. modal/file_pattern_matcher.py +32 -25
  32. modal/functions.py +31 -23
  33. modal/functions.pyi +57 -50
  34. modal/gpu.py +19 -26
  35. modal/image.py +47 -19
  36. modal/image.pyi +28 -21
  37. modal/io_streams.pyi +14 -12
  38. modal/mount.py +14 -5
  39. modal/mount.pyi +28 -25
  40. modal/network_file_system.py +7 -7
  41. modal/network_file_system.pyi +27 -24
  42. modal/object.py +2 -265
  43. modal/object.pyi +46 -130
  44. modal/parallel_map.py +2 -2
  45. modal/parallel_map.pyi +10 -7
  46. modal/partial_function.py +22 -3
  47. modal/partial_function.pyi +45 -27
  48. modal/proxy.py +1 -1
  49. modal/proxy.pyi +2 -1
  50. modal/queue.py +1 -1
  51. modal/queue.pyi +26 -23
  52. modal/runner.py +14 -3
  53. modal/sandbox.py +11 -7
  54. modal/sandbox.pyi +30 -27
  55. modal/secret.py +1 -1
  56. modal/secret.pyi +2 -1
  57. modal/token_flow.pyi +6 -4
  58. modal/volume.py +1 -1
  59. modal/volume.pyi +36 -33
  60. {modal-0.72.4.dist-info → modal-0.72.48.dist-info}/METADATA +2 -2
  61. {modal-0.72.4.dist-info → modal-0.72.48.dist-info}/RECORD +73 -71
  62. modal_proto/api.proto +151 -4
  63. modal_proto/api_grpc.py +113 -0
  64. modal_proto/api_pb2.py +998 -795
  65. modal_proto/api_pb2.pyi +430 -11
  66. modal_proto/api_pb2_grpc.py +233 -1
  67. modal_proto/api_pb2_grpc.pyi +75 -3
  68. modal_proto/modal_api_grpc.py +7 -0
  69. modal_version/_version_generated.py +1 -1
  70. {modal-0.72.4.dist-info → modal-0.72.48.dist-info}/LICENSE +0 -0
  71. {modal-0.72.4.dist-info → modal-0.72.48.dist-info}/WHEEL +0 -0
  72. {modal-0.72.4.dist-info → modal-0.72.48.dist-info}/entry_points.txt +0 -0
  73. {modal-0.72.4.dist-info → modal-0.72.48.dist-info}/top_level.txt +0 -0
modal/dict.py CHANGED
@@ -7,6 +7,7 @@ from synchronicity.async_wrap import asynccontextmanager
7
7
 
8
8
  from modal_proto import api_pb2
9
9
 
10
+ from ._object import EPHEMERAL_OBJECT_HEARTBEAT_SLEEP, _get_environment_name, _Object, live_method, live_method_gen
10
11
  from ._resolver import Resolver
11
12
  from ._serialization import deserialize, serialize
12
13
  from ._utils.async_utils import TaskContext, synchronize_api
@@ -16,7 +17,6 @@ from ._utils.name_utils import check_object_name
16
17
  from .client import _Client
17
18
  from .config import logger
18
19
  from .exception import RequestSizeError
19
- from .object import EPHEMERAL_OBJECT_HEARTBEAT_SLEEP, _get_environment_name, _Object, live_method, live_method_gen
20
20
 
21
21
 
22
22
  def _serialize_dict(data):
modal/dict.pyi CHANGED
@@ -1,4 +1,5 @@
1
1
  import collections.abc
2
+ import modal._object
2
3
  import modal.client
3
4
  import modal.object
4
5
  import synchronicity.combined_types
@@ -7,7 +8,7 @@ import typing_extensions
7
8
 
8
9
  def _serialize_dict(data): ...
9
10
 
10
- class _Dict(modal.object._Object):
11
+ class _Dict(modal._object._Object):
11
12
  def __init__(self, data={}): ...
12
13
  @classmethod
13
14
  def ephemeral(
@@ -56,6 +57,8 @@ class _Dict(modal.object._Object):
56
57
  def values(self) -> collections.abc.AsyncIterator[typing.Any]: ...
57
58
  def items(self) -> collections.abc.AsyncIterator[tuple[typing.Any, typing.Any]]: ...
58
59
 
60
+ SUPERSELF = typing.TypeVar("SUPERSELF", covariant=True)
61
+
59
62
  class Dict(modal.object.Object):
60
63
  def __init__(self, data={}): ...
61
64
  @classmethod
@@ -115,86 +118,86 @@ class Dict(modal.object.Object):
115
118
 
116
119
  delete: __delete_spec
117
120
 
118
- class __clear_spec(typing_extensions.Protocol):
121
+ class __clear_spec(typing_extensions.Protocol[SUPERSELF]):
119
122
  def __call__(self) -> None: ...
120
123
  async def aio(self) -> None: ...
121
124
 
122
- clear: __clear_spec
125
+ clear: __clear_spec[typing_extensions.Self]
123
126
 
124
- class __get_spec(typing_extensions.Protocol):
127
+ class __get_spec(typing_extensions.Protocol[SUPERSELF]):
125
128
  def __call__(self, key: typing.Any, default: typing.Optional[typing.Any] = None) -> typing.Any: ...
126
129
  async def aio(self, key: typing.Any, default: typing.Optional[typing.Any] = None) -> typing.Any: ...
127
130
 
128
- get: __get_spec
131
+ get: __get_spec[typing_extensions.Self]
129
132
 
130
- class __contains_spec(typing_extensions.Protocol):
133
+ class __contains_spec(typing_extensions.Protocol[SUPERSELF]):
131
134
  def __call__(self, key: typing.Any) -> bool: ...
132
135
  async def aio(self, key: typing.Any) -> bool: ...
133
136
 
134
- contains: __contains_spec
137
+ contains: __contains_spec[typing_extensions.Self]
135
138
 
136
- class __len_spec(typing_extensions.Protocol):
139
+ class __len_spec(typing_extensions.Protocol[SUPERSELF]):
137
140
  def __call__(self) -> int: ...
138
141
  async def aio(self) -> int: ...
139
142
 
140
- len: __len_spec
143
+ len: __len_spec[typing_extensions.Self]
141
144
 
142
- class ____getitem___spec(typing_extensions.Protocol):
145
+ class ____getitem___spec(typing_extensions.Protocol[SUPERSELF]):
143
146
  def __call__(self, key: typing.Any) -> typing.Any: ...
144
147
  async def aio(self, key: typing.Any) -> typing.Any: ...
145
148
 
146
- __getitem__: ____getitem___spec
149
+ __getitem__: ____getitem___spec[typing_extensions.Self]
147
150
 
148
- class __update_spec(typing_extensions.Protocol):
151
+ class __update_spec(typing_extensions.Protocol[SUPERSELF]):
149
152
  def __call__(self, **kwargs) -> None: ...
150
153
  async def aio(self, **kwargs) -> None: ...
151
154
 
152
- update: __update_spec
155
+ update: __update_spec[typing_extensions.Self]
153
156
 
154
- class __put_spec(typing_extensions.Protocol):
157
+ class __put_spec(typing_extensions.Protocol[SUPERSELF]):
155
158
  def __call__(self, key: typing.Any, value: typing.Any) -> None: ...
156
159
  async def aio(self, key: typing.Any, value: typing.Any) -> None: ...
157
160
 
158
- put: __put_spec
161
+ put: __put_spec[typing_extensions.Self]
159
162
 
160
- class ____setitem___spec(typing_extensions.Protocol):
163
+ class ____setitem___spec(typing_extensions.Protocol[SUPERSELF]):
161
164
  def __call__(self, key: typing.Any, value: typing.Any) -> None: ...
162
165
  async def aio(self, key: typing.Any, value: typing.Any) -> None: ...
163
166
 
164
- __setitem__: ____setitem___spec
167
+ __setitem__: ____setitem___spec[typing_extensions.Self]
165
168
 
166
- class __pop_spec(typing_extensions.Protocol):
169
+ class __pop_spec(typing_extensions.Protocol[SUPERSELF]):
167
170
  def __call__(self, key: typing.Any) -> typing.Any: ...
168
171
  async def aio(self, key: typing.Any) -> typing.Any: ...
169
172
 
170
- pop: __pop_spec
173
+ pop: __pop_spec[typing_extensions.Self]
171
174
 
172
- class ____delitem___spec(typing_extensions.Protocol):
175
+ class ____delitem___spec(typing_extensions.Protocol[SUPERSELF]):
173
176
  def __call__(self, key: typing.Any) -> typing.Any: ...
174
177
  async def aio(self, key: typing.Any) -> typing.Any: ...
175
178
 
176
- __delitem__: ____delitem___spec
179
+ __delitem__: ____delitem___spec[typing_extensions.Self]
177
180
 
178
- class ____contains___spec(typing_extensions.Protocol):
181
+ class ____contains___spec(typing_extensions.Protocol[SUPERSELF]):
179
182
  def __call__(self, key: typing.Any) -> bool: ...
180
183
  async def aio(self, key: typing.Any) -> bool: ...
181
184
 
182
- __contains__: ____contains___spec
185
+ __contains__: ____contains___spec[typing_extensions.Self]
183
186
 
184
- class __keys_spec(typing_extensions.Protocol):
187
+ class __keys_spec(typing_extensions.Protocol[SUPERSELF]):
185
188
  def __call__(self) -> typing.Iterator[typing.Any]: ...
186
189
  def aio(self) -> collections.abc.AsyncIterator[typing.Any]: ...
187
190
 
188
- keys: __keys_spec
191
+ keys: __keys_spec[typing_extensions.Self]
189
192
 
190
- class __values_spec(typing_extensions.Protocol):
193
+ class __values_spec(typing_extensions.Protocol[SUPERSELF]):
191
194
  def __call__(self) -> typing.Iterator[typing.Any]: ...
192
195
  def aio(self) -> collections.abc.AsyncIterator[typing.Any]: ...
193
196
 
194
- values: __values_spec
197
+ values: __values_spec[typing_extensions.Self]
195
198
 
196
- class __items_spec(typing_extensions.Protocol):
199
+ class __items_spec(typing_extensions.Protocol[SUPERSELF]):
197
200
  def __call__(self) -> typing.Iterator[tuple[typing.Any, typing.Any]]: ...
198
201
  def aio(self) -> collections.abc.AsyncIterator[tuple[typing.Any, typing.Any]]: ...
199
202
 
200
- items: __items_spec
203
+ items: __items_spec[typing_extensions.Self]
modal/environments.py CHANGED
@@ -8,6 +8,7 @@ from google.protobuf.wrappers_pb2 import StringValue
8
8
 
9
9
  from modal_proto import api_pb2
10
10
 
11
+ from ._object import _Object
11
12
  from ._resolver import Resolver
12
13
  from ._utils.async_utils import synchronize_api, synchronizer
13
14
  from ._utils.deprecation import renamed_parameter
@@ -15,7 +16,6 @@ from ._utils.grpc_utils import retry_transient_errors
15
16
  from ._utils.name_utils import check_object_name
16
17
  from .client import _Client
17
18
  from .config import config, logger
18
- from .object import _Object
19
19
 
20
20
 
21
21
  @dataclass(frozen=True)
modal/environments.pyi CHANGED
@@ -1,4 +1,5 @@
1
1
  import google.protobuf.message
2
+ import modal._object
2
3
  import modal.client
3
4
  import modal.object
4
5
  import modal_proto.api_pb2
@@ -16,7 +17,7 @@ class EnvironmentSettings:
16
17
  def __delattr__(self, name): ...
17
18
  def __hash__(self): ...
18
19
 
19
- class _Environment(modal.object._Object):
20
+ class _Environment(modal._object._Object):
20
21
  _settings: EnvironmentSettings
21
22
 
22
23
  def __init__(self): ...
modal/experimental.py CHANGED
@@ -1,16 +1,16 @@
1
1
  # Copyright Modal Labs 2022
2
- from typing import (
3
- Any,
4
- Callable,
5
- )
2
+ from dataclasses import dataclass
3
+ from typing import Any, Callable, Optional
6
4
 
7
- import modal._clustered_functions
8
- from modal.functions import _Function
5
+ from modal_proto import api_pb2
9
6
 
7
+ from ._clustered_functions import ClusterInfo, get_cluster_info as _get_cluster_info
8
+ from ._object import _get_environment_name
10
9
  from ._runtime.container_io_manager import _ContainerIOManager
11
- from .exception import (
12
- InvalidError,
13
- )
10
+ from ._utils.async_utils import synchronizer
11
+ from .client import _Client
12
+ from .exception import InvalidError
13
+ from .functions import _Function
14
14
  from .partial_function import _PartialFunction, _PartialFunctionFlags
15
15
 
16
16
 
@@ -65,5 +65,41 @@ def clustered(size: int, broadcast: bool = True):
65
65
  return wrapper
66
66
 
67
67
 
68
- def get_cluster_info() -> modal._clustered_functions.ClusterInfo:
69
- return modal._clustered_functions.get_cluster_info()
68
+ def get_cluster_info() -> ClusterInfo:
69
+ return _get_cluster_info()
70
+
71
+
72
+ @dataclass
73
+ class AppInfo:
74
+ app_id: str
75
+ name: str
76
+ containers: int
77
+
78
+
79
+ @synchronizer.create_blocking
80
+ async def list_deployed_apps(environment_name: str = "", client: Optional[_Client] = None) -> list[AppInfo]:
81
+ """List deployed Apps along with the number of containers currently running."""
82
+ # This function exists to provide backwards compatibility for some users who had been
83
+ # calling into the private function that previously backed the `modal app list` CLI command.
84
+ # We plan to add more Python API for exposing this sort of information, but we haven't
85
+ # settled on a design we're happy with yet. In the meantime, this function will continue
86
+ # to support existing codebases. It's likely that the final API will be different
87
+ # (e.g. more oriented around the App object). This function should be gracefully deprecated
88
+ # one the new API is released.
89
+ client = client or await _Client.from_env()
90
+
91
+ resp: api_pb2.AppListResponse = await client.stub.AppList(
92
+ api_pb2.AppListRequest(environment_name=_get_environment_name(environment_name))
93
+ )
94
+
95
+ app_infos = []
96
+ for app_stats in resp.apps:
97
+ if app_stats.state == api_pb2.APP_STATE_DEPLOYED:
98
+ app_infos.append(
99
+ AppInfo(
100
+ app_id=app_stats.app_id,
101
+ name=app_stats.description,
102
+ containers=app_stats.n_running_tasks,
103
+ )
104
+ )
105
+ return app_infos
modal/experimental.pyi ADDED
@@ -0,0 +1,29 @@
1
+ import modal._clustered_functions
2
+ import modal.client
3
+ import typing
4
+ import typing_extensions
5
+
6
+ def stop_fetching_inputs(): ...
7
+ def get_local_input_concurrency(): ...
8
+ def set_local_input_concurrency(concurrency: int): ...
9
+ def clustered(size: int, broadcast: bool = True): ...
10
+ def get_cluster_info() -> modal._clustered_functions.ClusterInfo: ...
11
+
12
+ class AppInfo:
13
+ app_id: str
14
+ name: str
15
+ containers: int
16
+
17
+ def __init__(self, app_id: str, name: str, containers: int) -> None: ...
18
+ def __repr__(self): ...
19
+ def __eq__(self, other): ...
20
+
21
+ class __list_deployed_apps_spec(typing_extensions.Protocol):
22
+ def __call__(
23
+ self, environment_name: str = "", client: typing.Optional[modal.client.Client] = None
24
+ ) -> list[AppInfo]: ...
25
+ async def aio(
26
+ self, environment_name: str = "", client: typing.Optional[modal.client.Client] = None
27
+ ) -> list[AppInfo]: ...
28
+
29
+ list_deployed_apps: __list_deployed_apps_spec
modal/file_io.pyi CHANGED
@@ -100,6 +100,8 @@ class __replace_bytes_spec(typing_extensions.Protocol):
100
100
 
101
101
  replace_bytes: __replace_bytes_spec
102
102
 
103
+ SUPERSELF = typing.TypeVar("SUPERSELF", covariant=True)
104
+
103
105
  T_INNER = typing.TypeVar("T_INNER", covariant=True)
104
106
 
105
107
  class FileIO(typing.Generic[T]):
@@ -112,37 +114,37 @@ class FileIO(typing.Generic[T]):
112
114
  def _validate_mode(self, mode: str) -> None: ...
113
115
  def _handle_error(self, error: modal_proto.api_pb2.SystemErrorMessage) -> None: ...
114
116
 
115
- class ___consume_output_spec(typing_extensions.Protocol):
117
+ class ___consume_output_spec(typing_extensions.Protocol[SUPERSELF]):
116
118
  def __call__(self, exec_id: str) -> typing.Iterator[typing.Optional[bytes]]: ...
117
119
  def aio(self, exec_id: str) -> typing.AsyncIterator[typing.Optional[bytes]]: ...
118
120
 
119
- _consume_output: ___consume_output_spec
121
+ _consume_output: ___consume_output_spec[typing_extensions.Self]
120
122
 
121
- class ___consume_watch_output_spec(typing_extensions.Protocol):
123
+ class ___consume_watch_output_spec(typing_extensions.Protocol[SUPERSELF]):
122
124
  def __call__(self, exec_id: str) -> None: ...
123
125
  async def aio(self, exec_id: str) -> None: ...
124
126
 
125
- _consume_watch_output: ___consume_watch_output_spec
127
+ _consume_watch_output: ___consume_watch_output_spec[typing_extensions.Self]
126
128
 
127
- class ___parse_watch_output_spec(typing_extensions.Protocol):
129
+ class ___parse_watch_output_spec(typing_extensions.Protocol[SUPERSELF]):
128
130
  def __call__(self, event: bytes) -> typing.Optional[FileWatchEvent]: ...
129
131
  async def aio(self, event: bytes) -> typing.Optional[FileWatchEvent]: ...
130
132
 
131
- _parse_watch_output: ___parse_watch_output_spec
133
+ _parse_watch_output: ___parse_watch_output_spec[typing_extensions.Self]
132
134
 
133
- class ___wait_spec(typing_extensions.Protocol):
135
+ class ___wait_spec(typing_extensions.Protocol[SUPERSELF]):
134
136
  def __call__(self, exec_id: str) -> bytes: ...
135
137
  async def aio(self, exec_id: str) -> bytes: ...
136
138
 
137
- _wait: ___wait_spec
139
+ _wait: ___wait_spec[typing_extensions.Self]
138
140
 
139
141
  def _validate_type(self, data: typing.Union[bytes, str]) -> None: ...
140
142
 
141
- class ___open_file_spec(typing_extensions.Protocol):
143
+ class ___open_file_spec(typing_extensions.Protocol[SUPERSELF]):
142
144
  def __call__(self, path: str, mode: str) -> None: ...
143
145
  async def aio(self, path: str, mode: str) -> None: ...
144
146
 
145
- _open_file: ___open_file_spec
147
+ _open_file: ___open_file_spec[typing_extensions.Self]
146
148
 
147
149
  @classmethod
148
150
  def create(
@@ -153,49 +155,49 @@ class FileIO(typing.Generic[T]):
153
155
  task_id: str,
154
156
  ) -> FileIO: ...
155
157
 
156
- class ___make_read_request_spec(typing_extensions.Protocol):
158
+ class ___make_read_request_spec(typing_extensions.Protocol[SUPERSELF]):
157
159
  def __call__(self, n: typing.Optional[int]) -> bytes: ...
158
160
  async def aio(self, n: typing.Optional[int]) -> bytes: ...
159
161
 
160
- _make_read_request: ___make_read_request_spec
162
+ _make_read_request: ___make_read_request_spec[typing_extensions.Self]
161
163
 
162
- class __read_spec(typing_extensions.Protocol[T_INNER]):
164
+ class __read_spec(typing_extensions.Protocol[T_INNER, SUPERSELF]):
163
165
  def __call__(self, n: typing.Optional[int] = None) -> T_INNER: ...
164
166
  async def aio(self, n: typing.Optional[int] = None) -> T_INNER: ...
165
167
 
166
- read: __read_spec[T]
168
+ read: __read_spec[T, typing_extensions.Self]
167
169
 
168
- class __readline_spec(typing_extensions.Protocol[T_INNER]):
170
+ class __readline_spec(typing_extensions.Protocol[T_INNER, SUPERSELF]):
169
171
  def __call__(self) -> T_INNER: ...
170
172
  async def aio(self) -> T_INNER: ...
171
173
 
172
- readline: __readline_spec[T]
174
+ readline: __readline_spec[T, typing_extensions.Self]
173
175
 
174
- class __readlines_spec(typing_extensions.Protocol[T_INNER]):
176
+ class __readlines_spec(typing_extensions.Protocol[T_INNER, SUPERSELF]):
175
177
  def __call__(self) -> typing.Sequence[T_INNER]: ...
176
178
  async def aio(self) -> typing.Sequence[T_INNER]: ...
177
179
 
178
- readlines: __readlines_spec[T]
180
+ readlines: __readlines_spec[T, typing_extensions.Self]
179
181
 
180
- class __write_spec(typing_extensions.Protocol):
182
+ class __write_spec(typing_extensions.Protocol[SUPERSELF]):
181
183
  def __call__(self, data: typing.Union[bytes, str]) -> None: ...
182
184
  async def aio(self, data: typing.Union[bytes, str]) -> None: ...
183
185
 
184
- write: __write_spec
186
+ write: __write_spec[typing_extensions.Self]
185
187
 
186
- class __flush_spec(typing_extensions.Protocol):
188
+ class __flush_spec(typing_extensions.Protocol[SUPERSELF]):
187
189
  def __call__(self) -> None: ...
188
190
  async def aio(self) -> None: ...
189
191
 
190
- flush: __flush_spec
192
+ flush: __flush_spec[typing_extensions.Self]
191
193
 
192
194
  def _get_whence(self, whence: int): ...
193
195
 
194
- class __seek_spec(typing_extensions.Protocol):
196
+ class __seek_spec(typing_extensions.Protocol[SUPERSELF]):
195
197
  def __call__(self, offset: int, whence: int = 0) -> None: ...
196
198
  async def aio(self, offset: int, whence: int = 0) -> None: ...
197
199
 
198
- seek: __seek_spec
200
+ seek: __seek_spec[typing_extensions.Self]
199
201
 
200
202
  @classmethod
201
203
  def ls(cls, path: str, client: modal.client.Client, task_id: str) -> list[str]: ...
@@ -214,17 +216,17 @@ class FileIO(typing.Generic[T]):
214
216
  timeout: typing.Optional[int] = None,
215
217
  ) -> typing.Iterator[FileWatchEvent]: ...
216
218
 
217
- class ___close_spec(typing_extensions.Protocol):
219
+ class ___close_spec(typing_extensions.Protocol[SUPERSELF]):
218
220
  def __call__(self) -> None: ...
219
221
  async def aio(self) -> None: ...
220
222
 
221
- _close: ___close_spec
223
+ _close: ___close_spec[typing_extensions.Self]
222
224
 
223
- class __close_spec(typing_extensions.Protocol):
225
+ class __close_spec(typing_extensions.Protocol[SUPERSELF]):
224
226
  def __call__(self) -> None: ...
225
227
  async def aio(self) -> None: ...
226
228
 
227
- close: __close_spec
229
+ close: __close_spec[typing_extensions.Self]
228
230
 
229
231
  def _check_writable(self) -> None: ...
230
232
  def _check_readable(self) -> None: ...
@@ -35,7 +35,7 @@ class _AbstractPatternMatcher:
35
35
  """
36
36
  return _CustomPatternMatcher(lambda path: not self(path))
37
37
 
38
- def with_repr(self, custom_repr) -> "_AbstractPatternMatcher":
38
+ def _with_repr(self, custom_repr) -> "_AbstractPatternMatcher":
39
39
  # use to give an instance of a matcher a custom name - useful for visualizing default values in signatures
40
40
  self._custom_repr = custom_repr
41
41
  return self
@@ -60,7 +60,24 @@ class _CustomPatternMatcher(_AbstractPatternMatcher):
60
60
 
61
61
 
62
62
  class FilePatternMatcher(_AbstractPatternMatcher):
63
- """Allows matching file paths against a list of patterns."""
63
+ """
64
+ Allows matching file Path objects against a list of patterns.
65
+
66
+ **Usage:**
67
+ ```python
68
+ from pathlib import Path
69
+ from modal import FilePatternMatcher
70
+
71
+ matcher = FilePatternMatcher("*.py")
72
+
73
+ assert matcher(Path("foo.py"))
74
+
75
+ # You can also negate the matcher.
76
+ negated_matcher = ~matcher
77
+
78
+ assert not negated_matcher(Path("foo.py"))
79
+ ```
80
+ """
64
81
 
65
82
  patterns: list[Pattern]
66
83
  _delayed_init: Callable[[], None] = None
@@ -95,18 +112,26 @@ class FilePatternMatcher(_AbstractPatternMatcher):
95
112
  self._set_patterns(pattern)
96
113
 
97
114
  @classmethod
98
- def from_file(cls, file_path: Path) -> "FilePatternMatcher":
115
+ def from_file(cls, file_path: Union[str, Path]) -> "FilePatternMatcher":
99
116
  """Initialize a new FilePatternMatcher instance from a file.
100
117
 
101
118
  The patterns in the file will be read lazily when the matcher is first used.
102
119
 
103
120
  Args:
104
121
  file_path (Path): The path to the file containing patterns.
122
+
123
+ **Usage:**
124
+ ```python
125
+ from modal import FilePatternMatcher
126
+
127
+ matcher = FilePatternMatcher.from_file("/path/to/ignorefile")
128
+ ```
129
+
105
130
  """
106
131
  uninitialized = cls.__new__(cls)
107
132
 
108
133
  def _delayed_init():
109
- uninitialized._set_patterns(file_path.read_text("utf8").splitlines())
134
+ uninitialized._set_patterns(Path(file_path).read_text("utf8").splitlines())
110
135
  uninitialized._delayed_init = None
111
136
 
112
137
  uninitialized._delayed_init = _delayed_init
@@ -151,33 +176,15 @@ class FilePatternMatcher(_AbstractPatternMatcher):
151
176
  return matched
152
177
 
153
178
  def __call__(self, file_path: Path) -> bool:
154
- """Check if the path matches any of the patterns.
155
-
156
- Args:
157
- file_path (Path): The path to check.
158
-
159
- Returns:
160
- True if the path matches any of the patterns.
161
-
162
- Usage:
163
- ```python
164
- from pathlib import Path
165
- from modal import FilePatternMatcher
166
-
167
- matcher = FilePatternMatcher("*.py")
168
-
169
- assert matcher(Path("foo.py"))
170
- ```
171
- """
172
179
  if self._delayed_init:
173
180
  self._delayed_init()
174
181
  return self._matches(str(file_path))
175
182
 
176
183
 
177
- # with_repr allows us to use this matcher as a default value in a function signature
184
+ # _with_repr allows us to use this matcher as a default value in a function signature
178
185
  # and get a nice repr in the docs and auto-generated type stubs:
179
- NON_PYTHON_FILES = (~FilePatternMatcher("**/*.py")).with_repr(f"{__name__}.NON_PYTHON_FILES")
180
- _NOTHING = (~FilePatternMatcher()).with_repr(f"{__name__}._NOTHING") # match everything = ignore nothing
186
+ NON_PYTHON_FILES = (~FilePatternMatcher("**/*.py"))._with_repr(f"{__name__}.NON_PYTHON_FILES")
187
+ _NOTHING = (~FilePatternMatcher())._with_repr(f"{__name__}._NOTHING") # match everything = ignore nothing
181
188
 
182
189
 
183
190
  def _ignore_fn(ignore: Union[Sequence[str], Callable[[Path], bool]]) -> Callable[[Path], bool]: