modal 0.68.30__py3-none-any.whl → 0.68.34__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.
modal/client.pyi CHANGED
@@ -26,7 +26,7 @@ class _Client:
26
26
  _stub: typing.Optional[modal_proto.api_grpc.ModalClientStub]
27
27
 
28
28
  def __init__(
29
- self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.68.30"
29
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.68.34"
30
30
  ): ...
31
31
  def is_closed(self) -> bool: ...
32
32
  @property
@@ -81,7 +81,7 @@ class Client:
81
81
  _stub: typing.Optional[modal_proto.api_grpc.ModalClientStub]
82
82
 
83
83
  def __init__(
84
- self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.68.30"
84
+ self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.68.34"
85
85
  ): ...
86
86
  def is_closed(self) -> bool: ...
87
87
  @property
@@ -10,13 +10,56 @@ then asking it whether file paths match any of its patterns.
10
10
  """
11
11
 
12
12
  import os
13
+ from abc import abstractmethod
13
14
  from pathlib import Path
14
- from typing import Callable
15
+ from typing import Callable, Optional
15
16
 
16
17
  from ._utils.pattern_utils import Pattern
17
18
 
18
19
 
19
- class FilePatternMatcher:
20
+ class _AbstractPatternMatcher:
21
+ _custom_repr: Optional[str] = None
22
+
23
+ def __invert__(self) -> "_AbstractPatternMatcher":
24
+ """Invert the filter. Returns a function that returns True if the path does not match any of the patterns.
25
+
26
+ Usage:
27
+ ```python
28
+ from pathlib import Path
29
+ from modal import FilePatternMatcher
30
+
31
+ inverted_matcher = ~FilePatternMatcher("**/*.py")
32
+
33
+ assert not inverted_matcher(Path("foo.py"))
34
+ ```
35
+ """
36
+ return _CustomPatternMatcher(lambda path: not self(path))
37
+
38
+ def with_repr(self, custom_repr) -> "_AbstractPatternMatcher":
39
+ # use to give an instance of a matcher a custom name - useful for visualizing default values in signatures
40
+ self._custom_repr = custom_repr
41
+ return self
42
+
43
+ def __repr__(self) -> str:
44
+ if self._custom_repr:
45
+ return self._custom_repr
46
+
47
+ return super().__repr__()
48
+
49
+ @abstractmethod
50
+ def __call__(self, path: Path) -> bool:
51
+ ...
52
+
53
+
54
+ class _CustomPatternMatcher(_AbstractPatternMatcher):
55
+ def __init__(self, predicate: Callable[[Path], bool]):
56
+ self._predicate = predicate
57
+
58
+ def __call__(self, path: Path) -> bool:
59
+ return self._predicate(path)
60
+
61
+
62
+ class FilePatternMatcher(_AbstractPatternMatcher):
20
63
  """Allows matching file paths against a list of patterns."""
21
64
 
22
65
  def __init__(self, *pattern: str) -> None:
@@ -105,17 +148,7 @@ class FilePatternMatcher:
105
148
  """
106
149
  return self._matches(str(file_path))
107
150
 
108
- def __invert__(self) -> Callable[[Path], bool]:
109
- """Invert the filter. Returns a function that returns True if the path does not match any of the patterns.
110
-
111
- Usage:
112
- ```python
113
- from pathlib import Path
114
- from modal import FilePatternMatcher
115
-
116
- inverted_matcher = ~FilePatternMatcher("**/*.py")
117
151
 
118
- assert not inverted_matcher(Path("foo.py"))
119
- ```
120
- """
121
- return lambda path: not self(path)
152
+ # with_repr allows us to use this matcher as a default value in a function signature
153
+ # and get a nice repr in the docs and auto-generated type stubs:
154
+ NON_PYTHON_FILES = (~FilePatternMatcher("**/*.py")).with_repr(f"{__name__}.NON_PYTHON_FILES")
modal/functions.pyi CHANGED
@@ -460,11 +460,11 @@ class Function(typing.Generic[P, ReturnType, OriginalReturnType], modal.object.O
460
460
 
461
461
  _call_generator_nowait: ___call_generator_nowait_spec
462
462
 
463
- class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
463
+ class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER]):
464
464
  def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER: ...
465
465
  async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER: ...
466
466
 
467
- remote: __remote_spec[ReturnType, P]
467
+ remote: __remote_spec[P, ReturnType]
468
468
 
469
469
  class __remote_gen_spec(typing_extensions.Protocol):
470
470
  def __call__(self, *args, **kwargs) -> typing.Generator[typing.Any, None, None]: ...
@@ -477,17 +477,17 @@ class Function(typing.Generic[P, ReturnType, OriginalReturnType], modal.object.O
477
477
  def _get_obj(self) -> typing.Optional[modal.cls.Obj]: ...
478
478
  def local(self, *args: P.args, **kwargs: P.kwargs) -> OriginalReturnType: ...
479
479
 
480
- class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
480
+ class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER]):
481
481
  def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
482
482
  async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
483
483
 
484
- _experimental_spawn: ___experimental_spawn_spec[ReturnType, P]
484
+ _experimental_spawn: ___experimental_spawn_spec[P, ReturnType]
485
485
 
486
- class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER]):
486
+ class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER]):
487
487
  def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
488
488
  async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
489
489
 
490
- spawn: __spawn_spec[ReturnType, P]
490
+ spawn: __spawn_spec[P, ReturnType]
491
491
 
492
492
  def get_raw_f(self) -> typing.Callable[..., typing.Any]: ...
493
493
 
modal/image.py CHANGED
@@ -38,7 +38,7 @@ from .cloud_bucket_mount import _CloudBucketMount
38
38
  from .config import config, logger, user_config_path
39
39
  from .environments import _get_environment_cached
40
40
  from .exception import InvalidError, NotFoundError, RemoteError, VersionError
41
- from .file_pattern_matcher import FilePatternMatcher
41
+ from .file_pattern_matcher import NON_PYTHON_FILES
42
42
  from .gpu import GPU_T, parse_gpu_config
43
43
  from .mount import _Mount, python_standalone_mount_name
44
44
  from .network_file_system import _NetworkFileSystem
@@ -721,7 +721,9 @@ class _Image(_Object, type_prefix="im"):
721
721
  context_mount=mount,
722
722
  )
723
723
 
724
- def add_local_python_source(self, *modules: str, copy: bool = False) -> "_Image":
724
+ def add_local_python_source(
725
+ self, *modules: str, copy: bool = False, ignore: Union[Sequence[str], Callable[[Path], bool]] = NON_PYTHON_FILES
726
+ ) -> "_Image":
725
727
  """Adds locally available Python packages/modules to containers
726
728
 
727
729
  Adds all files from the specified Python package or module to containers running the Image.
@@ -739,9 +741,22 @@ class _Image(_Object, type_prefix="im"):
739
741
  **Note:** This excludes all dot-prefixed subdirectories or files and all `.pyc`/`__pycache__` files.
740
742
  To add full directories with finer control, use `.add_local_dir()` instead and specify `/root` as
741
743
  the destination directory.
742
- """
743
744
 
744
- mount = _Mount.from_local_python_packages(*modules, ignore=~FilePatternMatcher("**/*.py"))
745
+ By default only includes `.py`-files in the source modules. Set the `ignore` argument to a list of patterns
746
+ or a callable to override this behavior, e.g.:
747
+
748
+ ```py
749
+ # includes everything except data.json
750
+ modal.Image.debian_slim().add_local_python_source("mymodule", ignore=["data.json"])
751
+
752
+ # exclude large files
753
+ modal.Image.debian_slim().add_local_python_source(
754
+ "mymodule",
755
+ ignore=lambda p: p.stat().st_size > 1e9
756
+ )
757
+ ```
758
+ """
759
+ mount = _Mount.from_local_python_packages(*modules, ignore=ignore)
745
760
  return self._add_mount_layer_or_copy(mount, copy=copy)
746
761
 
747
762
  def copy_local_dir(
modal/image.pyi CHANGED
@@ -120,7 +120,14 @@ class _Image(modal.object._Object):
120
120
  def copy_local_file(
121
121
  self, local_path: typing.Union[str, pathlib.Path], remote_path: typing.Union[str, pathlib.Path] = "./"
122
122
  ) -> _Image: ...
123
- def add_local_python_source(self, *module_names: str, copy: bool = False) -> _Image: ...
123
+ def add_local_python_source(
124
+ self,
125
+ *module_names: str,
126
+ copy: bool = False,
127
+ ignore: typing.Union[
128
+ collections.abc.Sequence[str], typing.Callable[[pathlib.Path], bool]
129
+ ] = modal.file_pattern_matcher.NON_PYTHON_FILES,
130
+ ) -> _Image: ...
124
131
  def copy_local_dir(
125
132
  self,
126
133
  local_path: typing.Union[str, pathlib.Path],
@@ -385,7 +392,14 @@ class Image(modal.object.Object):
385
392
  def copy_local_file(
386
393
  self, local_path: typing.Union[str, pathlib.Path], remote_path: typing.Union[str, pathlib.Path] = "./"
387
394
  ) -> Image: ...
388
- def add_local_python_source(self, *module_names: str, copy: bool = False) -> Image: ...
395
+ def add_local_python_source(
396
+ self,
397
+ *module_names: str,
398
+ copy: bool = False,
399
+ ignore: typing.Union[
400
+ collections.abc.Sequence[str], typing.Callable[[pathlib.Path], bool]
401
+ ] = modal.file_pattern_matcher.NON_PYTHON_FILES,
402
+ ) -> Image: ...
389
403
  def copy_local_dir(
390
404
  self,
391
405
  local_path: typing.Union[str, pathlib.Path],
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: modal
3
- Version: 0.68.30
3
+ Version: 0.68.34
4
4
  Summary: Python client library for Modal
5
5
  Author: Modal Labs
6
6
  Author-email: support@modal.com
@@ -19,7 +19,7 @@ modal/app.py,sha256=nt25_TXlzG4Kw_Ev4cgGaQnJaMnXN9-23tUh8dtR5xk,45454
19
19
  modal/app.pyi,sha256=3h538rJ0Z2opldsKLuQhDnvop05TfzNG-Uw_n9rEHa4,25197
20
20
  modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
21
21
  modal/client.py,sha256=JAnd4-GCN093BwkvOFAK5a6iy5ycxofjpUncMxlrIMw,15253
22
- modal/client.pyi,sha256=-tY4z-mP1GYUiq5K6vvyKUpt85eA7ZkM4uNhsBncxWA,7280
22
+ modal/client.pyi,sha256=oPO3kWChagaCsm2mvkei5siGHJFnPyws3mm5-HPW1Sk,7280
23
23
  modal/cloud_bucket_mount.py,sha256=G7T7jWLD0QkmrfKR75mSTwdUZ2xNfj7pkVqb4ipmxmI,5735
24
24
  modal/cloud_bucket_mount.pyi,sha256=CEi7vrH3kDUF4LAy4qP6tfImy2UJuFRcRbsgRNM1wo8,1403
25
25
  modal/cls.py,sha256=ONnrfZ2vPcaY2JuKypPiBA9eTiyg8Qfg-Ull40nn9zs,30956
@@ -35,12 +35,12 @@ modal/exception.py,sha256=GEV6xMnVnkle0gsFZVLB4B7cUMyw8HzVDvAvPr34ZV4,5185
35
35
  modal/experimental.py,sha256=jFuNbwrNHos47viMB9q-cHJSvf2RDxDdoEcss9plaZE,2302
36
36
  modal/file_io.py,sha256=pDOFNQU5m-x-k3oJauck4fOp3bZ55Vc-_LvSaN5_Bow,16465
37
37
  modal/file_io.pyi,sha256=GMhCCRyMftXYI3HqI9EdGPOx70CbCNi-VC5Sfy5TYnc,7631
38
- modal/file_pattern_matcher.py,sha256=vX6MjWRGdonE4I8QPdjFUnz6moBjSzvgD6417BNQrW4,4021
38
+ modal/file_pattern_matcher.py,sha256=V6P74Vc7LAuBFe_uepIaZmoDJiuAvqjFibe0GcMJwxo,5119
39
39
  modal/functions.py,sha256=ig7FjNHrzsYzIgpagLGphQYLybvtx_4m0Exp0ribO1c,67669
40
- modal/functions.pyi,sha256=6p1aCW16JNKJrlP5IfZc3Z-Cg4Q7TOkrgSjsJUlwgXw,25213
40
+ modal/functions.pyi,sha256=dsA-f4Jc-VvXWaKcPyzUSgDCBQ5ujKv55AL7rtkSCT4,25213
41
41
  modal/gpu.py,sha256=r4rL6uH3UJIQthzYvfWauXNyh01WqCPtKZCmmSX1fd4,6881
42
- modal/image.py,sha256=S4AjJf6yAL8I5rxDll2yOoNJI-2PJk6oUV-x14J9zck,82284
43
- modal/image.pyi,sha256=8R8Ac9eZ83ocM_1qrFUvH3rCbI5zRnq-Eq0xaAQq4nI,25105
42
+ modal/image.py,sha256=-swOPK80OXnFuhxoG3tT2zmJhas8yO-lr0jkOebBdKQ,82858
43
+ modal/image.pyi,sha256=Ryd3x_Bic7j40DPe0MveGqqwvNtum2qA-6lUd5lnHvk,25503
44
44
  modal/io_streams.py,sha256=QkQiizKRzd5bnbKQsap31LJgBYlAnj4-XkV_50xPYX0,15079
45
45
  modal/io_streams.pyi,sha256=bCCVSxkMcosYd8O3PQDDwJw7TQ8JEcnYonLJ5t27TQs,4804
46
46
  modal/mount.py,sha256=QKvrgpS_FMqrGdoyVZWeWnkNpQeDSLpuiwZFSGgRp_Y,29017
@@ -164,10 +164,10 @@ modal_proto/options_pb2_grpc.pyi,sha256=CImmhxHsYnF09iENPoe8S4J-n93jtgUYD2JPAc0y
164
164
  modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
165
165
  modal_version/__init__.py,sha256=RT6zPoOdFO99u5Wcxxaoir4ZCuPTbQ22cvzFAXl3vUY,470
166
166
  modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
167
- modal_version/_version_generated.py,sha256=4RH9bjhNthmyY78ol88860fus9pAZUVf10blOnT7wQw,149
168
- modal-0.68.30.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
169
- modal-0.68.30.dist-info/METADATA,sha256=2Vj0tlnlctjvBxVdv6VzeEy4fBarD7i9pPgo1aVqTqU,2329
170
- modal-0.68.30.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
171
- modal-0.68.30.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
172
- modal-0.68.30.dist-info/top_level.txt,sha256=1nvYbOSIKcmU50fNrpnQnrrOpj269ei3LzgB6j9xGqg,64
173
- modal-0.68.30.dist-info/RECORD,,
167
+ modal_version/_version_generated.py,sha256=cSqeESDUKR0n4LWrTDgUSVMiXO6jxImu422-0SmjVk8,149
168
+ modal-0.68.34.dist-info/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
169
+ modal-0.68.34.dist-info/METADATA,sha256=ICADQDMYMS8v8d6Mu4zTJK7wBNxoabfmKQyKKfUJ9Vk,2329
170
+ modal-0.68.34.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
171
+ modal-0.68.34.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
172
+ modal-0.68.34.dist-info/top_level.txt,sha256=1nvYbOSIKcmU50fNrpnQnrrOpj269ei3LzgB6j9xGqg,64
173
+ modal-0.68.34.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  # Copyright Modal Labs 2024
2
2
 
3
3
  # Note: Reset this value to -1 whenever you make a minor `0.X` release of the client.
4
- build_number = 30 # git: ff7c91e
4
+ build_number = 34 # git: fe44848