Skip to content

Unions

Tip

Code for examples in this page can be found in examples/6_unions.

One of the powerful features of confarg, which lies at the heart of complex configurations, is the ability to handle union types.

Let's go back to our database example introduced in Tutorial #1, where our app relies on this configuration to connect to a DB server:

@dataclass(kw_only=True)
class PostgreSQLConfig:
    host: str
    port: int = 5432
    schema_name: str

Say our app now needs to also support an SQLite backend. We introduce a new configuration:

@dataclass
class SQLiteConfig:
    dbpath: str

The two backends are mutually exclusive. In confarg, we translate this in our configuration in the most pythonic way, using a union type. We declare our configuration to be of type

type Config = PostgreSQLConfig | SQLiteConfig

We can now dynamically select among union variants. To select an SQLite backend, we simply need to provide the elements required by its configuration:

$ uv run myapp.py --dbpath /path/to.db.sqlite
SQLiteConfig(dbpath='/path/to.db.sqlite')

Choosing a DB server backend instead works similarly:

$ uv run myapp.py --host example.com --schema_name schema
PostgreSQLConfig(host='example.com', port=5432, schema_name='schema')

This works as well with configuration files, which could contain a configuration of either sort:

  • a PostgreSQLConfig configuration file:

    $ uv run myapp.py --config postgres.yaml
    PostgreSQLConfig(host='example.com', port=5432, schema_name='mydb')
    

  • or, a SQLiteConfig configuration file:

    $ uv run myapp.py --config sqlite.yaml
    SQLiteConfig(dbpath='/path/to/db.sqlite')
    

Note

Confarg is smart enough to know which type in the union is targeted from the provided input types. However, such implicit disambiguation is not always possible, or even desirable. We will see in Tutorial #8 how to set the target type explicitly.