はじめに
ipythonは便利なのですが、実行例などを貼り付けるときには通常のpythonのシェルでの表現に揃えたかったりします。一方でipythonのpromptには実行回数的なものが併記されるなど通常のpythonとは異なる表示になっています。
ipythonのprompt表示
In [1]: "hello" Out[1]: 'hello' In [2]: x = 1
通常のpythonのprompt表示
>>> "hello" 'hello' >>> x = 1
これを揃えたいというのがやりたいことです。
結論
結論から言うと、ipythonには --classic というオプションが存在していて、これを付けてあげるとpythonコマンドでのインタラクティブシェルのprompt表示に近づけることができます。
$ ipython -h
...
--classic
Gives IPython a similar feel to the classic Python prompt.
...
$ ipython --classic
Python 3.7.1 (default, Oct 22 2018, 10:41:28)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.2.0 -- An enhanced Interactive Python. Type '?' for help.
>>> "hello"
'hello'
>>> 1
1
ここから先は色々調べた結果のメモです。
追記
%doctest_mode を使えば、既に立ち上がり済みのシェルに対して変更ができます。
詳細
以下詳細。
startup scripts
とりあえず最初はipythonのシェルを立ち上がる前に読み込まれるスクリプトの指定方法を調べるところからはじめました。pythonのシェルで言うならPYTHONSTARTUPに対応するようなものです。
ドキュメントの以下のページが参考になりそうです。
$HOME/.ipython/profile_default/startup以下にスクリプトを置けば良いようです。README的なファイルも置いてくれているので親切です。
$ cat ~/.ipython/profile_default/startup/README
This is the IPython startup directory
.py and .ipy files in this directory will be run *prior* to any code or files specified
via the exec_lines or exec_files configurables whenever you load this profile.
Files will be run in lexicographical order, so you can control the execution order of files
with a prefix, e.g.::
00-first.py
50-middle.py
99-last.ipy
$ edit ~/.ipython/profile_default/startup/00-prompt.py
prompt configuration
先程のドキュメントのページにまさにpromptを変更する方法が記載されていました。
コードを補うと ~/.ipython/profile_default/startup/00-prompt.py などのようなファイルを作って以下の様なコードを書いてあげれば良さそうです。
00-prompt.py
import os # 当時は古い=legacyとして解釈していた(classicという言葉は思い浮かんでいなかった) def apply_legacy_prompt(): import IPython.terminal.prompts as prompts from IPython import get_ipython class Prompts(prompts.Prompts): def in_prompt_tokens(self, cli=None): return [(prompts.Token.Prompt, ">>> ")] def out_prompt_tokens(self, cli=None): return [] ip = get_ipython() ip.prompts = Prompts(ip) if os.environ.get("LEGACY") is not None: apply_legacy_prompt()
通常の実行では今まで通りのlegacyではないprompt表示でであって欲しいので環境変数でLEGACY=1などとした場合にlegacyな表示にしてみることにします。
上手く動きました。
$ LEGACY=1 ipython --no-banner >>> "hello" 'hello' >>> x = 1
そう言えば、startup scriptの profile_default という名前が気になりますね。
profile
profile defaultがあるなら他のprofileをつくることができそうですね。実際まじめにヘルプメッセージを読んでいなかったのですが以下の様な説明が見つかりました。
$ ipython -h
...
--profile-dir=<Unicode> (ProfileDir.location)
Default: ''
Set the profile location directly. This overrides the logic used by the
`profile` option.
--profile=<Unicode> (BaseIPythonApplication.profile)
Default: 'default'
The IPython profile to use.
...
ipython --log-level=DEBUG # set logging to DEBUG
ipython --profile=foo # start with profile foo
ipython profile create foo # create profile foo w/ default config files
ipython help profile # show the help for the profile subcmd
というわけで環境変数で分岐する必要はなさそうです。profileを作りましょう。
$ ipython profile create legacy [ProfileCreate] Generating default config file: '$HOME/.ipython/profile_legacy/ipython_config.py' [ProfileCreate] Generating default config file: '$HOME/.ipython/profile_legacy/ipython_kernel_config.py' $ edit
もう関数でwrapしたりする必要はなさそうですね。
00-prompt.py
import IPython.terminal.prompts as prompts from IPython import get_ipython class Prompts(prompts.Prompts): def in_prompt_tokens(self, cli=None): return [(prompts.Token.Prompt, ">>> ")] def out_prompt_tokens(self, cli=None): return [] ip = get_ipython() ip.prompts = Prompts(ip)
これで作ったprofileを指定してあげれば良さそうです。
# 出力などは省略 $ ipython --no-banner --profile=legacy
(ところでまだ改行が入る部分が完全に同じではなさそうです)
IPython.terminal.prompts
ここまでドキュメントに書いてある通りに徐ろにIPython.terminal.promptsをimportして使っていましたがちょっと中を覗いて見たくなってきたりしました。実はもう少し便利な何かが含まれているかもしれません。
そして、覗いてみたらなんと先程定義していたようなクラスがClassicPromptという名前で定義されていました。
IPython/terminal/prompts.py
class ClassicPrompts(Prompts): def in_prompt_tokens(self): return [ (Token.Prompt, '>>> '), ] def continuation_prompt_tokens(self, width=None): return [ (Token.Prompt, '... ') ] def rewrite_prompt_tokens(self): return [] def out_prompt_tokens(self): return []
そこで、あー、なるほどclassic...と思ったのちに何かオプションで変更可能なのではということでhelpを見返してみた所、冒頭に至るという感じでした。。。
$ ipython -h
--classic
Gives IPython a similar feel to the classic Python prompt.
はい。
追記 %doctest_mode
%doctest_mode が便利ということを教えてもらった。
$ ipython --no-banner In [1]: 1 Out[1]: 1 In [2]: %doctest_mode Exception reporting mode: Plain Doctest mode is: ON >>> 1 1 >>> %doctest_mode Exception reporting mode: Context Doctest mode is: OFF In [5]: 1 Out[5]: 1