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)