pythonの全角スペースによるエラーメッセージを明示的なものに変更してみた
/タイトル:pythonの全角スペースによるエラーメッセージを明示的なものに変更してみた/
はじめに
この記事は, 東京大学工学部電子情報工学科・電気電子工学科(通称EEIC)の3年後期実験の1つである「大規模ソフトウェアを手探る」のレポートとして書かれたものです.当実験は任意のOSSを1つ選んでその改造を試みるというものであり, 私たちの班「cuatro」はpythonを改造するOSSに選びました.
概要
私達の班「cuatro」では、以下の3点の改変を行いました.
- 全角文字を含むようなコードに対するエラーメッセージを改変する
- do-while文を実装する
- classのメソッドの引数の省略
2つめと3つめの記事については班のメンバーが書いておりますので, 興味のある方は合わせてご覧ください.なお改変したコードについては、本実験のgitlabリポジトリにあります.
環境
Ubuntu 18.04 LTS
python 3.11.0 (cpython)
変更したこと
全角空白文字" "を含むようなコードを書くと以下のようなエラーメッセージが表示されます.わかりにくいですが,8行目の部分では全角スペースのみを打ち込んでいます.
$ ./bin/python3 Python 3.11.0a1+ (heads/main-dirty:be21706f37, Oct 14 2021, 14:27:35) [GCC 7.5.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> File "<stdin>", line 1 ^ SyntaxError: invalid non-printable character U+3000
全角スペースのようなnon-printableな文字の入力があった際には同様のエラーメッセージが出力されます.ここで"U+3000"とは全角スペースのUnicodeが3000であることに対応しています.
このエラーメッセージを以下のように変更しました.全角スペースが含まれていることをより明示的に示したエラーメッセージとなっています.
$ ./bin/python3 Python 3.11.0a1+ (heads/main-dirty:be21706f37, Nov 7 2021, 05:13:17) [GCC 7.5.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> File "<stdin>", line 1 ^ SyntaxError: there are one or more Full-width spaces U+3000
ソースのクローンとビルド
c言語によるpythonのソースコードはgithub上に公開されていますのでそれをcloneしました.つまりターミナルで以下のコマンドを打ちました.
$ git clone https://github.com/python/cpython.git
ビルドはconfigureのあるディレクトリで以下のようにしました.CFLAGS="-O0 -g"の部分はデバッグ用のオプションなのでデバッガを使用しないとき(完成版をビルドする場合など)は省略します.
$ CFLAGS="-O0 -g" ./configure --prefix="インストール先へのパス" $ make clean $ make $ make install
ソースコードの改変
エラーメッセージをgrepコマンドで検索したところ,/Parser/tokenizer.c:1316に出力するエラーメッセージを生成していると思われる箇所がありました.
$ grep -r -n -I "invalid non-printable character" Parser/tokenizer.c:1316: syntaxerror(tok, "invalid non-printable character U+%s", hex); Lib/test/test_fstring.py:661: self.assertAllRaise(SyntaxError, r"invalid non-printable character U\+00A0",
周辺のコード(下)をみるとわかるのですが全角記号を入力したときのエラー出力は"if (Py_UNICODE_ISPRINTABLE(ch))"によって表示可能な文字か非表示文字(制御文字)かに場合分けされていて,今回変更したいのは全角スペースのエラーメッセージ"SyntaxError: invalid non-printable character U+3000"の形式から非表示文字のエラーメッセージの方だとわかります.
... // PyUnicode_FromFormatV() does not support %X char hex[9]; (void)PyOS_snprintf(hex, sizeof(hex), "%04X", ch); if (Py_UNICODE_ISPRINTABLE(ch)) { syntaxerror(tok, "invalid character '%c' (U+%s)", ch, hex); } else { syntaxerror(tok, "invalid non-printable character U+%s", hex); } return 0; ...
またこのとき,文字のUnicodeを変数hexで持っているのでそれによって更に細かく処理を分けることができます.つまり全角スペースのとき限定のエラーメッセージを出力することが出来るはずです.変更は以下のように行いました.
// PyUnicode_FromFormatV() does not support %X char hex[9]; (void)PyOS_snprintf(hex, sizeof(hex), "%04X", ch); if (Py_UNICODE_ISPRINTABLE(ch)) { syntaxerror(tok, "invalid character '%c' (U+%s)", ch, hex); } else { #if 1 //changed part int fwspace_flag = 1; char hex_compare[9] = "3000"; for(int i = 0; hex[i] != '\0'; i++){ if(hex[i] != hex_compare[i]){ fwspace_flag = 0; break; } } if (fwspace_flag){ syntaxerror(tok, "there are one or more Full-width spaces U+%s", hex); }else { syntaxerror(tok, "invalid non-printable character U+%s", hex); } #endif #if 0 //original syntaxerror(tok, "invalid non-printable character U+%s", hex); #endif } return 0;
先に述べたようにhexはinvalidな文字のUnicodeを文字列として持っているようなので,それと全角スペースの"3000"を比較することによって全角スペース特有のエラーメッセージを出力させました.
感想
今回はpythonの出力に関する部分の改変でしたので比較的簡単に実装することが出来たのでしょう.加えてここではエラーメッセージという強力なキーワードがありましたのでgrep文でなんとかなりそうだと取り掛かりの段階で感じていました.
当初は一旦grep文を封印してデバッガ(gdb)による追跡によって該当箇所を見つけ出そうともしたのですが, python全体の処理構造を理解していなったために断念してしまいました.今一度冷静になってみるとinvalidな文字を検出しうるのはトークン列取得のタイミングであろうことは推測できそうで幾分か悔しい思いです.
参考文献
大規模ソフトウェアを手探る(https://doss.eidos.ic.i.u-tokyo.ac.jp) Python Developer’s Guide(https://devguide.python.org)
おまけ(先輩方の記事のまとめ)
→別記事にまとめました.
2015年度
2016年度
2017年度, 2018年度, 2019年度
- なし