Stimulator

機械学習とか好きな技術話とかエンジニア的な話とかを書く

CLI上でtableを綺麗に見たくて各言語のtable表示について調べた

- はじめに -

データ分析、機械学習という仕事柄、csvやtsvを見る機会が多い。

処理する時は大体はpythonのpandasで読み込んで〜とするのだが、コンソール上で作業する時、どうしても「このcsvなんだっけ…」という事が発生する。

cat hoge.csv | head

等として上部だけ見たり、jsonならjqコマンドに流すのだが、いかんせん見栄えの問題で一瞬で判断出来なかったりする。
https://stedolan.github.io/jq/


そこで、table形式にしてコンソール上で表示して見れると嬉しいなと思って調べた事をまとめる記事。
端的に結果を最初に言うと、一般的なコンソールで使うだけならcsvkitなるCLIツールがかなり機能的で便利。tty-tableコマンドとしてjqコマンドのように扱えるtty-tableも綺麗に見れて良い。
CLIツールを今から自前で作るならGoでtablewriterpython上でも資産として利用するならpython-tabulateが便利そうという事が分かった。


追記:

ん〜!わかる!


 

- CLIにtable表示するパッケージのまとめ -

CLIツールとして有名どころではNode.js製のcli-tableがある。
また、cli-tableのAPIと互換性を持ちつつリメイクされたcli-table2や、BoxやProgressBar等の実装も含まれるpixl-cliを使う選択肢がある。

Goだとtablewriter、Pythonだとpython-tabulateが良い。

 

Node.js実装

cli-table

Node.jsで書かれたCLI向けのテーブルビジュアライザ。

github.com

npmが入ってればすぐインストールできる。

npm install cli-table

簡単に扱えて

var Table = require('cli-table');

var table = new Table({head: ['X (train)', 'Y (label)'], colWidths: [20, 20]});
table.push(['hogehoge', 'piyo1'], ['fugafuga', 'piyo2']);
console.log(table.toString());

f:id:vaaaaaanquish:20180503201742p:plain

中身でcolor.jsで色付けしてくれるので見やすい。
Horizontalにするにはlist、Verticalならdictを入れるだけ。
かなり簡易。

 

cli-table2

cli-tableの機能拡張リメイク

github.com

cli-tableのAPIを踏襲している上、セルごとに文字、色、paddingを設定できたり、行列にまたがるセルを生成できるのが強み。

npmで入れる

npm install cli-table2

基本的にはcli-tableと同じ感じで扱える。

var Table = require('cli-table2');
var table = new Table({head:['X', 'Y'], colWidths: [20, 20]});
table.push(
[{colSpan:2,content:'None'}],
['hogehoge','piyo1'],
['fuga', 'piyo2'],
['fugefuga2',{rowSpan:2,content:'piyo4\npiyo5'}],
['fugafuga3']
);
console.log(table.toString());

f:id:vaaaaaanquish:20180503205647p:plain

なんか趣向と外れてきてる気がするが面白い。

以下のAdvanced-usageに面白そうな例が沢山あるので参考に。
cli-table2/advanced-usage.md at master · jamestalmage/cli-table2 · GitHub

 

pixl-cli

上記2つとは少し違って、コマンドラインでNode.jsを作る時のユーティリティが詰まったやつ。
ユーザinputやgraphical info boxes(boxだけでなくtableも扱える)、ProgressBar等が含まれている。

github.com

boxもしくはtableを使うと良さげに表示できる

var cli = require('pixl-cli');
cli.print( cli.box("This is Example!! :D") + '\n');

f:id:vaaaaaanquish:20180503211430p:plain
良さそう。

var cli = require('pixl-cli');
var rows = [
    ['X (data)', 'Y (label)'],
    ['hoge', 'piyo1'],
    ['fuga', 'piyo2']];
cli.print(cli.table(rows)+'\n');

f:id:vaaaaaanquish:20180503211840p:plain

tableも複雑な事はできないが、list投げるだけなので所感は変わらず。

今回は割愛したがProgressBarも便利感あるし、Node.jsでCLIツール作る時はこれ使うと良さそう。

 

Ruby実装

terminal-table

ASCIIでtableをよしなに表示するやつ。

github.com

Rubyなのでgemでインストールする

gem install terminal-table

サンプルを適当に動かす。

require 'terminal-table'
rows = [['hoge', 1], ['piyo', 2], ['fuga', 'test']]
table = Terminal::Table.new :title => "Header Sample", :headings => ['X', 'Y'], :rows => rows
puts table

f:id:vaaaaaanquish:20180503213004p:plain

色も良いけど、やっぱりASCIIが可愛い。
よしなに文字幅等調整するようになってるので、tableのline変えても結構自由に動く。

でも個人的には、RubyCLIツール作る機会がほぼ無くなってしまったので使う機会があれば。

 

tty-table

rubyのTTY toolkit用に作られているtableビジュアライザ。

他のパッケージと違って、インストール時点でコマンドとして使えるのが良さ。
言語関係なくCLIで使うだけなら一番楽だと思う。

また、どの形式でtableを形成するか選べて、unicodeやASCIIが選べる。

www.npmjs.com

gemで入れる

gem install tty-table

TTY::TableやTTY::Table::Rendererが用意されているので、それぞれinitializeして表示。

require 'tty-table'
rows = [['hoge', 1], ['piyo', 2], ['fuga', 'test']]
table = TTY::Table.new rows
renderer = TTY::Table::Renderer::ASCII.new(table)
puts renderer.render

f:id:vaaaaaanquish:20180503214830p:plain

試しにASCIIで出したが良い感じ。

コマンドでも出せて、これが便利感がある。
例えば以下みたくcsvを表示してみる。

cat titanic.csv | head | tty-table

f:id:vaaaaaanquish:20180503215742p:plain
みんな大好きtitanicデータ。

コマンドだと--formatでjsonも指定できたり、--csv-delimiterで'\t'指定すればtsvをtable表示したりもできるので使い勝手がすごい。

 

Go実装

tablewriter

Goだとtablewriter一択だと勝手に思っている。

github.com

知らない間に以下も吸収されていた。
GitHub - crackcomm/go-clitable: Command line (ASCII) and Markdown table for Golang. You probably want to take a look at: https://github.com/olekukonko/tablewriter


機能面では一番使い勝手が良い。
CSVや他Separatorが扱えるだけでなく、行列内のセル、Markdown Formatまで実装されている。
Captionをつける機能もあって、多分CLIでtable作る時は一番便利に扱える。

go get  github.com/olekukonko/tablewriter

以下見たくgoファイル作ってツール化。

package main
import "os"
import "github.com/olekukonko/tablewriter"
func main() {
  data := [][]string{
    []string{"hoge", "String", "SAMPLE"},
    []string{"piyo", "Int", "10"},
    []string{"fuga", "Int", "10"},
  }
  table := tablewriter.NewWriter(os.Stdout)
  table.SetHeader([]string{"X", "TYPE", "VALUE"})
  for _, v := range data {
    table.Append(v)
  }
  table.Render()
}

f:id:vaaaaaanquish:20180503221950p:plain

inputにCSVも使える上、Docも充実。
tablewriter - GoDoc


自前でtable見るツール作るならベストだし、割りとGoでCLIツール作られている事が多くなってきたので絶対使いそう。

 

Python実装

python-prettytable

redhatのdprince氏が作っているprettytable。

github.com

READMEに気合いが入っていている。

pythonなのでpipで導入する。

pip install prettytable

READMEにはset_field_namesなるmethodがあると書いてあったが見当たらず以下のように

from prettytable import PrettyTable
x = PrettyTable()
x.field_names = ['X', 'Y', 'VALUE']
x.add_row(['hoge', 'piyo1', '1'])
x.add_row(['fuga', 'piyo2', '2'])
print(x.get_string())

f:id:vaaaaaanquish:20180503224618p:plain

prettytableの良いところとして、get_html_stringなるmethodがあり、html形式のtable取得ができるので、Webアプリ等に直接返す時にちょっと使えそう。

 

csvkit

CLIツールとして利用するなら最も機能が多い。
コマンドとして入るのですぐ使える。

github.com

pipで導入していく

pip install csvkit

コマンドとして使えるようになってるはず

csvlook titanic.csv

f:id:vaaaaaanquish:20180505103957p:plain

この他、csvgrepやcsvsort、csvjoin、csvstatといったサッとコマンドとして使いたい機能が全てまとまっている。

以下ドキュメントを見るのが最も分かりやすい。
csvkit 1.0.3 — csvkit 1.0.3 documentation

csvlook hoge.csv | head としたり、csvstat見たりすることで、やりたい事は大体実現されている気がする。
良い。

 

python-tabulate

多分多くのPythonパッケージではこれが利用されてると思う。

github.com

pipで導入していく

pip install tabulate

普通にリスト投げるだけで良いので扱いやすい点でユーザが多いのだと思う。

from tabulate import tabulate
table = [["hoge",1],["piyo",2],["fuga",3]]
headers = ["X (data)", "Y (label)"]
print(tabulate(table, headers, tablefmt="grid"))

f:id:vaaaaaanquish:20180503225745p:plain

outputの形式が充実していて、一般的なtableのgrid形式から、PHP MarkdownのpipeやEmacs org-mode、wikiLaTeX markup形式としても出せる。
また、入力としてpandasも対応している。
つよい。


 

でどれが良さそうなのよ

最初にも書いた通り、「すぐコマンドとして使いたい」ならcsvkitがベスト。
tty-tableも良い。cat csvしてtty-tableコマンドに流すだけ。

CLIツールを今から作るならtablewriterでコマンド作って流す形にするのが良さそう。
機能が一番充実しているし、Docも充実している。
Goは良いぞ。

python上でも資産として利用するならpython-tabulateが便利そう。
本当の事を言うと、コンソール上で使いたいというよりxonsh上で使いたいという気持ちが強いので、個人的にはこれを使っていく事になると思う。
python-tabulateにhtml形式のoutput実装してmergeしてもらえば今の所満足そう。

rubyのとnodeは使って見た上で保留。
pixl-cliはめっちゃ使えそうだけどCLIツール最近はGoで書いてて、Node.jsはもっぱらGCP周りで使うくらいなのでう〜ん…

他言語も保留。


 

おわりに

csv簡易table閲覧コマンド作ってxontribにしていくぞ!


 

xonshのPROMPTにdatetimeを表示する

- はじめに -

xonshで作業をしているとつい時間を忘れてしまうので、時間を表示してやるメモ。

POWERLINEで良くみるやつをxonshrcで実装。


 

- timeを表示する -

コンソール上の右側に表示するにはこんな感じ

from time import strftime
$RIGHT_PROMPT = lambda: strftime('[ %H:%M:%S ]')

f:id:vaaaaaanquish:20180316235141p:plain
こんな感じ


xonshrcを書く - Stimulatorでも書いた通り、コマンド前は$PROMPT、右は$RIGHT_PROMPT、下にバー状に出しておきたければ$BOTTOM_TOOLBARに関数を突っ込む。


上記をlambda使わずに書くと以下
文字列を返す関数をPROMPT系の変数に突っ込んでおけば、キー入力時に評価される。

from time import strftime
def get_time():
    def prompt():
        return strftime('[ %H:%M:%S ]')
    return prompt
$RIGHT_PROMPT = get_time()
$UPDATE_PROMPT_ON_KEYPRESS=True

文字色や背景色などのフォーマットは、xonshのprompt-toolkitの普通のやつと似たような感じにすれば良い。


 

- datetimeを出す -

右にあっても見なさそうだったのでPROMPTに出す。

from datetime import datetime as dt
prompt = " {INTENSE_RED}{user}{INTENSE_GREEN}@{INTENSE_BLUE}{hostname}{INTENSE_YELLOW} [ {cwd} ] {GREEN}$ "
$PROMPT = lambda: dt.now().strftime('[ %Y-%m-%d %H:%M:%S ]')  + prompt
$UPDATE_PROMPT_ON_KEYPRESS=True

キー入力ごとに更新される。
f:id:vaaaaaanquish:20180317003150g:plain


以下参考
https://github.com/xonsh/xonsh/blob/adcd20f72fcbe6962533cb3ad78a4a9ec396e150/xonsh/readline_shell.py
https://github.com/xonsh/xonsh/blob/72f3bc0d089ea91d4e5288bb1c44ebfbe81db43e/xonsh/ptk/shell.py

 

- おわりに -

xonshにはそもそもxonsh.tools内に時間表記のメソッドがあったりするが、イマイチだったのでtime、datetimeを入れてきた方が早そう。
(Tools (xonsh.tools) — xonsh 0.8.3 documentation)


リアルタイムに表示したくなって数日試したけど、あんまり良い方法がなかったのでアイデアが欲しい。
xonshではなくprompt-toolkit側を見ないとダメそうなのでちょっとしんどそう


 

Qiitaの特定記事やタグ付記事をいいね、ストックしているユーザを見るPythonスクリプト

- はじめに -

Qiitaは、プログラミングに関する知識を記録・共有するためのサービスです。

Qiitaアカウントには企業情報が紐付いている場合があり、Qiitaの様々な記事から情報を取得し分析する事で「機械学習を記事を多くストックしている勉強熱心な会社はここだ!」等といった事が分かるのではないかという思いから、Qiitaのアカウント情報を取得するスクリプトを書きました。

その時のただのメモです。


 

- Qiita API -

QiitaにはAPIが存在します。

以下のQiita記事でAPIPythonクライアントを書いている人が既に居たので、こちらを利用します。
Qiita API v2のPythonラッパー実装した - Qiita

pipで導入します。

pip install qiita_v2

以下から「個人用アクセストークン」にread権限を付けて発行します。
https://qiita.com/settings/applications

その際表示されるtokenをメモしておきます。

これで後はいいね数を取得するだけなのですが、実際にこのPythonクライアントの中を見てみると、APIに存在する「いいね数の取得」に対応していませんでした。
qiita_py/client.py at master · petitviolet/qiita_py · GitHub

仕方ないので以下のようにメソッドを追加しつつ、記事のID(stock_id)に対していいねしたユーザとストックしたユーザを取得するスクリプトを書きました。

from qiita_v2.client import QiitaClient

def list_item_likes(self, item_id, params=None, headers=None):
    return self.get("/items/{}/likes".format(item_id), params, headers)
QiitaClient.list_item_likes = list_item_likes

stock_id = ''
token = ''
client = QiitaClient(access_token=token)

# いいねしたユーザ
res = client.list_item_likes(stock_id)
like_users = [x["user"]['id'] for x in res.to_json()]
for i in range(int(int(res.result_count)/20)):
    res = client.list_item_likes(stock_id, params={"page":i+2})
    like_users += [x["user"]['id'] for x in res.to_json()]

# ストックしたユーザ
res = client.list_item_stockers(stock_id)
stock_users = [x["id"] for x in res.to_json()]
for i in range(int(int(res.result_count)/20)):
    res = client.list_item_stockers(stock_id, params={"page":i+2})
    stock_users += [x['id'] for x in res.to_json()]

APIの制限は認証している状態で1000回/時、そうでなければIPアドレスごとに60回/時です。
数記事であれば大丈夫でしょう。


 

- ユーザ情報の取得 -

list_item_likes、list_item_stockersのresponseとなるjsonには""organization""なるキーが含まれています。

しかし、Qiitaプロフィールで表示されるものには「organization」と「Organizations」が存在しており、Qiita Organizationに登録されていない会社は前者、登録されている会社は後者に含まれ、APIでは前者しか取得できないのが現状です。

データ分析のため、後者のOrganizationsを取得したい事からユーザ情報取得のスクリプトを書きました。

import requests
from bs4 import BeautifulSoup
import time

def get_accounts_org(accounts):
    data = {}
    for account in accounts:
        res = requests.get("https://qiita.com/{}".format(account))
        bs = BeautifulSoup(res.text, "lxml")
        organization = [y.attrs["content"] for y in [x.find("meta") for x in bs.findAll("li", attrs={"itemprop":"memberOf"})]]
        host = [x.text.strip() for x in bs.findAll("div", attrs={"class": "newUserPageProfile_info_body"}) if x.find("i", attrs={"class": "fa-building-o"}) is not None]
        data.update({account: {"org":organization, "host":host}})
        time.sleep(5)
    return data

like_orgs = get_accounts_org(like_users)

like_usersリストに入っているユーザ情報から「organization(host)」「Organizations(org)」を取得する事ができました。


 

- 実際に見てみる -

実際に「記事にいいねしたユーザ」について見てみました。

テストとして以下の記事を選びました。
技術共有サービスQiitaで開催されていた2017年Advent Calenderにて、いいね獲得数ランクによってIBM、Greenという会社から表彰された記事ですので、いいね数もそれ相応かと思います。
qiita.com

集計は適当にCounterで

from collections import Counter

d = Counter()
for y in [(k,v) for k,v in like_orgs.items() if v["org"] or v["host"]]:
    for x in y[1]["org"]:
        d[x]+=1
    for x in y[1]["host"]:
        if x not in y[1]["org"]:
            d[x]+=1
for x in d.most_common(10):
    print(x)

結果は以下のようになりました

('株式会社リブセンス', 21)
('株式会社ミクシィ', 5)
('フューチャーアーキテクト株式会社', 4)
('フリーランス', 4)
('スタディプラス株式会社', 4)
('株式会社オールアバウト', 3)
('株式会社モバイルファクトリー', 3)
('アイレット株式会社(cloudpack)', 3)
('IBM Japan', 3)
('株式会社ゆめみ', 3)

記事の著者もリブセンスという会社ですが、やはり同僚の記事はいいねしたくなる傾向にあるのでしょうか。
私も社内SNSに「アイツの記事バズってるよww」みたいに同僚の記事を貼る人間を見たことがあるので、そういう影響もあるでしょう。

いかんせん、データ数が少ないので何も言えませんが、取得して見るまではできました。


 

- Qiitaの特定タグのついた記事を集める -

Qiitaには、記事にタグを付ける機能があります。

特定のタグがついているAPIを叩くため、上記と同じくQiitaClientに新しくメソッドを追加して実行します。

from qiita_v2.client import QiitaClient

def get_tag_items(self, item_id, params=None, headers=None):
    return self.get("/tags/{}/items".format(item_id), params, headers)
QiitaClient.get_tag_items = get_tag_items

tag_name = '機械学習'
token = 'hoge'
client = QiitaClient(access_token=token)

res = client.get_tag_items(tag_name)


ひとまず機械学習タグのついた記事を100 * 20 件分取得してみます。
QiitaのAPI制限を考えると、一度取得した情報はcacheしておくのが正解だと思います(以下では一切やっていません)。

ids = []
users = []
for i in range(100):
    res = client.get_tag_items("機械学習", params={"page":i+1})
    ids += [x["id"] for x in res.to_json()]
    users += [x["user"]["id"] for x in res.to_json()]

上記スクリプトにより、機械学習タグのついた記事を投稿しているユーザ、投稿された記事のidリストが取得できました。


 

- 機械学習記事を投稿、いいねしたユーザ情報を見てみる -

前述したget_accounts_org関数を利用して、機械学習タグ付き記事を投稿したユーザが属する企業、機械学習タグ付き記事をいいねしたユーザが属する企業について、Countをとってみます。

以下が直近2000記事で見た「機械学習タグのついた記事」を多く投稿する社員のいる企業です。

('TIS株式会社', 6)
('株式会社トップゲート', 5)
('株式会社ブレインパッド', 4)
('Team AI', 4)
('株式会社リブセンス', 4)
('株式会社 ドワンゴ', 4)
('Nuco Inc.', 4)
('株式会社Nextremer', 4)
('株式会社アカツキ', 3)
('株式会社クラウドワークス', 3)

Qiitaで機械学習記事といえば、あのアカウントとあのアカウントとあのアカウントかな…?という一覧になりました。
データが増加してもQiita活動家が社内に居るか居ないか、に寄ってしまいそうです。

これ以下はほぼ1~2だったので面白い結果を得るまで数を増やすのは少し大変そうです。
 
 
以下は直近2000記事で見た「機械学習タグのついた記事」にいいねを多くした社員のいる企業です。

('株式会社リブセンス', 31)
('Fringe81株式会社', 24)
('株式会社クラウドワークス', 22)
('TIS株式会社', 21)
('株式会社VASILY', 16)
('株式会社リクルートライフスタイル', 12)
('Retty株式会社', 12)
('フューチャーアーキテクト株式会社', 11)
('P&D – Planning and Development – ', 11)
('株式会社アカツキ', 11)
('Shinonome, inc.', 11)
('株式会社LIFULL', 10)
('株式会社トップゲート', 10)
('株式会社オールアバウト', 10)
('株式会社ゆめみ', 9)
('フリーランス', 8)
('株式会社 ドワンゴ', 8)
('株式会社Nextremer', 8)
('株式会社ACCESS', 8)
('株式会社エイチームライフスタイル', 8)
('株式会社エイチームブライズ', 8)
('株式会社BitStar', 8)
('The University of Tokyo', 7)
('株式会社div(ディブ).', 7)
('株式会社WACUL', 6)
('株式会社Fusic', 6)
('Kyoto University', 6)
('株式会社Rosso', 6)
('株式会社サイバーエージェント', 6)
('エムスリー株式会社', 6)

リブセンスという会社はQiitaに相当な時間を使っているという事がわかりました。
こちらも上位は「あのアカウントかな…」と個人が見え隠れする結果となりました。

こちらは、東大、京大やフリーランスがトップ30に入ってきた辺りを見るに、データが集まれば少し改善しそうです。


 

- おわりに -

APIの制限数によって1日ではこの程度でした。

多くデータを集めた上で、「機械学習タグのついた記事にいいねするユーザ」の数を企業単位で集計することで、特定の技術に興味関心が強い企業が見られるかも…という結果になりました。

Qiita APIではユーザに紐付いたLinkedinやFacebookTwitterのIDも取得できるため、次回はそれらを含めた集計を出せればと思います。


 

共同通信と朝日新聞の記事URLを含むツイートを削除するPythonスクリプト

- はじめに -

この記事の起点となったのは、2018年1月25日、共同通信が配信した「山中氏、科学誌創刊に深く関与か」というタイトルの記事が、同日午後8時頃「山中所長が給与全額寄付」というタイトルの記事に書き換えられていた件である。

下記ツイートの通り、追記や編集の知らせ無しにネットメディアが大幅に修正された場合、記事公開当初と意見の辻褄が合わず、自身の発言に責任が取れなくなる場合がある。


自身が良いと共有した記事が卑猥、卑劣な記事になっている場合を防ぐため、特定のメディアの記事に言及していたツイートを削除するスクリプトについてメモしておく。


事前に必要な要件は以下の通り


 

- スクリプト -

最初に全体のスクリプト

API周りのKeyは、http://phiary.me/twitter-api-key-no-japanese から電話番号をアカウントに紐付けた後、https://apps.twitter.com/ にて取得する。

全ツイート情報を含むCSVは、Twitter公式のSettingsから、全ツイート取得の申請を出すと登録しているメールアドレスに30分程でダウンロードリンクが送られてくる。

f:id:vaaaaaanquish:20180126231631p:plain:w300

import tweepy
import urllib
import csv
import urllib.request

CONSUMER_KEY = ''
CONSUMER_SECRET = ''
ACCESS_TOKEN = ''
ACCESS_SECRET = ''
CSV_PATH = 'tweets.csv'
DOMAIN_LIST = ["this.kiji.is", "www.asahi.com"]

auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)
auth.set_access_token(ACCESS_TOKEN, ACCESS_SECRET)
api = tweepy.API(auth)


def expand(url):
    req = urllib.request.Request(url, method='HEAD')
    resp = urllib.request.urlopen(req)
    return resp.url


def expand_url(url):
    eurl = expand(url)
    while eurl != url:
        url = eurl
        eurl = expand(url)
    return eurl


def main():
    with open(CSV_PATH, 'r') as f:
        reader = csv.reader(f)
        header = next(reader)
        for i, row in enumerate(reader):
            flag = False
            for x in list(set(row[9].split(","))):
                if x != "":
                    try:
                        y = expand_url(x)
                        for domain in DOMAIN_LIST:
                            if urllib.parse.urlparse(y).netloc == domain:
                                flag = True
                    except KeyboardInterrupt:
                        raise
                    except:
                        pass
            if flag:
                api.destroy_status(row[0])

if __name__ == '__main__':
    main()

print等は適宜。

ツイートが削除されるスクリプトなので確かめながら使う。


 

- 適当な色々 -

以下は駄文である。

このスクリプトを書くにあたっての実験的な色々とか。

短縮URLの展開がurllibだけでできるようになってた

短縮URL 展開 Python」みたいに適当にググると、Python2系のhttplib.HTTPConnectionを使ってHEADメソッド投げるスクリプトが沢山でてくるのは知ってたけど、3系からurllib.request.RequestでHEADできるの知らなかった。

Python3系で短縮URLを展開するのは以下みたく

import urllib.request
root_u = "http://hogehoge"

def expand(url):
    """curl --head url"""
    req = urllib.request.Request(url, method='HEAD')
    resp = urllib.request.urlopen(req)
    return resp.url

def expand_url(url):
    """短縮URLをできるだけ展開する"""
    eurl = expand(url)
    while eurl != url:
        url = eurl
        eurl = expand(url)
    return eurl

print(root_u, expand_url(root_u))

個人的には使い所は今のところないがハッピーな気がする

 

CSVの読み込み

CSVは大体30分くらいでメールが来て、ツイート数226Kで40Mくらいのzipになってた。
前回ダウンロードした時より大分大きくなってる気がした。

試しに1000件くらいやってみたけど、割りと接続できないURLがあったので、そちらも削除した方が良いような気がした。

import urllib
import csv
with open('tweets.csv', 'r') as f:
    reader = csv.reader(f)
    header = next(reader)
    for i, row in enumerate(reader):
        # 複数URLはカンマ区切り
        for x in list(set(row[9].split(","))):
            if x != "":
                try:
                    y = expand_url(x)
                    print("base: {}\nexpanded: {}\ndomain: {}".format(x, y, urllib.parse.urlparse(y).netloc))
                except KeyboardInterrupt:
                    raise
                except:
                     print("Not Found : ", x)
        if i > 1000:
            break

headerはtweet_idとexpanded_urlsしか使ってないけど、textとか時間も考慮した方が良い気がした。

 

記事の書き換えについて

全体を通して嫌な気分になる話だったが、解決方法が見当たらず難しい問題だと思う。

日本の今の社会形態からして、記者が常に誠実かつ知識を多く習得し続けるというのは難しいだろうし、それらを補正するには専門家の意見を割く事になる。
記者側としても「スピード感持って数多くの読まれる記事を出したい」という気持ちは強いだろうし時間の制約は大きい。

その点ネットでは公開直後から意見が集まる訳なので、今回あくまで修正方法が下衆だったという話にして、今後「指摘があったので修正しました」「間違っていたので差し止めます」が気軽に言える社会になっていけば良いなと思う。

法律の範囲内であれば、間違う事自体は決して悪い事ではない。


 

- おわりに -

花金の飲み会おわりの勢いで書いた。

間違ってたら修正すればええねん。


 
追記 2018/01/27 0:14 :
ミスを指摘されたのでサイレントで修正しました。


 

自動運転シミュレータのCARLAを動かす

- はじめに -

Python APIを備えた自動車運転シミュレータである「CARLA: An Open Urban Driving Simulator」を動かすまでの記事です。

CARLAはConference on Robot Learning 2017でも発表された、Unreal Engine 4を使ったシミュレータパッケージです。
自動運転技術開発のための様々なカメラ、センサー情報をPythonで取得し、車操作に反映する事ができます。

とやかく言っても仕方ないので以下YouTubeの動画を一回参照して下さい。

www.youtube.com

本記事はCARLAの導入と触りまでを記述するものです。

特に物を作ってどうこうしてるアレではありません。

 

- CARLAについて -

CARLAは、CoRL2017発表論文のAbstにもあるように、自動運転技術の発展のためのOSSです。

作者

作者はAlexey Dosovitskiy、German Ros、Felipe Codevilla、Antonio Lopez、Vladlen Koltunらで、IntelTOYOTAの研究所、Computer Vision Centerのメンバーです。
(Toyota Research Instituteなのでシリコンバレーの方だと思います)

 

競合

競合パッケージではMicrosoftのAirSimが一番有名だと思います

CARLAと同じくUE4を利用しており、C++, Python, C# and Javaのクライアントがあります。
また、CARLA同様、カメラ画像情報だけでなく深度やSegmentationのセンサー情報を利用できます。
車だけでなくドローンも対応しており、Windowsバイナリもあるのでパパっと始められる点で優位です。

強いてCARLAを使った理由は環境(天気とか)が変えられる事くらいです。
正直buildしんどかったのでAirSimの方が良いのかなと思ったりしています。

あと他にもUdacityの教材リポジトリもありました。
GitHub - udacity/self-driving-car-sim: A self-driving car simulator built with Unity
こちらはUnityを直接触る内容のようです。
こういうのがちらほらあります。

 

CARLAパッケージ情報

Github
github.com

Document:http://carla.readthedocs.io/en/latest/

CoRL2017発表論文:http://vladlen.info/papers/carla.pdf

 

動作環境

Documentには、WindowsLinuxの項目がありますが、現在Windows版の説明部分はComing Soonとなっています。
How to build on Windows - CARLA Simulator


Linuxでは、Ubuntu 16.04かそれ以降のバージョンを推奨しています。
How to build on Linux - CARLA Simulator

他に環境構築をしていて気になった点は以下です

  • pyenvやvirtualenv環境下のPythonではPATH周りで死ぬ
    • 素直にaptでpython3-dev入れるのが現状良い
  • ストレージがそれなりに必要
    • UnrealEngine本体のbuildと素材のダウンロード
    • 50GBでやったら足りませんでした
  • sudoersアカウント欲しい
  • Unreal Engine 4がOpenGL 3.x or 4.xを使う
  • 別途Windowsが必要
    • Winからしかダウンロードできないパッケージを使う
    • .NETFrameworkも必要
    • UE4の権利関係の問題


正直注意点が多いのでクリーンでストレージも多いUbuntu 16.04が入ったハード1つ用意出来るならそれがベストです。
その中でVMWindows動かすとかが良いでしょう。


 

- CARLAインストール -

buildしていきます。
一応ドキュメントに従っていきますが、まあまあ大変です。
How to build on Linux - CARLA Simulator

 

私の環境

一応私の環境を書いておきます

MacbookPro上のVMware Fusion
Ubuntu 16.04
python3-dev
ストレージ 100GB
コア数 4
メモリ 8GB
(UE4のEditor開くだけでもmmapで1.5GBくらい確保してたのでそれなりに必要)

別途適当なWindows10端末

 

依存パッケージのインストール

CARLA依存パッケージは以下コマンドで導入
途中でrequestsが必要になったのでここでインストールしています。

sudo apt-get install clang-3.9 cmake ninja-build python-dev tzdata sed curl wget unzip autoconf libtool python3-dev libxml2-dev libxslt-dev git build-essential python3-pip python3-requests
pip3 install protobuf requests


以下を参考にUnrealEngineのbuildの依存パッケージを導入
Building On Linux - Epic Wiki

# Ubuntu 16.04
sudo apt-get install mono-mcs mono-devel mono-xbuild mono-dmcs mono-reference-assemblies-4.0 libmono-system-data-datasetextensions4.0-cil libmono-system-web-extensions4.0-cil libmono-system-management4.0-cil libmono-system-xml-linq4.0-cil cmake dos2unix clang-3.5 libfreetype6-dev libgtk-3-dev libmono-microsoft-build-tasks-v4.0-4.0-cil xdg-user-dirs


各Setupスクリプトでchmodコマンドが幾つか使われているので、自身のアカウントをsudoersに入れておく。
Ubuntuでsudoersに自身を入れるには以下

sudo gpasswd -a {アカウント名} sudo


Unreal Engineとの依存関係と互換性問題を解決するには、コンパイラC++ runtime libraryを全て統一する必要がある。
今回は素のUbuntuが用意できたのでドキュメントの通り、clang3.9とLLVMのlibc++を使用した。

sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/lib/llvm-3.9/bin/clang++ 100
sudo update-alternatives --install /usr/bin/clang clang /usr/lib/llvm-3.9/bin/clang 100

update-alternativesによってclang提供のメッセージが出ればOK

 

Unreal Engine 4のインストール

UnrealEngineをcloneしてくるが、EpicGamesのGithubリポジトリはcloseしているため、開発者として登録する必要がある。

以下URLの右上からサインアップしてGithubアカウントをProfileに記載する必要がある。
https://unrealengine.com

ここ
f:id:vaaaaaanquish:20180105214109p:plain:w400

サインアップしたら登録したメールアドレスの認証も行っておく(認証してないと後述の自動車素材がダウンロードできない)


サインアップ後、右上に人型アイコンができるのでそこから開発者用Profileページへ。
「接続済みアカウント」に行って下にスクロールしていくと、Githubアカウントを記載するフォームがあるので書く。
f:id:vaaaaaanquish:20180105214406p:plain:w400


https://github.com/EpicGamesとか適当にgithubページに行くと招待状が届いている。
invitationを表示してjoin memberする
f:id:vaaaaaanquish:20180105214423p:plain:w400
(背景が黒いのは自分が拡張しているから)


これでやっと以下スクリプトが通るようになるので、clone時にGithubアカウント認証して設定スクリプトを走らせてmakeする。

makeでは[x/N]みたいな表示が何回か出るし、素材ダウンロードもあるので適当なマシンでやると4時間くらいかかる。
最初適当に50GBくらいVMで容量設定したが、make時足りなくなって途中で止まったのでGPartedを久々に使う事になった。
ストレージは多めに見積もる必要がある。

git clone --depth=1 -b 4.17 https://github.com/EpicGames/UnrealEngine.git ~/UnrealEngine_4.17
cd ~/UnrealEngine_4.17
./Setup.sh
./GenerateProjectFiles.sh
make

 
Setup.shでは、「Success」となっていてもその下にエラーメッセージが表示されている場合がある(大抵権限周りで)。
~/.configを触る権限がないとかなのでsudoersでやり直し。

Setup.shの真のSUCCESSは以下画像参照
f:id:vaaaaaanquish:20180105215246p:plain:w400


makeする時もsudoersに入ってないアカウントで作業して、sudo等の扱い間違えると権限の関係で以下辺りで死ぬ。
chmodやchownコマンドで権限を読み書きできるようにして無理やり通しても、後続のCARLAビルドする時に死ぬので諦めてやり直すのが吉。

Refusing to run with the root privileges. (rootじゃmakeできないよー)
Access to the path "~~~~~" is denied (Access権限足りんよー)
CrushReport-linux-Shippingのレシピで失敗しました

  
権限周りを丁寧にやっていてもUE4Editorのレシピだけ失敗する場合がある。

Makefile:188: commands for object 'UE4Editor' failed
 make: *** [UE4Editor] Error 137

依存パッケージのビルド順の問題だったので以下のように1つ1つ愛のある手作業でビルドしていくと通った。

make ShaderCompileWorker
make UnrealLightmass
make UnrealPak
make UE4Editor
make CrashReportClient-Linux-Shipping RequiredTools UnrealFrontend

UnrealFrontend-Linux-Shippingじゃないの…と思ったけどUnrealFrontendっぽい。
参考:Compilation error: make: *** [UE4Editor] Error 137 - UE4 AnswerHub


もしUEのbuildをやり直したい時はmake cleanがないので以下のようにARGSを設定する
(Building On Linux - Epic WikiのEnhancing the Makefile)

make ARGS=-clean

 

CALRAのインストール

CARLAをcloneしてきてbuildする。
~/CARLA/Unreal/CarlaUE4/Contentへの追加や色々ダウンロード等が入るので、これもまた1時間ほどかかる。

(後で知ったけどCompiled versionもダウンロードできるっぽい…)
Release CARLA 0.7.0 · carla-simulator/carla · GitHub


Windows端末が必要な作業が後半にあるので、Setup中に出来る所までやっておくと吉。

ここでpyenv使ってる人はfaildやskippedが出たり、protobufやpyconfig.hが見つからないよと出る。
pyenv global systemして、UE4の関連パッケージインストールからやり直せば大丈夫。

git clone https://github.com/carla-simulator/carla ~/CARLA
cd ~/CARLA
./Setup.sh

SUCCESSとなったら設定おわり
f:id:vaaaaaanquish:20180105215841p:plain:w400

 
この後まさかのWindows端末が必要になる。
これも時間がかかるのでSetup中に出来る所まで以下やっておくと吉。

~/CARLA/Unreal/CarlaUE4/Content/配下に、追加で手動で自動車素材となるAutomotiveMaterialsを入れる作業である。
http://carla.readthedocs.io/en/latest/how_to_add_automotive_materials/

WindowsにAutomotive Materialsを追加したダミーのプロジェクトを作り、プロジェクト内にあるAutomotiveMaterialsディレクトリをLinux端末にコピーしてくる(現状ライセンスの関係で自分で入れる形になっているらしくこの方法の改善に取り組んでいるらしいが…)

 
以下URLからパッケージを0$で購入してダウンロードする訳だが、そのダウンロードがWindowsのLauncherアプリからしかダウンロードできない。
Automotive Materials by Epic Games in Epic Showcase,Materials - UE4 Marketplace

Windows端末で上記のパッケージのURLにアクセスして、右上の「ダウンロード」からEpic Games Launcher自体のインストールためのInstallerを落としてくる。

そしてInstaller起動してよしなにWindowsにインストール。
インストールできたらLauncherを起動して「次へ」をおしながら、.NETFrameworkが必要だったりするので、ウィンドウの表示に従ってポチポチしていけば良い。

起動したらLauncher内でマーケットプレイスに行けるので移動して、Automotiveで検索。
バージョンを4.17に合わせてプロジェクトを作成する(最新が4.18になってたので注意)。
f:id:vaaaaaanquish:20180105220528p:plain:w400
プロジェクト作成したらダウンロードがはじまる。

上記作業が終わったら、Windowsの「プロジェクト作成」で作ったプロジェクト配下にある{プロジェクト名}/Content/AutomotiveMaterialsディレクトリをコピーして、scp等でどうにかLinuxマシンに移動する。


Linuxに送信したら、Linux端末の~/CARLA/Unreal/CarlaUE4/Content/配下に送信してきたAutomotiveMaterialsディレクトリを丸々コピーする。
(ContentディレクトリはSetup.shによって生成される)

以下のようにPathを設定しておく

echo 'export UE4_ROOT=~/UnrealEngine_4.17' >> ~/.profile
source ~/.profile


Rebuildスクリプトを走らせて、素材ファイルのリンクと周辺のビルドをする。
OpenGL 3.x環境の場合はUE4Editorの起動時の引数に-opengl3が必要になるので、Rebuild.shの編集が必要。

Rebuild.sh 最後の方(65行目くらい)にある以下を変更
- ${UE4_ROOT}/Engine/Binaries/Linux/UE4Editor "${PWD}/CarlaUE4.uproject"
+ ${UE4_ROOT}/Engine/Binaries/Linux/UE4Editor -opengl3 "${PWD}/CarlaUE4.uproject" 
UE4_ROOT=~/UnrealEngine_4.17 ./Rebuild.sh


リンク作業のためにUE4Editorが起動する(ここでOpenGLが3以上でないと詰む)
初回起動時は初期化が入るのでそれなりの時間を要する。

f:id:vaaaaaanquish:20180105221116p:plain:w400
死んでいる様子

リンク作業は以下参照
http://carla.readthedocs.io/en/latest/how_to_add_automotive_materials/

コンテンツブラウザでContent/Static/Vehicles/GeneralMaterials/MaterialRedirectorsに行って「RedirectorInstance」を開き検索フォームで「Parent」を検索。DummyCarをM_Carpaintに名前を変えて上書き保存して終わり。
https://carla.readthedocs.io/en/latest/img/materials_screenshot_00.png
https://carla.readthedocs.io/en/latest/img/materials_screenshot_01.png

多分この作業で失敗すると人や車が表示されないので何度かやってみる。

 
以下のようにxbuild周りでエラーが出た場合は、UEのmakeをやり直すか権限設定を見直す。

ERROR: UBT ERROR: Failed to produce item
not found xbuild


 

- CARLA Serverの実行と操作 -

CARLAの操作はServerを建ててPython Clientから操作という形になる

具体的にはC lang APIを叩いており、ドキュメントにある図で言うとこんな感じ
https://carla.readthedocs.io/en/latest/img/carlaserver.svg

http://carla.readthedocs.io/en/latest/how_to_run/

ドキュメントの通りbuildに成功していれば~/CARLAディレクトリ直下に以下スクリプトができているので実行すると、UE4が起動しCarla Serverが動き始める。

./CarlaUE4.sh /Game/Maps/Town01 -carla-server -benchmark -fps=15

ない場合はbuild失敗してるか、compile済みのlatestから取ってくるか。

操作用のクライアントsampleを動かのに必要なパッケージを入れる

sudo pip3 install numpy Pillow pygame

別コンソールを開いて~/CALRA/PythonClient/内にあるsampleを動かせばひとまず操作できるようになる。

python3 ~/CALRA/PythonClient/manual_control.py

f:id:vaaaaaanquish:20180109215835p:plain:w400

manual_controlの操作は以下の通り

    W  : 進む
    S  : ブレーキ
    A  : 左
    D  : 右
    Q  : バッグ
    Space : ハンドブレーキ
    R   : restart

とりあえず楽しいです。


 

- おわりに -

ひとまず動作させるまでで終わりです。

本当は年末年始でAirSimも試し、クライアント作りまでやって設定色々書こうくらいまで思っていましたが、UE4のbuildに何回も失敗したり、Ubuntuが死んだり、CARLAが起動しても人Moduleが表示されなかったりとまあまあ辛かったのでここまでになりました。


CARLAが動けばAirSimも動くはずなのでチャレンジします。


次は自身で自動運転操作するまでの工程とか、CARLAのパラメータsettingについて書いていきたいと思います。

がんばるぞ。

 

Xonshのconfigを書く

- はじめに -

//------------------------
追記 2018/06/22:

以下の通り、config.jsonはサポートされなくなり、xonshrcになりました。

WARNING! old style configuration
(/Users/xxx/.config/xonsh/config.json)
is no longer supported. 
Please migrate to xonshrc.

xonshrcは以下にまとめました
vaaaaaanquish.hatenablog.com

//------------------------


この記事は、Xonsh Advent Calendar 2017 - Qiita 21日目の記事です。

xonshには.xonshrcに加えて、staticなjson形式のconfigファイルで設定するパラメータがある。

本記事では、config.jsonの書き方について記載する。


xonshrcについては以下
vaaaaaanquish.hatenablog.com


 

- config.jsonとは -

config.jsonはデフォルトでは ~/.config/xonsh/config.json に配置する。

より正確なPathは $XONSH_CONFIG_DIR/config.json である。

このファイルは、xonshrcより前に読み込まれ、外部シェルからのデータロードや、xonshrc内でも採用されるような重要な環境変数の設定、xontribの記載などを行うものである。

以下参考
http://xon.sh/xonshconfig.html


 

- configを書く -

現状keyとして読み込まれるのは、envxontribsforeign_shellsの3つ。

ここに書いておくと良い点も含めて下記に示す。

 

env

xonshrcファイルでも設定できるけど環境変数

静的なjsonファイルというだけなので、現状xonshとしてenv設定出来てメリットがあるのは以下くらいか。

{
    "env": {
        "XONSH_DEBUG": 1,
        "FOREIGN_ALIASES_OVERRIDE": True
     }
}

XONSH_DEBUGでデバッグ設定しておけば、xonshrc等のデバッグができる。
FOREIGN_ALIASES_OVERRIDEは、外部のShellのエイリアスを優先するかどうか。

Examples等では$EDITORとか設定してるけど、xonshrcの方が管理しやすい良い気がする。
Windowsなら$ALLUSERSPROFILEにrcファイルへのPathを書いておいてもよい。
Python系ならjupyterやpyenvへのPathなんかはここで書いても良い(気がする)。

 

xontribs

xontribsは、xonshの拡張であるxontribをlistで書いておくところ。

xonshrcでは以下のようにloadしないといけない所、パッケージ名をlistで書くだけになるのでちょっと楽。

xontrib load hoge

Sampleで自分のやつ。
名前を書いておくだけでよい。

{
    "xontribs": ["z","docker_tabcomplete","fzf-widgets"]
}

xontribは以下で書いたので参考に
vaaaaaanquish.hatenablog.com

 

foreign_shells

foreign_shellsでは、subprocessで呼び出す外部シェルの情報が記述できる。

より正確には、以下で呼び出されるforeign_shell_data()内で、subprocessとしてshellが一度起動され、該当shellのenvとaliasを読み込んでくる。
xonsh/foreign_shells.py at adcd20f72fcbe6962533cb3ad78a4a9ec396e150 · xonsh/xonsh · GitHub

 
list形式でdictを設定していく。dict中のkeyとしては以下が設定できる。
http://xon.sh/xonshconfig.html#foreign-shells

{
    "foreign_shells":[
        {
            # 起動したいshell
            "shell": "/bin/zsh",
            # 対話起動するか(しなくてもenv読み込めるshellなら不要)
            "interactive": True,
            # ユーザとしてlogin必要か
            "login": False,
            # 起動shellにおけるenvコマンド
            "envcmd": "env",
            # 起動shellにおけるaliasコマンド
            "aliascmd": "alias",
            # 起動するshellの引数(str)
            "extra_args": [],
            # 手動で起動shellにenv設定するためにdict
            "currenv": {},
            # 例外を安全に処理するか(Falseにするとraiseされる)
            "safe": True,
            # 色々読み込む前後にshellで実行させるコマンド
            "prevcmd": "",
            "postcmd": "",
            # 呼び出すshellのrcみたいなもん(以下参考に)
            # https://github.com/xonsh/xonsh/blob/adcd20f72fcbe6962533cb3ad78a4a9ec396e150/xonsh/foreign_shells.py#L33:title
            "funcscmd": "",
            # 呼び出したshellでsourceするファイル
            "sourcer": "",
            # 該当shellでスクリプトを実行する時のコマンド
            "runcmd": "",
            # 該当shellでexit-on-errorを設定するためのコマンド
            # "set -e"や"if errorlevel 1 exit 1"
            "seterrprevcmd": "",
            "seterrpostcmd": ""
        }
    ]
}

foreign_shellsで既に設定しているshell(xonshに乗り換える前に使っていた色々)をそのまま流用できたりする。


 

おわりに

あんまり書く機会がないかも知れないが、configについて記述した。

env、xontribs、foreign_shellsしか今の所書く事はないがxonshrcがちょっと簡略化される所がメリットか。


ひとまずアドベントカレンダー埋めた感じです。

qiita.com


 

Pythonモジュールの遅延import

- はじめに -

この記事は、Xonsh Advent Calendar 2017 - Qiita 18日目の記事です。

Pythonのmoduleのloadを実際に利用する前に遅延してやろうというTipsです。

加えて、xonshのxonshrcに記載する事で、xonshの起動も早くしようという話を書いています。


アジェンダ

 

- 遅延importを実現する -

遅延させて、使いたい時にpythonスクリプトをロードしたりするには大抵importlibを使います。

そこでimportlibでxonshの起動爆速化を狙おうとしていた所、同僚に「lazyasdっていうのがあるよ」と言われて調べたら大体それで出来たので、これで良いやという感じでした。

以下には一応どちらも記述しています。

 

importlibで動的ロード -

Pythonでモジュールをスクリプト上で動的にロードするにはimportlibを使います。

31.5. importlib — The implementation of import — Python 3.6.6rc1 documentation

import importlib

os = importlib.import_module("os")
print(os.listdir("."))

machineryを使えばimportのタイミングをhookできます。
https://docs.python.org/3.6/library/importlib.html#module-importlib.machinery

遅延してロードさせる場合は、Python3.5にて追加されたimportlib.util.LazyLoaderを使うと良いです。
31.5. importlib — The implementation of import — Python 3.6.6rc1 documentation


参考:Python: モジュールを動的にロードする - CUBE SUGAR CONTAINER


 

lazyasdを使った遅延import

同僚が「xonsh使ってるならxonsh.lazyasdの中にLazyObjectやBackgroundModuleLoaderがあるのでそれ使うと良いよ」と教えてくれました。

Lazy & Self-destructive Objects (xonsh.lazyasd) — xonsh 0.6.7 documentation


その後、調べてみるとlazyasdだけパッケージとして切り離されているようです。

GitHub - xonsh/lazyasd: Lazy & self-destructive tools for speeding up module imports

pipでinstallできるのでやります

pip install lazyasd


lazyasdで一番簡易にモジュール使う時ロードを実現できるのはデコレータを付けることです。

import importlib
from lazyasd import lazyobject

@lazyobject
def os():
    return importlib.import_module('os')

# importが発生するのはココ
print(os.listdir("."))

実際にモジュールを利用する時にimportする事が割りと簡単にできました。

 
compile済みの正規表現も遅延させて読み込めます。

import re
from lazyasd import lazyobject

@lazyobject
def hoge_re():
    return re.compile('hoge')

print(hoge_re.search("hoge piyo str") is not None)

 
より良い方法として、別スレッドで読み込むload_module_in_backgroundもあります。

from lazyasd import load_module_in_background

os = load_module_in_background('os')
print(os.listdir("."))

引数のreplacementsも便利です。

 
関数を引数に取るためlambda使ったりとちょっと面倒ですが、LazyObjectLazyDictといったClassも用意されています。

from lazyasd import LazyObject
from lazyasd import LazyDict
import re

# 最初にosを使う時にglobalにosをimportする
os = LazyObject(lambda: importlib.import_module('os'), globals(), 'os')
print(os.listdir("."))

# 一回目に使う時に正規表現をcompileする
RES = LazyDict({
        'dot': lambda: re.compile('.'),
        'all': lambda: re.compile('.*'),
        'two': lambda: re.compile('..'),
        }, globals(), 'RES')
print(RES["dot"].search("hogehoge.text") is not None)

「使うか分からないので、もし使うなら最初にロードしたいな」という時に使えます。


多分以下を見るかソースコードを見ると良いです。
Lazy & Self-destructive Objects (xonsh.lazyasd) — xonsh 0.6.7 documentation


 

- xonshrcに書いていく -

ここからはxonshrcに書いておいてxonsh起動までを爆速にしてやろうという話です。

xonshであればpip installする必要もなく、xonsh.lazyasdを利用できます。

使うか分からないがimportを忘れがちなやつを全部こうしてやります

from xonsh.lazyasd import lazyobject

@lazyobject
def os():
    return importlib.import_module('os')

 
全部listに突っ込んでおけば、execしてくれるSampleです。
import asのようにしたい場合は、dictか何かにしてformat第一引数xをよしなにやれば良いです

from xonsh.lazyasd import lazyobject
import importlib

# list版
lazy_module_list = ["requests", "numpy", "pandas", "matplotlib",...]
for x in lazy_module_list:
    t = "@lazyobject\ndef {}():\n    return importlib.import_module('{}')".format(x, x)
    exec(t)

# dictならこんな感じか
lazy_module_dict = {
    'requests': 'requests',
    'sys': 'sys',
    'random': 'random',
    'shutil': 'shutil',
    'pd': 'pandas',
    'np': 'numpy',
    'plt': 'matplotlib.pyplot',
    'Path': 'pathlib.Path',
        }
for k,v in lazy_module_dict.items():
    t = "@lazyobject\ndef {}():\n    return importlib.import_module('{}')".format(k, v)
    exec(t)

これをxonshrcに書いて優勝です


いやいや、絶対コンソール使ってたら使うでしょという物はbackgroundでimportしてやりましょう。

from lazyasd import load_module_in_background

background_module_list = ["os", "sys", "random", "shutil", "linecache",...]
for x in background_module_list:
    exec("{}=load_module_in_background('{}')".format(x, x))


参考:Lazy & Self-destructive Objects (xonsh.lazyasd) — xonsh 0.6.7 documentation


 

- おわりに -

lazyasd便利なので、環境に合わせてロードするやつとかも拡張として書いていきたい所。

importlibのLoader周りはあんまりExamplesもなくて厳しいですが、まあなんとか。


Xonshアドベントカレンダーの方もよろしくお願いします。

Xonsh Advent Calendar 2017 - Qiita