modal 1.0.6.dev5__py3-none-any.whl → 1.0.6.dev6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of modal might be problematic. Click here for more details.
- modal/client.pyi +2 -2
- modal/file_pattern_matcher.py +31 -0
- modal/functions.pyi +6 -6
- modal/mount.py +24 -3
- modal/mount.pyi +16 -3
- {modal-1.0.6.dev5.dist-info → modal-1.0.6.dev6.dist-info}/METADATA +1 -1
- {modal-1.0.6.dev5.dist-info → modal-1.0.6.dev6.dist-info}/RECORD +12 -12
- modal_version/__init__.py +1 -1
- {modal-1.0.6.dev5.dist-info → modal-1.0.6.dev6.dist-info}/WHEEL +0 -0
- {modal-1.0.6.dev5.dist-info → modal-1.0.6.dev6.dist-info}/entry_points.txt +0 -0
- {modal-1.0.6.dev5.dist-info → modal-1.0.6.dev6.dist-info}/licenses/LICENSE +0 -0
- {modal-1.0.6.dev5.dist-info → modal-1.0.6.dev6.dist-info}/top_level.txt +0 -0
modal/client.pyi
CHANGED
|
@@ -31,7 +31,7 @@ class _Client:
|
|
|
31
31
|
server_url: str,
|
|
32
32
|
client_type: int,
|
|
33
33
|
credentials: typing.Optional[tuple[str, str]],
|
|
34
|
-
version: str = "1.0.6.
|
|
34
|
+
version: str = "1.0.6.dev6",
|
|
35
35
|
):
|
|
36
36
|
"""mdmd:hidden
|
|
37
37
|
The Modal client object is not intended to be instantiated directly by users.
|
|
@@ -160,7 +160,7 @@ class Client:
|
|
|
160
160
|
server_url: str,
|
|
161
161
|
client_type: int,
|
|
162
162
|
credentials: typing.Optional[tuple[str, str]],
|
|
163
|
-
version: str = "1.0.6.
|
|
163
|
+
version: str = "1.0.6.dev6",
|
|
164
164
|
):
|
|
165
165
|
"""mdmd:hidden
|
|
166
166
|
The Modal client object is not intended to be instantiated directly by users.
|
modal/file_pattern_matcher.py
CHANGED
|
@@ -46,6 +46,18 @@ class _AbstractPatternMatcher:
|
|
|
46
46
|
|
|
47
47
|
return super().__repr__()
|
|
48
48
|
|
|
49
|
+
@abstractmethod
|
|
50
|
+
def can_prune_directories(self) -> bool:
|
|
51
|
+
"""
|
|
52
|
+
Returns True if this pattern matcher allows safe early directory pruning.
|
|
53
|
+
|
|
54
|
+
Directory pruning is safe when matching directories can be skipped entirely
|
|
55
|
+
without missing any files that should be included.
|
|
56
|
+
|
|
57
|
+
An example where pruning is not safe is for inverted patterns, like "!**/*.py".
|
|
58
|
+
"""
|
|
59
|
+
...
|
|
60
|
+
|
|
49
61
|
@abstractmethod
|
|
50
62
|
def __call__(self, path: Path) -> bool: ...
|
|
51
63
|
|
|
@@ -54,6 +66,15 @@ class _CustomPatternMatcher(_AbstractPatternMatcher):
|
|
|
54
66
|
def __init__(self, predicate: Callable[[Path], bool]):
|
|
55
67
|
self._predicate = predicate
|
|
56
68
|
|
|
69
|
+
def can_prune_directories(self) -> bool:
|
|
70
|
+
"""
|
|
71
|
+
Custom pattern matchers (like negated matchers) cannot safely prune directories.
|
|
72
|
+
|
|
73
|
+
Since these are arbitrary predicates, we cannot determine if a directory
|
|
74
|
+
can be safely skipped without evaluating all files within it.
|
|
75
|
+
"""
|
|
76
|
+
return False
|
|
77
|
+
|
|
57
78
|
def __call__(self, path: Path) -> bool:
|
|
58
79
|
return self._predicate(path)
|
|
59
80
|
|
|
@@ -173,6 +194,16 @@ class FilePatternMatcher(_AbstractPatternMatcher):
|
|
|
173
194
|
|
|
174
195
|
return matched
|
|
175
196
|
|
|
197
|
+
def can_prune_directories(self) -> bool:
|
|
198
|
+
"""
|
|
199
|
+
Returns True if this pattern matcher allows safe early directory pruning.
|
|
200
|
+
|
|
201
|
+
Directory pruning is safe when matching directories can be skipped entirely
|
|
202
|
+
without missing any files that should be included. This is for example not
|
|
203
|
+
safe when we have inverted/negated ignore patterns (e.g. "!**/*.py").
|
|
204
|
+
"""
|
|
205
|
+
return not any(pattern.exclusion for pattern in self.patterns)
|
|
206
|
+
|
|
176
207
|
def __call__(self, file_path: Path) -> bool:
|
|
177
208
|
if self._delayed_init:
|
|
178
209
|
self._delayed_init()
|
modal/functions.pyi
CHANGED
|
@@ -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[
|
|
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.
|
|
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[
|
|
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.
|
|
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[
|
|
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.
|
|
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."""
|
modal/mount.py
CHANGED
|
@@ -8,7 +8,7 @@ import re
|
|
|
8
8
|
import time
|
|
9
9
|
import typing
|
|
10
10
|
import warnings
|
|
11
|
-
from collections.abc import AsyncGenerator
|
|
11
|
+
from collections.abc import AsyncGenerator, Generator
|
|
12
12
|
from pathlib import Path, PurePosixPath
|
|
13
13
|
from typing import Callable, Optional, Sequence, Union
|
|
14
14
|
|
|
@@ -133,12 +133,27 @@ class _MountFile(_MountEntry):
|
|
|
133
133
|
class _MountDir(_MountEntry):
|
|
134
134
|
local_dir: Path
|
|
135
135
|
remote_path: PurePosixPath
|
|
136
|
-
ignore: Callable[[Path], bool]
|
|
136
|
+
ignore: Union[Callable[[Path], bool], modal.file_pattern_matcher._AbstractPatternMatcher]
|
|
137
137
|
recursive: bool
|
|
138
138
|
|
|
139
139
|
def description(self):
|
|
140
140
|
return str(self.local_dir.expanduser().absolute())
|
|
141
141
|
|
|
142
|
+
def _walk_and_prune(self, top_dir: Path) -> Generator[str, None, None]:
|
|
143
|
+
"""Walk directories and prune ignored directories early."""
|
|
144
|
+
for root, dirs, files in os.walk(top_dir, topdown=True):
|
|
145
|
+
# with topdown=True, os.walk allows modifying the dirs list in-place, and will only
|
|
146
|
+
# recurse into dirs that are not ignored.
|
|
147
|
+
dirs[:] = [d for d in dirs if not self.ignore(Path(os.path.join(root, d)).relative_to(top_dir))]
|
|
148
|
+
for file in files:
|
|
149
|
+
yield os.path.join(root, file)
|
|
150
|
+
|
|
151
|
+
def _walk_all(self, top_dir: Path) -> Generator[str, None, None]:
|
|
152
|
+
"""Walk all directories without early pruning - safe for complex/inverted ignore patterns."""
|
|
153
|
+
for root, _, files in os.walk(top_dir):
|
|
154
|
+
for file in files:
|
|
155
|
+
yield os.path.join(root, file)
|
|
156
|
+
|
|
142
157
|
def get_files_to_upload(self):
|
|
143
158
|
# we can't use .resolve() eagerly here since that could end up "renaming" symlinked files
|
|
144
159
|
# see test_mount_directory_with_symlinked_file
|
|
@@ -153,7 +168,13 @@ class _MountDir(_MountEntry):
|
|
|
153
168
|
raise NotADirectoryError(msg)
|
|
154
169
|
|
|
155
170
|
if self.recursive:
|
|
156
|
-
|
|
171
|
+
if (
|
|
172
|
+
isinstance(self.ignore, modal.file_pattern_matcher._AbstractPatternMatcher)
|
|
173
|
+
and self.ignore.can_prune_directories()
|
|
174
|
+
):
|
|
175
|
+
gen = self._walk_and_prune(local_dir)
|
|
176
|
+
else:
|
|
177
|
+
gen = self._walk_all(local_dir)
|
|
157
178
|
else:
|
|
158
179
|
gen = (dir_entry.path for dir_entry in os.scandir(local_dir) if dir_entry.is_file())
|
|
159
180
|
|
modal/mount.pyi
CHANGED
|
@@ -4,6 +4,7 @@ import modal._object
|
|
|
4
4
|
import modal._resolver
|
|
5
5
|
import modal._utils.blob_utils
|
|
6
6
|
import modal.client
|
|
7
|
+
import modal.file_pattern_matcher
|
|
7
8
|
import modal.object
|
|
8
9
|
import modal_proto.api_pb2
|
|
9
10
|
import pathlib
|
|
@@ -49,14 +50,24 @@ class _MountFile(_MountEntry):
|
|
|
49
50
|
...
|
|
50
51
|
|
|
51
52
|
class _MountDir(_MountEntry):
|
|
52
|
-
"""_MountDir(local_dir: pathlib.Path, remote_path: pathlib.PurePosixPath, ignore: Callable[[pathlib.Path], bool], recursive: bool)"""
|
|
53
|
+
"""_MountDir(local_dir: pathlib.Path, remote_path: pathlib.PurePosixPath, ignore: Union[Callable[[pathlib.Path], bool], modal.file_pattern_matcher._AbstractPatternMatcher], recursive: bool)"""
|
|
53
54
|
|
|
54
55
|
local_dir: pathlib.Path
|
|
55
56
|
remote_path: pathlib.PurePosixPath
|
|
56
|
-
ignore:
|
|
57
|
+
ignore: typing.Union[
|
|
58
|
+
collections.abc.Callable[[pathlib.Path], bool], modal.file_pattern_matcher._AbstractPatternMatcher
|
|
59
|
+
]
|
|
57
60
|
recursive: bool
|
|
58
61
|
|
|
59
62
|
def description(self): ...
|
|
63
|
+
def _walk_and_prune(self, top_dir: pathlib.Path) -> collections.abc.Generator[str, None, None]:
|
|
64
|
+
"""Walk directories and prune ignored directories early."""
|
|
65
|
+
...
|
|
66
|
+
|
|
67
|
+
def _walk_all(self, top_dir: pathlib.Path) -> collections.abc.Generator[str, None, None]:
|
|
68
|
+
"""Walk all directories without early pruning - safe for complex/inverted ignore patterns."""
|
|
69
|
+
...
|
|
70
|
+
|
|
60
71
|
def get_files_to_upload(self): ...
|
|
61
72
|
def watch_entry(self): ...
|
|
62
73
|
def top_level_paths(self) -> list[tuple[pathlib.Path, pathlib.PurePosixPath]]: ...
|
|
@@ -64,7 +75,9 @@ class _MountDir(_MountEntry):
|
|
|
64
75
|
self,
|
|
65
76
|
local_dir: pathlib.Path,
|
|
66
77
|
remote_path: pathlib.PurePosixPath,
|
|
67
|
-
ignore:
|
|
78
|
+
ignore: typing.Union[
|
|
79
|
+
collections.abc.Callable[[pathlib.Path], bool], modal.file_pattern_matcher._AbstractPatternMatcher
|
|
80
|
+
],
|
|
68
81
|
recursive: bool,
|
|
69
82
|
) -> None:
|
|
70
83
|
"""Initialize self. See help(type(self)) for accurate signature."""
|
|
@@ -22,7 +22,7 @@ modal/app.py,sha256=fCKq3TJ2Y5LB2WKNs6pp_5XECNH5avUL01jQljuoYRU,46603
|
|
|
22
22
|
modal/app.pyi,sha256=Z6wi_dkXywiaM2rvAvguj2Wgu9ZgPjMSLl1nH1a7EYI,42243
|
|
23
23
|
modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
|
|
24
24
|
modal/client.py,sha256=OwISJvkgMb-rHm9Gc4i-7YcDgGiZgwJ7F_PzwZH7a6Q,16847
|
|
25
|
-
modal/client.pyi,sha256=
|
|
25
|
+
modal/client.pyi,sha256=gGjeEIHx-7UiL2SULfl2zvT2qPbu9JsBG9gEzoAb_4o,15079
|
|
26
26
|
modal/cloud_bucket_mount.py,sha256=YOe9nnvSr4ZbeCn587d7_VhE9IioZYRvF9VYQTQux08,5914
|
|
27
27
|
modal/cloud_bucket_mount.pyi,sha256=-qSfYAQvIoO_l2wsCCGTG5ZUwQieNKXdAO00yP1-LYU,7394
|
|
28
28
|
modal/cls.py,sha256=EFrM949jNXJpmwB2G_1d28b8IpHShfKIEIaiPkZqeOU,39881
|
|
@@ -37,16 +37,16 @@ modal/environments.pyi,sha256=9-KtrzAcUe55cCP4020lSUD7-fWS7OPakAHssq4-bro,4219
|
|
|
37
37
|
modal/exception.py,sha256=RjfKTJH7-Gcf_33BGkvDch-ry1Zx9u6-0QLViNxNTaU,5520
|
|
38
38
|
modal/file_io.py,sha256=SCBfLk5gRieqdTVlA_f-2YHHtRp7Iy_sA6iR1zPsO3c,21100
|
|
39
39
|
modal/file_io.pyi,sha256=_Hm-59MrppfuBYxtzdJkA2Jf9zI5LlbPh_0gURk0_7s,15222
|
|
40
|
-
modal/file_pattern_matcher.py,sha256=
|
|
40
|
+
modal/file_pattern_matcher.py,sha256=urAue8es8jxqX94k9EYoZxxhtfgOlsEES8lbFHOorzc,7734
|
|
41
41
|
modal/functions.py,sha256=kcNHvqeGBxPI7Cgd57NIBBghkfbeFJzXO44WW0jSmao,325
|
|
42
|
-
modal/functions.pyi,sha256=
|
|
42
|
+
modal/functions.pyi,sha256=FJe_91dSrMCRNVT-YV1UhtxFKzIvL_C5q8xdk08-wT8,34840
|
|
43
43
|
modal/gpu.py,sha256=Fe5ORvVPDIstSq1xjmM6OoNgLYFWvogP9r5BgmD3hYg,6769
|
|
44
44
|
modal/image.py,sha256=3oF3wo5uJ9tlOV8yotixBJ-ZR1tOkRE1Qvq4E3G7Fhk,94159
|
|
45
45
|
modal/image.pyi,sha256=ha8QhMPDRmfTSoRlQ8CgV4-AZ7kYKK_-R2UXLyGPgQU,68172
|
|
46
46
|
modal/io_streams.py,sha256=FUDpBsVK8isqwyC7DtAcQZhaHlMFSaNZGhYJOg-SFW0,15590
|
|
47
47
|
modal/io_streams.pyi,sha256=5b3b93ztZeR8IpJtNIGffX24QLPgocE4-gAps8y7CKU,13824
|
|
48
|
-
modal/mount.py,sha256=
|
|
49
|
-
modal/mount.pyi,sha256=
|
|
48
|
+
modal/mount.py,sha256=VrExPZApUnuh9shJx-tzdmQJ9ASTSK-SEaYHDBkQl-k,36632
|
|
49
|
+
modal/mount.pyi,sha256=n6AuS8J3bTCQj750nVZZdVBvzCAlSM2fyxAt_5LLFik,20264
|
|
50
50
|
modal/network_file_system.py,sha256=92U94Wk2fP40LlgLDIHkTqQ-zc21YxaG6SdFQy8SudU,14731
|
|
51
51
|
modal/network_file_system.pyi,sha256=Td_IobHr84iLo_9LZKQ4tNdUB60yjX8QWBaFiUvhfi8,17685
|
|
52
52
|
modal/object.py,sha256=bTeskuY8JFrESjU4_UL_nTwYlBQdOLmVaOX3X6EMxsg,164
|
|
@@ -147,7 +147,7 @@ modal/requirements/2024.10.txt,sha256=qD-5cVIVM9wXesJ6JC89Ew-3m2KjEElUz3jaw_MddR
|
|
|
147
147
|
modal/requirements/PREVIEW.txt,sha256=KxDaVTOwatHvboDo4lorlgJ7-n-MfAwbPwxJ0zcJqrs,312
|
|
148
148
|
modal/requirements/README.md,sha256=9tK76KP0Uph7O0M5oUgsSwEZDj5y-dcUPsnpR0Sc-Ik,854
|
|
149
149
|
modal/requirements/base-images.json,sha256=f1bwyp2UkM844eoO9Qk30gQw_xrMqKpMSeJ6MErXnEk,995
|
|
150
|
-
modal-1.0.6.
|
|
150
|
+
modal-1.0.6.dev6.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
|
|
151
151
|
modal_docs/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,28
|
|
152
152
|
modal_docs/gen_cli_docs.py,sha256=c1yfBS_x--gL5bs0N4ihMwqwX8l3IBWSkBAKNNIi6bQ,3801
|
|
153
153
|
modal_docs/gen_reference_docs.py,sha256=d_CQUGQ0rfw28u75I2mov9AlS773z9rG40-yq5o7g2U,6359
|
|
@@ -170,10 +170,10 @@ modal_proto/options_pb2.pyi,sha256=l7DBrbLO7q3Ir-XDkWsajm0d0TQqqrfuX54i4BMpdQg,1
|
|
|
170
170
|
modal_proto/options_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
|
|
171
171
|
modal_proto/options_pb2_grpc.pyi,sha256=CImmhxHsYnF09iENPoe8S4J-n93jtgUYD2JPAc0yJSI,247
|
|
172
172
|
modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
173
|
-
modal_version/__init__.py,sha256=
|
|
173
|
+
modal_version/__init__.py,sha256=96w9g55OJHHqMubYKEzjyhkK-x8jBv5_ygXx1X-3xl8,120
|
|
174
174
|
modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
|
|
175
|
-
modal-1.0.6.
|
|
176
|
-
modal-1.0.6.
|
|
177
|
-
modal-1.0.6.
|
|
178
|
-
modal-1.0.6.
|
|
179
|
-
modal-1.0.6.
|
|
175
|
+
modal-1.0.6.dev6.dist-info/METADATA,sha256=vTMJNJ_y_IHj7UZ6iLp289XUWFkesgj3JkdgfMCCmiU,2461
|
|
176
|
+
modal-1.0.6.dev6.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
|
|
177
|
+
modal-1.0.6.dev6.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
|
|
178
|
+
modal-1.0.6.dev6.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
|
|
179
|
+
modal-1.0.6.dev6.dist-info/RECORD,,
|
modal_version/__init__.py
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|