pyconjp2016 pyftplib

23
Pythonpyftpdlibを使って FTPサーバーを作る際に 使ったテクニックの紹介 Shinya Okano PyConJP 2016

Upload: shinya-okano

Post on 24-Jan-2017

1.173 views

Category:

Technology


2 download

TRANSCRIPT

Page 1: Pyconjp2016 pyftplib

Pythonでpyftpdlibを使ってFTPサーバーを作る際に使ったテクニックの紹介

Shinya Okano

PyConJP 2016

Page 2: Pyconjp2016 pyftplib

お前、誰よ

•岡野真也

• tokibito

• Pythonを使って10年ぐらい仕事してます

• https://twitter.com/tokibito

•所属: 株式会社ビープラウド

Page 3: Pyconjp2016 pyftplib

伝えたいこと

ソフトウェアを開発する際、

闇雲にコードを書いて作っていくのではなく、

手順(工程)を意識すること

Page 4: Pyconjp2016 pyftplib

アウトライン

1. 題材としたFTPサーバーアプリケーションの紹介

2. ツール(アプリケーション)開発の流れ

3. 要件、仕様、設計を書き起こしてみよう

4. どこから書き始めるか

5. 先に外枠だけ作ってしまう(パッケージング、コマンド化)

6. 実装の仮組み

7. モジュールを分ける

8. テストコードを書く

9. 自動テストを設定する

10. 設定ファイルやログ出力の整備

11. Dockerで動かせるようにしてみる

Page 5: Pyconjp2016 pyftplib

1.題材としたFTPサーバーアプリケーションの紹介

• FTPについては詳しくは説明しません• コンピュータ間でファイルを転送するのに使うプロトコルです

• RFCいくつもあるので調べれば出てきます

• クライアントだとFFFTPなどが有名

• サーバー実装はいろいろあります

•今回のFTPサーバー• 1ユーザーのみ

• なるべく設定項目を減らす

• 既存のソフトウェアの設定が面倒なので、目的を果たす単機能なもの

• マスカレードアドレス、パッシブモードとポートは設定できること

• soloftpdという名前でPyPIに登録済み

• https://github.com/tokibito/soloftpd

Page 6: Pyconjp2016 pyftplib

2.ツール(アプリケーション)開発の流れ

1. 何をしたいのか、まとめる(企画、要件定義)

2. 要件を満たすシステムの入力や出力の詳細、データの流れやシステムの処理をまとめる(仕様策定)

3. 要件、仕様をどうやって実現するのか考えてまとめる(設計)

4. 設計に対応する実装をする、システムを構築する(実装)

5. 実装、構築して出来上がったものが、要件や仕様を満たしているか確認する(試験)

6. 完成

小さいツールなら、ある程度簡略化、省略するといいです。

参考:

http://tokibito.hatenablog.com/entry/2016/08/01/000820

Page 7: Pyconjp2016 pyftplib

3.要件、仕様、設計を書き起こしてみよう

•要件、仕様、設計の一例• https://github.com/tokibito/soloftpd/blob/master/README.rst• どんな機能が必要なのか、何を作るのか書き出してみよう• どのように使うのか、書き出してみよう

• コマンド名やコマンドラインオプション• 設定ファイル• 入力ファイル、出力ファイルの仕様など

• ちょっとしたツールなら、簡易的な記述でも十分• コードをすぐに書けそうな状態=設計ができてる

とても大切な工程です。

これができないまま進めると、完成は遠いです

何を作りたいのか、できるだけハッキリさせておきましょう。

Page 8: Pyconjp2016 pyftplib

補足. pyftpdlib

• FTPサーバーを実装するためのPythonモジュール

•認証のカスタマイズ

• ストレージのカスタマイズ

• FTPプロトコルの拡張

Page 9: Pyconjp2016 pyftplib

4.どこから書き始めるか

•設計はできました。

• では作りましょう。

• →どこから始める?

•パッケージ設定、ライセンス、README• あんまり何も考えなくても定型的な部分

• 後回しにすると面倒な部分

•最初のコミット

• https://github.com/tokibito/soloftpd/commit/80dc9419a97046e5ee6b148312859ef2c2ed1df3

Page 10: Pyconjp2016 pyftplib

5.先に外枠を作ってしまう

• まずは外枠のパッケージの部分を用意する

• setup.pyを書く• わからなければ、他のサードパーティモジュールなどの真似をすればいい

• https://packaging.python.org/distributing/

• コマンドツールであれば、console_scriptsを設定するのが便利

•誰かに見られて困るものでなければ、PyPIに登録して使えるようにしとくと楽• 自分専用のツールだったとしても登録しとけば、便利です

Page 11: Pyconjp2016 pyftplib

補足. ライセンス

• OSSを利用するのであれば、大雑把にでもライセンス体系は把握しておいたほうが良いでしょう

•自分のツールをどのライセンスで公開するのか、調べるのもいい勉強になります。

• PythonのライセンスはPSF License(Python Software Foundation License)です。• BSDスタイル、GPL互換

• https://docs.python.org/3/license.html

Page 12: Pyconjp2016 pyftplib

6.実装の仮組み

•動かしながら作れるほうが、イメージは湧きやすい• とりあえず入出力できるようにしてみる

•細かい部分を後回しにして、処理の流れを先に実装する• 関数やメソッドの呼び出しで、仮の値を返す実装

• 設定によって分岐する部分を固定値にする

• 仮実装の部分はTODOコメントを入れておく• 後でgrepなどで見つけやすいように

• FTPサーバーの例.• 設定ファイルの読み込みなどは後で実装

• 認証ハンドラを固定のユーザーで実装

• FTPサーバーを起動して、クライアントから接続できるように

Page 13: Pyconjp2016 pyftplib

7.モジュールを分ける

•仮組みができたら、細かい部分の実装をする

•機能に名前をつけられるなら、モジュール、クラスに分けておく

•機能単位で実装し、組み込んでいく

• FTPサーバーの例.• 設定ファイルを読み込むクラスを実装

• 実装したクラスを組み込み

• 認証クラスを実装

• 実装したクラスを組み込み

Page 14: Pyconjp2016 pyftplib

補足. FTPサーバーのクラス関係

Page 15: Pyconjp2016 pyftplib

補足. 関数 or クラス

•関数とクラスのどちらを使うか

• Pythonの関数とクラスの違い• 関数 = 処理

• クラス = 状態と処理

•大雑把な経験則:• 状態(入力)によって処理の分岐が発生するなら、クラスにしたほうがよい

• クラス同士を入れ子にする場合は、役割を文章で説明できる単位にする

• 処理の共通化• 仕様、設計の上で共通化していないものは、冗長でも分けておく(コピペ上等、設計直せ)

• クラスの継承は、安易に使わない• is-a関係とhas-a関係

Page 16: Pyconjp2016 pyftplib

8.テストコードを書く

• フレームワークは標準のunittestをまずは使う

• テストコードの書き方• 関数、クラス、モジュール単位でテストコードを書く

• 結合テストは、設計変更に弱いのでなるべく単体から。

•機能実装時に一緒にやれればなお良い

• テストコードが書きづらい?• 関数、クラス、モジュール設計の問題

• ほとんどの場合がこれ

• 処理をテストするために準備しないといけない状態が多すぎる

• 処理が大きすぎ

• 結合度が高すぎ

• 対象がテストしづらい場合• 非同期処理はテストしづらい。気合でやるしかない。つらい。

Page 17: Pyconjp2016 pyftplib

補足. テストフレームワーク、テストランナー

• テストフレームワーク• TestCase、TestSuiteやアサーション関数、テストフィクスチャなど、テストコードを書くための機能、枠組みを提供

• doctest、unittest、pytest、noseなどが提供

• テストランナー• テストコードを実行し、結果を表示する機能

• テストコードを自動的に見つける機能を持つものもある

• doctest、unittest、pytest、noseなどが提供

• フレームワークとランナーは、別のライブラリを使える• unittestフレームワークを使ってテストコードを書く

• pytestでテストコードを実行する

Page 18: Pyconjp2016 pyftplib

補足. テストの実行

• setup.pyのコマンドを使う方法• python setup.py test

• py.testを使う場合• py.test tests

Page 19: Pyconjp2016 pyftplib

9.自動テストを設定する

•復数のPythonバージョン、実装でテストを実行したい• toxを使おう

• https://tox.readthedocs.io/en/latest/

• https://github.com/tokibito/soloftpd/blob/master/tox.ini

•継続的インテグレーション(Continuous Integration, CI)をしたい• TravisCIやCircleCIを使おう

• Python復数バージョンでのマトリクスであればTravisCIのほうが簡単かも

• https://travis-ci.org/

• https://github.com/tokibito/soloftpd/blob/master/.travis.yml

Page 20: Pyconjp2016 pyftplib

10.設定ファイルやログ出力の整備

•設定ファイルのフォーマット• Pythonの標準モジュールだとJSONやiniファイルが使いやすい

• その他だとXML、YAMLなど。

•設定ファイルを読み込む処理をどこに持たせるか• 設定情報を扱うクラスに実装しておくとか

• 設定情報は辞書で扱うこともできるが、属性になっているほうがコードは見やすい• コード中の記号を減らせる

• 設定項目が少ないならクラス化しないのもアリ

• ログ出力は、loggingモジュールを使う• 設定ファイルから設定できるようにしておけば、作り込む必要はさほどない

Page 21: Pyconjp2016 pyftplib

補足. loggingモジュールでログを出力する

• モジュールのglobalにlogging.getLoggerでオブジェクトを用意しておいて、infoやerrorなどのメソッドを呼ぶ

• https://github.com/tokibito/soloftpd/blob/master/soloftpd/command.py#L8

Page 22: Pyconjp2016 pyftplib

11.Dockerで動かせるようにしてみる

• Dockerコンテナで動かせると便利なところ• OSのバージョン依存から脱却

• インストール手順の省略

• PyPIに登録しているならば、requirements.txtだけ作ればすぐに登録できるイメージを使える

• https://hub.docker.com/_/python/• official repository

• python:3.5-onbuild

•例:• https://github.com/tokibito/docker-soloftpd

• dockerコマンドのオプション指定が面倒な場合は、docker-composeを使うと楽

Page 23: Pyconjp2016 pyftplib

まとめ

• コードを書き始める前に• 作りたいものを決めて、要件、仕様、設計

• コードを書く• 外枠(パッケージ)

• 仮組み

• モジュール化

• テストコード

• 使いやすさ向上させる

自分にあった開発手順を持ち、

スムーズに品質良くソフトウェアを作れるようになれるといいですね。