Tombi, pre-commit, prek and uv.lock

Tags: python, django

In almost all my Python projects, I’m using pre-commit to handle/check formatting and linting. The advantage: pre-commit is the only tool you need to install. Pre-commit itself reads its config file and installs the formatters and linters you defined in there.

Here’s a typical .pre-commit-config.yaml:

default_language_version:
  python: python3

repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v6.0.0
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: check-yaml
        args: [--allow-multiple-documents]
      - id: check-toml
      - id: check-added-large-files
  - repo: https://github.com/astral-sh/ruff-pre-commit
    # Ruff version.
    rev: v0.15.6
    hooks:
      # Run the linter.
      - id: ruff
        args: ["--fix"]
      # Run the formatter.
      - id: ruff-format
  - repo: https://github.com/tombi-toml/tombi-pre-commit
    rev: v0.9.6
    hooks:
      - id: tombi-format
        args: ["--offline"]
      - id: tombi-lint
        args: ["--offline"]

The “tombi” at the end might be a bit curious. There’s already the build-in “check-toml” toml syntax checker, right? Well, tombi also does formatting and schema validation. And in a recent project, I handled configuration through toml files.

It was for a Django website where several geographical maps were shown, each with its own title, description, legend yes/no, etcetera. I made up a .toml configuration format so that a colleague could configure all those maps without needing to deal with the python code. I created a json schema as format specification (yes, json is funnily used for that purpose). With tombi, I could make sure the config files were valid.

Oh, and tombi has an LSP plugin, so my colleague got autocomplete and syntax help out of the box. nice.

I’m also using uv a lot. That generates an uv.lock file, in .toml format, with all the version pins. It is a toml file, but without the .toml extension. So pre-commit ignored it. Until suddenly it started complaining about the indentation. But only in a github action, not locally.

Note: the complaint about the indentation is probably correct, as there’s an issue in the uv bugtracker about changing the indentation from 4 to 2 in the lockfile.

The weird thing for me was that I pin the the versions of the plugins. So the behaviour locally and on github should be the same. Some observations:

  • Running tombi from the commandline on uv.lock resulted in re-formatting to two spaces, whatever the tombi version.

  • Pre-commit locally did not re-format the file, but pre-commit on the server did.

  • I tried it with the new rust-based alternative for pre-commit, prek (see https://github.com/j178/prek), which did re-format uv.lock.

Some further debugging showed that pre-commit was actually skipping the uv.lock file. But apparently not on github. I did some searching in pre-commit’s source code and tombi’s pre-commit hook definition. The only relevant part there was types: [toml]. So somehow pre-commit has a definition of what a toml file is. But I couldn’t find anything.

Until I spotted that pre-commit uses identify as the means to detect file types. (Looks like a handy library, btw!). And that project had a change a couple of weeks ago that identifies uv.lock as a toml file!

  • My colleague updated his pre-commit installation and yes: uv.lock was getting re-formatted.

  • So: github actions had a newer version than we had.

  • Weird, as I just updated my python tool install this morning. Ah: I installed it with homebrew instead of uv tool, that’s why it is still older.

Anyway: small mystery solved.