stylesheet

ラベル python の投稿を表示しています。 すべての投稿を表示
ラベル python の投稿を表示しています。 すべての投稿を表示

2024-08-17

Flaskアプリケーションをサブディレクトリで公開

Flaskで作成したアプリケーションをサブディレクトリにマウントして公開する方法。 Flaskの機能ではないのでドキュメントを見ていてもなかなか見つけにくいが、WSGIミドルウェアを使用するものが正解。

毎回調べている気がするのでメモしておく。

設定内容

以下のように、http://localhost/consoleで公開する。

# nginx config

upstream console {
  server 127.0.0.1:8110;
}

server {
  ...

  location /console {
    proxy_pass http://console;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }

  ...
}

このままではurl_for等のurl生成が壊れるので、 APPLICATION_ROOTを設定してやり、WSGIのミドルウェアを挟んでやるとうまく動作する。 環境変数を参照するようにすると、開発環境等にもコード修正なしで対応できる。

# app.py

import os
from flask import Flask, request, url_for, render_template
from werkzeug.serving import run_simple
from werkzeug.middleware.dispatcher import DispatcherMiddleware

application = Flask(__name__)

application_root = os.environ.get('FLASK_APPLICATION_ROOT', '')
application.config['APPLICATION_ROOT'] = application_root

application.wsgi_app = DispatcherMiddleware(run_simple, {application_root: application.wsgi_app})


@application.route('/')
def index():
    urls = (
        url_for('index', _external=True),
        url_for('index'),
        url_for('static', filename='css/style.css'))
    return '<br>'.join(urls)


if __name__ == "__main__":
    application.run(host=0.0.0.0)

今回は、WSGIコンテナにNGINX Unitを使用してみた。 インストール方法は割愛。

# config.json

{
    "listeners": {
        "*:8110": {
            "pass": "applications/console"
        }
    },
    "applications": {
        "console": {
            "type": "python",
            "user": "yamori",
            "group": "yamori",
            "working_directory": "/home/yamori/workspace/console/",
            "path": "/home/yamori/workspace/console/",
            "home": "/home/yamori/.anyenv/envs/pyenv/versions/console",
            "module": "app",
            "environment": {
                "FLASK_APPLICATION_ROOT": "/console"
            }
        }
    }
}

ソケットにputして反映させる。

$ sudo curl -X PUT --data-binary @config.json --unix-socket /run/control.unit.sock http://localhost/config/

以上で、webサーバー経由でのアクセスhttp://localhost/console、pythonファイルの直接実行時のhttp://localhost:5000どちらの場合でも正しいパスが生成される。

2023-05-28

diffusers の使い方を調べてGoogle Colab ノートブックファイルを作った

概要: この記事では、Google Colabのフリー版におけるAUTOMATIC1111版WebUIの制限について取り上げ、その代替手段としてHugging Faceのdiffusersを使用する方法について調査します。

最近、Google Colabのフリー版での利用制限が行われ、AUTOMATIC1111版WebUIの使用が禁止されました。さらに悪いことに、この制限はRemoteUI全体に広がっているようです。
この制限によって、以前は簡単にアクセスできていたWebユーザーインターフェース(WebUI)を使用することができなくなりました。AUTOMATIC1111版WebUIは、使いやすく便利なユーザーインターフェースでしたが、これからは別の方法を探さなければなりませんでした。

代替の手段を模索する必要がありました。
その結果、UIを持たないHugging Faceのdiffusersを利用することに決めました。diffusersは、モデルの推論や処理を行うためのPythonライブラリであり、UIを必要としないため、制限を受けずに利用することができます。これは私たちのニーズに合致していると考え、積極的に取り入れることにしました。

diffusersの使い方について詳しく調査しました。Hugging Faceの公式ドキュメンテーションやコミュニティのフォーラムなど、さまざまな情報源を活用しました。

2023-04-12

プログラマーには ChatGPT が必要だ

ChatGPT捗る。
これだけできると、非公開のプログラムコードも喰わせたくなる。
禁止されるのもわからないでもないな。

コードレビューの依頼

N.Y.

コードレビューをお願い

ChatGPT

もちろんです!コードを共有していただければ、レビューさせていただきます。

N.Y.
"""
マルチディスプレイ用の画像分割

python image_spliter.py -h

[requirements]
pillow
"""

import os
import glob
import argparse
import re
import logging
from PIL import Image, ImageFilter

logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)


def split_image(name:str, output:str, split:int, aspect:str):
    img = Image.open(name)
    ax, ay = map(lambda x: int(x), aspect.split(':'))
    aspect_ratio  = ay / ax

    out_w = int(img.width / split)
    out_h = int(out_w * aspect_ratio)
    if out_h > img.height:
        out_w = int(out_h * aspect_ratio)
    
    x = int(img.width / 2 - out_w * split / 2)
    y = int(img.height / 2 - out_h / 2)
    folder, file_name = os.path.split(name)
    base_name, ext = os.path.splitext(file_name)

    logger.info(f'processing... {name}, {img.width}x{img.height} -> {out_w}x{out_h}')

    for n in range(split):
        p = os.path.join(output, f'{base_name}_{n}{ext}')
        cropped = img.crop((x, y, x + out_w, y + out_h))
        cropped.save(p, quality=95)
        logger.info(f'cropped: {p}')
        x += out_w


def aspect_type(value:str) -> str:
    if not re.match(r'^\d+:\d+$', value):
        raise argparse.ArgumentError('invalid value')
    return value


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='マルチディスプレイ用の画像分割')
    parser.add_argument('input', help='入力ファイル globパターン')
    parser.add_argument('-o', '--output', default='output', help='出力ディレクトリ')
    parser.add_argument('-s', '--split', type=int, default=2, help='分割数')
    parser.add_argument('-a', '--aspect', default='16:9', help='ディスプレイの縦横比', type=aspect_type)
    args = parser.parse_args()

    os.makedirs(args.output, exist_ok=True)
    for s in glob.glob(args.input):
        split_image(s, args.output, args.split, args.aspect)

    logger.info('done')

ChatGPT

このコードは、指定された入力画像を複数の画像に分割し、それらを出力する Python スクリプトのようです。以下は、いくつかのコメントです。

  • コード全体の構造は良く整理されており、コードが読みやすくなっています。
  • それぞれの機能は、独自の関数に分割されており、単一の機能にフォーカスされています。
  • パラメータの解析には、Python の標準ライブラリである argparse が使用されています。
  • コマンドラインオプションのヘルプは、"-h" オプションで表示されます。
  • エラーの検査には、正規表現が使用されています。

最後に、いくつかの改善の提案です。

2022-09-18

Python SSL: UNSAFE_LEGACY_RENEGOTIATION_DISABLED エラーの回避策

URLError: <urlopen error [SSL:
UNSAFE_LEGACY_RENEGOTIATION_DISABLED] unsafe legacy renegotiation
disabled (_ssl.c:1131)>

上記は、セキュリティリスクのあるhttpsアドレスを開いたときに発生するエラーとなる。
SSLプロトコルの不具合で、中間者攻撃に使用される脆弱性のためopenssl側で無効化されている。OpenSSL 3.0.0からデフォルトで無効化された。

本来はサーバー側をアップデートするのが筋だが、なかなかそういうわけにもいかないことも多い。リスクを承知で、クライアント側の無効状態を解除する方法をメモ。

環境は以下の通り。

$ python -c 'import ssl; print(ssl.OPENSSL_VERSION)'
OpenSSL 3.0.2 15 Mar 2022

$ python --version
Python 3.8.10

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 22.04.1 LTS
Release:        22.04
Codename:       jammy

回避策A: opensslの設定変更

opensslの設定を変更する方法。 /etc/ssl/openssl.confを編集して、system_default_sectセクションにOptions = UnsafeLegacyRenegotiation設定を追加する。

# /etc/ssl/openssl.conf

...
[ssl_sect]
system_default = system_default_sect

[system_default_sect]
CipherString = DEFAULT:@SECLEVEL=2
Options = UnsafeLegacyRenegotiation

ただし、/etc/ssl/openssl.confを直接編集すると、システム全体に適用されてしまうので流石にそれはまずい。
実際は別の設定ファイルを用意して、環境変数でpythonプログラムへ渡してやると良い。

$ cp /etc/ssl/openssl.conf openssl_unsecure.conf
$ echo -e "Options = UnsafeLegacyRenegotiation\n" >> openssl_unsecure.conf
$ OPENSSL_CONF=/path/to/openssl_unsecure.conf python cherry.py

回避策B: プログラム改修

sslのオプションに0x4フラグを追加してやると回避できる。
開発版ではssl.OP_LEGACY_SERVER_CONNECT定数が追加されているが、現行バージョン3.10でも使用できないので直接、値を指定する。

import urllib.request
import ssl

ctx = ssl.create_default_context()
ctx.options |= 0x4  # ssl.OP_LEGACY_SERVER_CONNECT
resposne = urllib.request.urlopen('https://...', context=ctx)
...

build_openerを使う場合は、HTTPSHandlerを作成してやる。

opener = urllib.request.build_opener(urllib.request.HTTPSHandler(context=ctx))
...

開発版でのOP_LEGACY_SERVER_CONNECTについての記載。

.. data:: OP_LEGACY_SERVER_CONNECT

   Allow legacy insecure renegotiation between OpenSSL and unpatched servers
   only.

   This option is only available with OpenSSL 0.9.8m and later, and is disabled
   in default context since OpenSSL 3.0.0.

   .. versionadded:: 3.11

以上。
古いpython2向けのスクリプトをpython3向けへ移植している時に発生した。

参照

2021-01-22

AppEngineで課金設定をするにあたってすべきこと

Google AppEngineさん、今後は課金設定しないと使わせてくれないようで。

App Engine スタンダード アプリケーションを引き続きご利用いただくために、2021 年 1 月 31 日までにお支払い情報の追加をお願いいたします。
  
  2019 年 11 月 30 日以降、App Engine スタンダード環境ではデプロイ時に Cloud Build を使用しております。Cloud Build には、他のすべての Google Cloud 同様に、有効なお支払い方法が必要です。当時、今後もお客様にプロジェクトのデプロイを継続していただくために、App Engine プロジェクトを有効なお支払い方法を設定した請求先アカウントにリンクしていただくようお願いいたしました。

2021 年 1 月 31 日以降、すべての App Engine プロジェクトの継続には、有効なお支払い方法を設定した請求先アカウントとのリンクが必要です。

2021 年 1 月 31 日までに、有効なお支払い方法が設定された請求先アカウントとリンクされなかった App Engine アプリは、シャットダウンされます。運用中のインスタンスは終了し、プロジェクトにお支払い方法が追加されるまで新しいインスタンスを開始することはできません。

この変更による既存の App Engine の無料枠への影響はございません。また、使用量が無料枠の割り当ての範囲に収まっている場合は、App Engine の料金が発生することはございません。ただし、使用量が無料枠の使用限度を超えた場合は、料金が発生いたします。このたびの変更前は、無料枠を超えて使用した場合、1 日の割り当てがリセットされるまでアプリの稼働が止まりました。

ありがとうございます。
今後は破産のリスクに晒されることになりました。

無料枠を費用管理方法としてご利用の場合は、以下の方法のうちいずれかの方法でApp Engine の費用を管理されることをおすすめします。

- app.yamlで max_instances 設定に 1 を指定して、無料枠の超過リスクを減らします。この設定により、アプリのスケーリング能力が制限されますが、ハードリミットではないため、使用量と請求額が増える可能性があります。
- Cloud の予算アラートを設定して、予算上限に近づいたときに通知を受け取ります。
- アプリを手動で無効にします。ただし、この場合、無効にできるのはインスタンス時間やネットワーキングなどアプリの実行に関連した料金のみです。アプリを無効にした間も、Cloud Storage の容量に対する料金などの固定費は引き続き発生します。
- アプリをプログラムで無効にすると、指定したしきい値以上の費用が発生しないよう上限を設定できます。

親切にありがとうございます。ところで、一日単位で予算制限はできないのしょうか?G〇〇gle BotとかのBotが大挙して押し寄せるとコストが嵩むので。
ない?
なるほど、予算制限機能は先立って廃止されたのですね。

それでは、おすすめに従って、max_instanceの制限とプログラムで無効にする対策を行うように致します。

2018-09-01

ちょっとした確認用にpythonでhttpサーバーを起動

Pythonモジュールの実行で済むわけですが...

Python2.x
$ python -m SimpleHTTPServer
Python3.x
$ python3 -m http.server

これすら覚えられずに毎回検索しているわけで...

$ touch ~/.local/bin/httpserv
$ chmod +x ~/.local/bin/httpserv
$ $EDITOR ~/.local/bin/httpserv
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Usage: httpserv [PORT=8000]

import sys
try:
    # py2
    import SimpleHTTPServer
    SimpleHTTPServer.test()
except ImportError:
    # py3
    import http.server
    if sys.argv[1:]:
        port = int(sys.argv[1])
    else:
        port = 8000
    http.server.test(port=port)

コマンド化すると補間も効くので大丈夫だろうと言うことです。

2018-08-25

HerokuのDocker Deployで機械学習APIを5ステップで作成

HerokuでDockerが使えるようになっていたので、遅まきながら、機械学習を使ったAPIを5ステップで作成してみた。

STEP1: 事前準備

herokuへサインアップし、APPを作成。
また、herokuコマンドとdockerをインストールして使えるようにしておく。

STEP2: サンプルコードを入手

heroku公式のminicondaイメージのサンプルコードがあるので、コレをベースに使用。

$ git clone https://github.com/heroku-examples/python-miniconda.git

ざっと中身を確認すると、

  • python 2.7
  • miniconda環境構築済み (condaコマンドが使える)
  • scikit-learn導入済み
  • webフレームワークはflask
  • gunicornでwsgi
  • ポート番号は環境変数PORTで指定

となっていた。

STEP3: APIを実装

heroku用だろうと特に変わることはないので、いつものようにサクッと実装する。

開発中にライブラリを追加した場合は、Dockerfileへの反映も忘れずに。

pandas等condaで追加するもの -> Dockerfile内のconda installに追記
pipでインストールするもの -> requirements.txt

STEP4: ローカルで動作チェック

Dockerコンテナを起動して動作を確認する。

引数指定がめんどうなので、docker-compose.ymlを作成してdocker-composeを使用することにする。

# docker-compose.yml
version: "2"
services:
  app:
    build: .
    ports:
      - "8100:8100"
    environment:
      PORT: "8100"

コンテナ起動とブラウザで動作チェック。

$ docker-compose up
$ xdg-open http://localhost:8100

STEP5: herokuへデプロイ

ここからがherokuで行う操作となるが、いくつかコマンドを打つだけで本番反映まで行える。素晴らしい。

$ heroku login
Email: nobody@somewhere.com
Password: ********
$ heroku container:login
$ heroku container:push web -a アプリ名
$ heroku container:release web -a アプリ名

以上で完了。

終わり

Heroku の Docker Registry & Runtime (Docker Deploy)は、ほぼストレスフリーで簡単に使用できる完成度の高いプラットフォームと思う。

詰まったところといえば、サンプルコードのREADME.md記載の手順が少し古かったところぐらい。
container-registry用のpluginをインストールするよう書かれていたが、現在は不要。

作ったもの

結局、何を作ったかというとIPO銘柄の初値予想APIを作った。
過去のIPOデータを学習させてRidge回帰で暴騰率を予測させている。スコア的には残念だが、雰囲気はでていると思う。

ブラウザの場合は、http://ipo-cal.herokuapp.comへアクセスするとGUIで試せる。

API本体はhttp://ipo-cal.herokuapp.com/api/predictで公開している。
curlだと以下のように呼び出す。

$ curl -X POST \
  -H "Content-type: application/json" \
  -d '[{"code":"4385", "market":3, "sector":25, "price": 3000.0, "fund":122142.9, "kobo":18159.5, "uri":22554.8}]' \
  http://ipo-cal.herokuapp.com/api/predict

{"data":[{"code":"4385","inflation":189.6046720498996,"price":5700,"rank":3}],"status":"success"}

json文字列をhttp://ipo-cal.herokuapp.com/api/predictへpostするだけでOK。
配列にして一度に50件までまとめて実行できる。

レスポンスはJsendに概ね準拠している。

レスポンスがなかなか返ってこない時は、サーバーがスリープしている。コンテナが起動するまでしばらくかかるので、10秒ほど待機してからリトライしてみて欲しい。

2011-03-14

Windows7 x64でdulwich-0.7.0のビルドが通らなかった

mercurialでgitレポジトリをcloneできるようにするhg-gitプラグインをインストールしたときのメモ。

$ easy_install hg-git

Windows7 x64 + VC2008 + python2.7環境でeasy_installすると依存ライブラリdulwichのビルドで...

running build
running build_py
running build_ext
building 'dulwich._diff_tree' extension
C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\BIN\amd64\cl.exe /c /nologo /Ox /MD /W3 /GS- /DNDEBUG -Idulwich -IC:\usr\local\python2\include -IC:\usr\local\python2\PC /Tcdulwich/_diff_tree.c /Fobuild\temp.win-amd64-2.7\Release\dulwich/_diff_tree.obj
_diff_tree.c
dulwich/_diff_tree.c(267) : error C2065: 'mode_t' : 定義されていない識別子です。
dulwich/_diff_tree.c(267) : error C2146: 構文エラー : ')' が、識別子 'lmode' の前に必要です。
error: command '"C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\BIN\amd64\cl.exe"' failed with exit status 2

こけました...

2011-03-09

pythonでgoogleにログインする

insightとかinsightとかapiが公開されていないGoogleサービスにpythonでログインする方法をメモ。
その他のサービスは素直に gdata-python-client を使ったほうがイイ。

手順は以下の通り

2009-11-13

Trac-0.11.5.ja1をmod_wsgiで動作させるとメニューが表示されない

Trac-0.11.5.ja1をmod_wsgiで動作させるとメニューが表示されない症状は以下の方法で暫定対処。tracdでは問題ないのでmod_wsgi側の問題ぽいが・・・。

layout.htmlの53行目からfallbackを削除
--- <xi:include href="$chrome.theme"><xi:fallback /></xi:include>
+++ <xi:include href="$chrome.theme"></xi:include>

参照:
http://trac.edgewall.org/ticket/7463