This changelog documents user-facing updates (features, enhancements, fixes, and deprecations) to the modal client library. Patch releases are made on every change.
The client library is still in pre-1.0 development, and sometimes breaking changes are necessary. We try to minimize them and publish deprecation warnings / migration guides in advance, typically providing a transition window of several months.
You can also refer to the 1.0 migration guide for a summary of major breaking changes that we are rolling out before releasing version 1.0.
We appreciate your patience while we speedily work towards a stable release of the client.
- Adds support for new strict
bytestype formodal.parameter
Usage:
import typing
import modal
app = modal.App()
@app.cls()
class Foo:
a: bytes = modal.parameter(default=b"hello")
@modal.method()
def bar(self):
return f"hello {self.a}"
@app.local_entrypoint()
def main():
foo = Foo(a=b"world")
foo.bar.remote()Note: For parameterized web endoints you must base64 encode the bytes before passing them in as a query parameter.
- Include git commit info at the time of app deployment.
- Added
Image.cmd()for setting image default entrypoint args (a.k.a.CMD).
- Fixes a bug which could cause
Function.mapand sibling methods to stall indefinitely if there was an exception in the input iterator itself (i.e. not the mapper function)
- The
@modal.web_endpointdecorator is now deprecated. We are replacing it with@modal.fastapi_endpoint. This can be a simple name substitution in your code; the two decorators have identical semantics.
- The
keep_warm=parameter has been removed from the@modal.methoddecorator. This parameter has been nonfunctional since v0.63.0; all autoscaler configuration must be done at the level of the modal Cls.
- Adds
modal.fastapi_endpointas an alias formodal.web_endpoint. We will be deprecating themodal.web_endpointname (but not the functionality) as part of the Modal 1.0 release.
- The
wait_for_responseparameter of Modal's web endpoint decorators has been removed (originally deprecated in May 2024).
-
It is now possible to call
Cls.with_optionson an unhydrated Cls, e.g.ModelWithGPU = modal.Cls.from_name("my-app", "Model").with_options(gpu="H100")
Cls.with_options()now accept unhydated volume and secrets
- We're renaming several
App.functionandApp.clsparameters that configure the behavior of Modal's autoscaler:concurrency_limitis nowmax_containerskeep_warmis nowmin_containerscontainer_idle_timeoutis nowscaledown_window
- The old names will continue to work, but using them will issue a deprecation warning. The aim of the renaming is to reduce some persistent confusion about what these parameters mean. Code updates should require only a simple substitution of the new name.
- We're adding a new parameter,
buffer_containers(previously available as_experimental_buffer_containers). When your Function is actively handling inputs, the autoscaler will spin up additionalbuffer_containersso that subsequent inputs will not be blocked on cold starts. When the Function is idle, it will still scale down to the value given bymin_containers.
- Adds a new config field,
ignore_cache(also accessible via environment variables asMODAL_IGNORE_CACHE=1), which will force Images used by the App to rebuild but not clobber any existing cached Images. This can be useful for testing an App's robustness to Image rebuilds without affecting other Apps that depend on the same base Image layer(s).
- Adds a deprecation warning to the
workspaceparameter inmodal.Clslookup methods. This argument is unused and will be removed in the future.
- We've moved the
modal.functions.gatherfunction to be a staticmethod onmodal.FunctionCall.gather. The former spelling has been deprecated and will be removed in a future version.
- Fixes issue where running
modal shellwith a dot-separated module reference as input would not accept the required-mflag for "module mode", but still emitted a warning telling users to use-m
- Fixes an issue where
modal.runner.deploy_app()didn't work when called from within a running (remote) Modal Function
-
Introduces an
-mflag tomodal run,modal shell,modal serveandmodal deploy, which indicates that the modal app/function file is specified using python "module syntax" rather than a file path. In the future this will be a required flag when using module syntax.Old syntax:
modal run my_package/modal_main.py modal run my_package.modal_main
New syntax (note the
-mon the second line):modal run my_package/modal_main.py modal run -m my_package.modal_main
- Passing
App.lookupan invalid name now raises an error. App names may contain only alphanumeric characters, dashes, periods, and underscores, must be shorter than 64 characters, and cannot conflict with App ID strings.
- Fixes a bug where sandboxes returned from
Sandbox.list()were not snapshottable even if they were created with_experimental_enable_snapshot.
modal.FunctionCallis now available in the top-levelmodalnamespace. We recommend referencing the class this way instead of using the the fully-qualifiedmodal.functions.FunctionCallname.
Function.web_urlwill now return None (instead of raising an error) when the Function is not a web endpoint
- Deprecate the GPU classes (
gpu=A100(...)etc) in favor of just using strings (gpu="A100"etc)
- Adds a pending deprecation warning when looking up class methods using
Function.from_name, e.g.Function.from_name("some_app", "SomeClass.some_method"). The recommended way to reference methods of classes is to look up the class instead:RemoteClass = Cls.from_name("some_app", "SomeClass")
- Fixes an issue introduced in
0.73.19that prevented access to GPUs during image builds
- When using a parameterized class (with at least one
modal.parameter()specified), class instantiation with an incorrect construction signature (wrong arguments or types) will now fail at the.remote()calling site instead of container startup for the called class.
- Fixed the status message shown in terminal logs for ephemeral Apps to accurately report the number of active containers.
- Warns users if the
modal.Imageof a Function/Cls doesn't include all the globally imported "local" modules (using.add_local_python_source()), and the user hasn't explicitly set aninclude_sourcevalue of True/False. This is in preparation for an upcoming deprecation of the current "auto mount" logic.
-
Modal functions, methods and entrypoints can now accept variable-length arguments to skip Modal's default CLI parsing. This is useful if you want to use Modal with custom argument parsing via
argparseorHfArgumentParser. For example, the following function can be invoked withmodal run my_file.py --foo=42 --bar="baz":import argparse @app.function() def train(*arglist): parser = argparse.ArgumentParser() parser.add_argument("--foo", type=int) parser.add_argument("--bar", type=str) args = parser.parse_args(args = arglist)
modal runnow runs a single local entrypoints/function in the selected module. If exactly one local entrypoint or function exists in the selected module, the user doesn't have to qualify the runnable in the modal run command, even if some of the module's referenced apps have additional local entrypoints or functions. This partially restores "auto-inferred function" functionality that was changed in v0.72.48.
-
Introduces an
include_sourceargument in theApp.functionandApp.clsdecorators that let users configure which class of python packages are automatically included as source mounts in created modal functions/classes (what we used to call "automount" behavior). This will supersede the MODAL_AUTOMOUNT configuration value which will eventually be deprecated. As a convenience, themodal.Appconstructor will also accept aninclude_sourceargument which serves as the default for all the app's functions and classes.The
include_sourceargument accepts the following values:True(default in a future version of Modal) Automatically includes the Python files of the source package of the function's own home module, but not any other local packages. Roughly equivalent otMODAL_AUTOMOUNT=0in previous versions of Modal.False- don't include any local source. Assumes the function's home module is importable in the container environment through some other means (typically added to the providedmodal.Image's Python environment).None(the default) - use current soon-to-be-deprecated automounting behavior, including source of all first party packages that are not installed into site-packages locally.
-
Minor change to
MODAL_AUTOMOUNT=0: When running/deploying using a module path (e.g.modal run mypak.mymod), all non .pyc files of the source package (mypakin this case) are now included in the function's container. Previously, only the function's home.pymodule file + any__init__.pyfiles in its package structure were included. Note that this is only for MODAL_AUTOMOUNT=0. To get full control over which source files are included with your functions, you can setinclude_source=Falseon your function (see above) and manually specify the files to include using theignoreargument toImage.add_local_python_source.
- Deprecated
.lookupmethods on Modal objects. Users are encouraged to use.from_nameinstead. In most cases this will be a simple name substitution. See the 1.0 migration guide for more information.
- Fixes bug introduced in v0.72.48 where
modal rundidn't work with files having globalFunction.from_name()/Function.lookup()/Cls.from_name()/Cls.lookup()calls.
- Fixes a CLI bug where you couldn't reference functions via a qualified app, e.g.
mymodule::{app_variable}.{function_name}. - The
modal run,modal serveandmodal shellcommands get more consistent error messages in cases where the passed app or function reference isn't resolvable to something that the current command expects. - Removes the deprecated
__getattr__,__setattr__,__getitem__and__setitem__methods frommodal.App
- Introduced a new public method,
.hydrate, for on-demand hydration of Modal objects. This method replaces the existing semi-public.resolvemethod, which is now deprecated.
- The Image returned by
Sandbox.snapshot_filesystemnow hasobject_idand other metadata pre-assigned rather than require loading by subsequent calls to sandboxes or similar to set this data.
- Adds a new
oidc_auth_role_arnfield toCloudBucketMountfor using OIDC authentication to create the mountpoint.
- No longer prints a warning if
app.includere-includes an already included function (warning is still printed if another function with the same name is included)
- Internal refactor of the
modal.objectmodule. All entities exceptObjectfrom that module have now been moved to themodal._object"private" module.
- The
@modal.builddecorator is now deprecated. For storing large assets (e.g. model weights), we now recommend using amodal.Volumeover writing data to themodal.Imagefilesystem directly.
- Fixes bug introduced in v0.72.9 where
modal run SomeClass.some_methodwould incorrectly print a deprecation warning.
- Added an
environment_nameparameter to theApp.runcontext manager.
- Fixes a bug introduced in v0.72.2 when specifying
add_python="3.9"inImage.from_registry.
- The default behavior
Image.from_dockerfile()andimage.dockerfile_commands()if no parameter is passed toignorewill be to automatically detect if there is a valid dockerignore file in the current working directory or next to the dockerfile following the same rules asdockerignoredoes usingdockercommands. Previously no patterns were ignored.
FilePatternMatcherhas a new constructorfrom_filewhich allows you to read file matching patterns from a file instead of having to pass them in directly, this can be used forImagemethods accepting anignoreparameter in order to read ignore patterns from files.
- Modal Volumes can now be renamed via the CLI (
modal volume rename) or SDK (modal.Volume.rename).
- Adds
Image.from_id, which returns anImageobject from an existing image id.
- Sandboxes now support fsnotify-like file watching:
from modal.file_io import FileWatchEventType
app = modal.App.lookup("file-watch", create_if_missing=True)
sb = modal.Sandbox.create(app=app)
events = sb.watch("/foo")
for event in events:
if event.type == FileWatchEventType.Modify:
print(event.paths)- The sandbox filesystem API now accepts write payloads of sizes up to 1 GiB.
Image.from_dockerfile()andimage.dockerfile_commands()now auto-infer which files need to be uploaded based on COPY commands in the source ifcontext_mountis omitted. Theignore=argument to these methods can be used to selectively omit files using a set of glob patterns.
-
You can now point
modal launch vscodeat an arbitrary Dockerhub base image:modal launch vscode --image=nvidia/cuda:12.4.0-devel-ubuntu22.04
-
You can now run GPU workloads on Nvidia L40S GPUs:
@app.function(gpu="L40S") def my_gpu_fn(): ...
- Fixed a bug introduced in v0.68.39 that changed the exception type raise when the target object for
.from_name/.lookupmethods was not found.
- Standardized terminology in
.from_name/.lookup/.deletemethods to usenameconsistently wherelabelandtagwere used interchangeably before. Code that invokes these methods usinglabel=as an explicit keyword argument will issue a deprecation warning and will break in a future release.
- The internal
deprecation_erroranddeprecation_warningutilities have been moved to a private namespace
- Sandboxes now support additional filesystem commands
mkdir,rm, andls.
app = modal.App.lookup("sandbox-fs", create_if_missing=True)
sb = modal.Sandbox.create(app=app)
sb.mkdir("/foo")
with sb.open("/foo/bar.txt", "w") as f:
f.write("baz")
print(sb.ls("/foo"))- Two previously-introduced deprecations are now enforced and raise an error:
- The
App.spawn_sandboxmethod has been removed in favor ofSandbox.create Sandbox.createnow requires anAppobject to be passed
- The
- The
modal runCLI now has a--write-resultoption. When you pass a filename, Modal will write the return value of the entrypoint function to that location on your local filesystem. The return value of the function must be eitherstrorbytesto use this option; otherwise, an error will be raised. It can be useful for exercising a remote function that returns text, image data, etc.
Adds an ignore parameter to our Image add_local_dir and copy_local_dir methods. It is similar to the condition method on Mount methods but instead operates on a Path object. It takes either a list of string patterns to ignore which follows the dockerignore syntax implemented in our FilePatternMatcher class, or you can pass in a callable which allows for more flexible selection of files.
Usage:
img.add_local_dir(
"./local-dir",
remote_path="/remote-path",
ignore=FilePatternMatcher("**/*", "!*.txt") # ignore everything except files ending with .txt
)
img.add_local_dir(
...,
ignore=~FilePatternMatcher("**/*.py") # can be inverted for when inclusion filters are simpler to write
)
img.add_local_dir(
...,
ignore=["**/*.py", "!module/*.py"] # ignore all .py files except those in the module directory
)
img.add_local_dir(
...,
ignore=lambda fp: fp.is_relative_to("somewhere") # use a custom callable
)which will add the ./local-dir directory to the image but ignore all files except .txt files
Adds the requires_proxy_auth parameter to web_endpoint, asgi_app, wsgi_app, and web_server decorators. Requests to the app will respond with 407 Proxy Authorization Required if a webhook token is not supplied in the HTTP headers. Protects against DoS attacks that will unnecessarily charge users.
Cls.from_name(...)now works as a lazy alternative toCls.lookup()that doesn't perform any IO until a method on the class is used for a .remote() call or similar
- Fixed a bug introduced in v0.67.47 that suppressed console output from the
modal deployCLI.
We're removing support for .spawn()ing generator functions.
- Sandboxes now support a new filesystem API. The
open()method returns aFileIOhandle for native file handling in sandboxes.
app = modal.App.lookup("sandbox-fs", create_if_missing=True)
sb = modal.Sandbox.create(app=app)
with sb.open("test.txt", "w") as f:
f.write("Hello World\n")
f = sb.open("test.txt", "rb")
print(f.read())-
modal container execandmodal shellnow work correctly even when a pseudoterminal (PTY) is not present. This means, for example, that you can pipe the output of these commands to a file:modal shell -c 'uv pip list' > env.txt
- It is now possible to delete named
NetworkFileSystemobjects via the CLI (modal nfs delete ...) or API(modal.NetworkFileSystem.delete(...))
- Sandboxes now support filesystem snapshots. Run
Sandbox.snapshot_filesystem()to get an Image which can be used to spawn new Sandboxes.
- Adds
Image.add_local_python_sourcewhich works similarly to the old and soon-to-be-deprecatedMount.from_local_python_packagesbut for images. One notable difference is that the newadd_local_python_sourceonly includes.py-files by default
- Image build functions that use a
functools.wrapsdecorator will now have their global variables included in the cache key. Previously, the cache would use global variables referenced within the wrapper itself. This will force a rebuild for Image layers defined using wrapped functions.
- Fixed a bug introduced in v0.67.0 where it was impossible to call
modal.Clsmethods when passing a list of requested GPUs.
- Fixed a bug that executes the wrong method when a Modal Cls overrides a
@modal.methodinherited from a parent.
- Fixed a bug where pointing
modal runat a method on a Modal Cls would fail if the method was inherited from a parent.
New minor client version 0.67.x comes with an internal data model change for how Modal creates functions for Modal classes. There are no breaking or backwards-incompatible changes with this release. All forward lookup scenarios (.lookup() of a 0.67 class from a pre 0.67 client) as well as backwards lookup scenarios (.lookup() of a pre 0.67 class from a 0.67 client) work, except for a 0.62 client looking up a 0.67 class (this maintains our current restriction of not being able to lookup a 0.63+ class from a 0.62 client).
modal config set-environmentwill now raise if the requested environment does not exist.
- The
modal launchCLI now accepts a--detachflag to run the App in detached mode, such that it will persist after the local client disconnects.
- Adds
Image.add_local_file(..., copy=False)andImage.add_local_dir(..., copy=False)as a unified replacement for the oldImage.copy_local_*()andMount.add_local_*methods.
- Removed the
aiostreampackage from the modal client library dependencies.
Sandbox.exec now accepts arguments text and bufsize for streaming output, which controls text output and line buffering.
- Modal no longer supports Python 3.8, which has reached its official EoL.
- Escalates stuck input cancellations to container death. This prevents unresponsive user code from holding up resources.
- Input timeouts no longer kill the entire container. Instead, they just cancel the timed-out input, leaving the container and other concurrent inputs running.
- Fixed issue in
modal servewhere files used inImage.copy_*commands were not watched for changes
Sandbox.execcan now accepttimeout,workdir, andsecrets. See theSandbox.createfunction for context on how to use these arguments.
- Removed the
interactiveparameter fromfunctionandclsdecorators. This parameter has been deprecated since May 2024. Instead of specifying Modal Functions as interactive, usemodal run --interactiveto activate interactive mode.
- The
checkpointing_enabledoption, deprecated in March 2024, has now been removed.
- Output from
Sandbox.execcan now be directed to/dev/null,stdout, or stored for consumption. This behavior can be controlled via the newStreamTypearguments.
- Fixed a bug where the
Image.importscontext manager would not correctly propagate ImportError when using amodal.Cls.
- Fixed an issue where
modal runwould pause for 10s before exiting if there was a failure during app creation.
- The
modal container listCLI command now shows the containers within a specific environment: the active profile's environment if there is one, otherwise the workspace's default environment. You can pass--envto list containers in other environments.
- Fixed
modal servenot showing progress when reloading apps on file changes since v0.63.79.
- Fix a regression introduced in client version 0.64.209, which affects client authentication within a container.
- Fixed a bug where
Queue.putandQueue.put_manywould throwqueue.Fulleven iftimeout=None.
- The previously-deprecated
--confirmflag has been removed from themodal volume deleteCLI. Use--yesto force deletion without a confirmation prompt.
- Passing
wait_for_response=Falsein Modal webhook decorators is no longer supported. See the docs for alternatives.
- When writing to a
StreamWriterthat has already had EOF written, aValueErroris now raised instead of anEOFError.
- Memory snapshotting can now be used with parametrized functions.
- StreamWriters now accept strings as input.
- Fixed a bug where App rollbacks would not restart a schedule that had been removed in an intervening deployment.
- The
modal shellCLI command now takes a container ID, allowing you to shell into a running container.
modal shell --cmdnow can be shortened tomodal shell -c. This means you can use it likemodal shell -c "uname -a"to quickly run a command within the remote environment.
- The
Image.conda,Image.conda_install, andImage.conda_update_from_environmentmethods are now fully deprecated. We recommend usingmicromamba(viaImage.micromambaandImage.micromamba_install) instead, or manually installing and using conda withImage.run_commandswhen strictly necessary.
- Breaking Change:
Sandbox.tunnels()now returns aDictrather than aList. This dict is keyed by the container's port, and it returns aTunnelobject, just likemodal.forwarddoes.
modal.Functionandmodal.Clsnow support specifying alistof GPU configurations, allowing the Function's container pool to scale across each GPU configuration in preference order.
- The deprecated
_experimental_boostargument is now removed. (Deprecated in late July.)
- Sandboxes can now be created without an entrypoint command. If they are created like this, they will stay alive up until their set timeout. This is useful if you want to keep a long-lived sandbox and execute code in it later.
- Sandboxes now have a
cidr_allowlistargument, enabling controlled access to certain IP ranges. When not used (and withblock_network=False), the sandbox process will have open network access.
Introduce an experimental API to allow users to set the input concurrency for a container locally.
-
Creating sandboxes without an associated
Appis deprecated. If you are spawning aSandboxoutside a Modal container, you can lookup anAppby name to attach to theSandbox:app = modal.App.lookup('my-app', create_if_missing=True) modal.Sandbox.create('echo', 'hi', app=app)
-
App handles can now be looked up by name with
modal.App.lookup(name). This can be useful for associating Sandboxes with Apps:app = modal.App.lookup("my-app", create_if_missing=True) modal.Sandbox.create("echo", "hi", app=app)
- The default timeout for
modal.Image.run_functionhas been lowered to 1 hour. Previously it was 24 hours.
- Fixes an issue that could cause containers using
enable_memory_snapshot=Trueon Python 3.9 and below to shut down prematurely.
-
Added support for ASGI lifespan protocol:
@app.function() @modal.asgi_app() def func(): from fastapi import FastAPI, Request def lifespan(wapp: FastAPI): print("Starting") yield {"foo": "bar"} print("Shutting down") web_app = FastAPI(lifespan=lifespan) @web_app.get("/") def get_state(request: Request): return {"message": f"This is the state: {request.state.foo}"} return web_app
which enables support for
gradio>=v4amongst other libraries using lifespans
- Sandboxes now support port tunneling. Ports can be exposed via the
open_portsargument, and a list of active tunnels can be retrieved via the.tunnels()method.
- Fixed a regression in
modal launchto resume displaying output when starting the container.
-
Introduces new dataclass-style syntax for class parametrization (see updated docs)
@app.cls() class MyCls: param_a: str = modal.parameter() MyCls(param_a="hello") # synthesized constructor
-
The new syntax enforces types (
strorintfor now) on all parameters -
When the new syntax is used, any web endpoints (
web_endpoint,asgi_app,wsgi_apporweb_server) on the app will now also support parametrization through the use of query parameters matching the parameter names, e.g.https://myfunc.modal.run/?param_a="helloin the above example. -
The old explicit
__init__constructor syntax is still allowed, but could be deprecated in the future and doesn't work with web endpoint parametrization
- Added a
modal app rollbackCLI command for rolling back an App deployment to a previous version.
-
Commands in the
modal appCLI now accept an App name as a positional argument, in addition to an App ID:modal app history my-appAccordingly, the explicit
--nameoption has been deprecated. Providing a name that can be confused with an App ID will also now raise an error.
- Updated type stubs using generics to allow static type inferrence for functions calls, e.g.
function.remote(...).
ContainerProcesshandles now supportwait()andpoll(), likeSandboxobjects
-
Added support for dynamic batching. Functions or class methods decorated with
@modal.batchedwill now automatically batch their invocations together, up to a specifiedmax_batch_size. The batch will wait for a maximum ofwait_msfor more invocations after the first invocation is made. See guide for more details.@app.function() @modal.batched(max_batch_size=4, wait_ms=1000) async def batched_multiply(xs: list[int], ys: list[int]) -> list[int]: return [x * y for x, y in zip(xs, xs)] @app.cls() class BatchedClass(): @modal.batched(max_batch_size=4, wait_ms=1000) async def batched_multiply(xs: list[int], ys: list[int]) -> list[int]: return [x * y for x, y in zip(xs, xs)]
The batched function is called with individual inputs:
await batched_multiply.remote.aio(2, 3)
-
Sandboxes now have an
exec()method that lets you execute a command inside the sandbox container.execreturns aContainerProcesshandle for input and output streaming.sandbox = modal.Sandbox.create("sleep", "infinity") process = sandbox.exec("bash", "-c", "for i in $(seq 1 10); do echo foo $i; sleep 0.5; done") for line in process.stdout: print(line)
- Removed support for the undocumented
modal.apps.list_apps()function, which was internal and not intended to be part of public API.
- Removed client check for CPU core request being at least 0.1, deferring to server-side enforcement.
-
Volumes can now be mounted to an ad hoc modal shell session:
modal shell --volume my-vol-nameWhen the shell starts, the volume will be mounted at
/mnt/my-vol-name. This may be helpful for shell-based exploration or manipulation of volume contents.Note that the option can be used multiple times to mount additional models:
modal shell --volume models --volume data
- App deployment events are now atomic, reducing the risk that a failed deploy will leave the App in a bad state.
- The
_experimental_boostargument can now be removed. Boost is now enabled on all modal Functions.
- Setting
_allow_background_volume_commitsis no longer necessary and has been deprecated. Remove this argument in your decorators.
- Image layers defined with a
@modal.buildmethod will now include the values of any class variables that are referenced within the method as part of the layer cache key. That means that the layer will rebuild when the class variables change or are overridden by a subclass.
- Fixed an error when running
@modal.buildmethods that was introduced in v0.63.19
- Fixed bug where
self.method.local()would re-trigger lifecycle methods in classes
-
Adds
Cls.lookup()backwards compatibility with classes created by clients prior tov0.63.Important: When updating (to >=v0.63) an app with a Modal
classthat's accessed usingCls.lookup()- make sure to update the client of the app/service usingCls.lookup()first, and then update the app containing the class being looked up.
- Fixed a bug introduced in 0.63.0 that broke
modal.Cls.with_options
- Adds warning about future deprecation of
retriesfor generators. Retries are being deprecated as they can lead to nondetermistic generator behavior.
- Fixed a bug in
Volume.copy_files()where some source paths may be ignored if passed asbytes. Volume.read_file,Volume.read_file_into_fileobj,Volume.remove_file, andVolume.copy_filescan no longer take both string or bytes for their paths. They now only acceptstr.
- Fixes issue with
Cls.lookupnot working (at all) after upgrading to v0.63.0. Note: this doesn't fix the cross-version lookup incompatibility introduced in 0.63.0.
-
Changes how containers are associated with methods of
@app.cls()-decorated Modal "classes".Previously each
@methodand web endpoint of a class would get its own set of isolated containers and never run in the same container as other sibling methods. Starting in this version, all@methodsand web endpoints will be part of the same container pool. Notably, this means all methods will scale up/down together, and options likekeep_warmandconcurrency_limitwill affect the total number of containers for all methods in the class combined, rather than individually.Version incompatibility warning: Older clients (below 0.63) can't use classes deployed by new clients (0.63 and above), and vice versa. Apps or standalone clients using
Cls.lookup(...)to invoke Modal classes need to be upgraded to version0.63at the same time as the deployed app that's being called into. -
keep_warmfor classes is now an attribute of the@app.cls()decorator rather than individual methods.
- Added support for mounting Volume or CloudBucketMount storage in
Image.run_function. Note that this is typically not necessary, as data downloaded during the Image build can be stored directly in the Image filesystem.
- It is now an error to create or lookup Modal objects (
Volume,Dict,Secret, etc.) with an invalid name. Object names must be shorter than 64 characters and may contain only alphanumeric characters, dashes, periods, and underscores. The name check had inadvertently been removed for a brief time following an internal refactor and then reintroduced as a warning. It is once more a hard error. Please get in touch if this is blocking access to your data.
- The
modal app listcommand now reports apps created bymodal app runormodal app serveas being in an "ephemeral" state rather than a "running" state to reduce confusion with deployed apps that are actively processing inputs.
- All modal CLI commands now accept
-eas a short-form of--env
- Added support for entrypoint and shell for custom containers:
Image.debian_slim().entrypoint([])can be used interchangeably with.dockerfile_commands('ENTRYPOINT []'), and.shell(["/bin/bash", "-c"])can be used interchangeably with.dockerfile_commands('SHELL ["/bin/bash", "-c"]')
- Fix an issue with
@web_serverdecorator not working on image builder version 2023.12
@web_serverendpoints can now return HTTP headers of up to 64 KiB in length. Previously, they were limited to 8 KiB due to an implementation detail.
modal deploynow accepts a--tagoptional parameter that allows you to specify a custom tag for the deployed version, making it easier to identify and manage different deployments of your app.
web_endpoints now have the option to include interactive SwaggerUI/redoc docs by settingdocs=Trueweb_endpoints no longer include an OpenAPI JSON spec route by default
modal.Functionnow supports requesting ephemeral disk (SSD) via the newephemeral_diskparameter. Intended for use in doing large dataset ingestion and transform.
modal.Volumebackground commits are now enabled by default when usingspawn_sandbox.
- The
modal app stopCLI command now accepts a--name(or-n) option to stop an App by name rather than by ID.
- Background committing on
modal.Volumemounts is now default behavior.
- Added a
modal container stopCLI command that will kill an active container and reassign its current inputs.
modal.CloudBucketMountnow supports writing to Google Cloud Storage buckets.
- Using
memory=to specify the type ofmodal.gpu.A100is deprecated in favor ofsize=. Note thatsizeaccepts a string type ("40GB"or"80GB") rather than an integer, as this is a request for a specific variant of the A100 GPU.
- Added a
versionflag to themodal.VolumeAPI and CLI, allow opting in to a new backend implementation.
- Fixed a bug where other functions weren't callable from within an
asgi_apporwsgi_appconstructor function and side effects of@entermethods weren't available in that scope.
- Disabling background commits on
modal.Volumevolumes is now deprecated. Background commits will soon become mandatory behavior.
- Deprecated
wait_for_response=Falseon web endpoints. See the docs for alternatives.
- A deprecation warning is now raised when using
modal.Stub, which has been renamed tomodal.App. Additionally, it is recommended to useappas the variable name rather thanstub, which matters when using the automatic app discovery feature in themodal runCLI command.
- Added a
--stream-logsflag tomodal deploythat, if True, begins streaming the app logs once deployment is complete.
- Added support for looking up a deployed App by its deployment name in
modal app logs
- Added validation that App
name, if provided, is a string.
- The
@app.functiondecorator now raises an error when it is used to decorate a class (this was always invalid, but previously produced confusing behavior).
- The
modal app listoutput has been improved in several ways:- Persistent storage objects like Volumes or Dicts are no longer included (these objects receive an app ID internally, but this is an implementation detail and subject to future change). You can use the dedicated CLI for each object (e.g.
modal volume list) instead. - For Apps in a stopped state, the output is now limited to those stopped within the past 2 hours.
- The number of tasks running for each App is now shown.
- Persistent storage objects like Volumes or Dicts are no longer included (these objects receive an app ID internally, but this is an implementation detail and subject to future change). You can use the dedicated CLI for each object (e.g.
- Added the
regionparameter to themodal.App.functionandmodal.App.clsdecorators. This feature allows the selection of specific regions for function execution. Note that it is available only on some plan types. See our blog post for more details.
- Added deprecation warnings when using Python 3.8 locally or in a container. Python 3.8 is nearing EOL, and Modal will be dropping support for it soon.
- Deprecated the
Image.condaconstructor and theImage.conda_install/Image.conda_update_from_environmentmethods. Conda-based images had a number of tricky issues and were generally slower and heavier than images based onmicromamba, which offers a similar featureset and can install packages from the same repositories. - Added the
spec_fileparameter to allowImage.micromamba_installto install dependencies from a local file. Note thatmicromambasupports conda yaml syntax along with simple text files.
- Added a deprecation warning when object names are invalid. This applies to
Dict,NetworkFileSystem,Secret,Queue, andVolumeobjects. Names must be shorter than 64 characters and may contain only alphanumeric characters, dashes, periods, and underscores. These rules were previously enforced, but the check had inadvertently been dropped in a recent refactor. Please update the names of your objects and transfer any data to retain access, as invalid names will become an error in a future release.
- Added a command-line interface for interacting with
modal.Queueobjects. Runmodal queue --helpin your terminal to see what is available.
- Added a command-line interface for interacting with
modal.Dictobjects. Runmodal dict --helpin your terminal to see what is available.
-
Secret.from_dotenvnow accepts an optional filename keyword argument:@app.function(secrets=[modal.Secret.from_dotenv(filename=".env-dev")]) def run(): ...
- Passing a glob
**argument to themodal volume getCLI has been deprecated — instead, simply download the desired directory path, or/for the entire volume. Volume.listdir()no longer takes trailing glob arguments. Userecursive=Trueinstead.modal volume getandmodal nfs getperformance is improved when downloading a single file. They also now work with multiple files when outputting to stdout.- Fixed a visual bug where
modal volume geton a single file will incorrectly display the destination path.
- Improved feedback for deserialization failures when objects are being transferred between local / remote environments.
-
Added
Dict.deleteandQueue.deleteas API methods for deleting named storage objects:import modal modal.Queue.delete("my-job-queue")
-
Deprecated invoking
Volume.deleteas an instance method; it should now be invoked as a static method with the name of the Volume to delete, as with the other methods.
- The
modal.Dictobject now implements akeys/values/itemsAPI. Note that there are a few differences when compared to standard Python dicts:- The return value is a simple iterator, whereas Python uses a dictionary view object with more features.
- The results are unordered.
- Additionally, there was no key data stored for items added to a
modal.Dictprior to this release, so empty strings will be returned for these entries.
- We are introducing
modal.Appas a replacement formodal.Stuband encouraging the use of "app" terminology over "stub" to reduce confusion between concepts used in the SDK and the Dashboard. Support formodal.Stubwill be gradually deprecated over the next few months.
- Specifying a hard memory limit for a
modal.Functionis now supported. Pass a tuple ofmemory=(request, limit). Above thelimit, which is specified in MiB, a Function's container will be OOM killed.
modal.CloudBucketMountnow supports read-only access to Google Cloud Storage
- Iterators passed to
Function.map()and similar parallel execution primitives are now executed on the main thread, preventing blocking iterators from possibly locking up background Modal API calls, and risking task shutdowns.
- The return type of
Volume.listdir(),Volume.iterdir(),NetworkFileSystem.listdir(), andNetworkFileSystem.iterdir()is now aFileEntrydataclass from themodal.volumemodule. The fields of this data class are the same as the old protobuf object returned by these methods, so it should be mostly backwards-compatible.
- Cloudflare R2 bucket support added to
modal.CloudBucketMount
- When Volume reloads fail due to an open file, we now try to identify and report the relevant path. Note that there may be some circumstances in which we are unable to identify the specific file blocking a reload and will report a generic error message in that case.
- Values in the
modal.tomlconfig file that are spelled as0,false,"False", or"false"will now be coerced in Python toFalse, whereas previously only"0"(as a string) would have the intended effect.
- Fixed a recent regression that caused functions using
modal.interact()to crash.
- Queue methods
put,put_many,get,get_manyandlennow support an optionalpartitionargument (must be specified as akwarg). When specified, users read and write from new partitions of the queue independently.partition=Nonecorresponds to the default partition of the queue.
- User can now mount S3 buckets using Requester Pays. This can be done with
CloudBucketMount(..., requester_pays=True).
- Raise an error on
@web_server(startup_timeout=0), which is an invalid configuration.
- The
.new()method has now been deprecated on all Modal objects. It should typically be replaced with.from_name(...)in Modal app code, or.ephemeral()in scripts that use Modal - Assignment of Modal objects to a
Stubvia subscription (stub["object"]) or attribute (stub.object) syntax is now deprecated. This syntax was only necessary when using.new().
- Fixed a bug where images based on
micromambacould fail to build if requesting Python 3.12 when a different version of Python was being used locally.
- The
Sandbox'sLogsReaderis now an asynchronous iterable. It supports theasync forstatement to stream data from the sandbox'sstdout/stderr.
@stub.function()
async def my_fn():
sandbox = stub.spawn_sandbox(
"bash",
"-c",
"while true; do echo foo; sleep 1; done"
)
async for message in sandbox.stdout:
print(f"Message: {message}")- Add the
@web_serverdecorator, which exposes a server listening on a container port as a web endpoint.
- Allow users to write to the
Sandbox'sstdinwithStreamWriter.
@stub.function()
def my_fn():
sandbox = stub.spawn_sandbox(
"bash",
"-c",
"while read line; do echo $line; done",
)
sandbox.stdin.write(b"foo\\n")
sandbox.stdin.write(b"bar\\n")
sandbox.stdin.write_eof()
sandbox.stdin.drain()
sandbox.wait()- Fixed an bug where
Mountwas failing to include symbolic links.
When called from within a container, modal.experimental.stop_fetching_inputs() causes it to gracefully exit after the current input has been processed.
- The
@wsgi_app()decorator now uses a different backend based ona2wsgithat streams requests in chunks, rather than buffering the entire request body.
- Stubs/apps can now be "composed" from several smaller stubs using
stub.include(...). This allows more ergonomic setup of multi-file Modal apps.
- The
Image.extendmethod has been deprecated. This is a low-level interface and can be replaced by otherImagemethods that offer more flexibility, such asImage.from_dockerfile,Image.dockerfile_commands, orImage.run_commands.
- Fixes
modal volume putto support uploading larger files, beyond 40 GiB.
- Modal containers now display a warning message if lingering threads are present at container exit, which prevents runner shutdown.
- Bug fix: Stopping an app while a container's
@exit()lifecycle methods are being run no longer interrupts the lifecycle methods. - Bug fix: Worker preemptions no longer interrupt a container's
@exit()lifecycle method (until 30 seconds later). - Bug fix: Async
@exit()lifecycle methods are no longer skipped for sync functions. - Bug fix: Stopping a sync function with
allow_concurrent_inputs>1now actually stops the container. Previously, it would not propagate the signal to worker threads, so they would continue running. - Bug fix: Input-level cancellation no longer skips the
@exit()lifecycle method. - Improve stability of container entrypoint against race conditions in task cancellation.
- Fix issue with pdm where all installed packages would be automounted when using package cache (MOD-2485)
- For modal functions/classes with
concurrency_limit < keep_warm, we'll raise an exception now. Previously we (silently) respected theconcurrency_limitparameter.
modal run --interactive or modal run -i run the app in "interactive mode". This allows any remote code to connect to the user's local terminal by calling modal.interact().
@stub.function()
def my_fn(x):
modal.interact()
x = input()
print(f"Your number is {x}")This means that you can dynamically start an IPython shell if desired for debugging:
@stub.function()
def my_fn(x):
modal.interact()
from IPython import embed
embed()For convenience, breakpoints automatically call interact():
@stub.function()
def my_fn(x):
breakpoint()Image.run_functionnow allows you to pass args and kwargs to the function. Usage:
def my_build_function(name, size, *, variant=None):
print(f"Building {name} {size} {variant}")
image = modal.Image.debian_slim().run_function(
my_build_function, args=("foo", 10), kwargs={"variant": "bar"}
)- Mounted packages are now deduplicated across functions in the same stub
- Mounting of local Python packages are now marked as such in the mount creation output, e.g.
PythonPackage:my_package - Automatic mounting now includes packages outside of the function file's own directory. Mounted packages are mounted in /root/
- Most errors raised through usage of the CLI will now print a simple error message rather than showing a traceback from inside the
modallibrary. - Tracebacks originating from user code will include fewer frames from within
modalitself. - The new
MODAL_TRACEBACKenvironment variable (andtracebackfield in the Modal config file) can override these behaviors so that full tracebacks are always shown.
- Fixed a bug that could cause
cls-based functions to to ignore timeout signals.
volume getperformance is improved for large (> 100MB) files
- Support for function parameters in methods decorated with
@exithas been deprecated. Previously, exit methods were required to accept three arguments containing exception information (akin to__exit__in the context manager protocol). However, due to a bug, these arguments were always null. Going forward,@exitmethods are expected to have no parameters.
- Function calls can now be cancelled without killing the container running the inputs. This allows new inputs by different function calls to the same function to be picked up immediately without having to cold-start new containers after cancelling calls.
- An
InvalidErroris now raised when a lifecycle decorator (@build,@enter, or@exit) is used in conjunction with@method. Previously, this was undefined and could produce confusing failures.
- Reduced the amount of context for frames in modal's CLI framework when showing a traceback.
- The "dunder method" approach for class lifecycle management (
__build__,__enter__,__exit__, etc.) is now deprecated in favor of the modal@build,@enter, and@exitdecorators.
- In
modal token newandmodal token set, the--no-no-verifyflag has been removed in favor of a--verifyflag. This remains the default behavior.
- Fixes a regression from 0.57.40 where
@entermethods used a separate event loop.
- Adds a new environment variable/config setting,
MODAL_FORCE_BUILD/force_build, that coerces all images to be built from scratch, rather than loaded from cache.
- The
@enter()lifecycle method can now be used to run additional setup code prior to function checkpointing (when the class is decorated withstub.cls(enable_checkpointing=True). Note that there are currently some limitations on function checkpointing:- Checkpointing only works for CPU memory; any GPUs attached to the function will not available
- Networking is disabled while the checkpoint is being created
- Please note that function checkpointing is still a beta feature.
- Fixed an issue with displaying deprecation warnings on Windows systems.
- Modal client deprecation warnings are now highlighted in the CLI
- Fixes a regression in container scheduling. Users on affected versions (0.57.5—0.57.15) are encouraged to upgrade immediately.
- The legacy
image_python_versionconfig option has been removed. Use thepython_version=parameter on your image definition instead.
- Adds support for mounting an S3 bucket as a volume.
- Support for an implicit 'default' profile is now deprecated. If you have more than one profile in your Modal config file, one must be explicitly set to
active(usemodal profile activateor edit your.modal.tomlfile to resolve). - An error is now raised when more than one profile is set to
active.
- Improve error message when generator functions are called with
.map(...).
- Greatly improved streaming performance of generators and WebSocket web endpoints.
- Breaking change: You cannot use
.map()to call a generator function. (In previous versions, this merged the results onto a single stream, but the behavior was undocumented and not widely used.) - Incompatibility: Generator outputs are now on a different internal system. Modal code on client versions before 0.57 cannot trigger deployed functions with
.remote_gen()that are on client version 0.57, and vice versa.
Note that in version 0.56 and prior, Modal used a different numbering system for patch releases.
- When using
modal token newormodel token set, the profile containing the new token will now be activated by default. Use the--no-activateswitch to update themodal.tomlfile without activating the corresponding profile.
- The
modal profile listoutput now indicates when the workspace is determined by a token stored in environment variables.
- Variadic parameters (e.g. *args and **kwargs) can now be used in scheduled functions as long as the function doesn't have any other parameters without a default value
modal container exec's--no-ttyflag has been renamed to--no-pty.
- The singular form of the
secretparameter inStub.function,Stub.cls, andImage.run_functionhas been deprecated. Please update your code to use the plural form instead:secrets=[Secret(...)].
- In
modal profile list, the user's GitHub username is now shown as the name for the "Personal" workspace.
- The
modal token newandmodal token setcommands now create profiles that are more closely associated with workspaces, and they have more explicit profile activation behavior:- By default, these commands will create/update a profile named after the workspace that the token points to, rather than a profile named "default"
- Both commands now have an
--activateflag that will activate the profile associated with the new token - If no other profiles exist at the time of creation, the new profile will have its
activemetadata set to True
- With these changes, we are moving away from the concept of a "default" profile. Implicit usage of the "default" profile will be deprecated in a future update.
- Adds tty support to
modal container execfor fully-interactive commands. Example:modal container exec [container-id] /bin/bash
- The
modal profile listcommand now shows the workspace associated with each profile.
Mount.from_local_python_packagesnow places mounted packages at/rootin the Modal runtime by default (used to be/pkg). To override this behavior, the function now takes aremote_dir: Union[str, PurePosixPath]argument.
-
The Modal client library is now compatible with Python 3.12, although there are a few limitations:
- Images that use Python 3.12 without explicitly specifing it through
python_versionoradd_pythonwill not build properly unless the modal client is also running on Python 3.12. - The
condaandmicrocondabase images currently do not support Python 3.12 because an upstream dependency is not yet compatible.
- Images that use Python 3.12 without explicitly specifing it through
gpu.A100class now supports specifying GiB memory configuration using asize: strparameter. Thememory: intparameter is deprecated.
- You can now execute commands in running containers with
modal container exec [container-id] [command].
- The
modalcli now works more like thepythoncli in regard to script/module loading:- Running
modal my_dir/my_script.pynow putsmy_diron the PYTHONPATH. modal my_package.my_modulewill now mount to /root/my_package/my_module.py in your Modal container, regardless if using automounting or not (and any intermediary__init__.pyfiles will also be mounted)
- Running
- Modal now uses the current profile if
MODAL_PROFILEis set to the empty string.
- Dropped support for building Python 3.7 based
modal.Images. Python 3.7 is end-of-life since late June 2023.
- modal.Stub.function now takes a
block_networkargument.
- modal.Stub now takes a
volumesargument for setting the default volumes of all the stub's functions, similarly to themountsandsecretsargument.
modal serve: Setting MODAL_LOGLEVEL=DEBUG now displays which files cause an app reload during serve
modal runcli command now properly propagates--envvalues to object lookups in global scope of user code