yapfの導入
個人の開発スタイルのような感じですが、Windowsなどでpythonのソースを書いていて、vim
へコピー・ペーストをすることがよくあるのですが、何故かインデントのフォーマットが異なってしまうので、そのあとの手間が多いのでなんとかしないと行けないなあと思っていました。(他の言語ならそれほど大きく問題はありませんが、pythonではそういうわけにも行きません。)
そこで、yapf
とういうフォーマッタがあることがわかりました。
googleさん謹製のようです。
yapf
のインストール
readmeを見ればわかりますが以下の様にインストールします。
$ pip install yapf Collecting yapf Downloading yapf-0.17.0-py2.py3-none-any.whl (151kB) 100% |????????????????????????????????| 153kB 1.1MB/s Installing collected packages: yapf Successfully installed yapf-0.17.0
raspberrypiでインストールするとyapf
は~/.local/bin/yapf
にインストールされるようです。
実行するとこんな感じになります。
$ ~/.local/bin/yapf --version yapf 0.17.0 $ ~/.local/bin/yapf --help usage: yapf [-h] [-v] [-d | -i] [-r | -l START-END] [-e PATTERN] [--style STYLE] [--style-help] [--no-local-style] [-p] [files [files ...]] Formatter for Python code. positional arguments: files optional arguments: -h, --help show this help message and exit -v, --version show version number and exit -d, --diff print the diff for the fixed source -i, --in-place make changes to files in place -r, --recursive run recursively over directories -l START-END, --lines START-END range of lines to reformat, one-based -e PATTERN, --exclude PATTERN patterns for files to exclude from formatting --style STYLE specify formatting style: either a style name (for example "pep8" or "google"), or the name of a file with style settings. The default is pep8 unless a .style.yapf or setup.cfg file located in one of the parent directories of the source file (or current directory for stdin) --style-help show style settings and exit; this output can be saved to .style.yapf to make your settings permanent --no-local-style don't search for local style definition -p, --parallel Run yapf in parallel when formatting multiple files. Requires concurrent.futures in Python 2.X
テスト
公式サイトのサンプルを使って実験してみます。
x = { 'a':37,'b':42, 'c':927} y = 'hello ''world' z = 'hello '+'world' a = 'hello {}'.format('world') class foo ( object ): def f (self ): return 37*-+2 def g(self, x,y=42): return y def f ( a ) : return 37+-+a[42-x : y**3]
$ ~/.local/bin/yapf sample.py Traceback (most recent call last): File "/home/pi/.local/bin/yapf", line 11, in <module> sys.exit(run_main()) File "/home/pi/.local/lib/python2.7/site-packages/yapf/__init__.py", line 296, in run_main sys.exit(main(sys.argv)) File "/home/pi/.local/lib/python2.7/site-packages/yapf/__init__.py", line 188, in main parallel=args.parallel) File "/home/pi/.local/lib/python2.7/site-packages/yapf/__init__.py", line 236, in FormatFiles in_place, print_diff, verify) File "/home/pi/.local/lib/python2.7/site-packages/yapf/__init__.py", line 259, in _FormatFile logger=logging.warning) File "/home/pi/.local/lib/python2.7/site-packages/yapf/yapflib/yapf_api.py", line 91, in FormatFile verify=verify) File "/home/pi/.local/lib/python2.7/site-packages/yapf/yapflib/yapf_api.py", line 129, in FormatCode tree = pytree_utils.ParseCodeToTree(unformatted_source) File "/home/pi/.local/lib/python2.7/site-packages/yapf/yapflib/pytree_utils.py", line 102, in ParseCodeToTree tree = parser_driver.parse_string(code, debug=False) File "/usr/lib/python2.7/lib2to3/pgen2/driver.py", line 106, in parse_string return self.parse_tokens(tokens, debug) File "/usr/lib/python2.7/lib2to3/pgen2/driver.py", line 47, in parse_tokens for quintuple in tokens: File "/usr/lib/python2.7/lib2to3/pgen2/tokenize.py", line 429, in generate_tokens ("<tokenize>", lnum, pos, line)) File "sample.py", line 11 def g(self, x,y=42): ^ IndentationError: unindent does not match any outer indentation level
動かない…エラーメッセージから察するに、Webページのものをコピペして使うとpythonの文法まで崩れてしまっているようです。
Teratermのウインドウにドラッグ・アンド・ドロップすることで実行できる、scp
などでファイル転送をすると…
$ cat sample.py x = { 'a':37,'b':42, 'c':927} y = 'hello ''world' z = 'hello '+'world' a = 'hello {}'.format('world') class foo ( object ): def f (self ): return 37*-+2 def g(self, x,y=42): return y def f ( a ) : return 37+-+a[42-x : y**3] $ ~/.local/bin/yapf sample.py x = {'a': 37, 'b': 42, 'c': 927} y = 'hello ' 'world' z = 'hello ' + 'world' a = 'hello {}'.format('world') class foo(object): def f(self): return 37 * -+2 def g(self, x, y=42): return y def f(a): return 37 + -+a[42 - x:y**3]
うまく実行できたようです。ファイルを変換したものに上書きするには-i
をつければいいようです。他にも処理後との差分(diff)を表示する-d
や再帰的に実行する-r
のスイッチがあります。
$ ~/.local/bin/yapf -i sample.py pi@raspberrypi:~/yapf_sample $ cat sample.py x = {'a': 37, 'b': 42, 'c': 927} y = 'hello ' 'world' z = 'hello ' + 'world' a = 'hello {}'.format('world') class foo(object): def f(self): return 37 * -+2 def g(self, x, y=42): return y def f(a): return 37 + -+a[42 - x:y**3]
REPL環境での実行でモジュールとして使用することもできる様です。 githubのページでは以下のようになると書いてあるのですが、
>>> from yapf.yapflib.yapf_api import FormatCode # reformat a string of code >>> FormatCode("f ( a = 1, b = 2 )") 'f(a=1, b=2)\n'
自分の環境では
$ python Python 2.7.13 (default, Jan 19 2017, 14:48:08) [GCC 6.3.0 20170124] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> from yapf.yapflib.yapf_api import FormatCode # reformat a string of code >>> FormatCode("f ( a = 1, b = 2 )") (u'f(a=1, b=2)\n', True) >>>
となりました。python2だったからかな?念のためpython3でも実行してみました。
$ pip3 install yapf Collecting yapf Using cached yapf-0.17.0-py2.py3-none-any.whl Installing collected packages: yapf Successfully installed yapf-0.17.0 $ python3 Python 3.5.3 (default, Jan 19 2017, 14:11:04) [GCC 6.3.0 20170124] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from yapf.yapflib.yapf_api import FormatCode >>> FormatCode("f ( a = 1, b = 2 )") ('f(a=1, b=2)\n', True) >>> FormatCode("def g( ):\n a=1\n b = 2\n return a==b", lines=[(1, 1), (2, 3)]) ('def g():\n a = 1\n b = 2\n return a==b\n', True)
ユニコード文字列を指すのu
のプリフィックス以外は変わったところが無いみたい、あれ?
終わりに
コピペしてフォーマットが少し崩れるぐらいであれば、なんとかできるかなと思いましたが、派手に崩れる(文法的に崩れるレベル)とちょっとむずかしい様です。
RaspberryPiだけでなくWindowsのanaconda環境にもインストールしましたがこっちで使ったほうがいいのかもしれません。(コピペする前にこっちで整形をするという感じ)