未分類

前回までに、Linuxでシステムトレイにgtk&(C++/python)とqt&C++を使ってアイコン表示をしてみました。

今回はqt&pythonで同じことをしてみます。

未分類

前回、Linuxでシステムトレイにアイコン表示してみたのですがC++の割に22~44MBとメモリ消費が多かったので、

今回はpythonだとどうなのかと実験してみました。

未分類

前回

に引き続き、VALUE SERVERからConoHa VPSに移行して動かなくなってたところを直していきます。今回はコレですね。

bottleです。Hello, worldしただけなんですけどね。VALUE SERVERではpython3自体がインストールされていたので、Apacheのmod_cgiを使い、CGIでpythonを起動して動かしていました。

これを今回はdockerコンテナ上のnginxからWSGIで別のdockerコンテナ上のpythonに繋ぎ、bottleを動かします。まあ最近bottle聞かなくなりましたけど。。。

未分類

はじめに

前回はなんとかvscodeを使って、djangoをUbuntu上で動かしてみました。

今回はHello,Worldでないアプリを作ろうと考え、pythonらしく物体検出でもやってみることにしました。と、言っても検出ロジックは丸パクリです。

PythonでOpenCV DNNを利用して物体検知(Object Detection)する方法 | HaneCa

DNNはおろか、OpenCVすらよく分かってません。

仕様

  1. ユーザーがサイトに来る
  2. アプリがトップページを返す
  3. ユーザーが画像を指定してアップロードする
  4. アプリが画像から物体検出する
  5. アプリが検出済み画像をファイルに出力する
  6. アプリがファイルに出力した画像を挿し込んだトップページを返して3に戻る

リクエストが複数同時に来たときの仕様がありませんが、今回は固定ファイルに上書きします。結果は神のみぞ知るスタイルです。

設計

  • 画像あり/なしで表示を分けられるテンプレートトップページ
  • モデル空
  • 物体検出は固有のモデルでもビューでもフォームでもないものに実装
  • ビューで画像を受け取り、物体検出を呼び出し、結果を受けてテンプレートを呼び出す

実装

環境調整

djangoのような大きなモジュールをpipでインストールすると、vscodeのファイル監視が許容量オーバーしてしまいます。

Running Visual Studio Code on Linux

にあるように、システムの最大監視数を増やします(8,192→524,288)。

echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

これでvscodeを起動し直して、警告が出なければ大丈夫です。

でもって、新しくアプリケーション作るため、vscodeのターミナルから以下を実行します。

python manage.py startapp detect_app

フェーズ1

まずは物体検出を行わず、受け取った画像そのものを返して実装。つまり物体検出はハリボテ(モック)。

ビューはこんな感じ。コントローラがないので、全てビューで記述してます。受け取ったファイルの検証はしてません(公開は出来ない)。

from django.http import HttpResponseRedirect
from django.shortcuts import render
from .apps import DetectAppConfig
from .forms import UploadFileForm
from .upload import handle_uploaded_file
from .detect import detect


def upload_file(request):
    result = None
    if request.method == 'POST':
        form = UploadFileForm(request.POST, request.FILES)
        if form.is_valid():
            input_path = DetectAppConfig.image_filebase + DetectAppConfig.image_file_input
            output_path = DetectAppConfig.image_filebase + DetectAppConfig.image_file_output
            output_url = DetectAppConfig.image_urlbase + DetectAppConfig.image_file_output

            handle_uploaded_file(request.FILES['file'], input_path)
            detect(input_path, output_path)
            result = output_url
    else:
        form = UploadFileForm()
    return render(request, 'detect_app/upload.html', {'form': form, 'result': result})

中で使用されているフォームがこんな感じ。見事にファイルだけです。

from django import forms

class UploadFileForm(forms.Form):
    file = forms.FileField()

HTMLのテンプレートがこんな感じ。Bootstrap4を使ってますが、見た目はやはりアレです?。また画像ファイルのチェックをしていません(公開不可)。フォームデータと抽出結果のURLを渡されてビューから呼ばれます。

<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>物体検出</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
        integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
</head>

<body class="bg-light">
    <div class="container">
        <div class="py-5 text-center">
            <h1>物体検出</h1>
        </div>
        <div class="row justify-content-center">
            <form method="post" enctype="multipart/form-data">
                {% csrf_token %}
                <div>
                    <label>画像ファイル</label>
                </div>
                <div>
                    {{form.file}}
                </div>
                {% if result %}
                <div>
                    <img src="{{ result }}" class="img-responsive">
                </div>
                {% endif %}
                <hr>
                {% for error in form.file.errors %}
                {{error}}
                {% endfor %}
                <button class="btn btn-primary btn-lg btn-block" type="submit">送信</button>
            </form>
        </div>
    </div>
</body>

</html>

そしてこれがハリボテ本体。

import shutil

def detect(input, output):
    shutil.copy2(input, output)

他にも設定ファイルなどはいじってますが、最後にgitを張るので省略します。フェーズ1はこんな感じです。

入力画像は下のサイトのをお借りしました(超綺麗な写真なので縮小したけど)。ありがとうございます。

Group of People Having Fun Together Under the Sun · Free Stock Photo

フェーズ2

いよいよ物体検出です。まずはpythonにパッケージの追加から。

pip install opencv-python
pip freeze > requirements.txt

このまま実装するとpylintがopencvを認めてくれないので、設定でpython.linting.pylintArgsを検索して、[項目追加]

「–extension-pkg-whitelist=cv2」を追加する。

でもdnnはこれでもダメなようです。うまく回避する方法は見つかりませんでした。ドキュメントコメントがないと怒られてるワーニングも大量にあるので、これ以上は諦めます。

では、気を取り直して実装します。中身を入れたdetect.pyです。

import cv2

def detect(input, output):
    classes = {0: 'background',
                1: 'person', 2: 'bicycle', 3: 'car', 4: 'motorcycle', 5: 'airplane', 6: 'bus',
                7: 'train', 8: 'truck', 9: 'boat', 10: 'traffic light', 11: 'fire hydrant',
                13: 'stop sign', 14: 'parking meter', 15: 'bench', 16: 'bird', 17: 'cat',
                18: 'dog', 19: 'horse', 20: 'sheep', 21: 'cow', 22: 'elephant', 23: 'bear',
                24: 'zebra', 25: 'giraffe', 27: 'backpack', 28: 'umbrella', 31: 'handbag',
                32: 'tie', 33: 'suitcase', 34: 'frisbee', 35: 'skis', 36: 'snowboard',
                37: 'sports ball', 38: 'kite', 39: 'baseball bat', 40: 'baseball glove',
                41: 'skateboard', 42: 'surfboard', 43: 'tennis racket', 44: 'bottle',
                46: 'wine glass', 47: 'cup', 48: 'fork', 49: 'knife', 50: 'spoon',
                51: 'bowl', 52: 'banana', 53: 'apple', 54: 'sandwich', 55: 'orange',
                56: 'broccoli', 57: 'carrot', 58: 'hot dog', 59: 'pizza', 60: 'donut',
                61: 'cake', 62: 'chair', 63: 'couch', 64: 'potted plant', 65: 'bed',
                67: 'dining table', 70: 'toilet', 72: 'tv', 73: 'laptop', 74: 'mouse',
                75: 'remote', 76: 'keyboard', 77: 'cell phone', 78: 'microwave', 79: 'oven',
                80: 'toaster', 81: 'sink', 82: 'refrigerator', 84: 'book', 85: 'clock',
                86: 'vase', 87: 'scissors', 88: 'teddy bear', 89: 'hair drier', 90: 'toothbrush'}

    # Load a model imported from Tensorflow
    tensorflowNet = cv2.dnn.readNetFromTensorflow('./model/frozen_inference_graph.pb', './model/graph.pbtxt')

    # Input image
    img = cv2.imread(input)
    rows, cols, channels = img.shape

    # Use the given image as input, which needs to be blob(s).
    tensorflowNet.setInput(cv2.dnn.blobFromImage(img, size=(300, 300), swapRB=True, crop=False))

    # Runs a forward pass to compute the net output
    networkOutput = tensorflowNet.forward()

    # Loop on the outputs
    for detection in networkOutput[0,0]:
        score = float(detection[2])
        if score > 0.2:
            left = detection[3] * cols
            top = detection[4] * rows
            right = detection[5] * cols
            bottom = detection[6] * rows
    
            #draw a red rectangle around detected objects
            cv2.rectangle(img, (int(left), int(top)), (int(right), int(bottom)), (0, 0, 255), thickness=2)
            #draw category name in top left of rectangle
            cv2.putText(img, classes[int(detection[1])], (int(left), int(top-4)), cv2.FONT_HERSHEY_PLAIN, 1, (0, 0, 255), 2, 8)

    cv2.imwrite(output, img)

動かすにはトレーニング済みモデルデータと設定ファイルがいるのですが、これをダウンロードするスクリプトが↓です。

#!/bin/sh
mkdir -p model
wget -O - 'http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v2_coco_2018_03_29.tar.gz' | tar xvfzO - ssd_mobilenet_v2_coco_2018_03_29/frozen_inference_graph.pb > model/frozen_inference_graph.pb
wget -O - 'https://raw.githubusercontent.com/opencv/opencv_extra/master/testdata/dnn/ssd_mobilenet_v2_coco_2018_03_29.pbtxt' > model/graph.pbtxt

サーバー起動して、POSTしてみると…

一人多いですね。

前回と同じですが、このサーバーのGitList Giteaです。

デバッグ

djangoのデバッグも特別なことはありません。デバッグメニューからデバッグの開始を選びます。

Pythonを選びます。

Djangoを選びます。

ブレークポイントを張ります。

アップロードしてみます。

こんな感じで止まりました。変数も普通に見えてます。他と変わりません。

まとめ

今回はdjangoで物体検出するアプリを作って、デバッグまでしてみました。

次回はこのアプリを進化させ、モデルも使って、PostgreSQLにデータを入れてみたいと思います。

未分類

はじめに

前回はvscodeのメニュー表示バグの回避+αを記事にしました。

今回はようやくdjangoです。

git導入

まずは開発の基本gitです。バージョン管理すると失敗してもいつでも元に戻せます。というわけでgitをインストールします。

sudo apt install git

vscode上で環境構築

フォルダ作成

まずはvscodeを起動します。[フォルダを開く]から、~/python/を開き、右上のボタンからdjango_appsフォルダを作成して開きます。

gitリポジトリの作成

フォルダが開いたら次はメニューからターミナルを開きます。

開いたターミナルでgitリポジトリをその場に作ります。

git init

Python環境の作成とgit無視ファイルの設定

出来たら次にpythonの環境をその場に作ります。

python3 -m venv env

すると何やら左側に怪しい数字が出てきます。数字の出てるボタンをクリックします。

見てみると、さっきのpython環境を作成したときに出来たファイルのようです。つまり環境作成したときに346個のファイルが変更(追加)されたよと言っています。これはvscodeがデフォルトで持っているgitの機能で、さきほど作ったリポジトリを認識していて、その変更を検出したために変更ファイル数を教えてくれてたのです。ただ、自動生成されるファイルや外部から取得するファイルはgit管理対象にしないのがお約束なので、ここでは無視ファイルに指定します。いきなり346個も指定するのは辛いので、右上の階段状のボタンを押します。

これでフォルダ階層表示になるので、変更部分が分かりやすくなります。envを右クリックして、

[.gitignoreに追加]を選択します。これは、選択したフォルダ以下全てをgit管理対象にしない=無視するという意味です。

無視にすると、変更として検出されなくなり、無視設定ファイルである.gitignoreが作成されました。このファイルを開くと

346個のファイルが全て一覧されてしまっているので、

最上位フォルダのみに修正しておきます。 (以降このフォルダに追加された場合も自動で無視したいので)

これで心置きなくpython環境を設定できます。

vscode用python環境選択

次はvscodeにpython環境を認識させます。F1キーを押して、python: Select Interpreterと打ち込んでEnterしてください。

さっき作成したpython環境が./env/bin/pythonにあるので、それを選びます。

これでvscodeがこのフォルダで使用するpython環境が設定されました。

pipを使ったインストール

後はvscodeのメニューのターミナルから新しいターミナルを開きます。env環境で立ち上がるので、ここでpipでパッケージを更新しておきます。

pip install -U pip
pip list -o
pip install -U setuptools

これで更新が必要なpythonモジュールがなくなるので、djangoをインストールします。

pip install django

djangoのプロジェクト作成と確認

まずはsample_siteプロジェクトをカレントディレクトリに作成します。

django-admin startproject sample_site .

これでサービスが稼働可能な状態になるので、djangoの開発サーバーを起動してみます。

python manage.py runserver

プロンプトが戻らずに出力が止まり、http://127.0.0.1:8000/などと出てきていたら起動成功です。firefoxを立ち上げて、http://127.0.0.1:8000/を開いてみましょう。

まだ何もプログラミングもしてないのに、画面が出ましたね。これがフレームワークの力です。骨組みはすでにあるので肉付けしてくれという状態なのです。でもこれは確認用の画面なので、この画面を編集することはありません。

アプリケーション作成

まずはCtrl+Cで開発用サーバーを止めて、sample_appを作成します。

python manage.py startapp sample_app

ここまでで骨組みが出来ています。

pipのrequirements.txt作成

今、pythonの環境は全てgitからは無視ファイルとして扱っているため、保存されません。そのため、pythonの環境はいつでも復元できるようにインストールしたパッケージのリストをバージョン付きで保存します。それがrequirements.txtです。以下のコマンドで作成します。

pip freeze > requirements.txt

sqlite3データの無視ファイル追加

データベースのデータはgitには入れないので、このファイルも無視します。db.sqlite3を右クリックして[.gitignoreに追加]してください。あとは、pythonバイトコードのキャッシュ(.pyc)も無視ファイルに追加します。最終的に.gitignoreは↓な感じになります。

env/
db.sqlite3
*.pyc

gitリポジトリにコミット

切りが良いので、リポジトリにコミットしておきます。まずは数字の出ているところアイコンを押して、変更と書いてあるバーの右側の+を押します。

すると、[変更]の上に[ステージング済みの変更]という項目が出来上がり、元々検出されていた変更ファイル群が[ステージング済みの変更]に移動しました。後はコミットするだけなのですが、初回はその前にやることがあります。確認の意味でメッセージに「初回コミット」と入れて、一度チェックボタンを押して見ると…

こんな感じで怒られます。gitは原則コミットした人の名前とメールアドレスをちゃんと記述する慣習で、その設定がされていないとエラーになります。

初回だけなので、vscode内のターミナルからサクっと登録しておきます。

git config --global user.email "you@example.com"
git config --global user.name "Your Name"

※メールアドレスと名前は自分の物に変えてください

これでコミットできるようになったので、チェックボタンを押してコミットしてみてください。成功すれば何も出ずに16個の変更ファイルがなくなります。ターミナルからgit logとすると、履歴を見ることが出来ます。

(env) user@ubuntu1804:~/python/django_apps$ git log
commit de2a5a426e41514b9281ee3250d19560f92e632a (HEAD -> master)
Author: first_user <first_user@elephantcat.work>
Date:   Sat Feb 8 01:28:39 2020 +0900

    初回コミット
(env) user@ubuntu1804:~/python/django_apps$ 

Hello,Worldの作成

まずはViewから作成します。見たまんまなHello,Worldを返すビューです。

from django.http import HttpResponse

def index(request):
    return HttpResponse("<html><body>こんにちは!世界</body></html>")

このアプリのURLとビューの対応表です。

from django.urls import path

from . import views

urlpatterns = [
    path('', views.index, name='index'),
]

このサイトのURLとアプリの対応表です。

"""sample_site URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/3.0/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path('sample_app/', include('sample_app.urls')),
    path('admin/', admin.site.urls),
]

では、開発サーバー起動

python manage.py runserver

firefoxで確認

OK!なので、ステージングしてコミットします。

一応GitList Giteaに置いておきました。

まとめ

djangoでHello,Worldまでをgitにコミットしながらローカルvscode環境で実行させるところまでできた。

次回(↓)はアプリケーションとしての何かの機能を実装して、デバッグまでしてみたい。

未分類

はじめに

前回は、WindowsのDBeaverからVirtualBoxのUbuntuに入れたPostgreSQLにアクセスして簡単な表を見てみました。

今回は、一旦DBから離れてUbuntu 18.04上にVisual Studio codeをインストールして、Pythonの開発環境を整えてみます。

Visual Studio codeのインストール

最初に断っておくと、私はLinux上のVisual Studio code(以下vscode)を常用していません。理由はWindowsのvscodeからremote-sshで使っているからです。ただ最初はローカルにvscodeをインストールして使います。参考資料は以下になります。

Running Visual Studio Code on Linux

snap/apt?

Ubuntuにvscodeをインストールする方法はsnapを使う方法とaptを使う方法の2種類あります。snapはUbuntuでしか利用されない特殊なパッケージで、コンテナを前提としたアプリのインストールをするため、従来のパッケージ管理と大きく違います。アプリに余計なオーバーヘッドは要らないので、今回はaptで行きます。

aptリポジトリの追加

wget -O - https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > packages.microsoft.gpg
sudo install -o root -g root -m 644 packages.microsoft.gpg /usr/share/keyrings/
sudo sh -c 'echo "deb [arch=amd64 signed-by=/usr/share/keyrings/packages.microsoft.gpg] https://packages.microsoft.com/repos/vscode stable main" > /etc/apt/sources.list.d/vscode.list'
rm packages.microsoft.gpg

インストール

sudo apt-get install apt-transport-https
sudo apt-get update
sudo apt-get install code

これでコマンドラインからcodeと打つとvscodeが起動するようになりました。もちろんWindowsキーからcodeでも、アプリケーション表示からvscodeアイコンを選択しても起動します。

起動直後の画面はこんな感じかと思います。

VisualStudio codeのプラグイン追加

vscodeは豊富なプラグインが売りになっています。まずはプラグインの追加方法説明も兼ねて…

日本語化

してみます。vscodeの左側についているバーの一番下のアイコンが拡張機能です。ここを押すと…

各種拡張機能(プラグイン)の一覧が出てきます。一番上に検索ボックスがあるので、そこに Japanese と打ち込むと…

日本語に関連した拡張機能が一覧されます。では一番上にJapanese Language Pock fo…というのがあるのでこれのInstallボタンを押します。しばらくすると右下にポップアップが出てくるので、そこにある[Restart Now]ボタンを押すと…

日本語で起動してくれます。

vscodeには各種のプラグインがありますが、ここではこれだけにして、必要なときに入れていく方式を採ります。

Python開発環境の構築

vscodeで環境構築する前にpython自体の環境構築方法について説明を入れておきます。

python2とpython3

まずはpythonのバージョンについて…

pythonにはバージョン2系とバージョン3系があります。2系のスクリプトは3系で動かないことが多いです(文字列周りがかなり違う)。なのでpythonというとまずはバージョンを確認することが多いわけです。今回は18.04.3をクリーンインストールしたので2系は入っておらず、3系だけです。環境を構築する上で2系のことは考えないことにします。また3系でもバージョンはいくつかありますが、複数のバージョンを切り替えて使用することも考えないことにします。

pythonのパッケージ管理

最近のスクリプト言語は、使用するライブラリ(モジュール)などをパッケージ管理が出来るようになっています。インターネットから自由にダウンロードして、簡単に利用でき、また更新なども一発で出来るようになっています。pythonでもそのような機能がありますが、スクリプト言語のパッケージでよく問題になるのは、「インストールする場所」です。例えば複数のユーザーで共通で使いたいライブラリがあるとか、このアプリだけで使うライブラリなどがあった場合、同じ場所に突っ込むと、必要なバージョンが違うなどの不都合が発生したりします。

なので、この手のパッケージ管理はアプリケーションならアプリケーションの環境ごとに分けることが多いです。その環境を切り替えるコマンドがvenvです。

venvのインストール

このvenvはpython標準の機能なのですが、ubuntuは改悪してvenvを外しているので、最初にこれをインストールします。

sudo apt install python3-venv

venv環境の作成とactivate

次に環境をtestという名前で作成します。

python3 -m venv ~/.venv/test

最後に指定してる引数が環境を保存するディレクトリになります。そしてこのディレクトリの名前が環境の名前です。では早速作成した環境に切り替えてみます。

user@ubuntu1804:~$ . ~/.venv/test/bin/activate
(test) user@ubuntu1804:~$ 

環境を切り替えるとプロンプトが変わり、カッコ付きで環境の名前が入るようになります。この状態であれば、いくらパッケージをインストールしても他の環境に影響を与えることはありません。

パッケージの更新・追加

試しにpip自体のアップデートをしてみます。

(test) user@ubuntu1804:~$ pip install -U pip
Collecting pip
  Downloading https://files.pythonhosted.org/packages/54/0c/d01aa759fdc501a58f431eb594a17495f15b88da142ce14b5845662c13f3/pip-20.0.2-py2.py3-none-any.whl (1.4MB)
    100% |████████████████████████████████| 1.4MB 958kB/s 
Installing collected packages: pip
  Found existing installation: pip 9.0.1
    Uninstalling pip-9.0.1:
      Successfully uninstalled pip-9.0.1
Successfully installed pip-20.0.2
(test) user@ubuntu1804:~$

めちゃくちゃバージョンアップしました。他にアップデートできるものがないかどうか見てみます。

(test) user@ubuntu1804:~$ pip list -o
Package    Version Latest Type 
---------- ------- ------ -----
setuptools 39.0.1  45.1.0 wheel
(test) user@ubuntu1804:~$

setuptoolsもアップデートできるようなのでやっておきます。

(test) user@ubuntu1804:~$ pip install -U setuptools
Collecting setuptools
  Downloading setuptools-45.1.0-py3-none-any.whl (583 kB)
     |████████████████████████████████| 583 kB 6.5 MB/s 
Installing collected packages: setuptools
  Attempting uninstall: setuptools
    Found existing installation: setuptools 39.0.1
    Uninstalling setuptools-39.0.1:
      Successfully uninstalled setuptools-39.0.1
Successfully installed setuptools-45.1.0
(test) user@ubuntu1804:~$

では適当にbottleでもインストールしてみます。

(test) user@ubuntu1804:~$ pip install bottle
Collecting bottle
  Downloading bottle-0.12.18-py3-none-any.whl (89 kB)
     |████████████████████████████████| 89 kB 2.1 MB/s 
Installing collected packages: bottle
Successfully installed bottle-0.12.18
(test) user@ubuntu1804:~$

インストール済みのパッケージを一覧します。

(test) user@ubuntu1804:~$ pip list
Package       Version
------------- -------
bottle        0.12.18
pip           20.0.2 
pkg-resources 0.0.0  
setuptools    45.1.0 
(test) user@ubuntu1804:~$ 

これだけ分かればパッケージ管理は何とか出来ると思います。削除など必要であればググって調べてください。

venv環境のdeactivateと削除

次は今の環境を抜けて元の環境に戻します。

(test) user@ubuntu1804:~$ deactivate 
user@ubuntu1804:~$

環境自体の削除は最初に作ったディレクトリを消すだけです。

rm -rf ~/.venv/test

今回環境は~/.venvディレクトリの下に作りましたが、ココは私が好みで使ってるというだけなので、どこでも大丈夫です。ここまででvenvの使い方や雰囲気が分かったと思います。

vscodeで作業する

まずはターミナルからフォルダを作成します。

mkdir -p ~/python/sample

ようやくvscodeを使います。 vscodeの[フォルダを開く]ボタンを押して作ったフォルダを開きます。すると…

左側のペインにSAMPLEと書かれてる部分が現れました。同じ行の右側に並んでいるアイコン群の一番左に[新しいファイル]ボタンがあるので押して、sample.pyと入れます。

作成したファイルが開かれて、右下にPython用の拡張機能が推奨されてます。渡りに船なので[Install]です。

次はpylintのインストールを促されますが、まだpipが使えないので、ここでターミナルから環境を作っておきます。

python3 -m venv ~/python/sample/venv

すると…

賢いことにvscodeが環境を検出してコレ使うか?って聞いてくれています。当然[Yes]です。
環境も選択できたので、ついでにさっき聞かれていたpylintも[Install]しちゃいます。すると、画面下側にターミナルが出てきて、venv環境がactivateされた状態でbashが立ち上がり、自動でpylintがインストールされてます。到れり尽くせりですね。

では、このターミナルからrequestsパッケージをインストールしちゃいます。

(venv) user@ubuntu1804:~/python/sample$ pip install requests
Collecting requests
  Downloading https://files.pythonhosted.org/packages/51/bd/23c926cd341ea6b7dd0b2a00aba99ae0f828be89d72b2190f27c11d4b7fb/requests-2.22.0-py2.py3-none-any.whl (57kB)
    100% |████████████████████████████████| 61kB 5.2MB/s 
Collecting chardet<3.1.0,>=3.0.2 (from requests)
  Downloading https://files.pythonhosted.org/packages/bc/a9/01ffebfb562e4274b6487b4bb1ddec7ca55ec7510b22e4c51f14098443b8/chardet-3.0.4-py2.py3-none-any.whl (133kB)
    100% |████████████████████████████████| 143kB 3.8MB/s 
Collecting urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 (from requests)
  Downloading https://files.pythonhosted.org/packages/e8/74/6e4f91745020f967d09332bb2b8b9b10090957334692eb88ea4afe91b77f/urllib3-1.25.8-py2.py3-none-any.whl (125kB)
    100% |████████████████████████████████| 133kB 3.3MB/s 
Collecting idna<2.9,>=2.5 (from requests)
  Downloading https://files.pythonhosted.org/packages/14/2c/cd551d81dbe15200be1cf41cd03869a46fe7226e7450af7a6545bfc474c9/idna-2.8-py2.py3-none-any.whl (58kB)
    100% |████████████████████████████████| 61kB 5.5MB/s 
Collecting certifi>=2017.4.17 (from requests)
  Downloading https://files.pythonhosted.org/packages/b9/63/df50cac98ea0d5b006c55a399c3bf1db9da7b5a24de7890bc9cfd5dd9e99/certifi-2019.11.28-py2.py3-none-any.whl (156kB)
    100% |████████████████████████████████| 163kB 4.2MB/s 
Installing collected packages: chardet, urllib3, idna, certifi, requests
Successfully installed certifi-2019.11.28 chardet-3.0.4 idna-2.8 requests-2.22.0 urllib3-1.25.8
(venv) user@ubuntu1804:~/python/sample$ 

画面的には↓な感じです。

さて、これで必要な環境が整ったので、コードを書いてみます。

import requests
print(requests.get('https://www.google.co.jp/').text)

Google日本のトップページを取得して、標準出力に書き出すスクリプトです。では、右上の実行ボタンを押して実行しちゃいます。

サクっと実行出来ました。次はデバッグしてみます。左側のアイコンの上から4つ目の虫っぽいのをクリックします。

ソースコード2行目の左側で左クリックし、2行目にブレークポイントを張っておきます。

2行目に赤丸を確認してから、[Pythonでデバッグする]を押します。

すると…

プログラムが動いてから2行目実行直前で止まりました。左側には変数も表示されており、デバッグが出来ることが分かります。
上にある右向き三角ボタンを押して、実行を再開します。

プログラムが終了し、元の状態に戻りました。

まとめ

Ubuntu18.04上にvscodeをインストールし、venvで用意したpythonの実行環境を使い、vscode上でGoogleのトップ画面を取得するプログラム(sample.py)を作成し、実行・デバッグしてみました。

次回(↓)は、この環境でdjangoに挑戦します(?)。

未分類

前書き

前回はCGIのbottleとdjangoを50回取得した時間を比較しました。

では、WordPressを推してるVALUE-SERVERが力を入れてそうなPHPだと、どれくらいの時間なのでしょうか?

ただの静的なHTMLページも加えて比較してみました。

加えたもの

<?php echo "Hello, World"; ?>
Hello, World!

比較用のシェルはこうなります。

for url in django.cgi/polls/ hello.cgi/ hello.php hello.html;do
        time bash -c "for i in {0..99};do wget -q -O - https://elephantcat.work/cgi-bin/$url >/dev/null;done"
done

結果

Patterntime[s]
CGI Python + bottle 22.22
CGI Python + django 66.00
mod_php PHP56.12
static HTML4.85

これがCGI及びPythonインタプリタの起動の遅さということです。

なお、計測器からelephantcat.workへのping100回の平均値は6.421msでした。

未分類

前回前々回で作成したHello, World!なアプリを外からGetして時間を測定してみました。

for url in django.cgi/polls/ hello.cgi/;do
        time bash -c "for i in {0..49};do wget -q -O - https://elephantcat.work/cgi-bin/$url >/dev/null;done"
done

結果は

0.33user 0.31system 0:31.69elapsed 2%CPU (0avgtext+0avgdata 7176maxresident)k
0inputs+256outputs (0major+21808minor)pagefaults 0swaps
0.30user 0.34system 0:10.98elapsed 5%CPU (0avgtext+0avgdata 7180maxresident)k
0inputs+88outputs (0major+21767minor)pagefaults 0swaps
フレームワーク 50回取得にかかった時間[秒]
django 31.7
bottle 11.0

django遅いのは分かるけど、bottleでも十分遅いと思う。

CGI Python恐るべし。

未分類

前回はbottleだったので今回はdjangoを同じようにやってみようと思います。やることはdjango tutorialです。

venvの仮想環境構築

$ cd ~/public_html/elephantcat.work/cgi-bin/
$ python3 -m venv fordjango
$ . ./fordjango/bin/activate
(fordjango) $ 

djangoのインストール

(fordjango) $ pip install --upgrade pip
(fordjango) $ pip install django
(fordjango) $ python -m django --version
3.0.2

djangoプロジェクト作成

(fordjango) $ django-admin startproject djangoroot
(fordjango) $ cd djangoroot

このフォルダにはパスワードなども入るので、今のうちに.htaccessでwebからアクセスできないようにしておきます。

deny from all

django用のmysql設定

djangoはデフォルトだとdatabaseにsqlite3を使います。VALUE-SERVERでも sqlite3は使えるのですが、バージョンに問題があって、エラーになります。なので今回はmysqlを使います。私のようにエコプランの方はdatabase1つしかないのでテーブル名に衝突がないように注意してください。

さて、WordPressとか使うために事前にmysql自体の設定はしてますよね?そこは端折って、djangoからmysqlを使う設定をしていきます。

(fordjango) $ vi djangoroot/settings.py

↓な辺りをこんなふうに修正します。

DATABASES = {
    'default': {
        # 'ENGINE': 'django.db.backends.sqlite3',
        'ENGINE': 'django.db.backends.mysql',
        # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
        'NAME': 'databasename', # VALUE-SERVERではユーザーIDと同じ
        'USER': 'username', # ユーザーIDを入れてください
        'PASSWORD': 'password', # パスワードを入れてください
        'HOST': 'localhost',
        'PORT': '3306',
    }
}

ここでオススメされてるpython用のmysql接続モジュールをインストール

(fordjango) $ pip install mysqlclient

Pollsアプリの作成

(fordjango) $ python manage.py startapp polls

チュートリアルのとおりにviewを用意します(プロジェクト名だけdjangorootなので注意)。

from django.http import HttpResponse

def index(request):
    return HttpResponse("Hello, world. You're at the polls index.")
from django.urls import path

from . import views

urlpatterns = [
    path('', views.index, name='index'),
]
"""djangoroot URL Configuration
ドキュメントコメントなので省略
"""
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path('polls/', include('polls.urls')),
    path('admin/', admin.site.urls),
]

チュートリアルはまだ続くわけですが、この辺で一回画面が見たい…でもrunserverはレンタルサーバでは出来ないので、重い腰を上げてCGIで繋ぎます。

CGIでdjangoを呼ぶ

今さら断っておくと、djangoは正式にCGIをサポートしていません。サポートしているのはFastCGI/SCGI/WSGIとかです。理由はとにかく遅いから!

遅くてもいいからとにかく繋ぎたいんじゃ~という人のためにチョロっとCGIスクリプトを書いてくれたものがコレ

これをcgi-binに置くのですが、そのままでは使えないので、まずはこちらの環境に合わせてシバンをvenv用に直します。

#!(ホーム)/public_html/elephantcat.work/cgi-bin/fordjango/bin/python
...

次に最後の部分をdjangoプロジェクトの設定に合わせれば使えます。

...
# Change this to the directory above your site code.
sys.path.append("(ホーム)/public_html/elephantcat.work/cgi-bin/djangoroot")
# Change mysite to the name of your site package
os.environ['DJANGO_SETTINGS_MODULE'] = 'djangoroot.settings'
run_with_cgi(get_wsgi_application())

ここまでで実行可能にすればdjangoフレーム自体は動作します。が、最後にサイトの設定settings.pyを修正します。

許可ドメイン追加とデバッグ設定OFF。 デバッグ設定OFFにするとエラー発生時のデバッグ情報からパスワードなどの漏洩が防げます。

...
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False

ALLOWED_HOSTS = [
    'elephantcat.work'
]
...

あとはcgiスクリプトを実行可能にするだけ!

(fordjango) $ chmod u+x django.cgi

上記で動作するようになったpollsアプリがこちら

実際のチュートリアルはまだ続くのですが、今回はここまで。

一応再度書いておくと、djangoはCGIで使っちゃダメです。

未分類

2020年1月12日現在

VALUE-SERVERでPython

VALUE-SERVERではpython3が使用できる。

$ python3 --version
Python 3.6.8

VALUE-SERVERではapacheが動いていて、WSGIが使えない。

$ apachectl -M 2>/dev/null | grep wsgi
$

VALUE-SERVERではapacheが動いていて、CGIが使える。

$ apachectl -M 2>/dev/null | grep cgi
 cgi_module (shared)
$

つまり、django、flask、bottleなどのPython向けWeb Frameworkを使いたい場合の選択肢は、CGIのみということ。

Webサーバとの接続では軽量な順に

言語独自I/F ≧ WSGI > FastCGI > CGI

だと思うので、I/Fとしては最重量。そこで今回は最も軽量なFrameworkであるbottleでのHello, World!をやってみる。

bottleでのHello, World!

python3ではvenvが標準装備なので、これを使って仮想環境を構築し、そこにpipでbottleを入れる。

ディレクトリ構成は以下な感じ。

~/                                  # ホーム
~/public_html/                      #
~/public_html/domain/               # Webの公開ルートフォルダ
~/public_html/domain/cgi-bin        # CGI実行するファイルを置くフォルダ
~/public_html/domain/cgi-bin/forcgi # venvで構築する仮想環境

venvによる仮想環境作成とbottleインストール

$ cd ~/public_html/domain/cgi-bin
$ python3 -m venv forcgi
$ . ./forcgi/bin/activate
(forcgi)$ pip install --upgrade pip
(forcgi)$ pip install bottle
(forcgi)$ deactivate

bottleを使ったhello, worldアプリをpythonで書く(拡張子はCGIなので.cgiにしてます)。

#!$HOMEの内容/public_html/domain/cgi-bin/forcgi/bin/python
from bottle import route, run
@route('/')
def hello():
    return "Hello World!"
run(server='cgi')

シバンで、仮想環境のpythonを指定しているのが味噌で、こうしておくと、activateしなくてもpipで入れたパッケージをちゃんと見てくれます。

cgi-binディレクトリで、ちゃんとCGIが機能する設定を入れるため、.htaccessを書いておきます。

AddHandler cgi-script .cgi

これで、拡張子.cgiなパスはそのファイルで処理されます。後は先に作成したhello.cgiの実行権を与えればOK。

$ chmod u+x hello.cgi

このサーバにあるので、ConoHa VPSに引っ越したので、確認は/cgi-bin/hello.cgi/で出来ません。

ConoHa VPSで(WSGIですが)bottleを扱った記事は以下にあります。