modal 0.74.10__py3-none-any.whl → 0.74.12__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/__main__.py +2 -2
- modal/_functions.py +5 -0
- modal/_resolver.py +6 -0
- modal/_utils/function_utils.py +5 -0
- modal/_utils/grpc_utils.py +3 -1
- modal/app.py +5 -1
- modal/app.pyi +4 -0
- modal/cli/_traceback.py +2 -2
- modal/client.pyi +2 -2
- modal/functions.pyi +7 -6
- {modal-0.74.10.dist-info → modal-0.74.12.dist-info}/METADATA +1 -1
- {modal-0.74.10.dist-info → modal-0.74.12.dist-info}/RECORD +20 -20
- modal_proto/api.proto +9 -0
- modal_proto/api_pb2.py +590 -564
- modal_proto/api_pb2.pyi +51 -3
- modal_version/_version_generated.py +1 -1
- {modal-0.74.10.dist-info → modal-0.74.12.dist-info}/WHEEL +0 -0
- {modal-0.74.10.dist-info → modal-0.74.12.dist-info}/entry_points.txt +0 -0
- {modal-0.74.10.dist-info → modal-0.74.12.dist-info}/licenses/LICENSE +0 -0
- {modal-0.74.10.dist-info → modal-0.74.12.dist-info}/top_level.txt +0 -0
    
        modal/__main__.py
    CHANGED
    
    | @@ -2,7 +2,7 @@ | |
| 2 2 | 
             
            import sys
         | 
| 3 3 |  | 
| 4 4 | 
             
            from ._traceback import reduce_traceback_to_user_code
         | 
| 5 | 
            -
            from .cli._traceback import  | 
| 5 | 
            +
            from .cli._traceback import highlight_modal_warnings, setup_rich_traceback
         | 
| 6 6 | 
             
            from .cli.entry_point import entrypoint_cli
         | 
| 7 7 | 
             
            from .cli.import_refs import _CliUserExecutionError
         | 
| 8 8 | 
             
            from .config import config
         | 
| @@ -11,7 +11,7 @@ from .config import config | |
| 11 11 | 
             
            def main():
         | 
| 12 12 | 
             
                # Setup rich tracebacks, but only on user's end, when using the Modal CLI.
         | 
| 13 13 | 
             
                setup_rich_traceback()
         | 
| 14 | 
            -
                 | 
| 14 | 
            +
                highlight_modal_warnings()
         | 
| 15 15 |  | 
| 16 16 | 
             
                try:
         | 
| 17 17 | 
             
                    entrypoint_cli()
         | 
    
        modal/_functions.py
    CHANGED
    
    | @@ -459,6 +459,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type | |
| 459 459 | 
             
                    ephemeral_disk: Optional[int] = None,
         | 
| 460 460 | 
             
                    # current default: first-party, future default: main-package
         | 
| 461 461 | 
             
                    include_source: Optional[bool] = None,
         | 
| 462 | 
            +
                    experimental_options: Optional[dict[str, str]] = None,
         | 
| 462 463 | 
             
                    _experimental_proxy_ip: Optional[str] = None,
         | 
| 463 464 | 
             
                    _experimental_custom_scaling_factor: Optional[float] = None,
         | 
| 464 465 | 
             
                    _experimental_enable_gpu_snapshot: bool = False,
         | 
| @@ -819,6 +820,8 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type | |
| 819 820 | 
             
                                i6pn_enabled=i6pn_enabled,
         | 
| 820 821 | 
             
                                schedule=schedule.proto_message if schedule is not None else None,
         | 
| 821 822 | 
             
                                snapshot_debug=config.get("snapshot_debug"),
         | 
| 823 | 
            +
                                experimental_options=experimental_options or {},
         | 
| 824 | 
            +
                                # ---
         | 
| 822 825 | 
             
                                _experimental_group_size=cluster_size or 0,  # Experimental: Clustered functions
         | 
| 823 826 | 
             
                                _experimental_concurrent_cancellations=True,
         | 
| 824 827 | 
             
                                _experimental_proxy_ip=_experimental_proxy_ip,
         | 
| @@ -856,6 +859,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type | |
| 856 859 | 
             
                                    use_method_name=function_definition.use_method_name,
         | 
| 857 860 | 
             
                                    method_definitions=function_definition.method_definitions,
         | 
| 858 861 | 
             
                                    method_definitions_set=function_definition.method_definitions_set,
         | 
| 862 | 
            +
                                    experimental_options=experimental_options or {},
         | 
| 859 863 | 
             
                                    _experimental_group_size=function_definition._experimental_group_size,
         | 
| 860 864 | 
             
                                    _experimental_buffer_containers=function_definition._experimental_buffer_containers,
         | 
| 861 865 | 
             
                                    _experimental_custom_scaling=function_definition._experimental_custom_scaling,
         | 
| @@ -913,6 +917,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type | |
| 913 917 | 
             
                                    raise InvalidError(f"Function {info.function_name} is too large to deploy.")
         | 
| 914 918 | 
             
                                raise
         | 
| 915 919 | 
             
                            function_creation_status.set_response(response)
         | 
| 920 | 
            +
             | 
| 916 921 | 
             
                        # needed for modal.serve file watching
         | 
| 917 922 | 
             
                        serve_mounts = {m for m in all_mounts if m.is_local()}
         | 
| 918 923 | 
             
                        serve_mounts |= image._serve_mounts
         | 
    
        modal/_resolver.py
    CHANGED
    
    | @@ -10,6 +10,8 @@ from typing import TYPE_CHECKING, Optional | |
| 10 10 |  | 
| 11 11 | 
             
            from grpclib import GRPCError, Status
         | 
| 12 12 |  | 
| 13 | 
            +
            from modal_proto import api_pb2
         | 
| 14 | 
            +
             | 
| 13 15 | 
             
            from ._utils.async_utils import TaskContext
         | 
| 14 16 | 
             
            from .client import _Client
         | 
| 15 17 | 
             
            from .exception import NotFoundError
         | 
| @@ -34,6 +36,10 @@ class StatusRow: | |
| 34 36 | 
             
                    if self._spinner is not None:
         | 
| 35 37 | 
             
                        self._spinner.update(text=message)
         | 
| 36 38 |  | 
| 39 | 
            +
                def warning(self, warning: api_pb2.Warning):
         | 
| 40 | 
            +
                    if self._step_node is not None:
         | 
| 41 | 
            +
                        self._step_node.add(f"[yellow]:warning:[/yellow] {warning.message}")
         | 
| 42 | 
            +
             | 
| 37 43 | 
             
                def finish(self, message):
         | 
| 38 44 | 
             
                    if self._step_node is not None and self._spinner is not None:
         | 
| 39 45 | 
             
                        from ._output import OutputManager
         | 
    
        modal/_utils/function_utils.py
    CHANGED
    
    | @@ -584,6 +584,8 @@ class FunctionCreationStatus: | |
| 584 584 | 
             
                        suffix = _get_suffix_from_web_url_info(url_info)
         | 
| 585 585 | 
             
                        # TODO: this is only printed when we're showing progress. Maybe move this somewhere else.
         | 
| 586 586 | 
             
                        web_url = self.response.handle_metadata.web_url
         | 
| 587 | 
            +
                        for warning in self.response.server_warnings:
         | 
| 588 | 
            +
                            self.status_row.warning(warning)
         | 
| 587 589 | 
             
                        self.status_row.finish(
         | 
| 588 590 | 
             
                            f"Created web function {self.tag} => [magenta underline]{web_url}[/magenta underline]"
         | 
| 589 591 | 
             
                            f"{proxy_auth_suffix}{suffix}"
         | 
| @@ -595,7 +597,10 @@ class FunctionCreationStatus: | |
| 595 597 | 
             
                            custom_domain_status_row.finish(
         | 
| 596 598 | 
             
                                f"Custom domain for {self.tag} => [magenta underline]{custom_domain.url}[/magenta underline]"
         | 
| 597 599 | 
             
                            )
         | 
| 600 | 
            +
             | 
| 598 601 | 
             
                    else:
         | 
| 602 | 
            +
                        for warning in self.response.server_warnings:
         | 
| 603 | 
            +
                            self.status_row.warning(warning)
         | 
| 599 604 | 
             
                        self.status_row.finish(f"Created function {self.tag}.")
         | 
| 600 605 | 
             
                        if self.response.function.method_definitions_set:
         | 
| 601 606 | 
             
                            for method_definition in self.response.function.method_definitions.values():
         | 
    
        modal/_utils/grpc_utils.py
    CHANGED
    
    | @@ -69,12 +69,14 @@ RETRYABLE_GRPC_STATUS_CODES = [ | |
| 69 69 | 
             
                Status.INTERNAL,
         | 
| 70 70 | 
             
            ]
         | 
| 71 71 |  | 
| 72 | 
            +
             | 
| 72 73 | 
             
            @dataclass
         | 
| 73 74 | 
             
            class RetryWarningMessage:
         | 
| 74 75 | 
             
                message: str
         | 
| 75 76 | 
             
                warning_interval: int
         | 
| 76 77 | 
             
                errors_to_warn_for: typing.List[Status]
         | 
| 77 78 |  | 
| 79 | 
            +
             | 
| 78 80 | 
             
            def create_channel(
         | 
| 79 81 | 
             
                server_url: str,
         | 
| 80 82 | 
             
                metadata: dict[str, str] = {},
         | 
| @@ -150,7 +152,7 @@ async def retry_transient_errors( | |
| 150 152 | 
             
                attempt_timeout: Optional[float] = None,  # timeout for each attempt
         | 
| 151 153 | 
             
                total_timeout: Optional[float] = None,  # timeout for the entire function call
         | 
| 152 154 | 
             
                attempt_timeout_floor=2.0,  # always have at least this much timeout (only for total_timeout)
         | 
| 153 | 
            -
                retry_warning_message: Optional[RetryWarningMessage] = None
         | 
| 155 | 
            +
                retry_warning_message: Optional[RetryWarningMessage] = None,
         | 
| 154 156 | 
             
            ) -> ResponseType:
         | 
| 155 157 | 
             
                """Retry on transient gRPC failures with back-off until max_retries is reached.
         | 
| 156 158 | 
             
                If max_retries is None, retry forever."""
         | 
    
        modal/app.py
    CHANGED
    
    | @@ -613,6 +613,7 @@ class _App: | |
| 613 613 | 
             
                    i6pn: Optional[bool] = None,  # Whether to enable IPv6 container networking within the region.
         | 
| 614 614 | 
             
                    # Whether the function's home package should be included in the image - defaults to True
         | 
| 615 615 | 
             
                    include_source: Optional[bool] = None,  # When `False`, don't automatically add the App source to the container.
         | 
| 616 | 
            +
                    experimental_options: Optional[dict[str, Any]] = None,
         | 
| 616 617 | 
             
                    # Parameters below here are experimental. Use with caution!
         | 
| 617 618 | 
             
                    _experimental_scheduler_placement: Optional[
         | 
| 618 619 | 
             
                        SchedulerPlacement
         | 
| @@ -780,10 +781,11 @@ class _App: | |
| 780 781 | 
             
                            block_network=block_network,
         | 
| 781 782 | 
             
                            max_inputs=max_inputs,
         | 
| 782 783 | 
             
                            scheduler_placement=scheduler_placement,
         | 
| 783 | 
            -
                            _experimental_proxy_ip=_experimental_proxy_ip,
         | 
| 784 784 | 
             
                            i6pn_enabled=i6pn_enabled,
         | 
| 785 785 | 
             
                            cluster_size=cluster_size,  # Experimental: Clustered functions
         | 
| 786 786 | 
             
                            include_source=include_source if include_source is not None else self._include_source_default,
         | 
| 787 | 
            +
                            experimental_options={k: str(v) for k, v in (experimental_options or {}).items()},
         | 
| 788 | 
            +
                            _experimental_proxy_ip=_experimental_proxy_ip,
         | 
| 787 789 | 
             
                            _experimental_enable_gpu_snapshot=_experimental_enable_gpu_snapshot,
         | 
| 788 790 | 
             
                        )
         | 
| 789 791 |  | 
| @@ -836,6 +838,7 @@ class _App: | |
| 836 838 | 
             
                    # Use `max_inputs = 1` for single-use containers.
         | 
| 837 839 | 
             
                    max_inputs: Optional[int] = None,
         | 
| 838 840 | 
             
                    include_source: Optional[bool] = None,  # When `False`, don't automatically add the App source to the container.
         | 
| 841 | 
            +
                    experimental_options: Optional[dict[str, Any]] = None,
         | 
| 839 842 | 
             
                    # Parameters below here are experimental. Use with caution!
         | 
| 840 843 | 
             
                    _experimental_scheduler_placement: Optional[
         | 
| 841 844 | 
             
                        SchedulerPlacement
         | 
| @@ -947,6 +950,7 @@ class _App: | |
| 947 950 | 
             
                            max_inputs=max_inputs,
         | 
| 948 951 | 
             
                            scheduler_placement=scheduler_placement,
         | 
| 949 952 | 
             
                            include_source=include_source if include_source is not None else self._include_source_default,
         | 
| 953 | 
            +
                            experimental_options={k: str(v) for k, v in (experimental_options or {}).items()},
         | 
| 950 954 | 
             
                            _experimental_proxy_ip=_experimental_proxy_ip,
         | 
| 951 955 | 
             
                            _experimental_custom_scaling_factor=_experimental_custom_scaling_factor,
         | 
| 952 956 | 
             
                            _experimental_enable_gpu_snapshot=_experimental_enable_gpu_snapshot,
         | 
    
        modal/app.pyi
    CHANGED
    
    | @@ -192,6 +192,7 @@ class _App: | |
| 192 192 | 
             
                    max_inputs: typing.Optional[int] = None,
         | 
| 193 193 | 
             
                    i6pn: typing.Optional[bool] = None,
         | 
| 194 194 | 
             
                    include_source: typing.Optional[bool] = None,
         | 
| 195 | 
            +
                    experimental_options: typing.Optional[dict[str, typing.Any]] = None,
         | 
| 195 196 | 
             
                    _experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
         | 
| 196 197 | 
             
                    _experimental_proxy_ip: typing.Optional[str] = None,
         | 
| 197 198 | 
             
                    _experimental_custom_scaling_factor: typing.Optional[float] = None,
         | 
| @@ -239,6 +240,7 @@ class _App: | |
| 239 240 | 
             
                    block_network: bool = False,
         | 
| 240 241 | 
             
                    max_inputs: typing.Optional[int] = None,
         | 
| 241 242 | 
             
                    include_source: typing.Optional[bool] = None,
         | 
| 243 | 
            +
                    experimental_options: typing.Optional[dict[str, typing.Any]] = None,
         | 
| 242 244 | 
             
                    _experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
         | 
| 243 245 | 
             
                    _experimental_proxy_ip: typing.Optional[str] = None,
         | 
| 244 246 | 
             
                    _experimental_custom_scaling_factor: typing.Optional[float] = None,
         | 
| @@ -432,6 +434,7 @@ class App: | |
| 432 434 | 
             
                    max_inputs: typing.Optional[int] = None,
         | 
| 433 435 | 
             
                    i6pn: typing.Optional[bool] = None,
         | 
| 434 436 | 
             
                    include_source: typing.Optional[bool] = None,
         | 
| 437 | 
            +
                    experimental_options: typing.Optional[dict[str, typing.Any]] = None,
         | 
| 435 438 | 
             
                    _experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
         | 
| 436 439 | 
             
                    _experimental_proxy_ip: typing.Optional[str] = None,
         | 
| 437 440 | 
             
                    _experimental_custom_scaling_factor: typing.Optional[float] = None,
         | 
| @@ -479,6 +482,7 @@ class App: | |
| 479 482 | 
             
                    block_network: bool = False,
         | 
| 480 483 | 
             
                    max_inputs: typing.Optional[int] = None,
         | 
| 481 484 | 
             
                    include_source: typing.Optional[bool] = None,
         | 
| 485 | 
            +
                    experimental_options: typing.Optional[dict[str, typing.Any]] = None,
         | 
| 482 486 | 
             
                    _experimental_scheduler_placement: typing.Optional[modal.scheduler_placement.SchedulerPlacement] = None,
         | 
| 483 487 | 
             
                    _experimental_proxy_ip: typing.Optional[str] = None,
         | 
| 484 488 | 
             
                    _experimental_custom_scaling_factor: typing.Optional[float] = None,
         | 
    
        modal/cli/_traceback.py
    CHANGED
    
    | @@ -161,8 +161,8 @@ def setup_rich_traceback() -> None: | |
| 161 161 | 
             
                install(suppress=[synchronicity, grpclib, click, typer], extra_lines=1)
         | 
| 162 162 |  | 
| 163 163 |  | 
| 164 | 
            -
            def  | 
| 165 | 
            -
                """Patch the warnings module to make  | 
| 164 | 
            +
            def highlight_modal_warnings() -> None:
         | 
| 165 | 
            +
                """Patch the warnings module to make certain warnings more salient in the CLI."""
         | 
| 166 166 | 
             
                base_showwarning = warnings.showwarning
         | 
| 167 167 |  | 
| 168 168 | 
             
                def showwarning(warning, category, filename, lineno, file=None, line=None):
         | 
    
        modal/client.pyi
    CHANGED
    
    | @@ -27,7 +27,7 @@ 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 = "0.74. | 
| 30 | 
            +
                    self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.74.12"
         | 
| 31 31 | 
             
                ): ...
         | 
| 32 32 | 
             
                def is_closed(self) -> bool: ...
         | 
| 33 33 | 
             
                @property
         | 
| @@ -85,7 +85,7 @@ class Client: | |
| 85 85 | 
             
                _snapshotted: bool
         | 
| 86 86 |  | 
| 87 87 | 
             
                def __init__(
         | 
| 88 | 
            -
                    self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.74. | 
| 88 | 
            +
                    self, server_url: str, client_type: int, credentials: typing.Optional[tuple[str, str]], version: str = "0.74.12"
         | 
| 89 89 | 
             
                ): ...
         | 
| 90 90 | 
             
                def is_closed(self) -> bool: ...
         | 
| 91 91 | 
             
                @property
         | 
    
        modal/functions.pyi
    CHANGED
    
    | @@ -98,6 +98,7 @@ class Function( | |
| 98 98 | 
             
                    max_inputs: typing.Optional[int] = None,
         | 
| 99 99 | 
             
                    ephemeral_disk: typing.Optional[int] = None,
         | 
| 100 100 | 
             
                    include_source: typing.Optional[bool] = None,
         | 
| 101 | 
            +
                    experimental_options: typing.Optional[dict[str, str]] = None,
         | 
| 101 102 | 
             
                    _experimental_proxy_ip: typing.Optional[str] = None,
         | 
| 102 103 | 
             
                    _experimental_custom_scaling_factor: typing.Optional[float] = None,
         | 
| 103 104 | 
             
                    _experimental_enable_gpu_snapshot: bool = False,
         | 
| @@ -200,11 +201,11 @@ class Function( | |
| 200 201 |  | 
| 201 202 | 
             
                _call_generator_nowait: ___call_generator_nowait_spec[typing_extensions.Self]
         | 
| 202 203 |  | 
| 203 | 
            -
                class __remote_spec(typing_extensions.Protocol[ | 
| 204 | 
            +
                class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
         | 
| 204 205 | 
             
                    def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER: ...
         | 
| 205 206 | 
             
                    async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER: ...
         | 
| 206 207 |  | 
| 207 | 
            -
                remote: __remote_spec[modal._functions. | 
| 208 | 
            +
                remote: __remote_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
         | 
| 208 209 |  | 
| 209 210 | 
             
                class __remote_gen_spec(typing_extensions.Protocol[SUPERSELF]):
         | 
| 210 211 | 
             
                    def __call__(self, *args, **kwargs) -> typing.Generator[typing.Any, None, None]: ...
         | 
| @@ -219,19 +220,19 @@ class Function( | |
| 219 220 | 
             
                    self, *args: modal._functions.P.args, **kwargs: modal._functions.P.kwargs
         | 
| 220 221 | 
             
                ) -> modal._functions.OriginalReturnType: ...
         | 
| 221 222 |  | 
| 222 | 
            -
                class ___experimental_spawn_spec(typing_extensions.Protocol[ | 
| 223 | 
            +
                class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
         | 
| 223 224 | 
             
                    def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
         | 
| 224 225 | 
             
                    async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
         | 
| 225 226 |  | 
| 226 227 | 
             
                _experimental_spawn: ___experimental_spawn_spec[
         | 
| 227 | 
            -
                    modal._functions. | 
| 228 | 
            +
                    modal._functions.P, modal._functions.ReturnType, typing_extensions.Self
         | 
| 228 229 | 
             
                ]
         | 
| 229 230 |  | 
| 230 | 
            -
                class __spawn_spec(typing_extensions.Protocol[ | 
| 231 | 
            +
                class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
         | 
| 231 232 | 
             
                    def __call__(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
         | 
| 232 233 | 
             
                    async def aio(self, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]: ...
         | 
| 233 234 |  | 
| 234 | 
            -
                spawn: __spawn_spec[modal._functions. | 
| 235 | 
            +
                spawn: __spawn_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
         | 
| 235 236 |  | 
| 236 237 | 
             
                def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]: ...
         | 
| 237 238 |  | 
| @@ -1,16 +1,16 @@ | |
| 1 1 | 
             
            modal/__init__.py,sha256=7wz1AT_bpWJJEzXsAo3QMb7i87y7UGXwfneb0bGDhRg,2502
         | 
| 2 | 
            -
            modal/__main__.py,sha256= | 
| 2 | 
            +
            modal/__main__.py,sha256=sTJcc9EbDuCKSwg3tL6ZckFw9WWdlkXW8mId1IvJCNc,2846
         | 
| 3 3 | 
             
            modal/_clustered_functions.py,sha256=kTf-9YBXY88NutC1akI-gCbvf01RhMPCw-zoOI_YIUE,2700
         | 
| 4 4 | 
             
            modal/_clustered_functions.pyi,sha256=vllkegc99A0jrUOWa8mdlSbdp6uz36TsHhGxysAOpaQ,771
         | 
| 5 5 | 
             
            modal/_container_entrypoint.py,sha256=DymOImhc3uGRkIq_qXmBsEbWMB4EBMpfuXzz2S4BcGg,29404
         | 
| 6 | 
            -
            modal/_functions.py,sha256= | 
| 6 | 
            +
            modal/_functions.py,sha256=JGkxTOL6dcKIK2kCZfalzAO5JZXABVk0rc1Id1QgmCg,74900
         | 
| 7 7 | 
             
            modal/_ipython.py,sha256=TW1fkVOmZL3YYqdS2YlM1hqpf654Yf8ZyybHdBnlhSw,301
         | 
| 8 8 | 
             
            modal/_location.py,sha256=joiX-0ZeutEUDTrrqLF1GHXCdVLF-rHzstocbMcd_-k,366
         | 
| 9 9 | 
             
            modal/_object.py,sha256=JBIECWdfpRKCaCxVWZbC3Q1kF5Whk_EKvY9f4Y6AFyg,11446
         | 
| 10 10 | 
             
            modal/_output.py,sha256=Z0nngPh2mKHMQc4MQ92YjVPc3ewOLa3I4dFBlL9nvQY,25656
         | 
| 11 11 | 
             
            modal/_partial_function.py,sha256=8mmd5lvjZaC7qi0KAnLR1H590MlxNslAE2_Kr9biJUA,39704
         | 
| 12 12 | 
             
            modal/_pty.py,sha256=JZfPDDpzqICZqtyPI_oMJf_9w-p_lLNuzHhwhodUXio,1329
         | 
| 13 | 
            -
            modal/_resolver.py,sha256 | 
| 13 | 
            +
            modal/_resolver.py,sha256=-nolqj_p_mx5czVYj1Mazh2IQWpSMrTOGughVJqYfo8,7579
         | 
| 14 14 | 
             
            modal/_resources.py,sha256=5qmcirXUI8dSH926nwkUaeX9H25mqYu9mXD_KuT79-o,1733
         | 
| 15 15 | 
             
            modal/_serialization.py,sha256=wAgaALThfr-DBV9LMhM4qY_PCH7SRhA9xgoHL2bapBk,22963
         | 
| 16 16 | 
             
            modal/_traceback.py,sha256=IZQzB3fVlUfMHOSyKUgw0H6qv4yHnpyq-XVCNZKfUdA,5023
         | 
| @@ -18,11 +18,11 @@ modal/_tunnel.py,sha256=zTBxBiuH1O22tS1OliAJdIsSmaZS8PlnifS_6S5z-mk,6320 | |
| 18 18 | 
             
            modal/_tunnel.pyi,sha256=JmmDYAy9F1FpgJ_hWx0xkom2nTOFQjn4mTPYlU3PFo4,1245
         | 
| 19 19 | 
             
            modal/_type_manager.py,sha256=DWjgmjYJuOagw2erin506UUbG2H5UzZCFEekS-7hmfA,9087
         | 
| 20 20 | 
             
            modal/_watcher.py,sha256=K6LYnlmSGQB4tWWI9JADv-tvSvQ1j522FwT71B51CX8,3584
         | 
| 21 | 
            -
            modal/app.py,sha256= | 
| 22 | 
            -
            modal/app.pyi,sha256= | 
| 21 | 
            +
            modal/app.py,sha256=1GSPMMYRkG17FQwnA_PleK9YQrPQWXlN_4mpZ5VRA4Q,48468
         | 
| 22 | 
            +
            modal/app.pyi,sha256=bpC9uN_B4d_UtyXVuhGpXC2RM-IcsytS0ndu-GoKrHQ,27276
         | 
| 23 23 | 
             
            modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
         | 
| 24 24 | 
             
            modal/client.py,sha256=U-YKSw0n7J1ZLREt9cbEJCtmHe5YoPKFxl0xlkan2yc,15565
         | 
| 25 | 
            -
            modal/client.pyi,sha256= | 
| 25 | 
            +
            modal/client.pyi,sha256=eoSW7y3yv0zF18hQiPencyPpa0An7ZlLG331wvy9AK0,7593
         | 
| 26 26 | 
             
            modal/cloud_bucket_mount.py,sha256=YOe9nnvSr4ZbeCn587d7_VhE9IioZYRvF9VYQTQux08,5914
         | 
| 27 27 | 
             
            modal/cloud_bucket_mount.pyi,sha256=30T3K1a89l6wzmEJ_J9iWv9SknoGqaZDx59Xs-ZQcmk,1607
         | 
| 28 28 | 
             
            modal/cls.py,sha256=GvaNl8R5UsH7Vg88WEOyerdjvZEPK7xxi3nqHlyOW_c,33497
         | 
| @@ -39,7 +39,7 @@ modal/file_io.py,sha256=lcMs_E9Xfm0YX1t9U2wNIBPnqHRxmImqjLW1GHqVmyg,20945 | |
| 39 39 | 
             
            modal/file_io.pyi,sha256=NTRft1tbPSWf9TlWVeZmTlgB5AZ_Zhu2srWIrWr7brk,9445
         | 
| 40 40 | 
             
            modal/file_pattern_matcher.py,sha256=trosX-Bp7dOubudN1bLLhRAoidWy1TcoaR4Pv8CedWw,6497
         | 
| 41 41 | 
             
            modal/functions.py,sha256=kcNHvqeGBxPI7Cgd57NIBBghkfbeFJzXO44WW0jSmao,325
         | 
| 42 | 
            -
            modal/functions.pyi,sha256= | 
| 42 | 
            +
            modal/functions.pyi,sha256=2J7jPa2tQWhuUy67wg-MWvEx0rwrBNjX6UTEJbAUq_A,14855
         | 
| 43 43 | 
             
            modal/gpu.py,sha256=Kbhs_u49FaC2Zi0TjCdrpstpRtT5eZgecynmQi5IZVE,6752
         | 
| 44 44 | 
             
            modal/image.py,sha256=I-9_YZL0SSfnuGPywa3-4PlxDmJ-53p7ce3gP74SrOA,92877
         | 
| 45 45 | 
             
            modal/image.pyi,sha256=89zv12C1sFrJs7Es9SnX23_m208ASAdeNGCVTrhjzHI,25632
         | 
| @@ -96,10 +96,10 @@ modal/_utils/blob_utils.py,sha256=jWJovk4g-YNG3CvkvglOds4a6D1M0Tcal_59v7y9VsM,14 | |
| 96 96 | 
             
            modal/_utils/bytes_io_segment_payload.py,sha256=uunxVJS4PE1LojF_UpURMzVK9GuvmYWRqQo_bxEj5TU,3385
         | 
| 97 97 | 
             
            modal/_utils/deprecation.py,sha256=EXP1beU4pmEqEzWMLw6E3kUfNfpmNA_VOp6i0EHi93g,4856
         | 
| 98 98 | 
             
            modal/_utils/docker_utils.py,sha256=h1uETghR40mp_y3fSWuZAfbIASH1HMzuphJHghAL6DU,3722
         | 
| 99 | 
            -
            modal/_utils/function_utils.py,sha256= | 
| 99 | 
            +
            modal/_utils/function_utils.py,sha256=z6v8A5Ee827K9f2XV1c2_Ea6iv_zcaxbXySER-qW5XQ,26903
         | 
| 100 100 | 
             
            modal/_utils/git_utils.py,sha256=qtUU6JAttF55ZxYq51y55OR58B0tDPZsZWK5dJe6W5g,3182
         | 
| 101 101 | 
             
            modal/_utils/grpc_testing.py,sha256=H1zHqthv19eGPJz2HKXDyWXWGSqO4BRsxah3L5Xaa8A,8619
         | 
| 102 | 
            -
            modal/_utils/grpc_utils.py,sha256= | 
| 102 | 
            +
            modal/_utils/grpc_utils.py,sha256=oxDMEzr7BzE2NhQn53gnQwKCkpHruTbbBZg8-fuiP7E,8529
         | 
| 103 103 | 
             
            modal/_utils/hash_utils.py,sha256=zg3J6OGxTFGSFri1qQ12giDz90lWk8bzaxCTUCRtiX4,3034
         | 
| 104 104 | 
             
            modal/_utils/http_utils.py,sha256=yeTFsXYr0rYMEhB7vBP7audG9Uc7OLhzKBANFDZWVt0,2451
         | 
| 105 105 | 
             
            modal/_utils/jwt_utils.py,sha256=fxH9plyrbAemTbjSsQtzIdDXE9QXxvMC4DiUZ16G0aA,1360
         | 
| @@ -116,7 +116,7 @@ modal/_vendor/cloudpickle.py,sha256=avxOIgNKqL9KyPNuIOVQzBm0D1l9ipeB4RrcUMUGmeQ, | |
| 116 116 | 
             
            modal/_vendor/tblib.py,sha256=g1O7QUDd3sDoLd8YPFltkXkih7r_fyZOjgmGuligv3s,9722
         | 
| 117 117 | 
             
            modal/cli/__init__.py,sha256=waLjl5c6IPDhSsdWAm9Bji4e2PVxamYABKAze6CHVXY,28
         | 
| 118 118 | 
             
            modal/cli/_download.py,sha256=t6BXZwjTd9MgznDvbsV8rp0FZWggdzC-lUAGZU4xx1g,3984
         | 
| 119 | 
            -
            modal/cli/_traceback.py,sha256= | 
| 119 | 
            +
            modal/cli/_traceback.py,sha256=4ywtmFcmPnY3tqb4-3fA061N2tRiM01xs8fSagtkwhE,7293
         | 
| 120 120 | 
             
            modal/cli/app.py,sha256=87LWg3bTQQIHFOqs8iiJYD_X03omXBZ6lFYR0rMJV-I,8433
         | 
| 121 121 | 
             
            modal/cli/config.py,sha256=QvFsqO4eUOtI7d_pQAOAyfq_ZitjhPtav3C6GIDQcZM,1680
         | 
| 122 122 | 
             
            modal/cli/container.py,sha256=FYwEgjf93j4NMorAjGbSV98i1wpebqdAeNU1wfrFp1k,3668
         | 
| @@ -145,7 +145,7 @@ modal/requirements/2024.10.txt,sha256=qD-5cVIVM9wXesJ6JC89Ew-3m2KjEElUz3jaw_MddR | |
| 145 145 | 
             
            modal/requirements/PREVIEW.txt,sha256=qD-5cVIVM9wXesJ6JC89Ew-3m2KjEElUz3jaw_MddRo,296
         | 
| 146 146 | 
             
            modal/requirements/README.md,sha256=9tK76KP0Uph7O0M5oUgsSwEZDj5y-dcUPsnpR0Sc-Ik,854
         | 
| 147 147 | 
             
            modal/requirements/base-images.json,sha256=57vMSqzMbLBxw5tFWSaMiIkkVEps4JfX5PAtXGnkS4U,740
         | 
| 148 | 
            -
            modal-0.74. | 
| 148 | 
            +
            modal-0.74.12.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
         | 
| 149 149 | 
             
            modal_docs/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,28
         | 
| 150 150 | 
             
            modal_docs/gen_cli_docs.py,sha256=c1yfBS_x--gL5bs0N4ihMwqwX8l3IBWSkBAKNNIi6bQ,3801
         | 
| 151 151 | 
             
            modal_docs/gen_reference_docs.py,sha256=cvTgltucqYLLIX84QxAwf51Z5Vc2n6cLxS8VcrxNCAo,6401
         | 
| @@ -153,10 +153,10 @@ modal_docs/mdmd/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,2 | |
| 153 153 | 
             
            modal_docs/mdmd/mdmd.py,sha256=Irx49MCCTlBOP4FBdLR--JrpA3-WhsVeriq0LGgsRic,6232
         | 
| 154 154 | 
             
            modal_docs/mdmd/signatures.py,sha256=XJaZrK7Mdepk5fdX51A8uENiLFNil85Ud0d4MH8H5f0,3218
         | 
| 155 155 | 
             
            modal_proto/__init__.py,sha256=MIEP8jhXUeGq_eCjYFcqN5b1bxBM4fdk0VESpjWR0fc,28
         | 
| 156 | 
            -
            modal_proto/api.proto,sha256= | 
| 156 | 
            +
            modal_proto/api.proto,sha256=ZCqlmEi8Z_UPUsxnyY0rj8hIXr1HtHApqoGflR-ypAo,93046
         | 
| 157 157 | 
             
            modal_proto/api_grpc.py,sha256=DlrVx10B6HLy4LjWrgK7HJ7kUNDKqEvm6mUqh-6EVvs,112161
         | 
| 158 | 
            -
            modal_proto/api_pb2.py,sha256= | 
| 159 | 
            -
            modal_proto/api_pb2.pyi,sha256= | 
| 158 | 
            +
            modal_proto/api_pb2.py,sha256=MPC2-mCFuy8NxXzpKTiyexm-T1IfxN_p-qBAB9XI7c8,328268
         | 
| 159 | 
            +
            modal_proto/api_pb2.pyi,sha256=kz5M2tRlRtKNmnFb3b3nCEWZWZY85s0TNZ0xs2TroZM,446747
         | 
| 160 160 | 
             
            modal_proto/api_pb2_grpc.py,sha256=NQPpAa3Co-zFNB6L6BQoZjf-RaOlWFFPX8chQyGHQEo,242401
         | 
| 161 161 | 
             
            modal_proto/api_pb2_grpc.pyi,sha256=zK2Ms2Xws02cC9uy5U5A5sPULXx4T6A9o9BONqFcipg,56456
         | 
| 162 162 | 
             
            modal_proto/modal_api_grpc.py,sha256=K_GsrHIw2CayKh_nyuYAblQW_tOijUNW267Hs9vWUWU,14948
         | 
| @@ -170,9 +170,9 @@ modal_proto/options_pb2_grpc.pyi,sha256=CImmhxHsYnF09iENPoe8S4J-n93jtgUYD2JPAc0y | |
| 170 170 | 
             
            modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
         | 
| 171 171 | 
             
            modal_version/__init__.py,sha256=m94xZNWIjH8oUtJk4l9xfovzDJede2o7X-q0MHVECtM,470
         | 
| 172 172 | 
             
            modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
         | 
| 173 | 
            -
            modal_version/_version_generated.py,sha256 | 
| 174 | 
            -
            modal-0.74. | 
| 175 | 
            -
            modal-0.74. | 
| 176 | 
            -
            modal-0.74. | 
| 177 | 
            -
            modal-0.74. | 
| 178 | 
            -
            modal-0.74. | 
| 173 | 
            +
            modal_version/_version_generated.py,sha256=lCmI3tXFsN320hsNte42DBS1ZM1ZGpHOqEgCvda6x_Y,149
         | 
| 174 | 
            +
            modal-0.74.12.dist-info/METADATA,sha256=gfzeojo09jV7Mgv_PnfcvYiuo6cOZ9j_2oXvuTTrur4,2474
         | 
| 175 | 
            +
            modal-0.74.12.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
         | 
| 176 | 
            +
            modal-0.74.12.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
         | 
| 177 | 
            +
            modal-0.74.12.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
         | 
| 178 | 
            +
            modal-0.74.12.dist-info/RECORD,,
         | 
    
        modal_proto/api.proto
    CHANGED
    
    | @@ -1341,6 +1341,11 @@ message Function { | |
| 1341 1341 |  | 
| 1342 1342 | 
             
              AutoscalerSettings autoscaler_settings = 79;  // Bundle of parameters related to autoscaling
         | 
| 1343 1343 | 
             
              FunctionSchema function_schema = 80;
         | 
| 1344 | 
            +
             | 
| 1345 | 
            +
              // For server-side experimental functionality. Prefer using this over individual _experimental_* fields.
         | 
| 1346 | 
            +
              // Note the value type as string. Internally we'll coerce all values to string with str().
         | 
| 1347 | 
            +
              // On the server, it's necessary to convert back to the most natural type (e.g. int) when relevant.
         | 
| 1348 | 
            +
              map<string, string> experimental_options = 81;
         | 
| 1344 1349 | 
             
            }
         | 
| 1345 1350 |  | 
| 1346 1351 | 
             
            message FunctionAsyncInvokeRequest {
         | 
| @@ -1425,6 +1430,7 @@ message FunctionCreateResponse { | |
| 1425 1430 | 
             
              string __deprecated_web_url = 2  [ deprecated = true];  // Used up until 0.62.212
         | 
| 1426 1431 | 
             
              Function function = 4;
         | 
| 1427 1432 | 
             
              FunctionHandleMetadata handle_metadata = 5;
         | 
| 1433 | 
            +
              repeated Warning server_warnings = 6;
         | 
| 1428 1434 | 
             
            }
         | 
| 1429 1435 |  | 
| 1430 1436 | 
             
            message FunctionData {
         | 
| @@ -1490,6 +1496,8 @@ message FunctionData { | |
| 1490 1496 |  | 
| 1491 1497 | 
             
              AutoscalerSettings autoscaler_settings = 31;  // Bundle of parameters related to autoscaling
         | 
| 1492 1498 | 
             
              FunctionSchema function_schema = 32;
         | 
| 1499 | 
            +
             | 
| 1500 | 
            +
              map<string, string> experimental_options = 33;
         | 
| 1493 1501 | 
             
            }
         | 
| 1494 1502 |  | 
| 1495 1503 | 
             
            message FunctionExtended {
         | 
| @@ -3028,6 +3036,7 @@ message Warning { | |
| 3028 3036 | 
             
                WARNING_TYPE_UNSPECIFIED = 0;
         | 
| 3029 3037 | 
             
                WARNING_TYPE_CLIENT_DEPRECATION = 1;
         | 
| 3030 3038 | 
             
                WARNING_TYPE_RESOURCE_LIMIT = 2;
         | 
| 3039 | 
            +
                WARNING_TYPE_FUNCTION_CONFIGURATION = 3;
         | 
| 3031 3040 | 
             
              }
         | 
| 3032 3041 | 
             
              WarningType type = 1;
         | 
| 3033 3042 | 
             
              string message = 2;
         |