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.0.tar.gz (8.7 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.0-py3-none-any.whl (9.1 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: tff_dbt-0.2.0.tar.gz
  • Upload date:
  • Size: 8.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.3

File hashes

Hashes for tff_dbt-0.2.0.tar.gz
Algorithm Hash digest
SHA256 e7911c2f4b50acebc698589a40beca1fb9e0c8011e61939120d2e1f37b10e0c1
MD5 220a417ce00e0739d592fb51c54bbe92
BLAKE2b-256 a7ac1ac02a3d984507a9a34f96cbb6393491f276adc157770bb60ab592b63264

See more details on using hashes here.

File details

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

File metadata

  • Download URL: tff_dbt-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 9.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.3

File hashes

Hashes for tff_dbt-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5a5571b012eaafd596455216b01297faf588146a89323ee34095d117073e5b1f
MD5 3d212d738709177e86a11a6c5b5c9842
BLAKE2b-256 dbb933d53fee794090d2b51bb9af601c4ceff01de2bcfacaadb82aa6d61b98aa

See more details on using hashes here.

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