2016年のふりかえり

2016年も終わりですね。大学院には進まずに都内のWeb系企業に就職するので、学生生活もそろそろ終わりみたいです。 3月頃から渋谷新宿あたりにいると思うので、またご飯行きましょう🍺

OSS活動

f:id:nwpct1:20161231143547p:plain

Write Code Every Dayには程遠いですが、昨年に比べてコードを書く量も増えてきています。 さすがに技術的にも成長はしているみたいで、コーディングスピード(特にPython)があがったのもContributionsの数に繋がったのかな。 つくってきたOSSをふりかえる。

Kobin

趣味でWebフレームワークとドキュメンテーション、サンプルアプリ、周りのツール群を全部1人で書いていくのは思っていたよりも多くの時間が必要ですが、学んだことは思っていた以上にかなり多かったです。学生のうちに取り組んでおいてよかったことの1つですね。

github.com

まだまだ利用者が多いわけではないので、新しい構文やモジュールは気軽に導入していて、Pythonのバージョンアップで追加されたモジュールや構文を試す意欲が湧いていい。

来月の神戸Pythonの回で、Kobinのハンズオンをすることになったので、近くの方は是非参加して下さい。 自分の作ったフレームワークのハンズオンの依頼を受けるのはうれしいですね。

その他

Kobin関連以外だと、公開したソフトウェアはこのあたり。GoやPython、ElectronアプリケーションにAngular2のUIコンポーネント

ソフトウェア以外だと、PyCon JPやPyCon APAC/Koreaでの登壇やgihyo.jpでの連載ですね。 今年も英語の勉強がついつい後回しになっていてまずい。

あとはポートフォリオもAngular2で更新した。

http://c-bata.link

デザイン

自分でもロゴやWebデザインがある程度出来るといいなと思っていて、IllustratorとかSketchでロゴを書いたりしている。 悩んだときは id:denari01 に頼るようにしていて、相談すると自分では言葉にできない違和感をちゃんと日本語に落とし込んで解説してくれる。

Kobinのロゴいいよねって言ってくださる方が結構いらっしゃるんですが、これは彼が考えてくれたものです。そろそろなにか美味しいもの奢らないといけない。

Repository Design
Feedy Feedy
Comet comet
kobin-todo kobin-todo

写真

余談ですが、デザインの延長で写真に興味が湧いてきていて、旅行のときはSONYのα6000を持ち歩いてる。

高専の卒業と就職

7年間通った高専・専攻科も (卒研が無事に終われば) 今年度で卒業みたいです。長かったですね。

情報系の技術レベルに関して疑問に思うことは何度かありましたが、専攻科のクラスメートは信じられないくらい優秀で、他の高専でも id:puhitaku みたいにものづくりに対して半端ない熱量持ってる人がいるのでいい刺激になりました。 5年後・10年後には、みんなかなり活躍してる気がするので、自分もそのときには恥ずかしくないくらいには活躍したい。

卒研終わったらクラスメートとあと1回くらいは海外行こう。

2017年

技術的な興味を優先してきたのですが、ある程度形になってきたところで満足して、中途半端な完成度のまま放置しているものも多かったです。 最近は、Sphinxのメンテナをされてる方やJasper(Electron製のGithub Issues Reader)の開発をされている方のブログ記事を読んで、どういうものづくりしていきたいのかとか、モチベーションの保ち方について考えることがありました。

ちゃんと作り込むところまでモチベーションを保つには「自分が楽をできる」とか「お金になる」みたいな分かりやすい理由がもう少し必要そうです。 今は1つ実現したいアイデアがあるので、とりあえず .io ドメインを購入してKobinを使って実装しています。 4月までにはある程度形にしてプライベートベータで知り合いには試してもらえるようにしたい。

おわりに

GithubのメールアドレスやWantedlyへのスカウトメール・メッセージが、2015年ぐらいからかなり増えてきていて、知名度もあがってきたのかなと嬉しく思ってました。 うまくいかないことも結構ありましたが、こうやって文字に起こしてみるとそれなりに色々やってきたみたいです。

2017年もよろしくお願いします。

docker-composeにnginxのコンテナ追加したらRedirectでハマったのでメモ

自作のWSGIフレームワークKobinのExampleにおいて、手元でも静的ファイルをNginxで返すように変更を加えた。 ただしRedirectがうまくいかないので調査 & 雑にメモ (ところどころ英語)。

Problem

GitHub - kobinpy/kobin-example: Example application using Kobin python web-framework.

  1. ローカル(Mac)の http://127.0.0.1:8080マッピングしてdocker-compose up。
  2. Github OAuthでログイン
  3. GithubからのCallback URLは http://127.0.0.1:8080 に設定してるのでちゃんと帰ってくる
  4. RedirectResponseの設定するLocationのURLが http://127.0.0.1/ になってしまい困った。

Debugging by PDB in Docker Container

Add following options.

  server:
    :
    stdin_open: true
    tty: true
    command: /bin/bash

And attach container.

$ docker ps
$ docker attach <container id>

Insert import pdb; pdb.set_trace and run a server. But if you use slim image, there are no editors, curl and wget. So it's very difficult.

# pip install wsgicli
# wsgicli run app/__init__.py app --host 0.0.0.0 --port 80

Caution

When you just want to run the server, you can use docker-compose exec server /bin/bash. But it's not link from nginx image.

これでデバッグができるようになった。

Nginx

Nginxの設定で解決出来るものではないか調べてみた。

server {
    listen 80;

    location /static {
        alias /usr/src/public;
    }

    location / {
        proxy_pass http://server;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Port $server_port;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

ProxyまわりのHTTPヘッダをちゃんと理解する。

  • X-Forwarded-For: クライアントの IP アドレス。
  • X-Forwarded-Host: オリジナルのホスト名。クライアントが Host リクエストヘッダで渡す。
  • X-Forwarded-Server: プロキシサーバのホスト名。
  • X-Forwarded-Port : プロキシサーバのポート番号。

クライアントが要求していたページはHostヘッダで分かる。 httpsとかも使うときは、そのあたりのPortとscheme設定も気をつける必要がありそう。

(Pdb) p request.headers
{'HOST': '127.0.0.1', 'X_FORWARDED_HOST': '127.0.0.1', 'X_FORWARDED_PORT': '80', 'X_FORWARDED_FOR': '172.23.0.1', 'X_REAL_IP': '172.23.0.1', 'REFERER': 'http://127.0.0.1:8080/', ...

アプリケーション側でそこも考慮するべきなのかもしれない。 HTTP_REFERERの情報を見れば、ちゃんとリダイレクト出来そう。 Kobinの実装が悪いかもしれない。調べてみる。

HTTP Refererヘッダ

これを元にRedirectさせるべきか調べてみたけどどうやらそうではない。 このヘッダ書き換えられると困る。 DjangoやBottle、Flask(Werkzeug)もHTTP REFERERを見たりしてなかった。

Solution

とりあえずDockerのNginxコンテナを捨てる。理由は

  • KobinのRedirectResponseの実装に問題は無さそう
  • CloudFrontとかCDNでキャッシュさせれれば、そんなにアクセスこないのでwsgi-static-middlewareでも対応できる。
  • 本番でNginx使ったとしても、8080番で公開したりすることはないので、この問題にはならない。

自前のフレームワークだけあって調査がやりやすかった。

nginx実践入門 (WEB+DB PRESS plus)

nginx実践入門 (WEB+DB PRESS plus)

Core APIの概要とDjango REST Frameworkでの使い方

追記: この記事の内容はかなり古くなっています。翔泳社さんからDjangoの書籍を出版するこちらを読んで頂くのがおすすめです。


Django Advent Calendar 2016 - Qiita 18日目の記事です。

Django REST Frameworkは、DjangoでRESTfulなAPIを提供するときに非常に人気のあるフレームワークです。 既に使ってるよという方も多くいらっしゃるのではないでしょうか。

日経電子版さんも業務でも活用しているようです。

この記事では、REST Framework自体の使い方とかはあまり話しません。 REST Framnworkの作者である @_tomcriestie さんが策定・開発しているCore APIの概要と使い方について、これまでの経緯も含めて簡単に解説します。

Core APIが登場するまで

これまで、Django REST Frameworkと合わせて広く利用されていたライブラリの1つに、Django REST Swaggerというものがあります。 これはDjango REST FrameworkのSerializerやViewSetなどのクラス定義から、Swaggerの定義ファイルをこのライブラリが頑張って生成してくれていて、Swagger UIを提供してくれます。 このライブラリもまた非常によく出来ていて便利です。

Core APIの登場

そんな中、REST Frameworkの開発者であるtomcriestieさんが、Core APIの開発を始めました。 Django REST Frameworkで使われるようになったのもここ半年以内での話なので、初めて聞いたという方も多いのではないでしょうか。

これはWeb APIスキーマを定義しているもので、このCore APIからOpen API/SwaggerやJSON Hyper-Schemaなどに変換できます。 つまりこれまでは、Django REST FrameworkのViewSetsなどの情報から各ライブラリが頑張ってSwaggerの定義ファイルなどを生成していましたが、Django REST FrameworkがCore APIを生成することによって、SwaggerでもJSON Hyper-Schemaでも簡単に定義ファイルを生成できます。

当時はDjango REST FrameworkのViewSetsなどの情報からJSON Hyper-Schemaを生成するライブラリを開発していたのですが、Core APIの登場で必要なくなりました。完成する前に出てきてくれてよかったです。

Django REST FrameworkでCore APIを使う

それでは使い方を見ていきましょう。 といってもDjango REST Framework側でCore APIスキーマをほとんど自動生成してくれるため、覚えないといけないことは少ししかありません。

APIのdescriptionだけは自分で指定しないと、空文字が挿入されます。 フォーマットも決まっているのでこれに従ってください。

class CategoryViewSet(mixins.ListModelMixin,
                      mixins.RetrieveModelMixin,
                      viewsets.GenericViewSet):
    """
    retrieve:
        カテゴリの詳細を取得

    list:
        全カテゴリーの取得
    """
    queryset = Category.objects.all()
    serializer_class = CategorySerializer

こうしておくとDjango REST SwaggerのようなCore APIを使用するライブラリを使う時に、うまくDescriptionを入れてくれます。

f:id:nwpct1:20161218172232p:plain

REST Framework以外で、Core APIを使ってみたい人へ

DocumentLink など覚えないといけないCore APIの用語がいくつか存在しますが、そちらの解説は公式サイトに少しだけ書かれているので、勉強したい方はそちらを読んで下さい。

開発が活発でAPIもまたすぐに変わる可能性が高いので、特にいま使う予定が無いのであればここは読み飛ばしていただいて大丈夫です。

APIの変更に公式のserver-exampleのアップデートも追いついていない状態なので、Django REST Framework以外でcoreapiを使いたいときは気をつけてください。 coreapi (v2.0.9 現在)のSwaggerの定義ファイルを取得するサンプルは次のようになります。

import coreapi
from kobin import Kobin, Response
from coreapi import codecs
from openapi_codec import OpenAPICodec

app = Kobin()


def get_document():
    return coreapi.Document(
        url='/',
        title='Example API',
        content={
            'integer': 123,
            'dict': {'key': 'value'},
            'list': [1, 2, 3],
            'link': coreapi.Link(
                url='/',
                action='get',
                transform='inplace',
                fields=['optional', coreapi.Field('required', required=True, location='path')]
            ),
            'nested': {'child': coreapi.Link(url='/123')}
        }
    )


@app.route('/')
def swagger():
    codec = OpenAPICodec()
    content = codec.encode(get_document())
    return Response(content)

動かすときはこのようになります。 実際のユースケースでは、Acceptヘッダーの中身から適切なCore APIのcodecsを選択して返すというような形になります。

$ python3.6 -m venv venv
$ source venv/bin/activate
$ pip install coreapi kobin openapi-codec wsgicli
$ wsgicli run core_api_sample.py app
$ curl http://127.0.0.1:8000/ | jq .
{
  "swagger": "2.0",
  "info": {
    "title": "Example API",
    "version": ""
  },
  "host": "localhost:8080",
  "schemes": [
    "http"
  ],
  "paths": {
    "/": {
      "get": {
        "operationId": "link",
        "responses": {
          "200": {
            "description": ""
          }
        },
        "parameters": [
          {
            "name": "optional",
            "required": false,
            "in": "query",
            "description": "",
            "type": "string"
          },
          {
            "name": "required",
            "required": true,
            "in": "path",
            "description": "",
            "type": "string"
          }
        ]
      }
    },
    "/123": {
      "get": {
        "operationId": "child",
        "responses": {
          "200": {
            "description": ""
          }
        },
        "parameters": [],
        "tags": [
          "nested"
        ]
      }
    }
  }
}

Core APIからSwaggerの定義ファイルが取得出来ています。

Core APIまわりのツール群

Django REST Swagger以外のCore API周りのツール群は、Core APIのGithub Organizationにほぼまとまっているようです。 どんどん新しいライブラリが出てきているので、適宜チェックしておくといいかもしれません。

Codecs周り

Core APIは現在、 Core JSON (CoreAPIのJSON表現のようです)と Open API/SwaggerHALJSON Hyper-Schema への変換を正式にサポートしています。 他にも覗くと、API BluePrintなどの定義ファイルへの変換をサポートする予定があることがわかります。

Documentationまわり

またDjango REST Swaggerもかなり早い段階で、Core APIを使うようになりました (refs: Release v2.0.0)。 11月の初め頃までは使ってみると色々辛い部分がありましたが(詳しくは書きませんが)、個人的にはv3.5.3あたりでCoreAPI周りの実装がだいぶ追いついてきた印象です。

またDocumentationに関しては、Core Docsというのも最近登場しました。 まだほとんど使っている人はいないかと思いますが、Core APIスキーマを返すAPIのURLを指定すればドキュメンテーションを生成してくれるようです。

各言語のクライアント

2016/12/18現在だと、ちゃんと使えるそうなのはpython-clientのみのようです。 javascript-clientやswift-clientも作るみたいですね。

おわりに

Core API周りのツール群は、まだまだ整っているとは言えない状況ですが、Django REST Frameworkから利用する分には大体便利に使えるかと思います。 また @_tomcriestie さんの開発スピードが信じられないくらい早いので、近いうちに便利なものが出来るんじゃないかなという期待もあります。

リポジトリを見てみると、OpenAPI/SwaggerやJSON Hyper-Schema、HALなどへの変換ライブラリも @_tomcriestie さんがほぼ1人で実装を進めてて彼の実装力には本当に尊敬しかでないですね。 みなさんも是非使ってみてください。