modal.App

class App(object)

A Modal app (prior to April 2024 a “stub”) is a group of functions and classes deployed together.

The app serves at least three purposes:

  • A unit of deployment for functions and classes.
  • Syncing of identities of (primarily) functions and classes across processes (your local Python interpreter and every Modal containerr active in your application).
  • 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 @app.function() decorator. It both registers the annotated function itself and other passed objects, like schedules and secrets, with the app:

import modal

app = modal.App()

@app.function(
    secrets=[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.

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
    volumes: Dict[Union[str, PurePosixPath], _Volume] = {},  # default volumes for all functions
) -> None:

Construct a new app, optionally with default image, mounts, secrets, or volumes.

image = modal.Image.debian_slim().pip_install(...)
mount = modal.Mount.from_local_dir("./config")
secret = modal.Secret.from_name("my-secret")
volume = modal.Volume.from_name("my-data")
app = modal.App(image=image, mounts=[mount], secrets=[secret], volumes={"/mnt/data": volume})

name

@property
def name(self) -> Optional[str]:

The user-provided name of the App.

is_interactive

@property
def is_interactive(self) -> bool:

Whether the current app for the app is running in interactive mode.

app_id

@property
def app_id(self) -> Optional[str]:

Return the app_id of a running or stopped app.

description

@property
def description(self) -> Optional[str]:

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

lookup

@staticmethod
def lookup(
    label: str,
    client: Optional[_Client] = None,
    environment_name: Optional[str] = None,
    create_if_missing: bool = False,
) -> "_App":

Look up an app with a given name. When create_if_missing is true, the app will be created if it doesn’t exist.

app = modal.App.lookup("my-app", create_if_missing=True)

modal.Sandbox.create("echo", "hi", app=app)

set_description

def set_description(self, description: str):

image

@property
def image(self) -> _Image:

run

@contextmanager
def run(
    self,
    client: Optional[_Client] = None,
    show_progress: Optional[bool] = None,
    detach: bool = False,
    interactive: bool = False,
) -> 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.

Example

with app.run():
    some_modal_function.remote()

To enable output printing, use modal.enable_output():

with modal.enable_output():
    with app.run():
        some_modal_function.remote()

Note that you cannot invoke this in global scope of a file where you have Modal functions or Classes, since that would run the block when the function or class is imported in your containers as well. If you want to run it as your entrypoint, consider wrapping it:

if __name__ == "__main__":
    with app.run():
        some_modal_function.remote()

You can then run your script with:

python app_module.py

Note that this method used to return a separate “App” object. This is no longer useful since you can use the app itself for access to all objects. For backwards compatibility reasons, it returns the same app.

registered_functions

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

All modal.Function objects registered on the app.

registered_classes

@property
def registered_classes(self) -> Dict[str, _Function]:

All modal.Cls objects registered on the app.

registered_entrypoints

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

All local CLI entrypoints registered on the app.

indexed_objects

@property
def indexed_objects(self) -> Dict[str, _Object]:

registered_web_endpoints

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

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

local_entrypoint

def local_entrypoint(
    self, _warn_parentheses_missing: Any = None, *, name: Optional[str] = None
) -> Callable[[Callable[..., Any]], _LocalEntrypoint]:

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

@app.local_entrypoint()
def main():
    some_modal_function.remote()

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

modal run app_module.py

Note that an explicit app.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 app and function:

modal run app_module.py::app.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 app_module.py --foo 1 --bar "hello":

@app.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 app_module.py --help for more information on usage.

function

def function(
    self,
    _warn_parentheses_missing: Any = None,
    *,
    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
    secrets: Sequence[_Secret] = (),  # Optional Modal Secret objects with environment variables for the container
    gpu: Union[
        GPU_T, List[GPU_T]
    ] = None,  # GPU request as string ("any", "T4", ...), object (`modal.GPU.A100()`, ...), or a list of either
    serialized: bool = False,  # Whether to send the function over using cloudpickle.
    mounts: Sequence[_Mount] = (),  # Modal Mounts added to the container
    network_file_systems: Dict[
        Union[str, PurePosixPath], _NetworkFileSystem
    ] = {},  # Mountpoints for Modal NetworkFileSystems
    volumes: Dict[
        Union[str, PurePosixPath], Union[_Volume, _CloudBucketMount]
    ] = {},  # Mount points for Modal Volumes & CloudBucketMounts
    allow_cross_region_volumes: bool = False,  # Whether using network file systems from other regions is allowed.
    cpu: Optional[float] = None,  # How many CPU cores to request. This is a soft limit.
    # Specify, in MiB, a memory request which is the minimum memory required.
    # Or, pass (request, limit) to additionally specify a hard limit in MiB.
    memory: Optional[Union[int, Tuple[int, int]]] = None,
    ephemeral_disk: Optional[int] = None,  # Specify, in MiB, the ephemeral disk size for the Function.
    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,  # An optional maximum number of concurrent containers running the function (keep_warm sets minimum).
    allow_concurrent_inputs: Optional[int] = None,  # Number of inputs the container may fetch to run concurrently.
    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: Optional[
        int
    ] = None,  # An optional minimum number of containers to always keep warm (use concurrency_limit for maximum).
    name: Optional[str] = None,  # Sets the Modal name of the function within the app
    is_generator: Optional[
        bool
    ] = None,  # Set this to True if it's a non-generator function returning a [sync/async] generator object
    cloud: Optional[str] = None,  # Cloud provider to run the function on. Possible values are aws, gcp, oci, auto.
    region: Optional[Union[str, Sequence[str]]] = None,  # Region or regions to run the function on.
    enable_memory_snapshot: bool = False,  # Enable memory checkpointing for faster cold starts.
    checkpointing_enabled: Optional[bool] = None,  # Deprecated
    block_network: bool = False,  # Whether to block network access
    # Maximum number of inputs a container should handle before shutting down.
    # With `max_inputs = 1`, containers will be single-use.
    max_inputs: Optional[int] = None,
    i6pn: Optional[bool] = None,  # Whether to enable IPv6 container networking within the region.
    # The next group of parameters are deprecated; do not use in any new code
    interactive: bool = False,  # Deprecated: use the `modal.interact()` hook instead
    # Parameters below here are experimental. Use with caution!
    _experimental_scheduler_placement: Optional[
        SchedulerPlacement
    ] = None,  # Experimental controls over fine-grained scheduling (alpha).
    _experimental_buffer_containers: Optional[int] = None,  # Number of additional, idle containers to keep around.
    _experimental_proxy_ip: Optional[str] = None,  # IP address of proxy
) -> _FunctionDecoratorType:

Decorator to register a new Modal function with this app.

cls

@typing_extensions.dataclass_transform(field_specifiers=(parameter,), kw_only_default=True)
def cls(
    self,
    _warn_parentheses_missing: Optional[bool] = None,
    *,
    image: Optional[_Image] = None,  # The image to run as the container for the function
    secrets: Sequence[_Secret] = (),  # Optional Modal Secret objects with environment variables for the container
    gpu: Union[
        GPU_T, List[GPU_T]
    ] = None,  # GPU request as string ("any", "T4", ...), object (`modal.GPU.A100()`, ...), or a list of either
    serialized: bool = False,  # Whether to send the function over using cloudpickle.
    mounts: Sequence[_Mount] = (),
    network_file_systems: Dict[
        Union[str, PurePosixPath], _NetworkFileSystem
    ] = {},  # Mountpoints for Modal NetworkFileSystems
    volumes: Dict[
        Union[str, PurePosixPath], Union[_Volume, _CloudBucketMount]
    ] = {},  # Mount points for Modal Volumes & CloudBucketMounts
    allow_cross_region_volumes: bool = False,  # Whether using network file systems from other regions is allowed.
    cpu: Optional[float] = None,  # How many CPU cores to request. This is a soft limit.
    # Specify, in MiB, a memory request which is the minimum memory required.
    # Or, pass (request, limit) to additionally specify a hard limit in MiB.
    memory: Optional[Union[int, Tuple[int, int]]] = None,
    ephemeral_disk: Optional[int] = None,  # Specify, in MiB, the ephemeral disk size for the Function.
    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.
    allow_concurrent_inputs: Optional[int] = None,  # Number of inputs the container may fetch to run concurrently.
    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: Optional[int] = 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, oci, auto.
    region: Optional[Union[str, Sequence[str]]] = None,  # Region or regions to run the function on.
    enable_memory_snapshot: bool = False,  # Enable memory checkpointing for faster cold starts.
    checkpointing_enabled: Optional[bool] = None,  # Deprecated
    block_network: bool = False,  # Whether to block network access
    # Limits the number of inputs a container handles before shutting down.
    # Use `max_inputs = 1` for single-use containers.
    max_inputs: Optional[int] = None,
    # The next group of parameters are deprecated; do not use in any new code
    interactive: bool = False,  # Deprecated: use the `modal.interact()` hook instead
    # Parameters below here are experimental. Use with caution!
    _experimental_scheduler_placement: Optional[
        SchedulerPlacement
    ] = None,  # Experimental controls over fine-grained scheduling (alpha).
    _experimental_buffer_containers: Optional[int] = None,  # Number of additional, idle containers to keep around.
    _experimental_proxy_ip: Optional[int] = None,  # IP address of proxy
) -> Callable[[CLS_T], CLS_T]:

spawn_sandbox

def spawn_sandbox(
    self,
    *entrypoint_args: str,
    image: Optional[_Image] = None,  # The image to run as the container for the sandbox.
    mounts: Sequence[_Mount] = (),  # Mounts to attach to the sandbox.
    secrets: Sequence[_Secret] = (),  # Environment variables to inject into the sandbox.
    network_file_systems: Dict[Union[str, PurePosixPath], _NetworkFileSystem] = {},
    timeout: Optional[int] = None,  # Maximum execution time of the sandbox in seconds.
    workdir: Optional[str] = None,  # Working directory of the sandbox.
    gpu: GPU_T = None,
    cloud: Optional[str] = None,
    region: Optional[Union[str, Sequence[str]]] = None,  # Region or regions to run the sandbox on.
    cpu: Optional[float] = None,  # How many CPU cores to request. This is a soft limit.
    # Specify, in MiB, a memory request which is the minimum memory required.
    # Or, pass (request, limit) to additionally specify a hard limit in MiB.
    memory: Optional[Union[int, Tuple[int, int]]] = None,
    block_network: bool = False,  # Whether to block network access
    volumes: Dict[
        Union[str, PurePosixPath], Union[_Volume, _CloudBucketMount]
    ] = {},  # Mount points for Modal Volumes and CloudBucketMounts
    pty_info: Optional[api_pb2.PTYInfo] = None,
    _experimental_scheduler_placement: Optional[
        SchedulerPlacement
    ] = None,  # Experimental controls over fine-grained scheduling (alpha).
) -> _Sandbox:

Sandboxes are a way to run arbitrary commands in dynamically defined environments.

This function returns a SandboxHandle, which can be used to interact with the running sandbox.

Refer to the docs on how to spawn and use sandboxes.

include

def include(self, /, other_app: "_App"):

Include another app’s objects in this one.

Useful splitting up Modal apps across different self-contained files

app_a = modal.App("a")
@app.function()
def foo():
    ...

app_b = modal.App("b")
@app.function()
def bar():
    ...

app_a.include(app_b)

@app_a.local_entrypoint()
def main():
    # use function declared on the included app
    bar.remote()