Skip to main content

dbt adapter for Transformation Fitness Functions (tff)

Project description

sqlmesh-ff

PyPI version Python versions

Configurable fitness functions plugin for SQLMesh projects.

Ships SQLMesh linter rules (classification macros, SQL complexity, metadata, naming) and architectural checks (layer integrity, custom exclusions, schema contracts, dependency graph) with a unified Rich lint report.

Installation

# Install from PyPI:
uv add sqlmesh-ff

# Or using pip:
pip install sqlmesh-ff

Quick start

  1. Add fitness_functions.yaml to your SQLMesh project root (see Configuration).
  2. Add a small config.py bootstrap (see Where configuration lives) — SQLMesh requires the loader as a Python class and cannot load config.py and config.yaml in the same folder.
  3. Run lint:
sqlmesh-ff lint

Where configuration lives

There are three layers. Only the YAML/JSON files in your project are user-editable settings.

Layer File Role You edit this?
Plugin defaults sqlmesh_ff/config.py (installed package) Pydantic schema and built-in defaults (e.g. fan_out_warn: 15) No — library code, never overwritten
SQLMesh project settings.yaml Gateways, linter.rules, variables, CI/CD bot Yes — normal SQLMesh config
Fitness functions fitness_functions.yaml Thresholds, rule toggles, column naming/type rules, paths to JSON data Yes — main FF config
Loader bootstrap config.py (project root) Loads settings.yaml and registers FitnessLoader Rarely — ~15 lines of wiring
Contract data linter_contract_groups.json, linter_exclusions.json Repo-specific schema parity and dependency exclusions Yes — project data

Merge order for fitness settings: plugin defaults → fitness_functions.yaml → optional loader_kwargs overrides in config.py. Your YAML always wins over plugin defaults. The project config.py does not hold fitness thresholds — it only points at fitness_functions.yaml.

Why config.py exists: SQLMesh accepts loader: FitnessLoader only as a Python class, not as a YAML string. Because SQLMesh rejects having both config.py and config.yaml in one folder, projects use settings.yaml (SQLMesh settings) plus config.py (loader registration).

Example bootstrap:

from pathlib import Path

from sqlmesh.core.config import Config
from sqlmesh.utils.yaml import load as yaml_load
from sqlmesh_ff.loader import FitnessLoader

_settings = yaml_load(Path(__file__).parent / "settings.yaml")
config = Config.parse_obj(_settings).update_with({
    "loader": FitnessLoader,
    "loader_kwargs": {"fitness_functions_config": "fitness_functions.yaml"},
})

Enable individual SQLMesh rules in settings.yaml under linter.rules / linter.warn_rules.

Configuration

Fitness function settings live in fitness_functions.yaml at the project root. Override the file path or individual keys via loader_kwargs in config.py (advanced — most projects only set fitness_functions_config).

Example fitness_functions.yaml

contract_groups_path: linter_contract_groups.json
exclusions_path: linter_exclusions.json

layers:
  order: [sources, derived, core, marts, export]

checks:
  layer_integrity: { enabled: true }
  custom_exclusions: { enabled: true }
  schema_contracts: { enabled: true }
  dependency_graph:
    enabled: true
    fan_out_warn: 15
    fan_out_fail: 25
    fan_in_warn: 10

rules:
  classification_macros:
    enabled: true
    skip_layers: [sources]
    columns:
      product_type: "@product_type\\b|@PRODUCT_TYPE\\b"
  sql_complexity:
    enabled: true
    thresholds:
      decision_points: [15, 25]
      cte_count: [8, 12]
      join_count: [8, 12]
      line_count: [250, 400]
  mart_naming:
    enabled: true
    layer_name: marts
    rule: prefix_with_subdirectory
  column_names:
    enabled: true
    replacements: {}
  column_types:
    enabled: true
    rules: []
    equivalent_types:
      text: [text, varchar]
  metadata:
    owner: true
    description: true
    grain: true
  filename_equals_modelname:
    enabled: true

Project-specific JSON

Keep repo-specific contract and exclusion data in your project:

  • linter_contract_groups.json — cross-model schema parity groups
  • linter_exclusions.json — blocked dependency patterns and allowed exceptions

Reference their paths from fitness_functions.yaml. The plugin ships generic engines only; examples live in this README.

Rule name mapping

SQLMesh uses lowercase class names in linter.rules:

Config key SQLMesh rule name
classification_macros classificationmacros
sql_complexity sqlcomplexity
mart_naming martmodelnamingconvention
column_names columnnames
column_types columntypes
metadata.owner nomissingowner
metadata.description nomissingdescription
metadata.grain nomissinggrain
filename_equals_modelname filenameequalsmodelname

CLI

sqlmesh-ff lint [--project PATH] [--config PATH] [--checks CHECK,...] [--fail-level error|warning] [--group-by connascence|model]
  • Default: all enabled checks plus SQLMesh linter rules
  • --checks layer_integrity,custom_exclusions: run subset (for pre-push hooks)
  • --fail-level warning: treat warnings as failures
  • --group-by connascence|model: change how violations are grouped in the report (default: connascence)

Integration example

Example overrides. api_request should always be named api_call. _id columns should always be of type text and is_ columns should always be of type boolean.

column_names:
  replacements:
    api_request: api_call
column_types:
  rules:
    - name: id_is_text
      pattern: "_id$"
      data_type: text
    - name: boolean
      pattern: "^is_"
      data_type: boolean

Examples

A complete, runnable example project showcasing the configuration of sqlmesh-ff rules, exclusions, contracts, and a continuous integration workflow is located in the examples/ directory.

To run the linter against the example project locally, run:

sqlmesh-ff lint --project examples/minimal-sqlmesh-project

See examples/minimal-sqlmesh-project/fitness_functions.yaml to inspect the configured rules.

Development

Initialize your local environment and configure the Git pre-push hook:

make init

Run linter, tests, or check diff coverage:

make lint
make test
make coverage

Releases and PR titles

Releases are automated with release-please on merges to main. Use Conventional Commits in PR titles so changelog entries and semver bumps are correct.

PR titles must start with a type prefix, for example:

  • feat: add dependency graph fan-in check
  • fix: remove unused import in loader tests
  • docs: document fitness_functions.yaml merge order
  • ci: add release-please workflow

Supported types include feat, fix, docs, style, refactor, perf, test, build, ci, and chore. The PR title check in CI enforces this format.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tff_dbt-0.2.1.tar.gz (8.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tff_dbt-0.2.1-py3-none-any.whl (9.1 kB view details)

Uploaded Python 3

File details

Details for the file tff_dbt-0.2.1.tar.gz.

File metadata

  • Download URL: tff_dbt-0.2.1.tar.gz
  • Upload date:
  • Size: 8.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for tff_dbt-0.2.1.tar.gz
Algorithm Hash digest
SHA256 9d5b0e59980e07bab8f977d911bbcfa3a7fc50ccde7949ca141c108ec2cdf6f0
MD5 8a901356c46964c868ac5fd185247472
BLAKE2b-256 9a799f488c50a4a847d59c28a21703113e6b1ba9852f866d138a047b4dc820eb

See more details on using hashes here.

Provenance

The following attestation bundles were made for tff_dbt-0.2.1.tar.gz:

Publisher: release-please.yml on tjirab/tff

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file tff_dbt-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: tff_dbt-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 9.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for tff_dbt-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 326f83a885086d6bdee8e8cd56e879c8ea037616ad5ca4c9757fdc41f52769c0
MD5 0f3e963c0d72a0c1d187e1ee0e6cd23c
BLAKE2b-256 f3d132ee9c1c6aae259721fdabdcfef7da1384eaad5a46450ce2a7ce1b403c0d

See more details on using hashes here.

Provenance

The following attestation bundles were made for tff_dbt-0.2.1-py3-none-any.whl:

Publisher: release-please.yml on tjirab/tff

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page