この記事では、EmacsからRuffを使ってみます。
Ruff #
Ruffとは #
RuffはRustで書かれた高速なPythonのLinter & Formatterです。Flake8やisortを置き換えることができます。 Ruff の詳しい紹介は、検索していただくと素晴しい記事がたくさんありますので、そちらをご覧いただくのが良いかと思います。
以下からインストールと設定を行います。
Ruffのインストール #
Ruffのインストール方法は公式ドキュメントにあるようにpipを始めとし、様々なインストール方法があります。 私は、pipxで以下のようにインストールします:
pipx install ruff
Ruffの設定 #
Ruffは500以上のlintルールをサポートしており、isortやFlake8等のルールもRuffで再実装されています。また、lintルールの中には自動修正可能なものもあります。併せて、Blackと同等のCode fomatterとして利用できるようになっています。
公式ドキュメントによると、デフォルトでは、Flake8のEおよびFルールを有効しており、使い始めたばかりであれば、デフォルトのルールセットが最適とのことです。
私はどんなルールがあるのか知りたいので、基本的にはすべてのルールを有効化して、適用しないルールを都度追加するようにしています。
lintルールの設定ファイル #
Ruffは、 pyproject.toml
、 ruff.toml
、または .ruff.toml
ファイルで設定します。
設定ファイルが色々なディレクトリ階層にある場合は、Ruffを適用するPythonファイルに対して、ディレクトリ階層の中で「最も近い」設定ファイルが参照されます(pyproject.toml
は Ruffの設定がなければ無視されます)。
ディレクトリ階層内に設定ファイルが見つからない場合、Ruffはデフォルトの設定を使用し、ユーザ固有の設定ファイルが {config_dir}/ruff
内に存在する場合、そのファイルがデフォルト設定の代わりに使用されます。 {config_dir}
はここに記載のあるようにOSによって異なりますので、自身のOSに従って適切に配置します。
詳しい設定ファイルの参照ルールは公式ドキュメントのpyprojecttoml discoveryをご参照ください。
私は、どのプロジェクトでもユーザ固有の設定が適用したいで、 ~/.config/ruff/ruff.toml
(linuxの場合)としてのように設定しています。
# cache-dir
cache-dir = "~/.cache/ruff"
# Exclude a variety of commonly ignored directories.
exclude = [
".bzr",
".direnv",
".eggs",
".git",
".git-rewrite",
".hg",
".mypy_cache",
".nox",
".pants.d",
".pytype",
".ruff_cache",
".svn",
".tox",
".venv",
"__pypackages__",
"_build",
"buck-out",
"build",
"dist",
"node_modules",
"venv",
]
line-length = 300
indent-width = 4
[lint]
select = ["ALL"]
ignore = ["E501", "PGH", "DJ", "D", "T20", "ERA", "C90", "ANN101", "FA102", "RUF001", "PLR0915", "S101", "S311", "S104"]
fixable = ["I", "SIM118"]
unfixable = []
[format]
# Like Black, use double quotes for strings.
quote-style = "double"
# Like Black, indent with spaces, rather than tabs.
indent-style = "space"
# Like Black, respect magic trailing commas.
skip-magic-trailing-comma = false
# Like Black, automatically detect the appropriate line ending.
line-ending = "auto"
これでRuffの設定は完了です。以下のような形でターミナルからRuffを実行できます。
# lintエラーの表示
ruff check test.py
# 自動修正可能なlintエラーの修正
ruff check --fix test.py
# コードフォーマットを適用
ruff format test.py
EmacsでのRuffの利用 #
このようにとても便利なRuffをEmacsからも使いたいです。
私がRuffを使って、Emacsでやりたいことは以下の2つです。
- 現在のバッファのLinter及びCode formatterの結果をリアルタイムで表示
- 現在のバッファにCodo formatterを自動的に適用
- 現在のバッファのlintエラーを自動的に修正
では、一つずつやっていきましょう。
現在のバッファの Linter 及び Code formatter の結果をリアルタイムで表示 #
RuffのlintエラーをEmacsでリアルタイムに表示するパッケージは いくつかありますが、私はFlymakeを利用しているので、flymake-ruffを使います。
これにより知らない記法がFlymakeで表示されるので、とても勉強になり便利です。
(use-package flymake-ruff
:ensure t
:hook (eglot-managed-mode-hook . (lambda ()
(when (derived-mode-p 'python-mode 'python-ts-mode)
(flymake-ruff-load))))
:config
(setq flymake-ruff--default-configs '("ruff.toml" ".ruff.toml")))
現在のバッファに Codo formatter を自動的に適用 #
emacs-reformatterというパッケージは、stdinから読み込み、stdoutに書き出すreformatterをサポートし、refmormatコマンドを定義するためのシンプルなインターフェースと、オプションで保存時に自動適用するためのマイナーモードを提供しています。
そこで、 ruff format
コマンドを設定することで、Ruffのformatterを保存時に自動適用することができます。
(use-package reformatter
:hook
(python-ts-mode-hook . ruff-format-on-save-mode)
:config
(reformatter-define ruff-format
:program "ruff"
:args `("format" "--stdin-filename" ,buffer-file-name "-")))
現在のバッファのlintエラーを自動的に修正 #
import順の修正や使っていないライブラリのimport文削除等、自明なlintエラーは自動的に修正したいので、以下の関数をEmacsの設定ファイルに追記します。
(defun ruff-fix-buffer ()
"Use ruff to fix lint violations in the current buffer."
(interactive)
(let* ((temporary-file-directory (if (buffer-file-name)
(file-name-directory (buffer-file-name))
temporary-file-directory))
(temporary-file-name-suffix (format "--%s" (if (buffer-file-name)
(file-name-nondirectory (buffer-file-name))
"")))
(temp-file (make-temp-file "temp-ruff-" nil temporary-file-name-suffix))
(current-point (point)))
(write-region (point-min) (point-max) temp-file nil)
(shell-command-to-string (format "ruff check --fix %s" temp-file))
(erase-buffer)
(insert-file-contents temp-file)
(delete-file temp-file)
(goto-char current-point)))
この ruff-fix-buffer
関数を呼び出すことで、現在のバッファのlintエラーが修正されます。
また、以下の関数を定義して、 before-save-hook
に追加することで、バッファを保存する前に自動的にlintエラーを修正できるようにします。
なお、この関数は、major-modeがpython-modeまたはpython-ts-modeの場合にのみ実行できます。
(defun ruff-fix-before-save ()
(interactive)
(when (memq major-mode '(python-mode python-ts-mode))
(ruff-fix-buffer)))
(add-hook 'before-save-hook 'ruff-fix-before-save)
上記の関数群は、ruff-fix.elとして、パッケージにまとめているので、よろしければ使ってみてください。
これで Emacs から Ruff を使う環境を構築できました。