Heroku CLI を使わずに、BottleアプリをHerokuへデプロイする

通常、Heroku CLI (以前のHeroku Toolbelt)を使ってHerokuにデプロイしています。
Heroku CLI | Heroku Dev Center

ただ、Heroku CLIを使わずにデプロイする方法があるのか気になったため、その方法を調べた時のメモです。

目次

 

環境

 

Bottleアプリまわりの作業

Bottleのインストール
$ pip install bottle

 

Bottleアプリの作成

Hello, world的なものを作成します。

 

Bottleアプリ本体

app.py

import os
from bottle import get, run

@get('/')
def index():
    return 'Hello, heroku'

if __name__ == "__main__":
    run(host="0.0.0.0", port=int(os.environ.get("PORT", 5000)))

 
起動して動作確認します。

$ python app.py 

 

requirements.txtの作成
$ pip freeze > requirements.txt

 

Procfileの作成
web: python app.py

 

runtime.txtの作成
python-3.6.2

 
なお、サポートされているバージョンは現時点では以下の2つでした。

参考:Specifying a Python Runtime | Heroku Dev Center

 
そのため、

python-3.5.3

のようなサポート外のバージョンを指定すると、push時に

remote: -----> Python app detected
remote: -----> Installing python-3.5.3
remote: -----> Installing pip
remote: -----> Installing requirements with pip
remote:        /app/tmp/buildpacks/779a8bbfbbe7e1b715476c0b23fc63a2103b3e4131eda558669aba8fb5e6e05682419376144189b29beb5dee6d7626b4d3385edb0954bffea6c67d8cf622fd51/bin/steps/pip-install: line 7: /app/.heroku/python/bin/pip: No such file or directory
remote:  !     Push rejected, failed to compile Python app.
remote: 
remote:  !     Push failed

とエラーになりました。

 

Herokuの準備

アプリの作成

Personal appより作成します。

 

公開鍵の登録

ssh-keygen -t rsaで公開鍵を作成します。
Managing Your SSH Keys | Heroku Dev Center

 
作成した鍵は、HerokuのAccount settingsの SSH Keys に登録しておきます。

 

接続の確認

sshでHerokuにアクセスできるか確認します。

状況に応じて、~/.ssh/id_rsa_herokuとしてあるプライベート鍵ファイルのパスを変更します。

$ ssh -i ~/.ssh/id_rsa_heroku -v git@heroku.com
...
debug1: Authentications that can continue: publickey
debug1: Next authentication method: publickey
debug1: Offering RSA public key: ~/.ssh/id_rsa_heroku
debug1: Server accepts key: pkalg ssh-rsa blen 535
debug1: Authentication succeeded (publickey).
Authenticated to heroku.com ([50.19.85.156]:22).
debug1: channel 0: new [client-session]
debug1: Entering interactive session.
debug1: Sending environment.
debug1: Sending env LANG = ja_JP.UTF-8
PTY allocation request failed on channel 0
shell request failed on channel 0

と表示されればOKです。

 
なお、接続に失敗した場合は

$ ssh -i ~/.ssh/id_rsa_heroku -v git@heroku.com
...
debug1: Authentications that can continue: publickey
debug1: Next authentication method: publickey
debug1: Offering RSA public key: ~/.ssh/id_rsa_heroku
debug1: Authentications that can continue: publickey
debug1: No more authentication methods to try.
Permission denied (publickey).

となります。

その時は、ここを見ると解決するかもしれません。
Fixing problems with keys | Managing Your SSH Keys | Heroku Dev Center

 

Herokuへのデプロイ

コミット
$ git add .
$ git commit -m "first commit"

 

push

add remoteします。

Heroku_Git_URLは、アプリのSettingsページにあるHeroku Git URLの値を設定します。

$ git remote add heroku https://git.heroku.com/<Heroku_Git_URL>

 
あとはpushしてデプロイします。

$ git push heroku master

 
なお、git - Can a Rails app be deployed without using Heroku Toolbelt? If so, how? - Stack Overflowでは

git remote add heroku git@heroku.com:falling-wind-1624.git

となっていましたが、その方法だと

$ git push heroku master
Permission denied (publickey).
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

となり、デプロイに失敗します。

 

デプロイ後の確認

動作確認

ターミナルに

https://<app_name>.herokuapp.com/ deployed to Heroku

と記載されているURLにアクセスし、動作を確認します。

 

ログ確認

Heroku CLIを使っていないため、heroku logsが使えません。

そのため、アドオンでの確認となります。手元ではPapertrailを使いました。

ただ、無料プランであっても、Papertrailを追加するにはクレジットカード認証が必要でした。

 

参考資料

libsass-pythonを使って、IntelliJ + File WathersプラグインでSassをコンパイルする

IntelliJで書いているPythonアプリにて、Sassを使おうと考えました。

Sassのサイトを見たところ、Rubyが必要そうでした。
Sass: Install Sass

他の方法がないかを探したところ、LibSassがありました。

LibSass周りを調べると、libsass-pythonを使うことで、PythonでもSassをコンパイルできそうでした。
libsass-python: Sass/SCSS for Python — libsass 0.13.2 documentation

 
次に、特定のSassファイルだけをコンパイルする方法を調べたところ、IntelliJFile Watchersプラグインを使えば良さそうでした。
IntelliJ IDEAで特定のscssだけcssにコンパイルする - Qiita

 
あとはIntelliJlibsass-pythonを使う方法が分かればよいのですが、IntelliJのヘルプにはLibSassを使ったコンパイル方法は記載されていませんでした。
Compiling Sass, Less, and SCSS to CSS - Help | IntelliJ IDEA

 
そこで、libsass-pythonを使って、IntelliJ + FileWatherプラグインでSassコンパイルを試してみました。

 
目次

 

環境

 

libsass-pythonのインストー

pipでインストールします。

# venv環境を作り、activate
$ python -m venv env36
$ source env36/bin/activate

# libsassをインストール
(env36) $ pip install libsass
...
Successfully installed libsass-0.13.2 six-1.10.0

 

プロジェクトの用意

IntelliJでプロジェクトルートを開く

File > Open より、プロジェクトルートを開きます。

プロジェクトルートはこんな感じです。

$ ls -al
total 0
drwxr-xr-x   6 you  staff   204  7 31 12:35 .idea
drwxr-xr-x   7 you  staff   238  7 31 12:34 env36

 

Python SDKを追加
  • File > Project Structure
  • Project SDKNewボタン > Python SDK > Add Local
  • /path/to/project_root/env36/bin/python を選択

 

File Watchers プラグインの確認

IntelliJ IDEA > Preferences > Plugins より、File Watchersが有効になっていることを確認します。

 

Sassファイルを用意

IntelliJ IDEAで特定のscssだけcssにコンパイルする - Qiita同様の構成とします。

$ tree -L 2 -a
.
├── .idea
├── css
├── env36
└── scss
    ├── exclude.scss
    └── style.scss

 

File Watchersプラグインを設定

Preferencesの Tools > File Watchers > 「+」ボタン > SCSS にて、File Watchersプラグイン設定を起動し、以下の設定を行います。

 

Scopeの設定
  • ...ボタン > +ボタン > Local > 任意の名前
  • 中央のディレクトリ構造で、先ほど作成したscss/style.scss を選択し、 Include ボタン
  • Pattern欄に、 file:scss/style.scss が設定されたことを確認

 

Programの設定

...ボタンを押して、venv環境のPythonを指定します。

/path/to/project_root/env36/bin/python

 

Argumentsの設定

今回はlibsass-pythonのsasscを使ってSassをコンパイルします。

そのため、Argumentsにはコマンドラインからsasscを使う時の設定を記載します。
sassc — SassC compliant command line interface — libsass 0.13.2 documentation

今回は

  • -tオプションで、expandedフォーマットで出力
  • -mオプションで、mapファイルを出力
  • コンパイル後のファイルの拡張子をcssにして、別のディレクトリへと出力

とするため、Argumentsには

-m sassc -t expanded -m $FileName$ ../css/$FileNameWithoutExtension$.css

を指定します。

 
あとはOKボタンを押してPreferencesを閉じます。

 

Sassファイルのコンパイル

以下の2ファイルを用意して保存します。

style.scss

#content {
  background-color: blue;
  p {
    color: red;
  }
}

exclude.scss

p{
  color: green;
}

 
すると、File Watchersプラグインが動作し、Sassがコンパイルされます。

$ tree -L 2 -a
.
├── .idea
├── css
│   ├── style.css    << 増えた
│   └── style.css.map    << 増えた
├── env36
├── scss
│   ├── exclude.scss
│   └── style.scss
├── style.css

 
expandedなフォーマットでコンパイルされました。

style.css

#content {
  background-color: blue;
}

#content p {
  color: red;
}

/*# sourceMappingURL=style.css.map */

 
style.css.map

{
    "version": 3,
    "file": "style.css",
    "sources": [
        "../scss/style.scss"
    ],
    "names": [],
    "mappings": "AAAA,AAAA,QAAQ,CAAC;EACP,gBAAgB,EAAE,IAAI;CAIvB;;AALD,AAEE,QAFM,CAEN,CAAC,CAAC;EACA,KAAK,EAAE,GAAG;CACX"
}

Robot Framework + Selenium2Library + ImageMagickで、スクリーンショットの差分を確認する

Robot Frameworkを使ってWebサイトの検証をする中で、Webサイトの修正による差異はどこなのかを知りたいことがありました。

何か方法がないかを調べると、ImageMagickを使うのが良さそうでした。

 
そこで、Robot FrameworkからImageMagickを呼び出して、Robot Frameworkで撮影したスクリーンショットの差分を確認して見ることにしました。

 
2017/9/2追記

Selenium2Libraryですが、バージョン3からは SeleniumLibrary へと名称が変更されています。詳しくはこちらに書きました。
RobotFrameworkのSelenium2Libraryの名前が、SeleniumLibraryへと変更されてた - メモ的な思考的な

なお、本文はSelenium2Libraryのままにしてあります。

2017/9/2追記ここまで

 
目次

 

環境

  • Mac OS X 10.11.6
  • Python 3.6.1
  • Google Chrome 60.0.3112.78 (stable)
  • ChromeDriver 2.31 (stable)
  • RobotFramework 3.0.2
  • Selenium2Library 1.8.1dev1
    • GitHubから現在の最新版をインストール
    • PyPIにある1.8.0では、Python3系だとエラーが出てインストールできない
  • ImageMagick 7.0.6-3

 

準備

Selenium2Library のインストール

PyPIにあるバージョン1.8.0のSelenium2LibraryではPython3系では動作しないため、GitHubよりインストールします。

$ pip install git+https://github.com/robotframework/Selenium2Library

 

ImageMagickのインストール

Homebrewでインストールします。

$ brew install imagemagick
...
🍺  /usr/local/Cellar/imagemagick/7.0.6-3: 1,522 files, 22.7MB

 

ChromeDriverのインストール

Chrome v60ではスクリーンショットまわりの不具合があるため、最新版のv60をインストールします。

Chromeに合わせ、ChromeDriverも最新の2.31をHomebrewでインストールします。

# インストールする場合
$ brew install chromedriver

# アップグレードする場合
$ brew upgrade chromedriver

 

実装

今回は以下の仕様で実装します。

  • ImageMagickサイトにアクセス
  • スクリーンショットを撮る
    • 差分があれば、差分画像を表示
    • 差分がなければ、コンソールに「差分はありません」と表示

 

画像の差分比較

Robot FrameworkからImageMagickを実行して差分の比較結果を取得するには、OperatingSystemライブラリのrun and return rc and outputキーワードを使います。
Run And Return Rc And Output | OperatingSystem

また、画像の差分を比較し、どれだけ差分があるかはImageMagickcompareとオプションの-metric AEを使います。
MiniMagick を使って 2 枚の画像が同一かどうか判定する - Qiita

# ${rc}と${output}の間には、スペース2つ
# run and return rc and outputキーワードの後はスペース2つ、compare以降の実行時オプション部分はスペース1つずつ
${rc}  ${output} =  run and return rc and output  compare -metric AE before1.png before2.png diff1.png

 

画像ファイルを開く

RobotFrameworkのRunキーワードと、Macターミナルのopenコマンドを組み合わせます。
Macターミナルのopenコマンドの使用例 - Qiita

# ${output}には、差分がある場合は0よりも大きい値がセットされている
run keyword if  ${output} != 0  run  open diff2.png

 

ソースコード全体

imagemagick.robot

*** Settings ***

Library  Selenium2Library
Library  OperatingSystem


*** Keywords ***
スクリーンショットを撮って差分を確認する
    # headlessなChromeで確認する
    ${options} =  evaluate  sys.modules['selenium.webdriver'].ChromeOptions()  sys
    call method  ${options}  add_argument  --headless
    create webdriver  Chrome  chrome_options=${options}

    # ImageMagickのトップページへ
    go to  https://www.imagemagick.org/script/index.php

    # リロード前にスクリーンショットを撮る
    capture page screenshot  filename=before1.png

    # もう一度、リロード前のスクリーンショットを撮る
    capture page screenshot  filename=before2.png

    # ページをリロードする:たぶん広告が変わるはず
    reload page

    # リロード後のスクリーンショットを撮る
    capture page screenshot  filename=after.png

    # ブラウザを閉じる
    close browser

    # ImageMagickを使って、差分があるかを確認する
    ${rc}  ${output} =  run and return rc and output  compare -metric AE before1.png before2.png diff1.png

    # リロードする前なので、差分はないはず
    run keyword if  ${output} == 0  log to console  差分はありません

    ${rc}  ${output} =  run and return rc and output  compare -metric AE before1.png after.png diff2.png

    # リロード後は差分があるはずなので、差分を表示する
    run keyword if  ${output} != 0  run  open diff2.png


*** TestCases ***

ImageMagickサイトでのテスト
    スクリーンショットを撮って差分を確認する

 

実行

robotコマンドで実行してみます。

$ robot imagemagick.robot 
==============================================================================
Imagemagick                                                                   
==============================================================================
ImageMagickサイトでのテスト                                           差分はありません
ImageMagickサイトでのテスト                                           | PASS |
...

成功したようです。

また、画面に以下のような画像が表示されました。

f:id:thinkAmi:20170727221723p:plain

 

ソースコード

GitHubに上げました。imagemagick_sampleディレクトリ以下が今回のコードです。
thinkAmi-sandbox/RobotFramework-sample

Robot FrameworkでHeadless Chromeを使ってみた

Robot FrameworkにはSelenium2Libraryという、Seleniumと連携するためのライブラリがあります。

 
サポートされているブラウザは、ChromeFirefox・PhantomJSなど、ひと通り揃っています。
http://robotframework.org/Selenium2Library/Selenium2Library.html#Open%20Browser

 
そんな中、

を目にしました。

 
そのため、Robot FrameworkでHeadless Chromeが使えるのかを試してみたくなりました。

Python + SeleniumでHeadless Chromeを使う記事がありましたので、今回はこれを元に書いてみようと思います。
SeleniumからHeadless Chromeを使ってみた - Qiita

 
2017/9/2追記

Selenium2Libraryですが、バージョン3からは SeleniumLibrary へと名称が変更されています。詳しくはこちらに書きました。
RobotFrameworkのSelenium2Libraryの名前が、SeleniumLibraryへと変更されてた - メモ的な思考的な

なお、本文はSelenium2Libraryのままにしてあります。

2017/9/2追記ここまで

 
目次

 

環境

  • Mac OS X 10.11.6
  • Python 3.6.1
  • Google Chrome 59.0.3071.115 (stable)
  • chromedriver 2.30 (stable)
  • RobotFramework 3.0.2
  • Selenium2Library 1.8.1
    • pipでインストール可能
    • pip install robotframework-selenium2library

 

ソースコード

上記のQiita記事をRobotFramework用に書き直しただけなので、ソースコードだけ記載します。

なお、stable Chrome 59を使っているせいか、スクリーンショットは1x1の画像のままです。Qiita記事によるとChrome 61では解消されているようです。

また、GitHubにも上げてあります。selenium2_chrome_headless_sampleディレクトリ以下が今回のコードです。
thinkAmi-sandbox/RobotFramework-sample

 
2017/8/9 追記ここから

Chrome 60 + chromedriver 2.31 の場合、スクリーンショットが正しく撮影されるようになっています。

2017/8/9 追記おわり

 
selenium_google.robot

*** Settings ***

# Libraryは大文字小文字の区別があるようで、libraryとしてしまうとIDEが認識しない
Library  Selenium2Library


*** Keywords ***
GoogleでPythonを検索してスクリーンショットを撮り、結果を出力する
    # 以下のコードをRobot Framework風にした
    # http://qiita.com/orangain/items/db4594113c04e8801aad

    # 以下を参考に、Chromeのオプションを追加して、Chromeを起動する
    # https://groups.google.com/d/msg/robotframework-users/gPsiVaMo19A/cBRH7mr2BAAJ
    ${options} =  evaluate  sys.modules['selenium.webdriver'].ChromeOptions()  sys
    call method  ${options}  add_argument  --headless
    create webdriver  Chrome  chrome_options=${options}

    # Googleのトップ画面を開く
    go to  https://www.google.co.jp/

    # タイトルにGoogleが含まれていることを確認する
    ${page_title} =  get title
    should contain  ${page_title}  Google

    # 検索後を入力して送信する
    input text  name=q  Python
    # Robot FrameworkではEnterキーは\\13になる
    # https://github.com/robotframework/Selenium2Library/issues/4
    press key  name=q  \\13

    # Ajax遷移のため、適当に2秒待つ
    sleep  2sec

    # タイトルにPythonが含まれていることを確認する
    ${result_title} =  get title
    should contain  ${result_title}  Python

    # スクリーンショットを撮る
    # stableなChrome59だと、1x1の画像しか撮れない...
    capture page screenshot  filename=result_google_python.png

    # ログを見やすくするために改行を入れる
    log to console  ${SPACE}

    # 検索結果を表示する
    # ForでElementを回したかったことから、WebElementを取得し、そのAPIを利用する
    # http://robotframework.org/Selenium2Library/Selenium2Library.html#Get%20Webelements
    @{web_elements} =  get webelements  css=h3 > a
    :for  ${web_element}  in  @{web_elements}
    \  ${text} =  get text  ${web_element}
    \  log to console  ${text}
    # 以下を参考に、WebElementからattribute(href)を取得
    # https://groups.google.com/d/msg/robotframework-users/xx3KYxpDu_w/0hyulqKPKQAJ
    \  ${href} =  call method  ${web_element}  get_attribute  href
    \  log to console  ${href}

    # ブラウザを終了する
    close browser


*** TestCases ***

GoogleでPythonを検索するテスト
    GoogleでPythonを検索してスクリーンショットを撮り、結果を出力する

 

実行結果

コンソールには以下が表示されました。HeadlessのChromeでも動作するようでした。

$ robot selenium_google.robot 
==============================================================================
Selenium Google                                                               
==============================================================================
GoogleでPythonを検索するテスト                                         
Python - ウィキペディア
https://ja.wikipedia.org/wiki/Python
Pythonとは - python.jp
https://www.python.jp/about/
Python チュートリアル — Python 3.6.1 ドキュメント
https://docs.python.jp/3/tutorial/index.html
Python基礎講座(1 Pythonとは) - Qiita
http://qiita.com/Usek/items/ff4d87745dfc5d9b85a4
【入門者必見】Pythonとは?言語の特徴やシェア、仕事市場を徹底解説 | 侍 ...
http://www.sejuku.net/blog/7720
Python入門
http://www.tohoho-web.com/python/
Pythonに咬まれるな : 注意すべきセキュリティリスクのリスト | プログラミング ...
http://postd.cc/a-bite-of-python/
Download Python | Python.org
https://www.python.org/downloads/
初心者でもほぼ無料でPythonを勉強できるコンテンツ10選 - paiza開発日誌
http://paiza.hatenablog.com/entry/2015/04/09/%E5%88%9D%E5%BF%83%E8%80%85%E3%81%A7%E3%82%82%E3%81%BB%E3%81%BC%E7%84%A1%E6%96%99%E3%81%A7Python%E3%82%92%E5%8B%89%E5%BC%B7%E3%81%A7%E3%81%8D%E3%82%8B%E3%82%B3%E3%83%B3%E3%83%86%E3%83%B3%E3%83%8410
Pythonとは - はてなキーワード - はてなダイアリー
http://d.hatena.ne.jp/keyword/Python
GoogleでPythonを検索するテスト                                        | PASS |
------------------------------------------------------------------------------
Selenium Google                                                       | PASS |
1 critical test, 1 passed, 0 failed
1 test total, 1 passed, 0 failed

受け入れテストのフレームワークRobot Frameworkで、組込機能であるBuiltin Libraryを使ってみた

Python製の受け入れテストのフレームワークとして、Robot Frameworkがあります。

 
当初、ドキュメントが充実しているのでテストコードが書きやすいかなと思いました。

ただ、いざ書いてみるようとすると「こういう時はどう書けばよいのか」と悩みました。

そこで今回、Robot Frameworkの組込機能であるBuiltin Libraryを使ってみた時のメモを残します。

 
目次

 

環境

  • Python 3.6.1
  • Robot Framework 3.0.2
    • pipでインストール可能
    • pip install robotframework
  • テストコードを含むファイル:builtin_lib_test.robot
    • Robot Frameworkの対象ファイルは、拡張子がrobotのもの

 

コンソール出力まわり

コンソールにHello, world!を出力する

log to consoleキーワードを使い、コンソールへと出力します。

*** Settings ***
Documentation  ビルトインの機能を使ったテスト

*** TestCases ***
コンソールを使ったテスト
    # Hello, world!
    log to console  Hello, world!

ポイントは

  • log to consoleキーワードに値を渡す場合、半角スペース2つ + 出力したい文字列
    • Robot Frameworkでは、半角スペース2個をキーワードの区切りとして認識するため
  • キーワードに渡す文字列は、クォートで囲む必要なし
    • クォートで囲むと、そのクォートまでコンソールに出力
  • コメントはPython同様、文頭に#を書く

です。

 
robotコマンドを使って実行したところ、Hello, world!が出力されました。

$ robot builtin_library_samples/builtin_lib_test.robot 
==============================================================================
Builtin Lib Test :: ビルトインの機能を使ったテスト                            
==============================================================================
コンソールを使ったテスト                                              Hello, world!

なお、昔からあるコマンド

  • pybot
  • jybot
  • ipybot

などはdeprecated & 将来的には削除されると、GitHubのREADMEに記載がありました。
robotframework/robotframework: Generic test automation framework.

 

日本語をコンソールへ出力する

日本語を出力したい場合も、そのまま書けば良いです。

log to console  こんにちは!
# => こんにちは!

 

文字列まわり

文字列の中にスペースを2つ以上使う

Hello, world!では、文字列の中にスペースが1つ含まれていました。

ただ、2つ以上のスペースを入れると、文字列の中のスペースではなくキーワードの区切りと認識されてしまいます。

 
そのため、2つ以上のスペースを入れたい時は、組込定数${SPACE}を使います。

# スペースが2個以上必要な場合は、組込変数${SPACE}を利用する
log to console  スペースが${SPACE}${SPACE}${SPACE}3個必要
# => スペースが   3個必要

# 拡張変数表記も使える
log to console  スペースが${SPACE * 3}3個必要
# => スペースが   3個必要

 

文字列を結合する

Catenateを使います。

なお、デフォルトでは文字間にスペースが入ります。

文字間にスペースが不要な場合は、SEPARATOR=という書き方をします。

# 文字間スペースあり
${スペースあり} =  Catenate  スペース  あり
log to console  ${スペースあり}
# => スペース あり

# 文字間スペース無し
${スペースなし} =  Catenate  SEPARATOR=  スペース  無し
log to console  ${スペースなし}
# => スペース無し

 

文字列に該当の文字が含まれているか確認する

Should Containを使います。

Should Contain 対象の文字列 検索する文字列 の順に指定します。

# Pythonという文字列の中に、thという文字列が含まれるか
Should Contain  Python  th

 
なお、

もあります。

 

キーワードまわり

キーワードを自作する

今までは*** TestCases ***の下に直接log to consoleなどを記述しました。しかし、それだと他のテストケースでは再利用できません。

そのため、キーワード(いわゆる関数)の自作を行うため、

*** Keywords ***
コンソールに文字を出力する
    log to console  Hello, world!

と、*** Keywords ***に定義します。

*** TestCases ***では

*** TestCases ***
コンソールを使ったテスト
    コンソールに文字を出力する

のように、自作キーワードを利用します。

 
なお、キーワードには半角スペース1個を含めることもできます。

スペース があるキーワード を実行する
    log to console  スペースのあるキーワードです

 

キーワードの仮引数を定義する
[Arguments]を使う

[Arguments]で、キーワードの仮引数を定義できます。

なお、[Arguments]は一行で書く必要があります。キーワード内に[Arguments]が複数あると、used multiple timesエラーが発生します。

引数を受け取ってメッセージを出力する1
    [Arguments]  ${foo}  ${bar}
    log to console  ${foo}
    log to console  ${bar}

   
上記のキーワードは

*** TestCases ***
コンソールを使ったテスト
    引数を受け取ってメッセージを出力する1  foo=フー1  bar=バー1

と使うことができます。

実行すると、

フー1
バー1

となります。

 

継続行を使って[Arguments]を複数行表記にする

[Arguments]は一行で書く必要があるため、引数が多くある場合は見づらくなってしまいます。

そのため、...を使って継続行の表記もできます。
python 2.7 - If ElseIf in Robot Framework - Stack Overflow

 
上記の例の継続行表記は、

引数を受け取ってメッセージを出力する2
    [Arguments]  ${foo}
    ...          ${bar}
    log to console  ${foo}
    log to console  ${bar}

です。

なお、three-dotの後にはスペースを2つ以上入れないと、正しく認識されません。

 

キーワードの中に仮引数を埋め込む

キーワードの中に仮引数を埋め込むこともできます。

キーワードを埋め込んでコンソールに${メッセージ}を出力する
    log to console  ${メッセージ}

 
使う側は

キーワードを埋め込んでコンソールに分かりにくいハローを出力する
#=> 分かりにくいハロー

となります。

これを使う場合は分かりづらくなるため、「」などで囲んで引数であることを明示したほうがいいかもしれません。

 

キーワードの戻り値を定義する
[Return]を使う

[Return]で戻り値を定義できます。

Returnで戻り値を取得する
    [Return]  戻り値1

 
使う側は

${foo4} =  Returnで戻り値を取得する

となります。

 

Return From Keywordを使う

Return From Keyword[Return]と同じです。

ReturnFromKeywordで戻り値を取得する
    Return From Keyword  戻り値2

 

Return From Keyword Ifを使う

条件によって戻り値を返す場合は、Return From KeywordIfを使います。

ReturnFromKeywordIfで戻り値を取得する
    Return From Keyword If  ${true}  戻り値3

 

変数まわり

変数に値を設定する

set variableキーワードで値を設定します。

=の左側はスペース1つ、右側はスペース2つになります。

また、キーワードと引数を区別するため、set variableと実際に設定する値の間はスペースが2つ必要です。

${variable} =  set variable  こんばんは!
log to console  ${variable}
# => こんばんは!

 

条件付きで変数に値を設定する

set variable ifキーワードを使います。

以下の書き方は常に「条件を満たさない値」がコンソールに出力されます。

${condition_value} =  set variable if  False  空データ  条件を満たさない値
log to console  ${condition_value}
# => 条件を満たさない値

 

リスト変数を用意する

変数のタイプとして、${}の他に、

などがあります。

 
今回はリスト変数なので、@{FRUIT}のように、@を使って定義します。

今回は、*** Variables ***に記載して、グローバルな変数として使ってみます。

*** Variables ***
@{FRUIT}  りんご  みかん

*** Keywords ***
コンソールに配列を出力する
    log to console  ${\n}---$を出力---
    log to console  ${FRUIT}
    log to console  ---@を出力---
    log to console  @{FRUIT}
    log to console  @{FRUIT}[0]
    log to console  @{FRUIT}[1]

結果です。

---$を出力---
['りんご', 'みかん']
---@を出力---
りんご
りんご
みかん

 
また、set variableでも配列を変数に設定できます。

@{medals} =  set variable  金  銀  銅
log to console  ${medals}
# => ['金', '銀', '銅']

 

Ifまわり

条件に応じてキーワードを実行する(クォート版)

Run Keyword Ifを使います。

ポイントは

です。

キーワードの定義は以下となります。

Ifを使って処理を切り替えてログを出力する
    [Arguments]  ${引数}
    Run Keyword If  '${引数}' == '${EMPTY}'  log to console  空っぽです1
    ...  ELSE IF  '${引数}' == '${false}'  log to console  Falseです1
    ...  ELSE  log to console  ${引数}

使う側は

# 準備として、空の値を取得する
${empty_value} =  ReturnFromKeywordで空の戻り値を取得する

# 実際にキーワードを使うところ
Ifを使って処理を切り替えてログを出力する  引数=${empty_value}
Ifを使って処理を切り替えてログを出力する  引数=${false}
Ifを使って処理を切り替えてログを出力する  引数=値が入ってます1

です。

なお、キーワードで[Arguments]引数という文字列を定義しているため、キーワードを使う側も文字列引数を指定して値を渡しています。

 

条件に応じてキーワードを実行する(評価ネームスペース版)

もう一つの書き方として、評価ネームスペースを使う方法があります。

Robot Framework 2.9以降の場合、

Robot Framework 2.9 からは、変数自体を評価ネームスペース (evaluation namespace) の中で使えるようになりました。 ネームスペース中の変数は、波括弧のない $variable のような特殊な変数の書き方で表わせます。 この書き方にした変数はクオートする必要はなく、テストファイル中で一旦文字列に置き換えられたりもしません。

http://robotframework-ja.readthedocs.io/ja/latest/lib/BuiltIn.html#evaluating-expressions

とのことです。

試してみたところ、

  • ${EMPTY} ではうまく動作しない(クォートする必要がある)
    • Variable '$EMPTY' not found. エラー
  • 引数や${false}などは評価ネームスペースを使ってもうまく動作する

でした。

そのため、書き方は

Ifを使って処理を切り替えてログを出力する(評価ネームスペース版)
    [Arguments]  ${引数}
    Run Keyword If  $引数 == '${EMPTY}'  log to console  空っぽです2
    ...  ELSE IF  $引数 == $false  log to console  Falseです2
    ...  ELSE  log to console  ${引数}

となります。

キーワードを使う側は変わりません。

# 準備として、空の値を取得する
${empty_value} =  ReturnFromKeywordで空の戻り値を取得する

# 実際にキーワードを使うところ
Ifを使って処理を切り替えてログを出力する(評価ネームスペース版)  引数=${empty_value}
Ifを使って処理を切り替えてログを出力する(評価ネームスペース版)  引数=${false}
Ifを使って処理を切り替えてログを出力する(評価ネームスペース版)  引数=値が入ってます2

 

Forループまわり

Forループにはいくつか方法があります。
Robot Framework User Guide

今回は IN RANGE を使う方法と、listを使う方法を記載します。

IN RANGEを使ったForループ

Pythonのfor in rangeと似たようなものです。

ループブロックに関する注意点は

  • \ (バックスラッシュ)で示す
  • バックスラッシュの後ろには、スペースを2つ以上入れる

です。

Forループを使う(InRange版)
    :FOR  ${i}  IN RANGE  1  4
    \   log to console  ${i}回目

 

listを使ったForループ

今回はCreate Listキーワードを使ってリストを作成し、そのリストをループしてみます。

使い方は以下です。

@{items} =  create list  1  4  6
:FOR  ${v}  IN  @{items}
\   log to console  リストの値は${v}

 

Forループを抜ける

Exit For Loop If を使います。

:FOR  ${v}  IN  one  two  three
\   exit for loop if  $v == 'two'
\   log to console  ${v}

 

キーワードの実行まわり

今回は

  • Fail
  • Run Keyword And Return Status
  • Run Keyword And Ignore Error

について記載します。

Run Keyword系には、その他にも色々とありますので、公式ドキュメントを読んでみると良いかもしれません。

 

強制的にキーワードを失敗させる

Failを使います。失敗した時のメッセージを引数として渡せます。

Fail  常に失敗する

 

キーワードの成否を確認する

Run Keyword And Return Statusを使うと、失敗する可能性のあるキーワードの実行に対する成否判定を行うことができます。

そのため、「想定していた失敗であれば、テストが落ちないようにしたい」などの用途に使えます。

${成否} =  Run Keyword And Return Status  常に失敗するキーワードを実行する
log to console  ${成否}
#=> False

 

キーワードが失敗しても無視する

Run Keyword And Ignore Errorを使うと、

  • キーワードが失敗しても、テストを継続できる
  • キーワードが失敗した時のエラーメッセージを取得できる

を行えます。

用途しては、「キーワード失敗時のメッセージにより処理を分けたい」などでしょうか。

${エラーメッセージ} =  Run Keyword And Ignore Error  常に失敗するキーワードを実行する
log to console  ${エラーメッセージ}
#=> ('FAIL', '常に失敗する')

 

Pythonコードを実行する

Evaluate を使います。

 
以下では、Pythonコードを使ってゼロ埋めをしています。
python - Robot Framework string padding - Stack Overflow

${結果} =  Evaluate  '%06d %s' % (1, 'ゼロ埋めです')
log to console  ${結果}

 

条件を満たしていない場合、テストをパスする

Pass Execution Ifを使います。

Pass Execution If  ${true}  テストをパスしました!

なお、常にパスしたい時は Pass Execution を使います。

 

現在のテストまわりの情報を取得する

組込変数が用意されています。

例えば、

*** TestCases ***
コンソールを使ったテスト
    現在のテスト名を取得する

というテストケースの場合、

*** Keywords ***
現在のテスト名を取得する
    log to console  ${TEST NAME}
    #=> コンソールを使ったテスト

    log to console  ${SUITE NAME}
    #=> Robotframework Sample.Builtin Library Samples.Builtin Lib Test

という結果になります。

 

ソースコード

GitHubに上げました。builtin_library_samples/builtin_lib_test.robotファイルが、今回作成したファイルです。
thinkAmi-sandbox/RobotFramework-sample

#pycamp #glnagano #nseg Python Boot Camp in 長野にTAとして参加しました

6/10にギークラボ長野で開催された「Python Boot Camp in 長野」にTAとして参加しました。

 
日々Pythonをさわっているのですが、使っても使っても新たな発見があり、楽しく過ごしています。

そんな中、「Python Boot Camp in 長野」が企画され、講師として寺田さんがいらっしゃるということを聞きました。

せっかくの機会なので参加したいと相談したところ、TA(講師アシスタント)枠での参加をすすめられたため、TAとして参加しました。

TAは初めての経験であり、うまくできるか不安でした。ただ、事前にいろいろと資料が配布されて説明していただけるなど、TAに対するサポートがありました。そのため、当日どんなふうに動けばよいかのイメージをつかむことができ、不安がやわらぎました。

 
当日の講義中は、寺田さんのお話を聞きつつ、参加者の間を動いてTAのお仕事をしていました。

特に、「WindowsPythonをインストールしたはずなのにPythonの対話モードが起動しない」と詰まっていたのを解決できた時はホッとしました。

ただ、うまくいかないと相談されたときには対応できたものの、参加者がうまくいっていないのか考え込んでいるのかを見分けることが難しく、このあたりは自分の課題だと感じました。

 
また、休憩中には、Python 3 エンジニア認定基礎試験の出題範囲である「Pythonチュートリアル 第3版」に、寺田さんのサインをいただきました。試験を受ける時のお守りにしたいと思います。
基礎試験 | 一般社団法人Pythonエンジニア育成推進協会

 
その後の懇親会でもPythonに関するお話をうかがうことができ、いろいろとためになりました。

 
最後になりましたが、「Python Boot Camp in 長野」を企画・運営・参加されたみなさま、ありがとうございました。

 

WindowsでインストールしたはずなのにPythonの対話モードが起動しない時

参考までに対応した時の行動をメモしておきます。

  • WindowsPythonをダウンロード・インストールしたものの、Pythonの対話モードが起動しないという相談を受ける
  • コマンドプロンプトからset PATHを入力し、Windows環境変数PATHpython.exeが登録されているか確認
    • 登録されていない
  • ダウンロードしたPythonインストーラーを確認
    • amd64という記載があったため、Pythonは64bit版でインストールされることを確認
  • よく使われるC:\Program FilesC:\Program Files (x86)Pythonがいるか確認
  • PyCharmがインストールされていたため、設定されていたinterpreterを確認
    • %USERPROFILE%\AppData\Local\Programs\Python\Python36が指定
  • 参加者に環境変数に上記パスを追加していただいた
    • GUIから操作する時のメモ
      • Windows10のGUIの場合、Win + Xキーでシステムを選択 > システムの詳細設定 > 環境変数ボタン
      • AppDataは隠しフォルダのため、コントロールパネルのエクスプローラーのオプションから、隠しフォルダを一時的に表示する
  • 再度、Pythonの対話モードを試したところ、無事に起動

 
原因としては、Pythonインストーラーのデフォルトが

  • Install for all usersにチェックが入っていないため、%USERPROFILE%\AppData\Local\Programs\Python\Python36にインストールされる
  • Add Python to environment variablesにチェックが入っていないため、環境変数PATHに追加されない

であり、インストール時にInstall Nowを選択してインストールしたためかなと思いました*1

f:id:thinkAmi:20170611082231p:plain

(自分のWindowsはPython3.6が導入済みのため、Python3.5.3のインストール画面のスクリーンショットになりますが、内容は同じです)

*1:自分はいつもCustomize installationを選択してデフォルトから変更していたため、現場では気づかなかったものの記事を書いている時に気づきました

Docker + Alpine3.5 + Apache2.4 + Python3.xで、mod_pythonをソースコードからインストールしてみた

以前、Docker + Alpine3.5 + Apache2.4 + Python2.7で、mod_pythonソースコードからインストールしてみました。
Docker + Alpine3.5 + Apache2.4 + Python2.7で、mod_pythonをソースコードからインストールしてみた - メモ的な思考的な

 
その時はPython2.x系で試したため、今回はPython3.x系でmod_pythonコンパイル&インストールし、Hello worldしてみます。

 
目次

 

環境

  • Mac OS X 10.11.6
  • Docker for Mac 17.03.1-ce-mac12
  • Alpine3.5 + Apache2.4.25
    • 前回同様、Apacheはprefork MPMで動作するようにDockerfileを修正
  • Python3系は以下の2種類を試す
    • Alpine3.5のパッケージにあるPython3.5
    • Alpine EdgeのパッケージにあるPython3.6
      • Alpine3.6ならPython3.6が入るが、今回はAlpine3.5で検証
  • mod_python
    • GitHub上のmasterの最新版
    • 今回利用したコミットは8acf1b7

 
なお、動作させるためのDockerfileやhttpd.conf、Hello worldスクリプトは、以前のPython2系で使ったものを流用・一部修正していきます。

 

まとめ

長いので最初にまとめます。

  • 最新のmaster(8acf1b7)ではPython3.x系は動作しない*1
  • ブランチ 3.5.xソースコードであれば、Python3.x系は動作する
  • 最新のmasterで動作させるには、ビルド用のファイルをブランチ3.5.xのコミットへと戻す
    • configure〜make installはあまり詳しくないので、3.5.xから最新までの間のどこでビルド用のファイルが壊れたかまでは追っていない

 

準備:prefork MPMで動作するApache2.4のDockerイメージを作成

今回の件とはあまり関係ないですが、後で再利用できるよう、Apache2.4 + prefork MPMのDockerイメージを作成しておきます。

(長いので省略しますが)Dockerfileは以下を使います。
https://github.com/thinkAmi-sandbox/Docker_Apache-sample/blob/master/alpine_apache_prefork/Dockerfile

 
thinkami:httpd24_preforkという名前でDockerイメージを作成します。

$ docker image build -t thinkami:httpd24_prefork .

 

masterをコンパイル&インストー

mod_pythonのmasterをPython3.5でコンパイル&インストールするには、以前のDockerfileを以下の内容で修正します。

  • mod_pythonで使用するpythonpython3へと変更
  • mod_pythonをビルドする時に使うpython-devpython3-devへと変更
  • configureする時のwith-pythonオプションを--with-python=/usr/bin/python3へと変更
RUN apk --update --no-cache add python3 && \
    python3 -m ensurepip && \
    rm -r /usr/lib/python*/ensurepip && \
    pip3 install --upgrade pip setuptools && \
    rm -r /root/.cache && \
    # mod_pythonで必要なパッケージを追加
    apk add --no-cache --virtual .mod_python_build_libs git && \
    apk --no-cache --virtual .mod_python_build_libs add python3-dev && \
    apk add --no-cache --virtual .mod_python_build_libs apache2-dev && \
    apk add --no-cache --virtual .mod_python_build_libs flex && \
    # ./configureで必要
    apk add --no-cache --virtual .mod_python_build_libs build-base && \
    # sudo make installで必要
    apk add --no-cache --virtual .mod_python_build_libs sudo && \
    # GitHubからソースコードを持ってきてインストール
    cd /tmp && \
    mkdir mod_python && \
    cd mod_python && \
    git clone https://github.com/grisha/mod_python.git . && \
    ./configure --with-apxs=/usr/local/apache2/bin/apxs --with-python=/usr/bin/python3 --with-flex=/usr/bin/flex && \
    make && \
    sudo make install && \
    # 不要なパッケージやソースコードを一括削除
    apk del .mod_python_build_libs && \
    rm -r /tmp/mod_python

 
docker image builddocker container runします。

# Dockerイメージのビルド
$ docker image build -t thinkami:python3_httpd24_mod_python_1 .
...
Successfully built e2669af0e6a4

# Dockerコンテナの起動 => エラーで起動せず
$ docker container run -p 8081:80 --name mod_python_1 -v `pwd`/htdocs/:/usr/local/apache2/htdocs thinkami:python3_httpd24_mod_python_1
httpd: Syntax error on line 19 of /usr/local/apache2/conf/httpd.conf: Cannot load modules/mod_python.so into server: Error relocating /usr/local/apache2/modules/mod_python.so: PyErr_BadArgument: symbol not found

 
エラーPyErr_BadArgument: symbol not foundが表示され、Dockerコンテナが起動しません。

ブランチ3.5.xをコンパイル&インストー

最新のmasterでは動作しなかったため、動作するブランチを探したところ、以下のIssueにコメントがありました。
mod_python 3.5.0 (with support for Python 3!) available for pre-release testing. · Issue #9 · grisha/mod_python

3.5.xブランチを使えばいいようです。

 
そのため、Dockerfileの中でgit cloneしている部分をmasterから3.5.xブランチへと修正します。

# git clone https://github.com/grisha/mod_python.git . && \
git clone -b 3.5.x https://github.com/grisha/mod_python.git . && \

 
docker image build & docker container runします。

$ docker image build -t thinkami:python3_httpd24_mod_python_2 .

$ docker container run -p 8081:80 --name mod_python_2 -v `pwd`/htdocs/:/usr/local/apache2/htdocs thinkami:python3_httpd24_mod_python_2
[Fri Jun 09 03:51:43.408568 2017] [:notice] [pid 1] mod_python: Creating 8 session mutexes based on 256 max processes and 0 max threads.
[Fri Jun 09 03:51:43.408632 2017] [:notice] [pid 1] mod_python: using mutex_directory /tmp 
[Fri Jun 09 03:51:43.445271 2017] [mpm_prefork:notice] [pid 1] AH00163: Apache/2.4.25 (Unix) mod_python/3.5.0-9d223c3 Python/3.5.2 configured -- resuming normal operations
[Fri Jun 09 03:51:43.445310 2017] [core:notice] [pid 1] AH00094: Command line: 'httpd -D FOREGROUND'
[Fri Jun 09 03:52:06.023976 2017] [:notice] [pid 6] mod_python: (Re)importing module 'mptest'
172.17.0.1 - - [09/Jun/2017:03:52:05 +0000] "GET /test/mptest.py HTTP/1.1" 200 12

mod_pythonが起動したようです。

 
curlで動作確認をします。

$ curl localhost:8081/test/mptest.py -D - -v
*   Trying ::1...
* Connected to localhost (::1) port 8081 (#0)
> GET /test/mptest.py HTTP/1.1
> Host: localhost:8081
> User-Agent: curl/7.43.0
> Accept: */*
> 
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Date: Fri, 09 Jun 2017 03:52:05 GMT
Date: Fri, 09 Jun 2017 03:52:05 GMT
< Server: Apache/2.4.25 (Unix) mod_python/3.5.0-9d223c3 Python/3.5.2
Server: Apache/2.4.25 (Unix) mod_python/3.5.0-9d223c3 Python/3.5.2
< Transfer-Encoding: chunked
Transfer-Encoding: chunked
< Content-Type: text/plain
Content-Type: text/plain

< 
* Connection #0 to host localhost left intact
Hello World!

Hello worldが表示されたため、正常に動作しているようです。

 

masterにブランチ3.5.xをマージして、コンパイル&インストー

上記ではブランチ3.5.xで動かしましたが、3.5.xブランチの最新コミットは2013年10月であるため、それ以降に修正された不具合は反映されていません。

試しにブランチ3.5.xとmasterの差分を見ると、以下の4コミットでした。 Comparing master…3.5.x · grisha/mod_python

4コミットには以下の8ファイルが含まれていました。

 
そこで、masterにブランチ3.5.xを単純にマージし、動作するか試してみます。

 
Dockerfileでは

  • git cloneするのをmasterから3.5.xブランチへ
  • git mergeするために、ユーザの設定を任意の値へ

を修正します。

cd mod_python && \
# gitを使うためにユーザ設定を実施
git config --global user.email "you@example.com" && \
git config --global user.name "Your Name" && \
# 現在のmasterを取得
git clone https://github.com/grisha/mod_python.git . && \
# 3.5.xブランチを取得
git branch 3.5.x origin/3.5.x && \
# 3.5.xブランチをmasterにマージ
git merge 3.5.x && \
./configure --with-apxs=/usr/local/apache2/bin/apxs --with-python=/usr/bin/python3 --with-flex=/usr/bin/flex && \

 
docker image build & docker container runします。

$ docker image build -t thinkami:python3_httpd24_mod_python_3 .

$ docker container run -p 8081:80 --name mod_python_3 -v `pwd`/htdocs/:/usr/local/apache2/htdocs thinkami:python3_httpd24_mod_python_3
[Fri Jun 09 03:54:21.257839 2017] [:notice] [pid 1] mod_python: Creating 8 session mutexes based on 256 max processes and 0 max threads.
[Fri Jun 09 03:54:21.257983 2017] [:notice] [pid 1] mod_python: using mutex_directory /tmp 
[Fri Jun 09 03:54:21.391315 2017] [mpm_prefork:notice] [pid 1] AH00163: Apache/2.4.25 (Unix) mod_python/3.5.0-7e4861f Python/3.5.2 configured -- resuming normal operations
[Fri Jun 09 03:54:21.393658 2017] [core:notice] [pid 1] AH00094: Command line: 'httpd -D FOREGROUND'

mod_pythonが起動したようです。

 
curlで動作確認をします。

$ curl localhost:8081/test/mptest.py -D - -v
*   Trying ::1...
* Connected to localhost (::1) port 8081 (#0)
> GET /test/mptest.py HTTP/1.1
> Host: localhost:8081
> User-Agent: curl/7.43.0
> Accept: */*
> 
< HTTP/1.1 500 Internal Server Error
HTTP/1.1 500 Internal Server Error
< Date: Fri, 09 Jun 2017 03:54:36 GMT
Date: Fri, 09 Jun 2017 03:54:36 GMT
< Server: Apache/2.4.25 (Unix) mod_python/3.5.0-7e4861f Python/3.5.2
Server: Apache/2.4.25 (Unix) mod_python/3.5.0-7e4861f Python/3.5.2
< Content-Length: 528
Content-Length: 528
< Connection: close
Connection: close
< Content-Type: text/html; charset=iso-8859-1
Content-Type: text/html; charset=iso-8859-1

< 
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>500 Internal Server Error</title>
</head><body>
<h1>Internal Server Error</h1>
<p>The server encountered an internal error or
misconfiguration and was unable to complete
your request.</p>
<p>Please contact the server administrator at 
 you@example.com to inform them of the time this error occurred,
 and the actions you performed just before this error.</p>
<p>More information about this error may be available
in the server error log.</p>
</body></html>
* Closing connection 0

Internal Server Errorになりました。

 
Docker containerのコンソールを見ると

[Fri Jun 09 03:54:36.714955 2017] [:error] [pid 6] make_obcallback: could not import mod_python.apache.\n
[Fri Jun 09 03:54:36.716445 2017] [:error] [pid 6] make_obcallback: Python path being used "['/usr/lib/python35.zip', '/usr/lib/python3.5/', '/usr/lib/python3.5/plat-linux', '/usr/lib/python3.5/lib-dynload']".
[Fri Jun 09 03:54:36.716560 2017] [:error] [pid 6] get_interpreter: no interpreter callback found.
[Fri Jun 09 03:54:36.716596 2017] [:error] [pid 6] [client 172.17.0.1:36616] python_handler: Can't get/create interpreter.
172.17.0.1 - - [09/Jun/2017:03:54:36 +0000] "GET /test/mptest.py HTTP/1.1" 500 528

と、interpreterのcallbackが見当たらないようです。

よって、単純なマージではダメなようです。

 

masterのビルド用ファイルをブランチ3.5.xの時点に戻して、コンパイル&インストー

差分のあった8ファイルのうちビルド用ファイル

を、ブランチ3.5.x時の状態に戻した上でコンパイル&インストールします。

また、コンパイルにはAPKのEdgeにあるPython3の最新版を利用してみます。

Dockerfileの変更は

# Python3まわり
RUN apk --update --no-cache --repository http://dl-cdn.alpinelinux.org/alpine/edge/main/ add python3 && \
    python3 -m ensurepip && \
    rm -r /usr/lib/python*/ensurepip && \
    pip3 install --upgrade pip setuptools && \
    rm -r /root/.cache && \
    # python3-devもEdgeから持ってくる
    # そうでないと、Python3のバージョンが合わずに、Downgrading python3が発生する
    apk --no-cache --virtual .mod_python_build_libs --repository http://dl-cdn.alpinelinux.org/alpine/edge/main/ add python3-dev && \
...

# mod_pythonまわり
    cd mod_python && \
    # gitを使うためにユーザ設定を実施
    git config --global user.email "you@example.com" && \
    git config --global user.name "Your Name" && \
    # 現在のmasterを取得
    git clone https://github.com/grisha/mod_python.git . && \
    # 3.5.xブランチを取得
    git branch 3.5.x origin/3.5.x && \
    # 3.5.xブランチをmasterにマージ
    git merge 3.5.x && \
    # python3用に、masterブランチのうちの一部ファイルを3.5.xブランチの状態へ戻す => OK
    git checkout 2d013b353631feb7ffe29fe9946327a08fe55381 scripts/mod_python.in && \
    git checkout 54d42b1810d5abfffe5b407d8e763402a0d4b2f0 configure.in && \
    git checkout 9d223c39a8f27345d8c046d026468fc0c1ad5d53 configure && \
    git checkout 51f7b0366d66b58b5b82874fe8a2db346f77d7fc src/include/mod_python.h && \
    git checkout 51f7b0366d66b58b5b82874fe8a2db346f77d7fc src/include/mod_python.h.in && \
    git checkout affc26bd69b3f5e67e8233166ff0d40c43fb7302 src/mod_python.c && \
    # ビルド
    ./configure --with-apxs=/usr/local/apache2/bin/apxs --with-python=/usr/bin/python3 --with-flex=/usr/bin/flex && \
...

です。

 
docker image build & docker container runします。

$ docker image build -t thinkami:python3_httpd24_mod_python_4 .

$ docker container run -p 8081:80 --name mod_python_4 -v `pwd`/htdocs/:/usr/local/apache2/htdocs thinkami:python3_httpd24_mod_python_4
[Fri Jun 09 03:56:17.740963 2017] [:notice] [pid 1] mod_python: Creating 8 session mutexes based on 256 max processes and 0 max threads.
[Fri Jun 09 03:56:17.741173 2017] [:notice] [pid 1] mod_python: using mutex_directory /tmp 
[Fri Jun 09 03:56:17.959878 2017] [mpm_prefork:notice] [pid 1] AH00163: Apache/2.4.25 (Unix) mod_python/3.5.0-2b3da9d Python/3.6.1 configured -- resuming normal operations
[Fri Jun 09 03:56:17.960025 2017] [core:notice] [pid 1] AH00094: Command line: 'httpd -D FOREGROUND'

 
curlで動作確認をします。

$ curl localhost:8081/test/mptest.py -D - -v
*   Trying ::1...
* Connected to localhost (::1) port 8081 (#0)
> GET /test/mptest.py HTTP/1.1
> Host: localhost:8081
> User-Agent: curl/7.43.0
> Accept: */*
> 
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Date: Fri, 09 Jun 2017 03:56:34 GMT
Date: Fri, 09 Jun 2017 03:56:34 GMT
< Server: Apache/2.4.25 (Unix) mod_python/3.5.0-2b3da9d Python/3.6.1
Server: Apache/2.4.25 (Unix) mod_python/3.5.0-2b3da9d Python/3.6.1
< Transfer-Encoding: chunked
Transfer-Encoding: chunked
< Content-Type: text/plain
Content-Type: text/plain

< 
* Connection #0 to host localhost left intact
Hello World!

Hello Worldが表示されました。

念のため、Dockerのコンソールを見ると

[Fri Jun 09 03:56:35.416889 2017] [:notice] [pid 6] mod_python: (Re)importing module 'mptest'
172.17.0.1 - - [09/Jun/2017:03:56:34 +0000] "GET /test/mptest.py HTTP/1.1" 200 12

と、無事に動いているようです。

 

ソースコード

GitHubに上げました。
thinkAmi-sandbox/Docker_Apache-sample

関係するディレクトリは以下の通りです。

  • alpine_apache_python3x_mod_python
    • 今回試したソースコード一式
    • executable_py36_all_in_oneは、Alpine3.5のDockerイメージから、Apache2.4(prefork) + EgeのPython3.x + mod_python が動作する環境を作るDockerfile
  • alpine_apache_prefork
    • Apache2.4をprefork MPMでビルドするDockerfile

*1:前回の記事にある通りこのコミットは自分のプルリクのものですが、動作しない原因は自分のコミットではありません、たぶん