jqfpyにloadfile(),dumpfile()を追加していた
loadfile(),dumpfile()がほしくなったので追加した。
jqfpy
jqfpyは、
jsonをparseするためにDSLを覚えるのがめんどくさい(jq)。 各自自分の慣れた言語のワンライナーで十分なのでは?
という発想のもとのpackage(自分はpython)。
重要なことは誰でも最初から使えるということ(pythonの知識があるなら)。
例えば以下のようなファイルがある時に、category情報の一覧を取り出したいときは以下の様に書く。
flask.json
{ "data": [ { "category": "2.6", "downloads": 5287 }, { "category": "2.7", "downloads": 2274964 }, { "category": "3.2", "downloads": 23 }, { "category": "3.3", "downloads": 723 }, { "category": "3.4", "downloads": 120625 }, { "category": "3.5", "downloads": 899583 }, { "category": "3.6", "downloads": 4135489 }, { "category": "3.7", "downloads": 787076 }, { "category": "3.8", "downloads": 3190 }, { "category": "null", "downloads": 44777 } ], "package": "flask", "type": "python_minor_downloads" }
これをjqfpyで取り出すのは以下のような形。
$ jqfpy -c '[row["category"] for row in get("data")]' flask.json ["2.6", "2.7", "3.2", "3.3", "3.4", "3.5", "3.6", "3.7", "3.8", "null"]
ただのリスト内包表記。ただのリスト内包表記なので誰でも書ける。
get()
は渡されたファイルをloadした結果を返す。loadした結果をどうこうするということなので、単にdictをイジるだけの操作ということになる。
ショートカット
とはいえ毎回書くのがめんどくさいと思うこともあるのでショートカットを用意したくなる。
$ jqfpy -c 'get("data[]/category")' flask.json ["2.6", "2.7", "3.2", "3.3", "3.4", "3.5", "3.6", "3.7", "3.8", "null"]
個人的にはなるべく対応関係を意識せずにすむようにしたいので、JSON Referenceに近いかたちの表記でアクセスできるようにしている ("[", "]"などは対応関係を意識する必要がある)。
複数ファイル
複数のファイルが渡されていたときの挙動。これはおそらくjqなどと同様。
$ jqfpy -c 'get("data[]/category")' flask.json django.json ["2.6", "2.7", "3.2", "3.3", "3.4", "3.5", "3.6", "3.7", "3.8", "null"] ["2.6", "2.7", "3.2", "3.3", "3.4", "3.5", "3.6", "3.7", "3.8", "null"]
というところまでが復習。
loadfile()
ところで、直接JSONデータを渡すのではなくファイル名を渡したい場合がある、そして複数のファイル名を組み合わせたJSONを元に変換を書きたいことがある。そんなわけで loadfile()
が欲しくなった。
$ echo '["django.json", "flask.json"]' | jqfpy '{name: h.loadfile(name) for name in get()}' { "django.json": { "data": [ { "category": "2.6", "downloads": 1320 }, { "category": "2.7", "downloads": 762206 }, { "category": "3.2", "downloads": 24 }, { "category": "3.3", "downloads": 381 }, { "category": "3.4", "downloads": 56461 }, { "category": "3.5", "downloads": 250227 }, { "category": "3.6", "downloads": 1078975 }, { "category": "3.7", "downloads": 595086 }, { "category": "3.8", "downloads": 1764 }, { "category": "null", "downloads": 30137 } ], "package": "django", "type": "python_minor_downloads" }, "flask.json": { "data": [ { "category": "2.6", "downloads": 5287 }, { "category": "2.7", "downloads": 2274964 }, { "category": "3.2", "downloads": 23 }, { "category": "3.3", "downloads": 723 }, { "category": "3.4", "downloads": 120625 }, { "category": "3.5", "downloads": 899583 }, { "category": "3.6", "downloads": 4135489 }, { "category": "3.7", "downloads": 787076 }, { "category": "3.8", "downloads": 3190 }, { "category": "null", "downloads": 44777 } ], "package": "flask", "type": "python_minor_downloads" } }
--here
相対位置をどのように解釈するかのoptionを追加した。defaultはcurrent working directoryからの相対位置。--here
を付けると、その位置からの相対位置になる。
# ./x.jsonを見る $ jqfpy 'h.loadfile("x.json")' a/b/main.json # ./a/b/x.jsonを見る $ jqfpy --here a/b/ 'h.loadfile("x.json")' a/b/main.json
--relative-path
--relative-path
を付けると渡されたファイルからの相対位置になる。
# ./a/b/x.jsonを見る $ jqfpy --relative-path 'h.loadfile("x.json")' a/b/main.json
dumpfile()
loadfile()
と同様に dumpfile()
もある。こちらは逆に複数のファイルに分割して出力したくなることがある。このとき出力は不要なので /dev/null
にリダイレクトしたくなるかもしれない。
$ echo '["flask.json", "django.json"]' | jqfpy '[h.dumpfile(get("data[]/category", d=h.loadfile(name)), f"""{name.replace(".json", ".path")}""") for name in get()]; None' null $ ls *.path django.path flask.path
python3.8 だとさらに便利に。
pypiの特定のpython packageを利用しているversionの割合を見てみる方法について
python2.xのサポートが2020-01-01T00:00:00でおわるということで、最近は drop python2
というようなissueも増えてきています(node-gypはどうするんでしょうね..)。
そのようなissueの中で、使われているversionの情報を調べて共有しくれたりする人がだいたいどこかに居るのですが、その人たちがどのようにしてそれを調べているのか気になったので手順などを調べることにしてみました。
pypistats
一番手軽に行えるのはpypistatsを見ることかもしれません。
こちらはウェブのインターフェイスもあるようです。例えばflaskを例に調べてみると以下の様なページが見つかります。
時系列的な遷移を知りたい場合にはグラフの方がありがたいですが、直近1ヶ月のサマリー程度が見えれば十分ですよね。issueの上で議論する上では。
そういうときにはCLIでテキトーに調べるのが楽です。このpypistatsのCLIを作ってくれている人もいてこちらを使うのが手軽です。
先程のとおりにflaskを、そして直近一ヶ月という期間で調べるには以下の様なコマンドを実行すれば良いようです。formatも指定できるので更に他のコマンドで整形したい場合などはjsonで、issueなどで共有したい場合にはmarkdownで(defaultはmarkdownです)出力してあげれば良さそうです。
$ pip install pypistats $ pypistats python_minor --last-month flask | category | percent | downloads | |----------|--------:|----------:| | 3.6 | 50.00% | 4,135,489 | | 2.7 | 27.50% | 2,274,964 | | 3.5 | 10.88% | 899,583 | | 3.7 | 9.52% | 787,076 | | 3.4 | 1.46% | 120,625 | | null | 0.54% | 44,777 | | 2.6 | 0.06% | 5,287 | | 3.8 | 0.04% | 3,190 | | 3.3 | 0.01% | 723 | | 3.2 | 0.00% | 23 | | Total | | 8,271,737 |
つまりこういう形で共有できるわけです。
category | percent | downloads |
---|---|---|
3.6 | 50.00% | 4,135,489 |
2.7 | 27.50% | 2,274,964 |
3.5 | 10.88% | 899,583 |
3.7 | 9.52% | 787,076 |
3.4 | 1.46% | 120,625 |
null | 0.54% | 44,777 |
2.6 | 0.06% | 5,287 |
3.8 | 0.04% | 3,190 |
3.3 | 0.01% | 723 |
3.2 | 0.00% | 23 |
Total | 8,271,737 |
python_minor以外にも幾つかのsub commandが用意されています。
$ pypistats -h usage: pypistats [-h] [-V] {recent,overall,python_major,python_minor,system} ... positional arguments: {recent,overall,python_major,python_minor,system} ...
データの在り処
数値だけが分かってもデータの在り処がはっきりしていないとスッキリしないですよね。実はFAQに書いてあります。
https://pypistats.org/faqs#what-is-the-source-of-the-download-data
What is the source of the download data?
PyPI provides download records as a publicly available dataset on Google's BigQuery. You can access the data with a Google Cloud account here.
そんなわけでpypi自身がbigquery上にデータセットを公開してくれていたりしています。これを使っていたようです。ここです。
https://bigquery.cloud.google.com/table/the-psf:pypi.downloads
そしてデイリーでバッチを起動して集めているようです。そのあたりのこともFAQに書いてあります。
pypinfo
似たようなコマンドでpypinfoというコマンドを使っているひともいました。こちらは自分自身でbigqueryにアクセスしてデータを取ってくるようです。
ちなみにbigqueryへのアクセスの仕方がすごく丁寧にreadmeに書いてあるので親切です。
$ pip install pypinfo $ pypinfo --auth=<path/to/your_credentials.json> # or GOOGLE_APPLICATION_CREDENTIALS=<path/to/your_credentials.json> $ pypistats python_minor flask --last-month | category | percent | downloads | |----------|--------:|----------:| | 3.6 | 50.00% | 4,135,489 | | 2.7 | 27.50% | 2,274,964 | | 3.5 | 10.88% | 899,583 | | 3.7 | 9.52% | 787,076 | | 3.4 | 1.46% | 120,625 | | null | 0.54% | 44,777 | | 2.6 | 0.06% | 5,287 | | 3.8 | 0.04% | 3,190 | | 3.3 | 0.01% | 723 | | 3.2 | 0.00% | 23 | | Total | | 8,271,737 |
同じようにmarkdownで出力してくれます。
category | percent | downloads |
---|---|---|
3.6 | 50.00% | 4,135,489 |
2.7 | 27.50% | 2,274,964 |
3.5 | 10.88% | 899,583 |
3.7 | 9.52% | 787,076 |
3.4 | 1.46% | 120,625 |
null | 0.54% | 44,777 |
2.6 | 0.06% | 5,287 |
3.8 | 0.04% | 3,190 |
3.3 | 0.01% | 723 |
3.2 | 0.00% | 23 |
Total | 8,271,737 |
pypistatsの方が手軽なので日常的にはpypistatsで済ませることが多いかもしれません。そしてもう少し細かく気にしたくなったら自分でbigqueryを叩きに行くかpypinfoを使う感じになりそうです。
おまけ
ちなみにflaskとdjangoの比較
flaskの方がdjangoの4倍くらいdownloadされている?(2019-04月のデータ)
flaskとdjango
category | percent | downloads | category | percent | downloads |
---|---|---|---|---|---|
3.6 | 50.00% | 4,135,489 | 3.6 | 38.86% | 1,078,975 |
2.7 | 27.50% | 2,274,964 | 2.7 | 27.45% | 762,206 |
3.5 | 10.88% | 899,583 | 3.7 | 21.43% | 595,086 |
3.7 | 9.52% | 787,076 | 3.5 | 9.01% | 250,227 |
3.4 | 1.46% | 120,625 | 3.4 | 2.03% | 56,461 |
null | 0.54% | 44,777 | null | 1.09% | 30,137 |
2.6 | 0.06% | 5,287 | 3.8 | 0.06% | 1,764 |
3.8 | 0.04% | 3,190 | 2.6 | 0.05% | 1,320 |
3.3 | 0.01% | 723 | 3.3 | 0.01% | 381 |
3.2 | 0.00% | 23 | 3.2 | 0.00% | 24 |
Total | 8,271,737 | Total | 2,776,581 |
code
# ちょっと手抜きだけれど。。 $ pypistats python_minor --month 2019-04 -f markdown flask > flask.md $ pypistats python_minor --month 2019-04 -f markdown django > django.md $ paste flask.md django.md | sed 's/|[ \t]|/|/g' > diff.md
ちなみにautopep8とyapfとblack
今度はformatter
autopep8とyapfとblack
category | percent | downloads | category | percent | downloads | category | percent | downloads |
---|---|---|---|---|---|---|---|---|
2.7 | 45.09% | 383,360 | 3.6 | 38.69% | 165,974 | 3.6 | 56.17% | 258,994 |
3.6 | 29.36% | 249,583 | 2.7 | 32.29% | 138,492 | 3.7 | 40.72% | 187,724 |
3.7 | 14.17% | 120,421 | 3.7 | 16.93% | 72,631 | null | 2.86% | 13,187 |
3.5 | 5.88% | 49,984 | 3.5 | 7.88% | 33,783 | 3.8 | 0.14% | 659 |
3.4 | 5.05% | 42,938 | null | 3.50% | 15,021 | 2.7 | 0.06% | 255 |
null | 0.35% | 2,956 | 3.4 | 0.65% | 2,794 | 3.5 | 0.05% | 216 |
2.6 | 0.08% | 678 | 3.8 | 0.06% | 252 | 3.4 | 0.00% | 15 |
3.8 | 0.02% | 178 | 3.3 | 0.00% | 8 | 3.3 | 0.00% | 8 |
3.3 | 0.00% | 24 | 2.6 | 0.00% | 6 | 2.6 | 0.00% | 6 |
3.2 | 0.00% | 2 | Total | 428,961 | Total | 461,064 | ||
Total | 850,124 |
code
$ pypistats python_minor --month 2019-04 -f markdown autopep8 > autopep8.md $ pypistats python_minor --month 2019-04 -f markdown yapf > yapf.md $ pypistats python_minor --month 2019-04 -f markdown black > black.md $ paste autopep8.md yapf.md black.md | sed 's/|[ \t]|/|/g' > diff2.md
まじめに取り扱える便利ななにかがほしいかも(pandasを取り出す気力がなかった)