CUBE SUGAR CONTAINER

技術系のこと書きます。

Python: REPL に複数行をペーストしたときの挙動が変わって困った件について

表題のとおりなんだけど、最近 Python の REPL に複数行のコードをペーストしたときの挙動が以前と変わってしまい困っていた。 その Python というのは、具体的には Homebrew でインストールしたものや、Pyenv を使ってソースコードからビルドしたもの。

使っている環境は次のとおり。

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.15.7
BuildVersion:   19H114
$ brew info readline | head -n 1
readline: stable 8.1 (bottled) [keg-only]
$ pyenv --version
pyenv 1.2.22

もくじ

TL;DR

結論から先に述べると、これは Python のビルドに使った GNU Readline のオプションが関係している。 具体的には、バージョン 8.1 から enable-bracketed-paste というオプションのデフォルト値が off から on に変更されたらしい。

そのため、問題のワークアラウンドとしては、このオプションの値を明示的に指定すれば良い。 たとえば、以下のようなコマンドで GNU Readline の設定ファイルを作ろう。

$ echo "set enable-bracketed-paste off" >> ~/.inputrc

これで、以前と同じ挙動になる。

問題についてもうちょっと詳しく

たとえば、次のような 2 行をコピーアンドペーストしたいときを想定する。

a = 1
b = 2

以前の REPL であれば、次のように別々の行として認識された。

>>> a = 1
>>> b = 2

それが、問題のある環境では以下のように SyntaxError 例外になってしまう。

>>> a = 1
b = 2
  File "<stdin>", line 1
    a = 1
b = 2
         ^
SyntaxError: multiple statements found while compiling a single statement

複数行を一度にペーストできないのは REPL として使い勝手が良くない。

上記の問題が起こる環境には、バージョンは問わず割と最近にビルドしたもの、という共通点があった。 そのため、GNU Readline が怪しいという当たりはついていたので、そこを重点的に調べていったところ以下のページにたどり着いた。

github.com

どうやら GNU Readline のバージョン 8.1 から、enable-bracketed-paste というオプションがデフォルトで on になったらしい。 その影響を受けて、複数行のペーストが上記のような挙動に変わってしまった。

最近の端末エミュレータには、Bracketed Paste Mode というものがある。 これは、通常の文字の入力とペーストによる入力を区別できるようにすることを意図して追加された機能らしい。 そして、GNU Readline はコマンドラインの編集操作に関する機能を提供するライブラリとなっている。 これまで、GNU Readline はペースト時のデフォルトの設定として Bracketed Paste Mode を無効にしていたようだ。 それが、8.1 からデフォルトの設定が変更になって、今回の問題が生じた、ということらしい。

それはそれとして、Python の REPL が Bracketed Paste に対応するという話もあると思う。 対応していると、空行の入ったスクリプトのコードをそのまま入れてもエラーにならないという嬉しさがあるはず。 ただ、調べたところ以下のチケットはあったが現状であまり話は前に進んでいないようだ。

bugs.python.org

いじょう。