Configuration Contracts
Relevant source files
The following files were used as context for generating this wiki page:
Configuration Contracts define the structured data models and logic used to manage configurations across dbt-common and its dependent adapters. These contracts ensure that configuration data is validated, merged correctly based on specific behaviors (like appending lists or updating dictionaries), and compared accurately during project execution.
The system relies on Python dataclasses integrated with the mashumaro library for serialization and jsonschema for structural validation. By utilizing a base configuration class, the project provides a unified interface for accessing and manipulating configuration keys, whether they are built-in fields or user-defined "extra" properties.
Sources: dbt_common/contracts/config/base.py, dbt_common/dataclass_schema.py
Core Architecture
The architecture of Configuration Contracts is built upon a hierarchy of mixins and base classes that provide serialization, validation, and dictionary-like access to configuration objects.
Class Hierarchy and Mixins
The primary building block is dbtClassMixin, which bridges Python dataclasses with JSON schema validation.
| Class / Mixin | Description |
|---|---|
dbtClassMixin | Inherits from DataClassMessagePackMixin. Provides to_dict, from_dict, and validate methods using jsonschema. |
BaseConfig | The foundational class for all configuration objects. Inherits from AdditionalPropertiesAllowed and Replaceable. Implements dictionary-like access (__getitem__, get). |
ExtensibleDbtClassMixin | A variant of the mixin that explicitly allows additional properties in the JSON schema. |
Sources: dbt_common/dataclass_schema.py:65-103, dbt_common/contracts/config/base.py:18-20
Logic Flow for Configuration Access
The BaseConfig class provides a robust get method that searches for keys in a specific order: defined attributes, extra properties, and finally metadata fields.
Merge and Comparison Behaviors
Configuration Contracts define how two configuration sets should be combined. This is critical when merging project-level defaults with model-specific overrides.
Merge Behaviors
The MergeBehavior enum determines the strategy used when a source and data dictionary share the same key.
| Behavior | Logic | Typical Use Case |
|---|---|---|
Clobber | The new value completely replaces the old value. | Standard scalar fields (e.g., materialized). |
Append | Both values are converted to lists and concatenated. | tags, pre-hook, post-hook. |
Update | Performs a dictionary update (dict.update()). | quoting, meta, column_types. |
DictKeyAppend | Appends values to specific keys within a dictionary. | grants. |
Object | Merges fields of nested configuration objects. | snapshot_meta_column_names. |
Sources: dbt_common/contracts/config/base.py:118-124, dbt_common/contracts/config/base.py:177-185
Implementation of Field Merging
The _merge_field_value function implements the logic for these behaviors. For example, DictKeyAppend allows a user to prefix a key with + to indicate that the list should be extended rather than overwritten.
# Example of DictKeyAppend logic in _merge_field_value
if new_key.startswith("+"):
new_key = key.lstrip("+")
extend = True
if new_key in new_dict and extend:
value = other_value[key]
new_dict[new_key].extend(_listify(value))
Sources: dbt_common/contracts/config/base.py:228-235
Validation and Serialization
Configurations are validated against JSON schemas generated automatically from the dataclass definitions.
Validation Process
The dbtClassMixin utilizes jsonschema to validate dictionaries before they are transformed into class instances.
Custom Serialization Strategies
Specific types like datetime and bool use custom strategies defined in dbtMashConfig to maintain consistency across different versions of the underlying serialization library (mashumaro).
- DateTime: Automatically handles ISO format and UTC suffixes.
- UncoercedBool: Prevents the library from coercing truthy/falsy values to booleans during validation, ensuring strict type checking.
Sources: dbt_common/dataclass_schema.py:31-50
Metadata and Comparison
Configurations support fine-grained control over how fields are compared and whether they are included in serialized outputs.
Compare Behavior
The CompareBehavior controls whether a field is included in the same_contents check, which acts as a specialized equality operator for configuration objects.
@classmethod
def same_contents(cls, unrendered: Dict[str, Any], other: Dict[str, Any]) -> bool:
seen = set()
for fld, target_name in cls._get_fields():
key = target_name
seen.add(key)
if CompareBehavior.should_include(fld):
if not cls.compare_key(unrendered, other, key):
return False
# ... checks remaining extra keys
return True
Sources: dbt_common/contracts/config/base.py:87-105
Field Metadata Example
Fields are often annotated with metadata to instruct the contract system on how to handle them.
@dataclass
class ThingWithMergeBehavior(BaseConfig):
tags: List[str] = field(
metadata={"merge": MergeBehavior.Append},
default_factory=list
)
meta: Dict[str, int] = field(
metadata={"merge": MergeBehavior.Update},
default_factory=dict
)
Sources: tests/unit/test_model_config.py:25-30
Conclusion
Configuration Contracts provide a robust, type-safe framework for managing complex settings. By combining Python dataclasses with extensible merge strategies and JSON schema validation, the system ensures that configuration data remains consistent and predictable throughout the dbt execution lifecycle.