この記事は Robot Framework Advent Calendar 2017 - Qiita の14日目の記事です。
今回はRobot Frameworkを使ってFTPサーバと通信してみます。
目次
環境
- Python 3.6.3
- Robot Framework 3.0.2
- FtpLibrary 1.4
- 後述の通り、現時点のリリース版ではPython3系に対応していないため、GitHubからインストール
- FTPサーバ
- pyftpdlib 1.5.3
FTPサーバの用意
pyftpdlibによる実装
今回は、Python製FTPサーバである pyftpdlib
を使います。
giampaolo/pyftpdlib: Extremely fast and scalable Python FTP server library
GitHubにあるREADMEに従い、実装します。
FTPサーバの設定は
- FTPサーバのルートディレクトリ
- ポートは21ではなく、別のポート
- Mac等の場合、使用するポートが21の場合、
Permission denied
となる - Why do I get socket.error "Permission denied" error on ftpd starting? - pyftpdlib/faqs.rst at master · giampaolo/pyftpdlib
- Mac等の場合、使用するポートが21の場合、
とします。
import pyftpdlib.authorizers import pyftpdlib.handlers import pyftpdlib.servers import pathlib import shutil def run_server(): # FTPサーバのルートディレクトリを作る # __file__のパスを絶対パスに変換し、ルートディレクトリのパスも追加する ftp_server_root_path = pathlib.Path(__file__).resolve().parent.joinpath('serv') # ディレクトリがある場合は、一度削除しておく if ftp_server_root_path.exists(): # pathlibのrmdir()ではファイルがあると削除できないので、shutilを使う shutil.rmtree(ftp_server_root_path.as_posix()) # ディレクトリを作って、パーミッションは777にしておく ftp_server_root_path.mkdir() ftp_server_root_path.chmod(0o777) print(ftp_server_root_path) # 認証ユーザーを作る authorizer = pyftpdlib.authorizers.DummyAuthorizer() authorizer.add_user('user', 'password', ftp_server_root_path.as_posix(), perm='elradfmw') # 個々の接続を管理するハンドラを作る handler = pyftpdlib.handlers.FTPHandler handler.authorizer = authorizer # FTPサーバーを立ち上げる # Unix系だと、21番ポートはsuper userがbindできるポートなので、それ以外のポートにする # https://github.com/giampaolo/pyftpdlib/blob/master/docs/faqs.rst#why-do-i-get-socket-error-permission-denied-error-on-ftpd-starting server = pyftpdlib.servers.FTPServer(("127.0.0.1", 12345), handler) server.serve_forever() if __name__ == '__main__': run_server()
ちなみに、ファイルパス操作は pathlib
モジュールを使いました。Python3.4から使えます。
# os.pathを使う場合 import os path_by_os = os.path.abspath(os.path.dirname(__file__)) # pathlibを使う場合 import pathlib path_by_pathlib = pathlib.Path(__file__).resolve().parent
となります。個人的には、pathlibの方が見やすいと感じました。
pyftpdlibの起動
上記のスクリプトを ftp_server.py
として保存し、起動します。
$ python ftp_server.py /path/to/ftp_library_sample/serv [I 2017-12-14 05:37:03] >>> starting FTP server on 127.0.0.1:12345, pid=34184 <<< [I 2017-12-14 05:37:03] concurrency model: async [I 2017-12-14 05:37:03] masquerade (NAT) address: None [I 2017-12-14 05:37:03] passive ports: None
Robot FrameworkのFTPLibraryを使う
インストール
Robot FrameworkでFTPサーバと通信するためには、 Robot-Framework-FTP-Library
を使います。
https://github.com/kowalpy/Robot-Framework-FTP-Library
このライブラリはpipでインストールできます。
ただ、2017/12/14現在、PyPIにあるものはPython3系に対応していません*1。
そのため、GitHubからインストールします。
Please support python3+ · Issue #10 · kowalpy/Robot-Framework-FTP-Library
$ pip install git+https://github.com/kowalpy/Robot-Framework-FTP-Library
Robot Frameworkのテストケースを実装
ドキュメントに従って実装します。
https://kowalpy.github.io/Robot-Framework-FTP-Library/FtpLibrary.html
今回は、
を実装してみます。
*** Settings *** Library FtpLibrary *** Variables *** ${USER} user ${PASSWORD} password *** TestCases *** FTPサーバに接続する ftp connect 127.0.0.1 ${USER} ${PASSWORD} 12345 Mkd foo # Welcome Messageを表示 ${welcome} = Get Welcome # 改行して見やすくする Log To Console ${\n} ${welcome} # カレントディレクトリを表示 ${path} = Dir Log To Console ${\n} ${path} FTP CLOSE FTPサーバにファイルをアップロードする ftp connect 127.0.0.1 ${USER} ${PASSWORD} 12345 Mkd upload Cwd upload Upload File result_google_python.png uploaded.png FTP CLOSE FTPサーバからファイルをダウンロードする ftp connect 127.0.0.1 ${USER} ${PASSWORD} 12345 # uploadディレクトリとupload.pngは前のテストで作成済なので、それを利用する Cwd upload Download File uploaded.png downloaded.png FTP CLOSE
テストの実行
実行前のディレクトリの様子です。
テストを実行します。問題なく終了しました。
$ robot test_ftp.robot ========================= Test Ftp ========================= FTPサーバに接続する ... 220 pyftpdlib 1.5.3 ready. .. ['drwxr-xr-x 2 you group 68 Dec 14 06:00 foo'] FTPサーバに接続する | PASS | ------------------------- FTPサーバにファイルをアップロードする | PASS | ------------------------- FTPサーバからファイルをダウンロードする | PASS | ------------------------- Test Ftp | PASS | 3 critical tests, 3 passed, 0 failed 3 tests total, 3 passed, 0 failed =========================
実行後のディレクトリの様子です。
アップロードとダウンロードがうまくいっているようです。
FTPサーバ側にもログが残っていました。
[I 2017-12-14 06:00:00] >>> starting FTP server on 127.0.0.1:12345, pid=34184 <<< [I 2017-12-14 06:00:00] concurrency model: async [I 2017-12-14 06:00:00] masquerade (NAT) address: None [I 2017-12-14 06:00:00] passive ports: None [I 2017-12-14 06:01:00] 127.0.0.1:55030-[] FTP session opened (connect) [I 2017-12-14 06:01:00] 127.0.0.1:55030-[user] USER 'user' logged in. [I 2017-12-14 06:01:00] 127.0.0.1:55030-[user] MKD /path/to/ftp_library_sample/serv/foo 257 [I 2017-12-14 06:01:00] 127.0.0.1:55030-[user] FTP session closed (disconnect). [I 2017-12-14 06:01:00] 127.0.0.1:55033-[] FTP session opened (connect) [I 2017-12-14 06:01:00] 127.0.0.1:55033-[user] USER 'user' logged in. [I 2017-12-14 06:01:00] 127.0.0.1:55033-[user] MKD /path/to/ftp_library_sample/serv/upload 257 [I 2017-12-14 06:01:00] 127.0.0.1:55033-[user] CWD /path/to/ftp_library_sample/serv/upload 250 [I 2017-12-14 06:01:00] 127.0.0.1:55033-[user] STOR /path/to/ftp_library_sample/serv/upload/uploaded.png completed=1 bytes=213754 seconds=0.014 [I 2017-12-14 06:01:00] 127.0.0.1:55033-[user] FTP session closed (disconnect). [I 2017-12-14 06:01:00] 127.0.0.1:55036-[] FTP session opened (connect) [I 2017-12-14 06:01:00] 127.0.0.1:55036-[user] USER 'user' logged in. [I 2017-12-14 06:01:00] 127.0.0.1:55036-[user] CWD /path/to/ftp_library_sample/serv/upload 250 [I 2017-12-14 06:01:00] 127.0.0.1:55036-[user] RETR /path/to/ftp_library_sample/serv/upload/uploaded.png completed=1 bytes=213754 seconds=0.004 [I 2017-12-14 06:01:00] 127.0.0.1:55036-[user] FTP session closed (disconnect).
ソースコード
GitHubに上げました。ftp_library_sample
ディレクトリの中が今回のファイルです。
thinkAmi-sandbox/RobotFramework-sample: Robot Framewrok samples