MicroPython 対話インタプリタモード (別名 REPL)

この章は、MicroPython の対話インタプリタモードのいくつかの特性を扱います。この機能の一般的に使用される用語は、REPL(read-eval-print-loop)です。以後、この用語を対話プロンプトを示すものとして使います。

自動インデント

python の文をコロンで終わるところまで入力すると(if, for while など)、プロンプトが3つのドット(...)に変わり、カーソルは4文字の空白でインデントされます。リターンを押すと、次の行は通常の文のために同じレベルでインデントを継続するか、適切な追加のインデントレベルになります。バックスペースキーを押すと、インデントのレベルが1つ取り消されます。

カーソルを先頭に戻して RETURN を押すと、入力したコードを実行します。以下は、for 文を入力した後にどうなっているかを示しています(下線はカーソル位置を示しています)。

>>> for i in range(30):
...     _

if 文を入力すると、インデントの追加のレベルが提供されます。

>>> for i in range(30):
...     if i > 3:
...         _

ここで break を入力して RETURN キーを押し、BACKSPACE キーを押してください。

>>> for i in range(30):
...     if i > 3:
...         break
...     _

最後に print(i) を入力し、RETURN キーを押し、BACKSPACE キーを押して、再び RETURN キーを押してください。

>>> for i in range(30):
...     if i > 3:
...         break
...     print(i)
...
0
1
2
3
>>>

前の2行がすべてスペースであった場合には、自動インデントが適用されません。これは、2回 RETURN を押せば、複合文を入力し終えることができ、その後、3回目の押下で入力が終了し、実行することを意味します。

自動補間

REPL でコマンドを入力しているとき、入力中のものが、何かの名前の先頭部分に該当していれば、次に TAB を押すことにより入力している可能性のあるものが表示されます。たとえば m を入力して TAB キーを押すと、 machine に展開されます。続けてドット . を入力して もう一度 TAB キーを押してください。次のようなものが表示されるはずです。

>>> machine.
__name__        info            unique_id       reset
bootloader      freq            rng             idle
sleep           deepsleep       disable_irq     enable_irq
Pin

複数の候補があれば、それだけのワードが展開されます。たとえば machine.Pin.AF3 を入力して TAB キーを押すと、 machine.Pin.AF3_TIM に展開されます。TAB をもう一度押すと、次の候補が表示されます。

>>> machine.Pin.AF3_TIM
AF3_TIM10       AF3_TIM11       AF3_TIM8        AF3_TIM9
>>> machine.Pin.AF3_TIM

実行中のプログラムの中断

Ctrl+C キーを押すことで実行中のプログラムを中断できます。これは KeyboardInterrupt 例外を発生させ、REPL に戻ります。KeyboardInterrupt をプログラム中で捕らえることはできません。

For example:

>>> for i in range(1000000):
...     print(i)
...
0
1
2
3
...
6466
6467
6468
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
KeyboardInterrupt:
>>>

貼付けモード

ターミナルウィンドウで何かのコードを貼り付けたい場合、自動インデント機能のせいでうまくいかないことがあります。たとえば、次の Python コードがある場合:

def foo():
    print('This is a test to show paste mode')
    print('Here is a second line')
foo()

通常の REPL にこれを貼り付けてみると、次のようになってしまいます。

>>> def foo():
...         print('This is a test to show paste mode')
...             print('Here is a second line')
...             foo()
...
  File "<stdin>", line 3
IndentationError: unexpected indent

Ctrl-E を押すと、貼付けモードとなり、基本的に自動インデント機能が無効となり、プロンプトが >>> から === に変わります。次に例を示します。

>>>
paste mode; Ctrl-C to cancel, Ctrl-D to finish
=== def foo():
===     print('This is a test to show paste mode')
===     print('Here is a second line')
=== foo()
===
This is a test to show paste mode
Here is a second line
>>>

貼付けモードでは、空白行を貼り付けることができるようになります。ファイルであるかのように貼り付けたテキストがコンパイルされます。Ctrl+Dキーを押すと、ペーストモードを終了し、コンパイルを開始します。

ソフトリセット

ソフトリセットは python インタプリタをリセットしますが、MicroPython ボードに接続している手段(USBシリアル、または無線LAN)をリセットしないよう試みます。

ソフトリセットは Ctrl-D を押すことで起こせます。python コード中からも次を実行することでソフトリセットを起こせます。

machine.soft_reset()

たとえば MicroPython ボードをリセットし、dir() 命令を実行すると、次のようになるでしょう。

>>> dir()
['__name__', 'pyb']

ここで、いくつかの変数の作成と dir() 命令を繰り返します。

>>> i = 1
>>> j = 23
>>> x = 'abc'
>>> dir()
['j', 'x', '__name__', 'pyb', 'i']
>>>

ここで Ctrl-D を押して、再度 dir() を行ってみれば、変数がもはや存在しないことがわかります。

MPY: sync filesystems
MPY: soft reboot
MicroPython v1.5-51-g6f70283-dirty on 2015-10-30; PYBv1.0 with STM32F405RG
Type "help()" for more information.
>>> dir()
['__name__', 'pyb']
>>>

特殊変数 _ (下線)

REPL を使っているときには、計算を実行した結果を使いたいことがあります。MicroPython は、前の文の結果を変数 _ (下線)に格納します。ですから、変数に結果を保存するために、アンダースコアが使えます。たとえば次のように使えます。

>>> 1 + 2 + 3 + 4 + 5
15
>>> x = _
>>> x
15
>>>

raw モードと raw ペーストモード

raw モード(raw REPL ともいいます)は、人が通常使うものではありません。これはプログラムで使うよう意図されていて、本質的にはエコーが無効になったペーストモードのように動作します。オプションでフロー制御もできます。

raw モードには、Ctrl+A キーを使って入ります。続いて python コード と Ctrl-D を送信します。Ctrl-D は「OK」によって承認され、その後 python コードがコンパイルされ、実行されます。任意の出力(またはエラー)が戻ってきます。Ctrl-B を入力すると、raw モードを終了して、通常の(フレンドリーな) REPL に戻ります。

raw ペーストモードは、フロー制御を含む raw REPL 内の追加モードで、コードを受信すると同時にコンパイルします。これにより、デバイスへのコードの高速転送がより堅牢になり、(標準の raw モードとは異なり)コンパイル前のコードの冗長なコピーを保存しなくてもよいため、受信時の RAM の使用量も少なくなります。

raw ペーストモードでは、以下のプロトコルを使用します。

  1. ctrl-A で通常の raw REPL に入ります。
  2. 次の3バイトを書き込みます: b"\x05A\x01" (ctrl-E、"A "、ctrl-A)。
  3. 2バイト読み込んで、デバイスが raw ペースト モードになったかどうかを確認します:
    • 結果が b"R\x00" の場合、デバイスはコマンドを理解していますが、raw ペーストをサポートしていません。
    • 結果が b"R\x01" の場合、デバイスは raw ペーストをサポートしていて、このモードに入っています。
    • 上記のいずれでもなければ結果は b"ra" となるはずで、デバイスは raw ペーストをサポートしていません。この場合は、後続のバイト列 b"w REPL; CTRL-B to exit\r\n>" を読み取って破棄してください。
  4. デバイスが raw ペーストモードになったら続行し、そうでない場合は標準の raw モードにフォールバックします。
  5. 2バイト読み込みます。これは16ビット符号なしリトルエンディアン整数のフロー制御 window-size-increment (バイト単位)です。remaining-window-size 変数のための初期値はこの数に設定されるべきです。
  6. デバイスにコードを書き出します。
    • 送信するバイトがある間に remaining-window-size のバイト数分まで書き込んで、書き込んだバイト数分だけ remaining-window-size を減少させます。
    • remaining-window-size が0の場合、または読み込み待ちのバイトがある場合は、1バイト読み込みます。もし、このバイトが b"\x01" であれば、ステップ5でのwindow-size-increment だけ remaining-window-size を増加させます。もし、このバイトが b"\x04" であれば、デバイスはデータ受信を終了したいので、 b"\x04" をデバイスに書き込み、それ以降はコードを送信しないようにして下さい。(注記: デバイスから読み出されるのを待っているバイトがある場合でも、すぐに読み込んで対処する必要はありません。reamining-window-size が0より大きい限り、デバイスは受信バイトを消費し続けます。)
  7. すべてのコードがデバイスに書き込まれたら、データの終了を示すために b"\x04" を書き込みます。
  8. b"\x04" が受信されるまでデバイスから読み出します。この時点でデバイスは、送信されたコードを全て受信してコンパイルし、実行しています。
  9. デバイスは、実行中のコードによって生成された文字を出力します。コードが終了すると b"\x04" が出力され、続いて捕えられない例外が出力され、再び b"\x04" が出力されます。その後、標準の raw REPLに戻り b">" を出力します

たとえば、通常の(対話的な) REPL で書き始めるとすると、次のようになります:

b"\x01\x05A\x01print(123)\x04"

こうすると、デバイスは次のような反応をします:

b"\r\nraw REPL; CTRL-B to exit\r\n>R\x01\x80\x00\x01\x04123\r\n\x04\x04>"

デバイスとの入出力を詳しく書くと次のようになります:

# ステップ1: raw REPL に入る
write: b"\x01"
read: b"\r\nraw REPL; CTRL-B to exit\r\n>"

# ステップ2-5: raw ペーストモードに入る
write: b"\x05A\x01"
read: b"R\x01\x80\x00\x01"

# ステップ6-8: コードを書き出す
write: b"print(123)\x04"
read: b"\x04"

# ステップ9: コードを実行した結果を読み込む
read: b"123\r\n\x04\x04>"

この場合、フロー制御の window-size-increment は 128 であり、開始時にすぐに利用可能な2つのウィンドウに相当するデータがあり、1つは初期 window-size-increment 値から、もう1つは送信された明示的な b"\x01" 値からです。つまり、フロー制御文字の着信を待ったりチェックしたりする前に、最大256バイトまで書き込めるということです。

tools/pyboard.py プログラムは MicroPython ボード上の python ファイルを実行するために、raw REPL と raw ペーストモードを使っています。