Make組ブログ

Python、Webサービスや製品開発、ライブラリー開発についてhirokikyが書きます

DjangoCongress JP 2019を主催したよという話

DjangoCongress JP 2019というイベントを主催しました。

f:id:hirokiky:20190518151953j:plain

2018年もDjangoCongress JP 2018 を主催しましたが、今年も主催しました。 全員であわせて130人くらいが参加するイベントになりました。 「130人にもなるとやっぱ人多いなぁ」 と、集合写真を撮っているときに思いました。 スタッフのみんなお疲れ様でした。去年とメンバーはあまり変わらず、考え方や効率の良さも相変わらずでとてもやりやすかったですね。 参加者の方も積極的にツイートしたり、手伝ってくれたり、交流したり、暖かいコミュニティだなぁ、ありがたいなぁと思っていました。

映画やアニメやゲームもそうですが、大体のものというのは2がつまらなくなってしまいがちですが、今回のDjangoCongress JPは2回目も大盛り上がりでした。 そして何よりスタッフも参加者の方もみんなで楽しめたかなと思います。

djangocongress.jp

f:id:hirokiky:20190521204035p:plain

後日、当日の動画を公開しますのでお待ちください。

写真

当日の写真はこちらから

photos.app.goo.gl

パーティー

とくに今年は、去年用意できなかったオフィシャルのパーティーを用意できました。 スタッフのayakoさんが中心になって頑張ってくれました 。やっぱり発注となると金額も大きいですし、規模も読みにくいですし、タイミングも重要なのでかなりヒヤヒヤしますね。

でも当日うまくいってよかったです。

f:id:hirokiky:20190518185043j:plain

f:id:hirokiky:20190518181603j:plain

発表

今年は僕も発表しました。 英語の発表ですが、練習もしていたのでそれなりに詰まらずに話せたかなぁと思います。 今年はさらに動画撮影もありましたので、後日動画も公開されます(自分の発表を観てまた落ち込みそうですが)。

f:id:hirokiky:20190518160206j:plain

gitpitch.com

他の人の発表などについては参加者の方のレポートを読むのがオススメです。ぜひ他の人の記事も読んでね。 たくさん記事あるので後でまとめるかも。

kumappp.hatenablog.com

mizzsugar.hatenablog.com

まとめ

来年もぜひイベント開催したいのでぜひ楽しみにしていてください。 スタッフになりたい方も募集中ですので、次回のDjangoCongress JPのキックスタート会ができたらぜひ来てください(そして継続して手伝ってくれると嬉しいです)。 僕は来年はより身を引いて、周りの人にもっと任せたり、仕組みで解決していけるようにしたいなぁと思います。

雑談: YouTubeの話題を共有できなくて寂しいのでオススメのYouTuberを共有したい会

ブログを読まなくなって久しいhirokikyです。 最近はYouTubeばかり観ています。Twitterのみる頻度が減って「話題」についていけて無い寂しさを感じる今日この頃です。

そこで オススメのYouTuberを紹介して僕の友達にも観てもらおう という記事です。

そしてよく「YouTubeよく観るよ」というと「どれを観たら良いのか全くわからない」とよく返されるので僕のオススメを紹介します。 チャンネル登録数は50か60くらいです。チャンネル登録してまめにYouTubeを観始めたのはここ3,4年くらいでしょうか。

すべては紹介できないのでピックアップして紹介します。公式チャンネルとかは紹介しません。 基本的には気になった動画を観ていればYouTubeがレコメンドしてくれるので、順番も特に気にせず観たいように観ていくのが良いと思います。

よく観る、とても好き枠

東海オンエア

一番のオススメは東海オンエアです。 どの動画もクオリティ高く、東海オンエアの面白いネタを提供してくれます。 言ってしまえばバラエティです。とくに「ぷっすま」とかの雰囲気の、「テレビとしてはチープだけど面白い深夜番組」と思えば正解です。

www.youtube.com

ここ1年くらいの再生数の多い面白そうだなぁという動画を観ていれば大体かなり面白いです。 バラエティ的なやつも面白いですが、46道府県旅行やコント系の動画も結構好きです。

東海オンエアの空気感が好きになれば、東海オンエアのサブチャンネルもオススメです (「サブチャンネル」というのはYouTuberのメインではないサブ的な内容を出すチャンネルを総称してこう呼びます)。

www.youtube.com

僕が東海オンエアで一番好きなのは ミリオンエア ですが、ちょっと背景知ってないと面白くないと思います。

SUSURU TV

ラーメンYouTuberのSUSURU TVです。 編集も観やすいしレビューも分かりやすいし良いです。 ラーメン特に好きじゃなくても楽しく見れると思います。

www.youtube.com

とくにSUSURU TVはサブチャンネルがメインよりも面白いと個人的に思ってます。 3人の掛け合いが良いのでとてもオススメです。

www.youtube.com

僕は好き系

僕は大好きだけど万人にはオススメしないよ、という系のチャンネルをまとめてみました。

PDRさん

時事ネタを批判する系のYouTuberです。 昔から好きでかなり長いこと観ています。時事系が好きというよりも、PDRさんの独特の雰囲気とかが好きです。

www.youtube.com

PrimitiveTechnology

原始的な生活を実際に頑張ってやる人です。 普通にすごい。更新頻度は低いですが観ていて面白いです。 定期的に話題にはなるので知ってる人は多いかも。

www.youtube.com

kouichitv

独特のコント、ネタを出しているYouTuberです。 「何が面白いの?」と言われると難しいですが、とりあえず3分くらいの動画を観てみて好きになればかなりハマると思います。

www.youtube.com

ウィッキー

東海オンエア個チャンネル

東海オンエアのメンバーそれぞれが活動している「個別チャンネル」系です。 上から順に好きです。東海オンエアがすごく好きだなーとか、この人好きだなぁというのがあればオススメします。 とくに虫さんのラジオは仕事の友としてもとても良いです。

夕闇に誘いし漆黒の天使たち

東海オンエア系のバラエティです。 テイストは東海オンエアに近いですが、編集にも凝ってるところがあったり面白いです。 小柳の勢いが好きです。

www.youtube.com

VTuber

今話題のVTuberです。 「にじさんじ」所属のVTuberだけでも100近くいますし、「先週デビューした」とか「デビュー1ヶ月おめでとう」とか言うレベルの速度の早い世界です。 とくに僕が好きだなぁというのを紹介しますが、VTuberは本当にテイストの好みで合う人を観るのが良いかなと思います。

生配信のアーカイブなんかは、適度にグダってくれるのでラジオ代わりに仕事中に聞いています。 喫茶店で適度に雑音が聞こえて、聞くでもなく聞かないでもないくらいに話が聞こえてると集中できるみたいなやつです。

ジョー力一

にじさんじ所属でも新しいくらいですが、とてもオススメです。 男性のライバーさんですが、話のキレや突発的なネタの面白さがすごい良いです。 年代も近そうなので、話のネタがドン刺さりで良いです。芸人か小噺をする仕事をしてたんじゃないかなぁと思っています。

www.youtube.com

とりあえずこれ観て

www.youtube.com

月野美兎

にじさんじ所属のトップの美兎さんです。 まぁクセは強めですが、話は面白いし持ってくるネタも面白い。 動画中もサービス精神旺盛で楽しいです。めんどくさいサブカル女子感MAXですが、それの最終進化系なのかなと広い心で観るとハマります。

www.youtube.com

エルフのえる

にじさんじ所属のライバーさんです。 にじさんじ1期生で長めですが、最近は新興に押され気味な雰囲気です。 でも独特のゆるさとか話し方が好きなのでよく観ています。とくに観ていて不快感になることがないし、毒もないのでラジオ代わりに聞くと癒やされます。

www.youtube.com

VTuber

観てるけど毎回全部観るほどではないよーという人たち

趣味系

他、僕の趣味で観ている系です。 この辺は完全に僕の趣味なので、オススメと言うよりも「この趣味が合うなら観ると良いよ」くらいです。 熱中して観るというわけではないですが、長い間ちょいちょい観ているという感じですね。 最近だと英語の「あいうえおフォニックス」がオススメです。

お笑い系だと(サンドイッチマンの)GrapeCompanyやザコシ、ラーメンズ、ひろしもチャンネル登録してますが、今回はYouTuberに絞ってるので除外しました。

他、定番系

まぁ特別好きと言うわけでもないけど定番だし登録してたまに観てる枠。 もしくは昔観てたけど最近観ていないチャンネル。

MEGWIN、フィッシャーズ、ジョーブログとかも昔観てました最近は観ていない。

まとめ

オススメは間違いなく東海オンエアです。 バラエティとして観るなら東海オンエアだけで良いと言ってもいいです。

あと最近はザ・YouTuberよりもVTuberをよく観ています。 とくににじさんじ所属のライバーをよく観ます。 昔からニコニコのらんたんとかうるすぐ馬冶かよ とかを観てたので、完全にその延長ですね。

YouTuberは色々報道されたり「よく分からんけどなりたい職業ランキングに入ったやつ」と認識されることがSNSで多いように見えますが、僕はけっこう面白いなぁと思っています。

ぜひ観てみてください。そして僕と東海オンエアの話で盛り上がりましょう。

雑談: 約束の時間に早く行き過ぎる病

僕は待ち合わせというのがすごく苦手です。何なら、先に予定が入っていると不安になります。 楽しい飲み会でも、「1週間後にこの時間で」となると、その間の時間ソワソワしてしまって急激にテンションが下がります(予定が始まればすごく楽しい)。

それで、僕は約束の時間に早く行き過ぎる傾向があるなと最近ずっと考えています。 人に聞いてみるとそういう傾向の人は意外とたくさんいるなぁと発見しました。

今日も車のディーラーのところに朝10時に行ったんですが、9時38分には到着していました (お店が開いていなかったのでマクドで時間をつぶしました)。 今朝については僕がお客さん側なので、「出迎えるために準備しないと」という心配もないはずです。 全然ゆったり行けば良いはずなんですよね。極論ちょっと渋滞して遅れてしまっても「道が混んでてー」と言えば、5分10分遅れちゃっても死にはしないです (良くないことですが、商機を逃してしまうような心配だとか、相手が怒って帰っちゃうような心配はまずないわけですよね)。

考えてみたんですが、僕は時間をピッタリ合わせるのが苦手なのかもしれないです。 そういう意味では遅刻する人と同じ体質と言えます。感覚的に、10分で間に合うところでも30分取りたくなってしまう。 むしろ余った20分の時間をつぶすためにカフェに入ったのに、椅子に座った瞬間すぐ外に出たくなるくらいの感覚です。 面白いのが、毎日の出勤とかは焦燥感はないんですが、たまの1回のイベントとなるとすごくソワソワすることです。

ただ自分が早く行くのは自分が制御できる時間なので気が楽です。 遅刻すると相手の時間を奪ってしまうので避けたいと思っています。 結局、約束の時間っていうのはお互いに無駄になる時間を最小に下げるゲームなので、早く行き過ぎるのも遅刻も変わらないと思います(両者の時間を総合するとすれば)。 ただ気持ちの問題として自分が先に行って時間をつぶすほうが楽で良いんですよね。

同じ気持ちの人いますか? 5分くらい遅刻しても良いかな、くらいで生きていったほうがいいんでしょうか。

PythonでValueError: I/O operation on closed fileを避けるためにwith open() return せずにコンテキストマネージャーにする

Pythonでファイルを with open してファイルを読む前に return しちゃうとファイルがクローズしてしまいます。 ValueError: I/O operation on closed file エラーが発生します。

def load():
    with open(...) as f:
        return csv.reader(f)


>>> for row in load():
...     print(row)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: I/O operation on closed file.

こういうときは contextlib.contextmanager を使ってコンテキストマネージャーにすると、ファイルを全部読み込むまでちゃんとファイルが閉じられません。

import contextlib


@contextlib.contextmanager
def load():
    with open() as f:
        yield csv.reader(f)


>>> with load() as rows:
...     for row in rows:
...         print(row)

yield from を使って書くとループの途中で抜けたときにジェネレーターが生き続けるんで、ファイルがクローズされなくなります。

def load():
    with open(...) as f:
        yield from csv.reader(f)

そうするとGCがジェネレーターを消すまでファイルが開きっぱなしになります。 with load() で書けるようにしとけば、その with を抜けたときにファイルもクローズされます。

勉強になりました!

匠メソッドでステークホルダーをこぼさない発想法 〜 使う人と買う人が違うときがある?

匠メソッドのステークホルダーモデルは、価値分析モデルや要求分析ツリーと比べると簡単に見られがちに思います。 ですが、 ステークホルダーをこぼすと、価値も要求もこぼれていきます 。 今日はステークホルダーを発想していく方法を紹介します。

罠なのは、ステークホルダーを考えるとき、その製品を直接使う人だけを想像しがちなことです。 例えばPythonのオンライン学習サイトである PyQ であれば、「Pythonを学習したい人」や「チームでPythonを教えたい人」などは簡単に想像できます。

  • PyQ
    • Pythonを学習したい人
    • チームでPythonを教えたい人

ここで終わってしまうと危険です。 発想を広げていきましょう。

ステップ1: 人のまとまりから発想する

使う人というよりも「人のまとまり」で想像してみましょう。 例えば「組織」という軸で考えて、上から掘り下げて考えていきましょう。

  • 個人
    • Pythonをこれから学びたい人
    • Pythonを知っているけど使いこなせてない人
    • ...
  • 組織
    • 会社
      • 研修会社
      • Pythonをこれから使いたい会社
      • Pythonを使っている会社
    • 学校
      • ゼミで教えたい人
      • 授業で教えたい人

「まとまり」が分かればそこに属する他の人も考えられます 。 発想を広げるために「どんな組織やまとまりがあるだろう」と考えてみましょう。 まとまりが分かれば、その中にいる人を考えていきましょう(細かくは次のステップで)。

他にも組織であれば「地域社会」や「コミュニティ」など考えられます。 製品に直接関係ないものであれば無理に書き出す必要はありません。

ステップ2: 組織にいる人は使う人だけでない

会社や組織の面白いところは、「使う人」と「買う人」が別れていることがよくあるところです。 以下のステークホルダーモデルには、「決算する人」という視点が書けています。

  • Pythonを使っている会社
    • Pythonを教える先輩
    • 新しく入社した人

「教える先輩」というのはどんな人でしょうか?入社2,3年目で、次に入社する人に技術を教える先輩が想像できます。

  • 「先輩」はなぜ「教えなきゃいけない」のでしょうか?
  • 誰が製品を導入するのでしょうか?

組織というものは複雑で色んな人が連携していますので、大切なステークホルダーをこぼしがちです。 そういった 人の日々や関係を想像してステークホルダーを埋めていきましょう

  • Pythonを使っている会社
    • 人事部長
    • チームリーダー(教育担当)
    • Pythonを教える先輩
    • 新しく入社した人

「教える先輩」は複数人いそうですが、他にも「教育担当」の人が考えられます。 その人は「教える」だけでなく「新しい人を働けるように成長させよう」という責務を持っていると考えられます。 極端に別人でなくても 「そういった役割を持っている」人がいればステークホルダーとして分離しておきましょう

ステークホルダーモデルにおいて「決済権を持っている人」や「統括して面倒を見ている人」の視点は抜けがち です。 特に僕は製品や使う人への指向がかなり強いので、他にいる人のことを忘れてしまいます。

ですがそのステークホルダーを見逃さないことで、例えば「教育担当者の人は結果をレポートで集計してほしい」のような要求に気づけます。 (もちろんどこにフォーカスするのかは後の価値分析モデルや要求分析ツリーで判断すれば良いです)。

そもそもなぜ気付けるのか

匠メソッドのような方法論や発想法も大事ですが、そもそも視点に気付けるのは「自分がよく理解しているから」こそです。 「決済する人は別の人だろう」と知っていること、つまり根本的に製品に関わる人の生活や悩み、文化を理解していることが大切だと僕は思います。 ステークホルダーをこぼさないようにしよう、こう発想していこうと紹介しましたが、根本的にお客様の悩みや自分が普段感じている問題、誰が悩んでいるのかの顔を思い浮かべられるようになっておきましょう。

そのためにも大切なのは、人に接することと、課題を聞くことだと思います。 価値は課題から生まれます。課題は人が持っています。 プログラミングや製品開発と言うとすごく固いイメージもありますが、実は誰よりも人を理解している必要があるのかもしれません。

PyQはこういったプロセスを通して「どんな人が製品に関わるのか」、「その人たちの悩みや要求は何なのか」を考えて製品を作っています。 PyQはまったく完璧な製品だとは思いませんが、日々、製品に関わる人に目を向けて作るようにしています。

pyq.jp

Marketo(マルケト)のメールで変数(トークン)からリンクを作るときはスキーム(https...)をなしにすべきらしい

Marketo で動的なメールを書くときの話です。 以下の条件での話です

  • Marketoでメールを書く
  • メール内のリンクを、リードのフィールド値などトークンを使いたい
  • メール内のリンクのクリックをトラッキングしたい

このとき、変数の値を https://example.com/ とするとトラッキングが効きません(URLは正常に機能します)。 トラッキングを有効にするには、 変数の値は example.com/ としておいて、メールを書いてリンクを設定する際に https://{{ lead.URLのフィールド }} とする必要があります。

Marketoがメールのリンクをトラッキング用のリンクに置き換える仕様上、こういうワークアラウンドが必要みたいです。

nation.marketo.com

aiohttpのWebSocketクライアントの実装をソースコードリーディングしていく

こんにちは、最近 aiohttp をすごく使っています。 Web-DBな処理はDjangoで実装して、非同期処理が必要なサーバーやクライアントをaiohttpで書くという住み分けをしています (今までNode.jsを使っていたところをaiohttpで実装しています)。

平たく言うとaiohttpはかなり最高なので、今日はそのWebSocketクライアントの実装をソースコードリーディングしていきましょう。

(なぜ読むかと言うと、Dockerのexec start APIがWebSocketのようでそうでない変則的な仕様になっていて、それに対応するためにWebSocketクライアントの実装を読んでいました。 exec start wsのようなAPIを用意してくれると良いのですが)

ws_connectから読んでいこう

aiohttpClientSession にある .ws_connect() メソッドから実装を紐解いていきます。 まずはこのメソッドのドキュメントを読んでおきましょう。 https://aiohttp.readthedocs.io/en/stable/client_quickstart.html#websockets

今回は細かい仕様には焦点を当てずに、大まかな実装がどのようになっているかを見ていきましょう。 ClientSession.ws_connect の実装はここにあります (_ws_connect というメソッドに処理があるのでこれを見ていきます)。 https://github.com/aio-libs/aiohttp/blob/v3.5.4/aiohttp/client.py#L662

まずは必要になるヘッダーの処理が書かれています。 必須になる Connection: UpgradeUpgrade: Websocket などが設定されます。 https://github.com/aio-libs/aiohttp/blob/v3.5.4/aiohttp/client.py#L686-L711 ヘッダーの値はここにあります。 https://github.com/aio-libs/aiohttp/blob/v3.5.4/aiohttp/hdrs.py#L89-L90

次に、対象のURLにリクエストを送信する処理があります。 ここで Connection: Upgrade のリクエストを送って、WebSocket通信を開始します。

        # send request
        resp = await self.request(method, url,
                                  headers=real_headers,
                                  read_until_eof=False,
                                  auth=auth,
                                  proxy=proxy,
                                  proxy_auth=proxy_auth,
                                  ssl=ssl,
                                  proxy_headers=proxy_headers)

Upgradeのリクエストは read_until_eof=False オプションを指定して、レスポンスを読み切るまで待たないようにしています。

レスポンスが返ってきた後は、ステータスコード101 かや、ヘッダーがWebSocketであるかどうかを検証します。 https://github.com/aio-libs/aiohttp/blob/v3.5.4/aiohttp/client.py#L714-L721h

その後はWebSocket通信のキーの計算などの処理が入りますが、ここは割愛します。 https://github.com/aio-libs/aiohttp/blob/v3.5.4/aiohttp/client.py#L714-L721h

ここで、今後WebSocketで通信できるように先程のレスポンスからコネクションを取り出します。

            conn = resp.connection
            assert conn is not None
            proto = conn.protocol
            assert proto is not None
            transport = conn.transport
            assert transport is not None

https://github.com/aio-libs/aiohttp/blob/v3.5.4/aiohttp/client.py#L714-L721h

細かいことを抜きにしてしまえば、この transport.write() で書きこめばUpgrade後の通信を通してサーバーに書き込みができます。 ですが、書き込み時にヘッダーをつけたり、圧縮したり、読み込んだ通信結果を解釈して WSMessage クラスでラップしたり、ping/pongをやりとりしたりする処理が必要になります。

そのために readerwriter を設定します。

            reader = FlowControlDataQueue(
                proto, limit=2 ** 16, loop=self._loop)  # type: FlowControlDataQueue[WSMessage]  # noqa
            proto.set_parser(WebSocketReader(reader, max_msg_size), reader)
            writer = WebSocketWriter(
                proto, transport, use_mask=True,
                compress=compress, notakeover=notakeover)

https://github.com/aio-libs/aiohttp/blob/v3.5.4/aiohttp/client.py#L797-L802

FlowControlDataQueue は読み込んだデータを貯めておくキューで、実際に読み込んだ通信を解釈しているのは WebSocketReader というクラスです。 また、書き込みを行う WebSocketWriter も作成されています。

readerとwriterの詳細は後述します。 変則的にWebSocketのプロトコルを少し変えたい場合などはこの reader/writer を差し替えれば良いわけですが、そのフックポイントはありません

あとはこの reader と writer をまとめた ClientWebSocketResponse というインスタンスで返せば ws_connect の処理は終了になります (self._ws_response_class で返していますが、これは ClientSession のコンストラクタで受け取る ws_response_class 引数です。デフォルトで ClientWebSocketResponse です)。

            return self._ws_response_class(reader,
                                           writer,
                                           protocol,
                                           resp,
                                           timeout,
                                           autoclose,
                                           autoping,
                                           self._loop,
                                           receive_timeout=receive_timeout,
                                           heartbeat=heartbeat,
                                           compress=compress,
                                           client_notakeover=notakeover)

https://github.com/aio-libs/aiohttp/blob/v3.5.4/aiohttp/client.py#L807-L818

ClientWebSocketResponse にまとめることで、ライブラリーのユーザーからはreader/writerを分けて考えずに使えるようになっています。また、コネクションをクローズする処理などまとめられています。

aiohttpのClientWebSocketResponseの実装はこちらです。 https://github.com/aio-libs/aiohttp/blob/v3.5.4/aiohttp/client_ws.py#L28

平たく言うと receive メソッドでは reader.read() を、 send_str メソッドでは writer.send() を呼び出すためクラスです。他にも例外を処理したり、 ping/pong を勝手に処理してくれたり使いやすくするためのクラスでもあります。

Reader/Writer

WebSocketReaderの実装はここにあります。 https://github.com/aio-libs/aiohttp/blob/v3.5.4/aiohttp/http_websocket.py#L241

このreaderは feed_datafeed_eof を実装しています。 ws_connectproto.set_parser することで、このメソッドに通信されてきた文字を渡すことができます。 役割としてはWebSocketの通信 (メッセージなのか、ping/pongなのか) を解釈して、 WSMessage クラスにラップした上でキューに書き込みます。 このキューというのは ClientWebSocketResponse_reader に設定されている FlowControlDataQueue です。

WebSocketWriterの実装はここにあります。 https://github.com/aio-libs/aiohttp/blob/v3.5.4/aiohttp/http_websocket.py#L544

writerは sendcloseping, pong を実装しています。 ClientWebSocketReader_writer に設定されているので、 self._writer.send のように呼ばれます。

WebSocketWriter は書き込まれた内容を self.transport.write を使ってサーバーに送信します。 writerはメッセージの圧縮や、ヘッダーを足す処理をしてtransportに書き込みます。

まとめ

ざっと処理の流れを確認するとこのようになっています。 細かいことは割愛しましたが、ますUpgradeのリクエストを送って、そのあとはその通信をそのまま利用してWebSocketのやり取りをするというのがよく分かりました。