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.
We appreciate your patience while we speedily work towards a stable release of the client.
Image.from_dockerfile()
andimage.dockerfile_commands()
now auto-infer which files need to be uploaded based on COPY commands in the source ifcontext_mount
is 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 vscode
at 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
/.lookup
methods was not found.
- Standardized terminology in
.from_name
/.lookup
/.delete
methods to usename
consistently wherelabel
andtag
were 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_error
anddeprecation_warning
utilities 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_sandbox
method has been removed in favor ofSandbox.create
Sandbox.create
now requires anApp
object to be passed
- The
- The
modal run
CLI now has a--write-result
option. 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 eitherstr
orbytes
to 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 deploy
CLI.
We're removing support for .spawn()
ing generator functions.
- Sandboxes now support a new filesystem API. The
open()
method returns aFileIO
handle 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 exec
andmodal shell
now 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
NetworkFileSystem
objects 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_source
which works similarly to the old and soon-to-be-deprecatedMount.from_local_python_packages
but for images. One notable difference is that the newadd_local_python_source
only includes.py
-files by default
- Image build functions that use a
functools.wraps
decorator 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.Cls
methods when passing a list of requested GPUs.
- Fixed a bug that executes the wrong method when a Modal Cls overrides a
@modal.method
inherited from a parent.
- Fixed a bug where pointing
modal run
at 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-environment
will now raise if the requested environment does not exist.
- The
modal launch
CLI now accepts a--detach
flag 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
aiostream
package 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 serve
where files used inImage.copy_*
commands were not watched for changes
Sandbox.exec
can now accepttimeout
,workdir
, andsecrets
. See theSandbox.create
function for context on how to use these arguments.
- Removed the
interactive
parameter fromfunction
andcls
decorators. This parameter has been deprecated since May 2024. Instead of specifying Modal Functions as interactive, usemodal run --interactive
to activate interactive mode.
- The
checkpointing_enabled
option, deprecated in March 2024, has now been removed.
- Output from
Sandbox.exec
can now be directed to/dev/null
,stdout
, or stored for consumption. This behavior can be controlled via the newStreamType
arguments.
- Fixed a bug where the
Image.imports
context manager would not correctly propagate ImportError when using amodal.Cls
.
- Fixed an issue where
modal run
would pause for 10s before exiting if there was a failure during app creation.
- The
modal container list
CLI 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--env
to list containers in other environments.
- Fixed
modal serve
not 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.put
andQueue.put_many
would throwqueue.Full
even iftimeout=None
.
- The previously-deprecated
--confirm
flag has been removed from themodal volume delete
CLI. Use--yes
to force deletion without a confirmation prompt.
- Passing
wait_for_response=False
in Modal webhook decorators is no longer supported. See the docs for alternatives.
- When writing to a
StreamWriter
that has already had EOF written, aValueError
is now raised instead of anEOFError
.
- Memory snapshotting can now be used with parameterized 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 shell
CLI command now takes a container ID, allowing you to shell into a running container.
modal shell --cmd
now 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_environment
methods are now fully deprecated. We recommend usingmicromamba
(viaImage.micromamba
andImage.micromamba_install
) instead, or manually installing and using conda withImage.run_commands
when strictly necessary.
- Breaking Change:
Sandbox.tunnels()
now returns aDict
rather than aList
. This dict is keyed by the container's port, and it returns aTunnel
object, just likemodal.forward
does.
modal.Function
andmodal.Cls
now support specifying alist
of GPU configurations, allowing the Function's container pool to scale across each GPU configuration in preference order.
- The deprecated
_experimental_boost
argument 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_allowlist
argument, 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
App
is deprecated. If you are spawning aSandbox
outside a Modal container, you can lookup anApp
by 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_function
has been lowered to 1 hour. Previously it was 24 hours.
- Fixes an issue that could cause containers using
enable_memory_snapshot=True
on 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>=v4
amongst other libraries using lifespans
- Sandboxes now support port tunneling. Ports can be exposed via the
open_ports
argument, and a list of active tunnels can be retrieved via the.tunnels()
method.
- Fixed a regression in
modal launch
to resume displaying output when starting the container.
-
Introduces new dataclass-style syntax for class parameterization (see updated docs)
@app.cls() class MyCls: param_a: str = modal.parameter() MyCls(param_a="hello") # synthesized constructor
-
The new syntax enforces types (
str
orint
for now) on all parameters -
When the new syntax is used, any web endpoints (
web_endpoint
,asgi_app
,wsgi_app
orweb_server
) on the app will now also support parameterization through the use of query parameters matching the parameter names, e.g.https://myfunc.modal.run/?param_a="hello
in 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 parameterization
- Added a
modal app rollback
CLI command for rolling back an App deployment to a previous version.
-
Commands in the
modal app
CLI now accept an App name as a positional argument, in addition to an App ID:modal app history my-app
Accordingly, the explicit
--name
option 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(...)
.
ContainerProcess
handles now supportwait()
andpoll()
, likeSandbox
objects
-
Added support for dynamic batching. Functions or class methods decorated with
@modal.batched
will now automatically batch their invocations together, up to a specifiedmax_batch_size
. The batch will wait for a maximum ofwait_ms
for 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.exec
returns aContainerProcess
handle 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-name
When 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_boost
argument can now be removed. Boost is now enabled on all modal Functions.
- Setting
_allow_background_volume_commits
is no longer necessary and has been deprecated. Remove this argument in your decorators.
- Image layers defined with a
@modal.build
method 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.build
methods 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
class
that'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
retries
for 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_files
can no longer take both string or bytes for their paths. They now only acceptstr
.
- Fixes issue with
Cls.lookup
not 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
@method
and 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@methods
and web endpoints will be part of the same container pool. Notably, this means all methods will scale up/down together, and options likekeep_warm
andconcurrency_limit
will 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.63
at the same time as the deployed app that's being called into. -
keep_warm
for 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 list
command now reports apps created bymodal app run
ormodal app serve
as 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
-e
as 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_server
decorator not working on image builder version 2023.12
@web_server
endpoints 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 deploy
now accepts a--tag
optional 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_endpoint
s now have the option to include interactive SwaggerUI/redoc docs by settingdocs=True
web_endpoint
s no longer include an OpenAPI JSON spec route by default
modal.Function
now supports requesting ephemeral disk (SSD) via the newephemeral_disk
parameter. Intended for use in doing large dataset ingestion and transform.
modal.Volume
background commits are now enabled by default when usingspawn_sandbox
.
- The
modal app stop
CLI command now accepts a--name
(or-n
) option to stop an App by name rather than by ID.
- Background committing on
modal.Volume
mounts is now default behavior.
- Added a
modal container stop
CLI command that will kill an active container and reassign its current inputs.
modal.CloudBucketMount
now supports writing to Google Cloud Storage buckets.
- Using
memory=
to specify the type ofmodal.gpu.A100
is deprecated in favor ofsize=
. Note thatsize
accepts 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
version
flag to themodal.Volume
API and CLI, allow opting in to a new backend implementation.
- Fixed a bug where other functions weren't callable from within an
asgi_app
orwsgi_app
constructor function and side effects of@enter
methods weren't available in that scope.
- Disabling background commits on
modal.Volume
volumes is now deprecated. Background commits will soon become mandatory behavior.
- Deprecated
wait_for_response=False
on 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 useapp
as the variable name rather thanstub
, which matters when using the automatic app discovery feature in themodal run
CLI command.
- Added a
--stream-logs
flag tomodal deploy
that, 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.function
decorator now raises an error when it is used to decorate a class (this was always invalid, but previously produced confusing behavior).
- The
modal app list
output 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
region
parameter to themodal.App.function
andmodal.App.cls
decorators. 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.conda
constructor and theImage.conda_install
/Image.conda_update_from_environment
methods. 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_file
parameter to allowImage.micromamba_install
to install dependencies from a local file. Note thatmicromamba
supports conda yaml syntax along with simple text files.
- Added a deprecation warning when object names are invalid. This applies to
Dict
,NetworkFileSystem
,Secret
,Queue
, andVolume
objects. 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.Queue
objects. Runmodal queue --help
in your terminal to see what is available.
- Added a command-line interface for interacting with
modal.Dict
objects. Runmodal dict --help
in your terminal to see what is available.
-
Secret.from_dotenv
now 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 get
CLI has been deprecated — instead, simply download the desired directory path, or/
for the entire volume. Volume.listdir()
no longer takes trailing glob arguments. Userecursive=True
instead.modal volume get
andmodal nfs get
performance 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 get
on 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.delete
andQueue.delete
as API methods for deleting named storage objects:import modal modal.Queue.delete("my-job-queue")
-
Deprecated invoking
Volume.delete
as 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.Dict
object now implements akeys
/values
/items
API. 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.Dict
prior to this release, so empty strings will be returned for these entries.
- We are introducing
modal.App
as a replacement formodal.Stub
and encouraging the use of "app" terminology over "stub" to reduce confusion between concepts used in the SDK and the Dashboard. Support formodal.Stub
will be gradually deprecated over the next few months.
- Specifying a hard memory limit for a
modal.Function
is now supported. Pass a tuple ofmemory=(request, limit)
. Above thelimit
, which is specified in MiB, a Function's container will be OOM killed.
modal.CloudBucketMount
now 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 aFileEntry
dataclass from themodal.volume
module. 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.toml
config 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_many
andlen
now support an optionalpartition
argument (must be specified as akwarg
). When specified, users read and write from new partitions of the queue independently.partition=None
corresponds 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
Stub
via 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
micromamba
could fail to build if requesting Python 3.12 when a different version of Python was being used locally.
- The
Sandbox
'sLogsReader
is now an asynchronous iterable. It supports theasync for
statement 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_server
decorator, which exposes a server listening on a container port as a web endpoint.
- Allow users to write to the
Sandbox
'sstdin
withStreamWriter
.
@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
Mount
was 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 ona2wsgi
that 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.extend
method has been deprecated. This is a low-level interface and can be replaced by otherImage
methods that offer more flexibility, such asImage.from_dockerfile
,Image.dockerfile_commands
, orImage.run_commands
.
- Fixes
modal volume put
to 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>1
now 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_limit
parameter.
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_function
now 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
modal
library. - Tracebacks originating from user code will include fewer frames from within
modal
itself. - The new
MODAL_TRACEBACK
environment variable (andtraceback
field 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 get
performance is improved for large (> 100MB) files
- Support for function parameters in methods decorated with
@exit
has 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,@exit
methods 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
InvalidError
is 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@exit
decorators.
- In
modal token new
andmodal token set
, the--no-no-verify
flag has been removed in favor of a--verify
flag. This remains the default behavior.
- Fixes a regression from 0.57.40 where
@enter
methods 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_version
config 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 activate
or edit your.modal.toml
file 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 new
ormodel token set
, the profile containing the new token will now be activated by default. Use the--no-activate
switch to update themodal.toml
file without activating the corresponding profile.
- The
modal profile list
output 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-tty
flag has been renamed to--no-pty
.
- The singular form of the
secret
parameter inStub.function
,Stub.cls
, andImage.run_function
has 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 new
andmodal token set
commands 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
--activate
flag 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
active
metadata 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 exec
for fully-interactive commands. Example:modal container exec [container-id] /bin/bash
- The
modal profile list
command now shows the workspace associated with each profile.
Mount.from_local_python_packages
now places mounted packages at/root
in 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_version
oradd_python
will not build properly unless the modal client is also running on Python 3.12. - The
conda
andmicroconda
base 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.A100
class now supports specifying GiB memory configuration using asize: str
parameter. Thememory: int
parameter is deprecated.
- You can now execute commands in running containers with
modal container exec [container-id] [command]
.
- The
modal
cli now works more like thepython
cli in regard to script/module loading:- Running
modal my_dir/my_script.py
now putsmy_dir
on the PYTHONPATH. modal my_package.my_module
will now mount to /root/my_package/my_module.py in your Modal container, regardless if using automounting or not (and any intermediary__init__.py
files will also be mounted)
- Running
- Modal now uses the current profile if
MODAL_PROFILE
is set to the empty string.
- Dropped support for building Python 3.7 based
modal.Image
s. Python 3.7 is end-of-life since late June 2023.
- modal.Stub.function now takes a
block_network
argument.
- modal.Stub now takes a
volumes
argument for setting the default volumes of all the stub's functions, similarly to themounts
andsecrets
argument.
modal serve
: Setting MODAL_LOGLEVEL=DEBUG now displays which files cause an app reload during serve
modal run
cli command now properly propagates--env
values to object lookups in global scope of user code