format_code / lint_code for Python, backed by ruff (com.blockether/ruff).
ruff runs IN-PROCESS: the Rust ruff_python_formatter / ruff_linter crates are
linked as a cdylib and called over the FFM API. There is no ruff binary to
install, no subprocess, no virtualenv, and no PATH lookup — the same code path
works from the native image. Configuration is RUFF'S OWN discovery: for every
source, ruff walks up from the file to the nearest .ruff.toml / ruff.toml /
pyproject.toml with a [tool.ruff] table and honours it whole — extend,
per-file-ignores, target-version, formatter options — so a run here and a
ruff CLI run agree on every source it reads. With no config anywhere the
tool still runs on ruff's defaults and SAYS SO in a hint.
ONE divergence, the same one the sandbox ruff shim documents: the FFI is a
one-source-one-call linter/formatter, so the WALK is ours and a config's
exclude / extend-exclude globs are NOT applied to it — the noise
directories in skip-dirs are pruned instead. per-file-ignores DO apply:
ruff matches them itself against the path we hand it.
Both handlers accept the SAME argument shapes as the Clojure pack: a code string, {"code"}, {"path"}, {"paths"}, or nothing (whole project).
`format_code` / `lint_code` for Python, backed by ruff (com.blockether/ruff).
ruff runs IN-PROCESS: the Rust ruff_python_formatter / ruff_linter crates are
linked as a cdylib and called over the FFM API. There is no `ruff` binary to
install, no subprocess, no virtualenv, and no PATH lookup — the same code path
works from the native image. Configuration is RUFF'S OWN discovery: for every
source, ruff walks up from the file to the nearest `.ruff.toml` / `ruff.toml` /
`pyproject.toml` with a `[tool.ruff]` table and honours it whole — `extend`,
`per-file-ignores`, `target-version`, formatter options — so a run here and a
`ruff` CLI run agree on every source it reads. With no config anywhere the
tool still runs on ruff's defaults and SAYS SO in a `hint`.
ONE divergence, the same one the sandbox `ruff` shim documents: the FFI is a
one-source-one-call linter/formatter, so the WALK is ours and a config's
`exclude` / `extend-exclude` globs are NOT applied to it — the noise
directories in `skip-dirs` are pruned instead. `per-file-ignores` DO apply:
ruff matches them itself against the path we hand it.
Both handlers accept the SAME argument shapes as the Clojure pack:
a code string, {"code"}, {"path"}, {"paths"}, or nothing (whole project).(config-finder)config-for, memoised per directory for ONE tool call: a project walk asks
once per directory instead of once per file, and a config edited between calls
is still picked up.
`config-for`, memoised per directory for ONE tool call: a project walk asks once per directory instead of once per file, and a config edited between calls is still picked up.
(config-for p)The ruff configuration file governing p (a file or a directory), or nil when
the tree has none. Discovery is RUFF'S OWN — .ruff.toml, ruff.toml, then a
pyproject.toml carrying a [tool.ruff] table, walking ancestors — so vis
sees exactly the file the ruff CLI would use, extend chains and all.
The ruff configuration file governing `p` (a file or a directory), or nil when the tree has none. Discovery is RUFF'S OWN — `.ruff.toml`, `ruff.toml`, then a `pyproject.toml` carrying a `[tool.ruff]` table, walking ancestors — so vis sees exactly the file the `ruff` CLI would use, `extend` chains and all.
(discover-python-source-paths root)Default targets when neither path nor paths is given: the conventional
source roots that exist (src, the workspace root's top-level packages,
tests), falling back to the whole workspace root.
Default targets when neither `path` nor `paths` is given: the conventional source roots that exist (`src`, the workspace root's top-level packages, `tests`), falling back to the whole workspace root.
(expand-python-files root targets)Absolute paths of every Python source file under targets (files kept as-is,
DIRECTORIES walked recursively, skip-dirs pruned). Sorted + distinct.
Absolute paths of every Python source file under `targets` (files kept as-is, DIRECTORIES walked recursively, `skip-dirs` pruned). Sorted + distinct.
What to tell the caller when the project pins nothing: ruff still ran, with ITS defaults, and the fix is a config file rather than a tool flag.
What to tell the caller when the project pins nothing: ruff still ran, with ITS defaults, and the fix is a config file rather than a tool flag.
(py-format-fn arg)(py-format-fn env arg)Format Python source with ruff via the language facade (format_code).
Accepts:
changed 0:
both a path that does not exist and one that carries no Python at all.
Rule set and wrap width come from the project's ruff configuration file
(ruff.toml / [tool.ruff]), discovered by ruff itself per file; config
pins one explicitly and line_length overrides it. Syntactically invalid
Python is left VERBATIM rather than failing the call, so a formatter run never
destroys a half-written file.Format Python source with ruff via the language facade (`format_code`).
Accepts:
- a raw code string / {"code": ...} -> report changed? + char delta (NO text)
- {"path": "src/foo.py"} -> format that file IN PLACE
- {"paths": ["src" "tests"]} -> format those paths IN PLACE; a
DIRECTORY is walked RECURSIVELY (every .py/.pyi under it, minus venv /
cache / build dirs)
- nothing / {} -> format the project's source roots
A named target that resolves to nothing is an ERROR, not a silent `changed 0`:
both a path that does not exist and one that carries no Python at all.
Rule set and wrap width come from the project's ruff configuration file
(`ruff.toml` / `[tool.ruff]`), discovered by ruff itself per file; `config`
pins one explicitly and `line_length` overrides it. Syntactically invalid
Python is left VERBATIM rather than failing the call, so a formatter run never
destroys a half-written file.(py-lint-fn arg)(py-lint-fn env arg)Lint Python with ruff via the language facade (lint_code). Accepts:
path and paths are UNIONED; a target that resolves to nothing is an
ERROR, not a silent clean — both a path that does not exist and one that
holds no Python at all, because zero files read reads exactly like zero
findings. The RULE SET is the project's ruff configuration file, discovered
by ruff itself (.ruff.toml / ruff.toml / [tool.ruff] in
pyproject.toml, nearest ancestor wins) and reported back as config; with
no such file the run falls back to ruff's defaults (E4, E7, E9, F) and returns
a hint telling the caller to add one. config pins a file explicitly and
select / ignore (a string, or a list of selectors like ["F" "B" "E501"])
override it. Findings carry the ruff code as type, is_fixable, and a
level derived from the code: syntax/pyflakes fatals are errors, conventions
(W/C/N/D/I/UP…) info, the rest warnings. files is how many files were
LINTED (a snippet counts as 1), NOT how many carried findings, and targets
echoes the requested path/paths relative to the workspace root: a CLEAN run
still has to say what it looked at.Lint Python with ruff via the language facade (`lint_code`). Accepts:
- a raw code string / {"code": ...} -> lint the snippet
- {"path": "src/foo.py"} -> lint that file
- {"paths": ["src" "tests"]} -> lint those paths (dirs recursive)
- nothing / {} -> lint the project's source roots
`path` and `paths` are UNIONED; a target that resolves to nothing is an
ERROR, not a silent `clean` — both a path that does not exist and one that
holds no Python at all, because zero files read reads exactly like zero
findings. The RULE SET is the project's ruff configuration file, discovered
by ruff itself (`.ruff.toml` / `ruff.toml` / `[tool.ruff]` in
`pyproject.toml`, nearest ancestor wins) and reported back as `config`; with
no such file the run falls back to ruff's defaults (E4, E7, E9, F) and returns
a `hint` telling the caller to add one. `config` pins a file explicitly and
`select` / `ignore` (a string, or a list of selectors like ["F" "B" "E501"])
override it. Findings carry the ruff code as `type`, `is_fixable`, and a
`level` derived from the code: syntax/pyflakes fatals are errors, conventions
(W/C/N/D/I/UP…) info, the rest warnings. `files` is how many files were
LINTED (a snippet counts as 1), NOT how many carried findings, and `targets`
echoes the requested path/paths relative to the workspace root: a CLEAN run
still has to say what it looked at.(relativize-path root p)p written relative to root when it lives under it, else left absolute.
`p` written relative to `root` when it lives under it, else left absolute.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |