Module Constructor
The main object's constructor is exposed as the entrypoint function for a module.
Its parameters are available as flags in the dagger call
command directly.
This is a simple way to accept module-wide configuration, or just to set a few attributes without having to create setter functions for them.
In the Python SDK, the @dagger.object_type
decorator wraps
@dataclasses.dataclass
, which means that an __init__()
method
is automatically generated, with parameters that match the declared class attributes.
Here is an example with an object that has typed attributes:
from dagger import function, object_type
@object_type
class MyModule:
greeting: str = "Hello"
name: str = "World"
@function
def message(self) -> str:
return f"{self.greeting}, {self.name}!"
And here is an example call for this module:
dagger call --name=Daggerverse message
The result will be:
Hello, Daggerverse!
Constructor arguments are documented through their attribute documentation, using the same annotation used on function arguments:
"""A hello world example, using a constructor."""
from typing import Annotated
from dagger import Doc, function, object_type
@object_type
class MyModule:
"""Functions for greeting the world"""
greeting: Annotated[str, Doc("The greeting to use")] = "Hello"
name: Annotated[str, Doc("Who to greet")] = "World"
@function
def message(self) -> str:
"""Return the greeting message"""
return f"{self.greeting}, {self.name}!"
The flags can be seen documented in the output of dagger call --help
:
Flags:
--greeting string The greeting to use (default "Hello")
--name string Who to greet (default "World")
Function Commands:
message Return the greeting message
Dagger Modules have only one constructor. Constructors of custom types are not registered; they are constructed by the function that chains them.
Exclude argument in constructor
Same as any data class, attributes can be excluded from the
generated __init__()
function, using dataclasses.field(init=False):
"""A simple hello world example, using a constructor."""
import dataclasses
from typing import Annotated
from dagger import Doc, function, object_type
@object_type
class MyModule:
"""Functions for greeting the world"""
greeting: str = dataclasses.field(default="Hello", init=False)
name: Annotated[str, Doc("Who to greet")] = "World"
@function
def message(self) -> str:
"""Return the greeting message"""
return f"{self.greeting}, {self.name}!"
In this case, only the name
flag was added and is visible in the output:
Flags:
--name string Who to greet (default "World")
Function Commands:
message Return the greeting message
Constructor-only arguments
The opposite is also possible. To define an argument that only exists in the constructor, but not as a class attribute, define it as an init-only variable:
"""An example module controlling constructor parameters."""
import dataclasses
import dagger
from dagger import dag, function, object_type
@object_type
class MyModule:
base: dagger.Container = dataclasses.field(init=False)
variant: dataclasses.InitVar[str] = "alpine"
def __post_init__(self, variant: str):
self.base = dag.container().from_(f"python:{variant}")
@function
def container(self) -> dagger.Container:
return self.base
Complex or mutable defaults
For default values that are more complex, dynamic or just mutable, use a factory function without arguments in dataclasses.field(default_factory=...):
"""An example module using default factory functions."""
import dataclasses
import dagger
from dagger import dag, function, object_type
@object_type
class MyModule:
base: dagger.Container = dataclasses.field(
default_factory=lambda: dag.container().from_("python:alpine"),
)
packages: list[str] = dataclasses.field(default_factory=list)
@function
def container(self) -> dagger.Container:
return self.base.with_exec(["apk", "add", "git", *self.packages])
Asynchronous constructor
If a constructor argument needs an async call to set the default value, it's
possible to replace the default constructor function from __init__()
to
a factory class method which must be named create
:
"""An example module using a factory constructor"""
from typing import Annotated
from dagger import Doc, dag, function, object_type
@object_type
class MyModule:
"""Functions for testing a project"""
parallelize: int
@classmethod
async def create(
cls,
parallelize: Annotated[
int | None, Doc("Number of parallel processes to run")
] = None,
):
if parallelize is None:
parallelize = int(
await dag.container().from_("alpine").with_exec(["nproc"]).stdout()
)
return cls(parallelize=parallelize)
@function
def debug(self) -> str:
"""Check the number of parallel processes"""
return f"Number of parallel processes: {self.parallelize}"