docs/current_docs/extending/modules/constructors.mdx
import PartialComplexConstructorOutput from '../../partials/_complex_constructor_output.mdx';
Every Dagger module has a constructor. The default one is generated automatically and has no arguments.
It's possible to write a custom constructor. The mechanism to do this is SDK-specific.
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.
:::important Dagger modules have only one constructor. Constructors of custom types are not registered; they are constructed by the function that chains them. :::
The default constructor for a module can be overridden by registering a custom constructor. Its parameters are available as flags in the dagger command directly.
:::important If you plan to use constructor fields in other module functions, ensure that they are declared as public (in Go and TypeScript). This is because Dagger stores fields using serialization and private fields are omitted during the serialization process. As a result, if a field is not declared as public, calling methods that use it will produce unexpected results. :::
Here is an example module with a custom constructor:
<Tabs groupId="language" queryString="sdk"> <TabItem value="go" label="Go">:::important
Dagger @object_type classes are Python data classes that become exposed to the Dagger API. This means that you can adjust the generation of the constructor using standard data class features, such as fields and post-init processing. Refer to the official dataclasses documentation to learn more.
:::
:::info
In the PHP SDK the constructor must be the magic method __construct.
As with any method, only public methods with the #[DaggerFunction] attribute will be registered with Dagger.
:::
</TabItem>
<TabItem value="java" label="Java">
:::info In the Java SDK, the constructor must be public. A public empty constructor is also required in order to create the object from the serialized data. ::: </TabItem> </Tabs>
Here is an example call for this Dagger Function:
<Tabs groupId="shell"> <TabItem value="System shell"> ```shell dagger -c '. --name=Foo | message' ``` </TabItem> <TabItem value="Dagger Shell"> ```shell title="First type 'dagger' for interactive mode." . --name=Foo | message ``` </TabItem> <TabItem value="Dagger CLI"> ```shell dagger call --name=Foo message ``` </TabItem> </Tabs>The result will be:
Hello, Foo!
Constructors can be passed both simple and complex types (such as Container, Directory, Service etc.) as arguments. Default values can be assigned in both cases.
:::important Explicitly declare the type even when it can be inferred, so that the Dagger SDK can extend the GraphQL schema correctly. :::
Here is an example of a Dagger module with a default constructor argument of type Container:
Since @object_type classes are Python data classes, you can fine tune the generation of the constructor in different ways.
Here is a more complex example:
A few important notes:
field(init=False). So, even though the container field is not added as a constructor argument it's still a required field that must be initialized either with a default value or in post-init processing.__post_init__ function is called at the end of the auto-generated constructor which can be used, for example, to initialize field values that depend on other field values.None in an attribute is the same as setting dataclasses.field(default=None). dataclasses.field() is only required to specify other non-default values, like init or to set the default using a factory function.dagger.field descriptor wraps dataclasses.field, adding two important differences:
dagger.field() is used to expose the attribute as a Dagger Function in the API (a simple getter), but it also becomes a part of the object's state.dataclasses.field() can be set with dagger.field() which sets opinionated defaults, but you can specify both simple values and factory functions via the same default argument while with dataclasses.field() you need to use separate default and default_factory arguments.For default values that are more complex, dynamic or just mutable, use a factory function without arguments in
dataclasses.field(default_factory=...) or dagger.field(default=...):
If a constructor argument needs an asynchronous call to set the default value, it's
possible to replace the default constructor function from __init__() to
a factory class method named create, as in the following code listing:
:::warning
This factory class method must be named create.
:::
This default value can also be assigned directly in the field:
:::important
When assigning default values to complex types in TypeScript, it is necessary to use the ?? notation for this assignment. It is not possible to use the classic TypeScript notation for default arguments because the argument in this case is not a TypeScript primitive.
:::