メインコンテンツへスキップ

EmacsからRuffを使う

··2 分·
Python Ruff Emacs
Makoto Morinaga
著者
Makoto Morinaga
技術メモ、コーディング、環境構築のための個人ノート。
目次

この記事では、EmacsからRuffを使ってみます。

Ruff
#

Ruffとは
#

RuffはRustで書かれた高速なPythonのLinter & Formatterです。Flake8やisortを置き換えることができます。 Ruff の詳しい紹介は、検索していただくと素晴しい記事がたくさんありますので、そちらをご覧いただくのが良いかと思います。

以下からインストールと設定を行います。

Ruffのインストール
#

Ruffのインストール方法は公式ドキュメントにあるようにpipを始めとし、様々なインストール方法があります。 私は、pipxで以下のようにインストールします:

Terminal
pipx install ruff

Ruffの設定
#

Ruffは500以上のlintルールをサポートしており、isortやFlake8等のルールもRuffで再実装されています。また、lintルールの中には自動修正可能なものもあります。併せて、Blackと同等のCode fomatterとして利用できるようになっています。

公式ドキュメントによると、デフォルトでは、Flake8のEおよびFルールを有効しており、使い始めたばかりであれば、デフォルトのルールセットが最適とのことです。

私はどんなルールがあるのか知りたいので、基本的にはすべてのルールを有効化して、適用しないルールを都度追加するようにしています。

lintルールの設定ファイル
#

Ruffは、 pyproject.tomlruff.toml 、または .ruff.toml ファイルで設定します。 設定ファイルが色々なディレクトリ階層にある場合は、Ruffを適用するPythonファイルに対して、ディレクトリ階層の中で「最も近い」設定ファイルが参照されます(pyproject.toml は Ruffの設定がなければ無視されます)。

ディレクトリ階層内に設定ファイルが見つからない場合、Ruffはデフォルトの設定を使用し、ユーザ固有の設定ファイルが {config_dir}/ruff 内に存在する場合、そのファイルがデフォルト設定の代わりに使用されます。 {config_dir}ここに記載のあるようにOSによって異なりますので、自身のOSに従って適切に配置します。

詳しい設定ファイルの参照ルールは公式ドキュメントのpyprojecttoml discoveryをご参照ください。

私は、どのプロジェクトでもユーザ固有の設定が適用したいで、 ~/.config/ruff/ruff.toml (linuxの場合)としてのように設定しています。

toml
# 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を実行できます。

Terminal
# 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で表示されるので、とても勉強になり便利です。

emacs-lisp
(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を保存時に自動適用することができます。

emacs-lisp
(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の設定ファイルに追記します。

emacs-lisp
(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の場合にのみ実行できます。

emacs-lisp
(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 を使う環境を構築できました。

関連記事

EmacsでのlspをベースとしたPython開発環境
··4 分
Emacs Lsp Python
EmacsでのElpyをベースとしたPython開発環境
··3 分
Elpy Python Emacs
Elpy RPC Process and Python Version
··1 分
Elpy Python Emacs