これは エムスリー Advent Calendar 2018 5日目の記事です。
こんにちは、エムスリー・エンジニアリングG・基盤開発チーム小本です。
年末ということで、個人的に今年のうれしかったことである「Pythonのコントリビューターになったこと」について書きます。
起きた不具合
社内のバッチ処理サーバーでPythonを使うべく作業中(Python標準のコマンドである)pip や virtualenv を使うと以下のようなエラー1 が起き、異常終了してしまいました。
51_billappxz%pip list DEPRECATION: The default format will switch to columns in the future. You can use --format=(legacy|columns) (or define a format=(legacy|columns) in your pip.conf under the [list] section) to disable this warning. pip (9.0.1) setuptools (38.2.4) Traceback (most recent call last): File "/data01/billappxz/scripts/ThirdParty/python27/bin/pip", line 11, in <module> load_entry_point('pip==9.0.1', 'console_scripts', 'pip')() File "/data01/billappxz/scripts/ThirdParty/python27/lib/python2.7/site-packages/pip-9.0.1-py2.7.egg/pip/__init__.py", line 233, in main return command.main(cmd_args) File "/data01/billappxz/scripts/ThirdParty/python27/lib/python2.7/site-packages/pip-9.0.1-py2.7.egg/pip/basecommand.py", line 251, in main timeout=min(5, options.timeout)) as session: File "/data01/billappxz/scripts/ThirdParty/python27/lib/python2.7/site-packages/pip-9.0.1-py2.7.egg/pip/basecommand.py", line 72, in _build_session insecure_hosts=options.trusted_hosts, File "/data01/billappxz/scripts/ThirdParty/python27/lib/python2.7/site-packages/pip-9.0.1-py2.7.egg/pip/download.py", line 329, in __init__ self.headers["User-Agent"] = user_agent() File "/data01/billappxz/scripts/ThirdParty/python27/lib/python2.7/site-packages/pip-9.0.1-py2.7.egg/pip/download.py", line 93, in user_agent from pip._vendor import distro File "/data01/billappxz/scripts/ThirdParty/python27/lib/python2.7/site-packages/pip-9.0.1-py2.7.egg/pip/_vendor/distro.py", line 1050, in <module> _distro = LinuxDistribution() File "/data01/billappxz/scripts/ThirdParty/python27/lib/python2.7/site-packages/pip-9.0.1-py2.7.egg/pip/_vendor/distro.py", line 594, in __init__ if include_lsb else {} File "/data01/billappxz/scripts/ThirdParty/python27/lib/python2.7/site-packages/pip-9.0.1-py2.7.egg/pip/_vendor/distro.py", line 922, in _get_lsb_release_info stdout, stderr = stdout.decode('utf-8'), stderr.decode('utf-8') File "/data01/billappxz/scripts/ThirdParty/python27/lib/python2.7/encodings/utf_8.py", line 16, in decode return codecs.utf_8_decode(input, errors, True) UnicodeDecodeError: 'utf8' codec can't decode byte 0xd5 in position 24: invalid continuation byte
不具合の原因を探る
不具合が起きたのは古くから稼働しているサーバーで LANG=ja_JP.eucJP
になっていました。
発生したのはUnicodeDecodeError なのでいかにも関係ありそうです。LANG=
にして実行してみると・・・おや?エラーが起きない。
そこで、スタックトレースで表示された周辺のコードを見てみます。
props = {} for line in lines: line = line.decode('utf-8') if isinstance(line, bytes) else line kv = line.strip('\n').split(':', 1) if len(kv) != 2: # Ignore lines without colon. continue k, v = kv props.update({k.replace(' ', '_').lower(): v.strip()}) return props
https://github.com/pypa/pip/blob/41f09b555651768d1540db7173abe009c10cfe3e/pip/_vendor/distro.py#L953
utf-8 がハードコードされている!
このモジュールでは Linuxのバージョンを得るために lsb_release
コマンドを subprocess.Popen
で実行して出力をパースしています。lsb_release
は ASCII の範囲の文字しか出力しないので "utf-8" でも動作します。しかし、問題が起きたサーバーでは、
lsb_release
コマンドがインストールされていないLANG=ja_JP.eucJP
という条件が重なっていました。subprocess.Popen
実行時に EUC-JPの bash: lsb_release: コマンドが見つかりません
が出力され、それを "utf-8" でデコードしようとしてUnicodeDecodeErrorになっていました。
ところで、Pythonではシステムの文字コードを sys.getfilesystemencoding()
などの関数で得られるようになっています。修正するのは容易なはずなので、自分で直して見ることにしました。
Github でプルリクエストを送る&レビュー
今回の不具合は標準ライブラリの pip のものでした。pip はPython本体とは別レポジトリ( pypa/pip)になっています。アクセス制限などはされていないので、とりあえず Issue を作り Pull request を送りました。
しかし、
- 「これは pip そのものではなく Vendoring しているdistroの不具合だ」→ distro に issue を立てて修正依頼
- 「news/ にファイルを追加してるけど、不具合修正の時は要らない」→ファイルを削除
など、作業手順の不備があり2すぐにはマージされませんでした。
やったね!
コメントされては直してを繰り返し、1カ月ぐらいかかりましたがプルリクエスト はマージされ Pip 10.0 でリリースされました。最終的に私のコミット内容は実質2行(ライブラリをアップデートしただけ)になってしまっ足し、Pythonインタープリター本体ではなく標準ライブラリの不具合修正ですが、貢献は貢献です。
やったね!
実績解除
ところで、エムスリーでははてなさんやペパポさんのを参考に「エンジニア実績システム」を設けています。
OSS へのコミット・プルリクエスト コントリビュートしたGitHubのスター数:1000〜
にYESと書けるようになりました!
次は2行より多くのコミットをするべく、今週末は最近話題のresponder のコードを読んでみようかと思います。
補足:エムスリーでは勤務中にOSS活動ができるの?
今回の不具合修正は勤務時間内に行いました。これは、
- 比較的重要なサーバー(顧客向けレポートを扱う)で起きた不具合だった
- Pythonを新たに導入する過程で起きた不具合だった(「代わりに他言語を使う」といった選択肢がなかった)
- 他に緊急のタスクが無かった
- 短時間で直せる不具合だった
・・・といった状況だったからです。
そのため、業務中ではOSS活動ができるか?(するべきか?)と言えば、それはケースバイケースです。
しかし、上述のようにエンジニア実績システムを作ってOSSへの貢献を社内で共有していますし、有志が毎週 OSSもくもく会を開いています。過去には社員が個人的に開発したOSSを業務で使用したこともあります。
エンジニアを募集しています!
エムスリーでは一緒に働く仲間を募集中です。お気軽にお問い合わせください。
-
当時のログが見つからないのでGitHub上の他人のログを貼り付けています。↩
-
Contributing に書いてあったんですけどね。ドキュメントをよく読むべきでした。↩