modal.Stub

class Stub(object)

A Stub is a description of how to create a Modal application.

The stub object principally describes Modal objects (Function, Image, Secret, etc.) associated with the application. It has three responsibilities:

  • Syncing of identities across processes (your local Python interpreter and every Modal worker active in your application).
  • Making Objects stay alive and not be garbage collected for as long as the app lives (see App lifetime below).
  • Manage log collection for everything that happens inside your code.

Registering functions with an app

The most common way to explicitly register an Object with an app is through the @stub.function decorator. It both registers the annotated function itself and other passed objects, like schedules and secrets, with the app:

import modal

stub = modal.Stub()

@stub.function(
    secret=modal.Secret.from_name("some_secret"),
    schedule=modal.Period(days=1),
)
def foo():
    pass

In this example, the secret and schedule are registered with the app.

@typechecked
def __init__(
    self,
    name: Optional[str] = None,
    *,
    image: Optional[_Image] = None,  # default image for all functions (default is `modal.Image.debian_slim()`)
    mounts: Sequence[_Mount] = [],  # default mounts for all functions
    secrets: Sequence[_Secret] = [],  # default secrets for all functions
    **blueprint: Provider,  # any Modal Object dependencies (Dict, Queue, etc.)
) -> None:

Construct a new app stub, optionally with default image, mounts, secrets

Any “blueprint” objects are loaded as part of running or deploying the app, and are accessible by name on the running container app, e.g.:

stub = modal.Stub(key_value_store=modal.Dict())

@stub.function
def store_something(key: str, value: str):
    stub.app.key_value_store.put(key, value)
## name

```python
@property
def name(self) -> str:

The user-provided name of the Stub.

app

@property
def app(self) -> Optional[_App]:

Reference to the currently running app, if any.

description

@property
def description(self) -> str:

The Stub’s name, if available, or a fallback descriptive identifier.

is_inside

@typechecked
def is_inside(self, image: Optional[_Image] = None) -> bool:

Returns if the program is currently running inside a container for this app.

run

@contextlib.asynccontextmanager
def run(
    self,
    client: Optional[_Client] = None,
    stdout=None,
    show_progress: Optional[bool] = None,
    detach: bool = False,
    output_mgr: Optional[OutputManager] = None,
) -> AsyncGenerator[_App, None]:

Context manager that runs an app on Modal.

Use this as the main entry point for your Modal application. All calls to Modal functions should be made within the scope of this context manager, and they will correspond to the current app.

See the documentation for the App class for more details.

serve

def serve(
    self,
    client: Optional[_Client] = None,
    stdout=None,
    show_progress: Optional[bool] = None,
    timeout: float = 1e10,
) -> None:

Run an app until the program is interrupted.

serve_update

def serve_update(
    self,
    existing_app_id: str,
    is_ready: Event,
) -> None:

deploy

@typechecked
def deploy(
    self,
    name: Optional[
        str
    ] = None,  # Unique name of the deployment. Subsequent deploys with the same name overwrites previous ones. Falls back to the app name
    namespace=api_pb2.DEPLOYMENT_NAMESPACE_WORKSPACE,
    client=None,
    stdout=None,
    show_progress=None,
    object_entity: str = "ap",
) -> _App:

Deploy an app and export its objects persistently.

Typically, using the command-line tool modal deploy <module or script> should be used, instead of this method.

Usage:

if __name__ == "__main__":
    stub.deploy()

Deployment has two primary purposes:

  • Persists all of the objects in the app, allowing them to live past the current app run. For schedules this enables headless “cron”-like functionality where scheduled functions continue to be invoked after the client has disconnected.
  • Allows for certain kinds of these objects, deployment objects, to be referred to and used by other apps.

registered_functions

@property
def registered_functions(self) -> Dict[str, _FunctionHandle]:

All modal.Function objects registered on the stub.

registered_entrypoints

@property
def registered_entrypoints(self) -> Dict[str, LocalEntrypoint]:

All local CLI entrypoints registered on the stub.

registered_web_endpoints

@property
def registered_web_endpoints(self) -> List[str]:

Names of web endpoint (ie. webhook) functions registered on the stub.

local_entrypoint

@decorator_with_options
def local_entrypoint(self, raw_f=None, name: Optional[str] = None):

Decorate a function to be used as a CLI entrypoint for a Modal App.

These functions can be used to define code that runs locally to set up the app, and act as an entrypoint to start Modal functions from. Note that regular Modal functions can also be used as CLI entrypoints, but unlike local_entrypoint, those functions are executed remotely directly.

Example

@stub.local_entrypoint
def main():
    some_modal_function.call()

You can call the function using modal run directly from the CLI:

modal run stub_module.py

Note that an explicit stub.run() is not needed, as an app is automatically created for you.

Multiple Entrypoints

If you have multiple local_entrypoint functions, you can qualify the name of your stub and function:

modal run stub_module.py::stub.some_other_function

Parsing Arguments

If your entrypoint function take arguments with primitive types, modal run automatically parses them as CLI options. For example, the following function can be called with modal run stub_module.py --foo 1 --bar "hello":

@stub.local_entrypoint
def main(foo: int, bar: str):
    some_modal_function.call(foo, bar)

Currently, str, int, float, bool, and datetime.datetime are supported. Use modal run stub_module.py --help for more information on usage.

function

@decorator_with_options
@typechecked
def function(
    self,
    raw_f=None,  # The decorated function
    *,
    image: Optional[_Image] = None,  # The image to run as the container for the function
    schedule: Optional[Schedule] = None,  # An optional Modal Schedule for the function
    secret: Optional[_Secret] = None,  # An optional Modal Secret with environment variables for the container
    secrets: Sequence[_Secret] = (),  # Plural version of `secret` when multiple secrets are needed
    gpu: GPU_T = None,  # GPU specification as string ("any", "T4", "A10G", ...) or object (`modal.GPU.A100()`, ...)
    serialized: bool = False,  # Whether to send the function over using cloudpickle.
    mounts: Sequence[_Mount] = (),
    shared_volumes: Dict[str, _SharedVolume] = {},
    allow_cross_region_volumes: bool = False,  # Whether using shared volumes from other regions is allowed.
    cpu: Optional[float] = None,  # How many CPU cores to request. This is a soft limit.
    memory: Optional[int] = None,  # How much memory to request, in MiB. This is a soft limit.
    proxy: Optional[_Proxy] = None,  # Reference to a Modal Proxy to use in front of this function.
    retries: Optional[Union[int, Retries]] = None,  # Number of times to retry each input in case of failure.
    concurrency_limit: Optional[int] = None,  # Limit for max concurrent containers running the function.
    container_idle_timeout: Optional[int] = None,  # Timeout for idle containers waiting for inputs to shut down.
    timeout: Optional[int] = None,  # Maximum execution time of the function in seconds.
    interactive: bool = False,  # Whether to run the function in interactive mode.
    keep_warm: Union[bool, int, None] = None,  # An optional number of containers to always keep warm.
    name: Optional[str] = None,  # Sets the Modal name of the function within the stub
    is_generator: Optional[bool] = None,  # If not set, it's inferred from the function signature
    cloud: Optional[str] = None,  # Cloud provider to run the function on. Possible values are aws, gcp, auto.
) -> _FunctionHandle:  # Function object - callable as a regular function within a Modal app

Decorator to register a new Modal function with this stub.

webhook

@decorator_with_options
@typechecked
def webhook(
    self,
    raw_f,
    *,
    method: str = "GET",  # REST method for the created endpoint.
    label: Optional[
        str
    ] = None,  # Label for created endpoint. Final subdomain will be <workspace>--<label>.modal.run.
    wait_for_response: bool = True,  # Whether requests should wait for and return the function response.
    image: Optional[_Image] = None,  # The image to run as the container for the function
    secret: Optional[_Secret] = None,  # An optional Modal Secret with environment variables for the container
    secrets: Sequence[_Secret] = (),  # Plural version of `secret` when multiple secrets are needed
    gpu: GPU_T = None,  # GPU specification as string ("any", "T4", "A10G", ...) or object (`modal.GPU.A100()`, ...)
    mounts: Sequence[_Mount] = (),
    shared_volumes: Dict[str, _SharedVolume] = {},
    allow_cross_region_volumes: bool = False,  # Whether using shared volumes from other regions is allowed.
    cpu: Optional[float] = None,  # How many CPU cores to request. This is a soft limit.
    memory: Optional[int] = None,  # How much memory to request, in MiB. This is a soft limit.
    proxy: Optional[_Proxy] = None,  # Reference to a Modal Proxy to use in front of this function.
    retries: Optional[Union[int, Retries]] = None,  # Number of times to retry each input in case of failure.
    concurrency_limit: Optional[int] = None,  # Limit for max concurrent containers running the function.
    container_idle_timeout: Optional[int] = None,  # Timeout for idle containers waiting for inputs to shut down.
    timeout: Optional[int] = None,  # Maximum execution time of the function in seconds.
    keep_warm: Union[bool, int, None] = None,  # An optional number of containers to always keep warm.
    cloud: Optional[str] = None,  # Cloud provider to run the function on. Possible values are aws, gcp, auto.
):

Register a basic web endpoint with this application.

This is the simple way to create a web endpoint on Modal. The function behaves as a FastAPI handler and should return a response object to the caller.

Endpoints created with @stub.webhook are meant to be simple, single request handlers and automatically have CORS enabled. For more flexibility, use @stub.asgi.

To learn how to use Modal with popular web frameworks, see the guide on web endpoints.

All webhook requests have a 150s maximum request time for the HTTP request itself. However, the underlying functions can run for longer and return results to the caller on completion.

The two wait_for_response modes for webhooks are as follows:

  • wait_for_response=True - tries to fulfill the request on the original URL, but returns a 302 redirect after ~150s to a result URL (original URL with an added __modal_function_id=... query parameter)
  • wait_for_response=False - immediately returns a 202 ACCEPTED response with a JSON payload: {"result_url": "..."} containing the result “redirect” URL from above (which in turn redirects to itself every ~150s)

asgi

@decorator_with_options
@typechecked
def asgi(
    self,
    asgi_app,  # The asgi app
    *,
    label: Optional[
        str
    ] = None,  # Label for created endpoint. Final subdomain will be <workspace>--<label>.modal.run.
    wait_for_response: bool = True,  # Whether requests should wait for and return the function response.
    image: Optional[_Image] = None,  # The image to run as the container for the function
    secret: Optional[_Secret] = None,  # An optional Modal Secret with environment variables for the container
    secrets: Sequence[_Secret] = (),  # Plural version of `secret` when multiple secrets are needed
    gpu: GPU_T = None,  # GPU specification as string ("any", "T4", "A10G", ...) or object (`modal.GPU.A100()`, ...)
    mounts: Sequence[_Mount] = (),
    shared_volumes: Dict[str, _SharedVolume] = {},
    allow_cross_region_volumes: bool = False,  # Whether using shared volumes from other regions is allowed.
    cpu: Optional[float] = None,  # How many CPU cores to request. This is a soft limit.
    memory: Optional[int] = None,  # How much memory to request, in MiB. This is a soft limit.
    proxy: Optional[_Proxy] = None,  # Reference to a Modal Proxy to use in front of this function.
    retries: Optional[Union[int, Retries]] = None,  # Number of times to retry each input in case of failure.
    concurrency_limit: Optional[int] = None,  # Limit for max concurrent containers running the function.
    container_idle_timeout: Optional[int] = None,  # Timeout for idle containers waiting for inputs to shut down.
    timeout: Optional[int] = None,  # Maximum execution time of the function in seconds.
    keep_warm: Union[bool, int, None] = None,  # An optional number of containers to always keep warm.
    cloud: Optional[str] = None,  # Cloud provider to run the function on. Possible values are aws, gcp, auto.
    _webhook_type=api_pb2.WEBHOOK_TYPE_ASGI_APP,
):

Register an ASGI app with this application.

Asynchronous Server Gateway Interface (ASGI) is a standard for Python synchronous and asynchronous apps, supported by all popular Python web libraries. This is an advanced decorator that gives full flexibility in defining one or more web endpoints on Modal.

To learn how to use Modal with popular web frameworks, see the guide on web endpoints.

The two wait_for_response modes for webhooks are as follows:

  • wait_for_response=True - tries to fulfill the request on the original URL, but returns a 302 redirect after ~150s to a result URL (original URL with an added __modal_function_id=fc-1234abcd query parameter)
  • wait_for_response=False - immediately returns a 202 ACCEPTED response with a json payload: {"result_url": "..."} containing the result “redirect” url from above (which in turn redirects to itself every 150s)

wsgi

@decorator_with_options
def wsgi(
    self,
    wsgi_app,
    **kwargs,
):

Exposes a WSGI app. For a list of arguments, see the documentation for asgi.

interactive_shell

def interactive_shell(self, cmd=None, image=None, **kwargs):

Run an interactive shell (like bash) within the image for this app.

This is useful for online debugging and interactive exploration of the contents of this image. If cmd is optionally provided, it will be run instead of the default shell inside this image.

Example

import modal

stub = modal.Stub(image=modal.Image.debian_slim().apt_install("vim"))

if __name__ == "__main__":
    stub.interactive_shell("/bin/bash")

Or alternatively:

import modal

stub = modal.Stub()
app_image = modal.Image.debian_slim().apt_install("vim")

if __name__ == "__main__":
    stub.interactive_shell(cmd="/bin/bash", image=app_image)