以下の内容はhttps://pod.hatenablog.com/entry/2019/02/02/141932より取得しました。


ipythonのシェルのpromptをpythonのシェルの表示に変える方法のメモ

はじめに

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



以上の内容はhttps://pod.hatenablog.com/entry/2019/02/02/141932より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14