たけし備忘録

自分の好奇心の赴くままに勉強メモ LL系が大好き Python bash Julia C

PyAudioの基本メモ2 音声入出力

PyAudioの基本メモ1の続きです。

今回はPyAudioの基本的な使い方を書きます。
ほとんどPyAudio: PortAudio v19 Python Bindingsの例題のままです。

音声の出力

import pyaudio
import wave

CHUNK = 1024
filename="好きなwavファイル"

wf = wave.open(filename, 'rb')

p = pyaudio.PyAudio()

stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
                channels=wf.getnchannels(),
                rate=wf.getframerate(),
                output=True)

""" 
   format  : ストリームを読み書きするときのデータ型
   channels: ステレオかモノラルかの選択 1でモノラル 2でステレオ
   rate    : サンプル周波数
   output  : 出力モード

"""

# 1024個読み取り
data = wf.readframes(CHUNK)

while data != '':
    stream.write(data)          # ストリームへの書き込み(バイナリ)
    data = wf.readframes(CHUNK) # ファイルから1024個*2個の

stream.stop_stream()
stream.close()

p.terminate()

これで好きなwavファイルを再生できる

音声の入力

import pyaudio
import wave

CHUNK = 1024
FORMAT = pyaudio.paInt16 # int16型
CHANNELS = 2             # ステレオ
RATE = 44100             # 441.kHz
RECORD_SECONDS = 5       # 5秒録音
WAVE_OUTPUT_FILENAME = "output.wav"

p = pyaudio.PyAudio()

stream = p.open(format=FORMAT,
                channels=CHANNELS,
                rate=RATE,
                input=True,
                frames_per_buffer=CHUNK)

print("* recording")

frames = []

for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
    data = stream.read(CHUNK)
    frames.append(data)

print("* done recording")

stream.stop_stream()
stream.close()
p.terminate()

wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames))
wf.close()

frames_per_bufferがよくわからん
とりあえず1024にしておけば大丈夫そう

リアルタイムで音声入出力

マイク入力した音声をリアルタイムでスピーカーから出力します。
マイクはOSで設定したデフォルト、スピーカーもOSで設定したデフォルトの機器を使います。
※注意! イヤホンをしていないとハウリングして耳がキィーンとします。

# -*- coding:utf-8 -*-

import pyaudio

CHUNK=1024
RATE=44100
p=pyaudio.PyAudio()

stream=p.open(	format = pyaudio.paInt16,
		channels = 1,
		rate = RATE,
		frames_per_buffer = CHUNK,
		input = True,
		output = True) # inputとoutputを同時にTrueにする

while stream.is_active():
	input = stream.read(CHUNK)
	output = stream.write(input)
	
stream.stop_stream()
stream.close()
p.terminate()

print "Stop Streaming"

リアルタイムで音声加工

マイク入力した音声をリアルタイムで加工して出力します。

# -*- coding:utf-8 -*-

import pyaudio

CHUNK=1024*2
RATE=44100
p=pyaudio.PyAudio()

stream=p.open(	format = pyaudio.paInt16,
		channels = 1,
		rate = RATE,
		frames_per_buffer = CHUNK,
		input = True,
		output = True) # inputとoutputを同時にTrueにする


def audio_trans(input):
    # なんかしらの処理
    return ret

while stream.is_active():
	input = stream.read(CHUNK)

        input = audio_trans(input)

	output = stream.write(input)
	
stream.stop_stream()
stream.close()
p.terminate()

print "Stop Streaming"

加工する場合はCHUNKを2倍の2048にして少し処理を遅くしました。

なぜならばまず、アウトプットする時のストリームへの書き込みに必要となる時間がCHUNK=1024とすると
(書き込みに必要な時間)=CHUNK/RATE=0.023[sec] でした。
timeモジュールで測定してみましたが確かにそのくらいの時間のようです。
ストリームから音声を読み取る時間と入力音声を加工する処理の時間の合計が、書き込みに必要な時間より遅いと

音声出力中→→→終了 空白の時間   音声出力...→→
読み取り→→→→加工 処理中......... 書き込み

              ↑
              ここで空白の時間(音声が出ない→声が途切れて聞こえる)

という現象が起こりました。(わかりにく)
この空白の時間が人間が感じ取れないほど短い時間(またサンプル周期よりも小さい時間)であればプツプツ音は出ないようです。 
また読み込みよりも書き込みのほうが圧倒的に時間がかかるようなのでCHUNKを大きくしても書き込み時間だけを気にしていれば平気そうです。
このため、CHUNKを2倍にして書き込みに必要な時間を 0.046[sec]としました。
読み取り時間+処理時間 がテキトーに処理を書いた(ピッチ抽出など)ときおよそ 0.03[sec]程度だったのでこれで空白の時間はなくなりました
が、入力から出力の遅れ時間は当たり前のごとく増えました。
適宜CHUNK幅を調整して遅れさせてください。

PyAudioの基本メモ1

PyAudioの使い方を時々忘れてしまうのでメモ書き
用語の名前は私が勝手に読んでいる名前もあるので一般的な呼び方では無いこともあります。

基本的な用語

音源
 基本的には全てバイナリデータ(16進数 0x13 0x76など)

静的な音源
 wavファイル, rawファイル, mp3ファイルなど

動的な音源
 マイクに現在入力している音声など

wav, raw, mp3の違い
 通常の静的な音源は

 ヘッダ(サンプル周波数などのメタデータ)
       +
   ボディ(音源の本体)

 という構成になっている。

 rawファイル→ヘッダ無し, ボディ有り
 wavファイル→ヘッダ有り, ボディ有り
 mp3ファイル→ヘッダ有り, ボディ有り

 htmlみたいなもんだと思っていいかもしれない。たまにフッタも付いてるやつもある。

wavファイルとmp3ファイルの違い
 wavファイルはrawファイルにヘッダを付けたもの
 mp3ファイルは音源のある周波数(44.1kHz? 22.05kHzだったかな)以上の音をカットしてサイズ小さくしている。

 まとめると
 wavファイル→生音声
 mp3ファイル→加工された音声(音楽聞けりゃオッケー程度)

動的な音源からの読み込み
 後述するチャンクというサイズごとに読み取る必要がある。

静的な音源からの読み込み
 チャンク毎に読み取ってもいいし、全部一気に読み取ってもOK。

ストリーム
 データストリームとも言う。ストリーミング方式の動画などがある。データの流れ。
 音声で言うと、マイクからは常に音声データが流れて来ていて、ストリームに流れている。河みたいなものだと思えばいい。
 ストリームに書き込むとは 河にデータを流すこと
 ストリームを読み込むとは 河からデータを取ってくること
 

パラメータなどの用語

チャンク(CHUNK)
 音源から1回読み込むときのデータサイズ。1024(=2の10乗) とする場合が多い

サンプル周波数
 サンプルレートだとか、フレームレートとも言われる。よくfsとかsrと書く。次元は[個/sec]。
 データを読み取るときに、何秒ごとに1点のデータを取ってくるかを決める値。
 逆数はサンプル周期と言われTsなどと書く。
 よく使われるのは 44.1kHz, 16kHzなど。

 (例) fs = 44.1[kHz]の場合
   1/fs=Ts=22.7[μsec]毎に1点データを取ってくる。
 
 たまにナイキスト周波数とか言われることがあるけどナイキスト周波数はサンプル周波数の半分の周波数のこと(標本化定理)。

フレーム数
 少なくともPythonのwaveモジュール上ではバイナリデータの個数。
 0x00 などが1024個入ってるデータは1024フレームと言う。
 たぶん分野だとか動画だとかそういうジャンルによって使われ方が違う。
 よくわかってない。

音源の秒数の計算
 音源の秒数の計算には
 サンプル周波数 バイナリデータ数
 の2つが必要。
 
 (秒数)=バイナリデータ数/サンプル周波数

 という計算で出せる。

 (例) CHUNK個のデータをfsというサンプル周波数で取ってきた。
  (秒数)=CHUNK/fs

読み込んだバイナリデータを数値に変換

 マイクから音声を読み込んで、プロットする時の基本的な動きは

 1. ストリームから音声をCHUNK(ここでは1024とする)の長さでバイナリ形式で読み込む

 2. 読み込んだバイナリデータはCHUNKの2倍の長さの2048(罠!)

 3. バイナリデータをint16型に変換
  ただしint16型に変換するのが通常なだけでfloat64型などにも変換できる。意味は無い。

 4. int16型に変換すると長さがCHUNKと同じになる
   バイナリ2つ "0x16 0x08" などのセットで1つの値になる。
   バイナリ1つは8bitなのでint16の16bit数値にするには2つ分必要。なので2048個のバイナリデータを先頭から2つずつint16型の数値に変換すると全体として1/2のサイズ、1024個のint16データになる。
   意味は無いがfloat64型に変換すると8個ずつ読み込んでfloat64の数値にするので2048個の1/8のサイズになる。

 5. int16に変換した1024個のデータをプロット
  フィルタなどを掛けるときはint型に戻してからのほうがPythonでは扱いやすい(そもそもバイナリのままバイナリとして扱えるのか?)


 

Pythonでtreeコマンド実装

Windowsにもtreeコマンドがありますがなんとも気に入らなかったので自分で使う用にtreeコマンドを実装しました。
bashだとかCだとかでやったほうが日本語周りはかなり楽なんだろうけど、あえて日本語周りが大変なpythonで練習。
普段あんまりpythonで日本語扱うことが少ないのでいい機会かもしれない。
ということで獣のごとくテキトーに思うまま実装しました。保守性は皆無です。

コマンドプロンプト文字コードはcp932で、Cygwinは確かめて無いので知りません。変えてないからデフォルトのままのはず。

以下よりソースコード

[tree.py]

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import os
import sys

def print_enc(str):
    try:
        print str
    except:
	print str.encode("utf-8")

def tree(path = u".", d = 0, depth = 2, tab=u""):
	#深さの指定で再帰中断
	if d==depth:
	    return
	else:
	    #指定path下(未指定でカレントディレクトリ)のフォルダとファイルを取得
            file_list = []
	    dir_list = []
	    for p in os.listdir(path):
	        if os.path.isfile(path+u"/"+p):
		    file_list.append(p)
		if os.path.isdir(path+u"/"+p):
		    dir_list.append(p)
			
	    #ルートを表示
	    if d==0:
		print u"/%s" % os.getcwd().split("\\")[-1]
		
	    #ファイルの表示
	    for i in xrange(len(file_list)):
		if i!=len(file_list)-1:
		    print tab+u"┣━",
		    print_enc(file_list[i])
		else:
		    if len(dir_list)==0:
			print tab+u"┗━",
			print_enc(file_list[i])
		    else:
			print tab+u"┣━",
			print_enc(file_list[i])

	    #ディレクトリの表示
	    for i in xrange(len(dir_list)):
		if i!=len(dir_list)-1:
		    print tab+u"┣━",
		    print_enc(dir_list[i])
		    tree(path = path+u"/"+dir_list[i], d=d+1, depth=depth, tab=tab+u"┃\t")
		else:
		    print tab+u"┗━",
		    print_enc(dir_list[i])
		    tree(path = path+u"/"+dir_list[i], d=d+1, depth=depth, tab=tab+u"  \t")
            
            return


if __name__=="__main__":
    if len(sys.argv)==2 and sys.argv[1]:
        tree(depth=int(sys.argv[1]))
    else:
        tree()


すんごい見づらいし汚い感じですが一応動きました。
ただ、使うターミナルの設定だとかlessを使った場合だとかで表示がかなり違うのでpythonだとめんどいなぁという印象。3系だと文字周りが簡単になったとか聞くけどどうなんだろうか。
とりあえず

  • 標準モジュールだけで作成
  • 日本語を表示できること(特殊文字は除く)

が実現できたのでいいかな

自分で使う用なのでexe化してしまってテキトーにpath通してしまえばいいや
使い方は(現時点では)

$python tree.py 3

という形で使います。
コマンドライン引数の3は表示するディレクトリの深さです。上の場合3階層まで表示します。1でカレントディレクトリ内を表示します。
デフォルトでは2になっています。

以下より様々な表示結果


[テスト用フォルダ]
f:id:takeshiD:20151218024604p:plain

[コマンドプロンプト(引数:デフォルト)]

python tree.py

f:id:takeshiD:20151218024616p:plain

[コマンドプロンプト(引数:1)]

python tree.py 1

f:id:takeshiD:20151218024620p:plain

[コマンドプロンプト(lessをパイプでつなげた状態)]

python tree.py | less

f:id:takeshiD:20151218024624p:plain

[コマンドプロンプト(引数:4)]

python tree.py 4

f:id:takeshiD:20151218024627p:plain


[Cygwin(引数:デフォルト)]

python tree.py

f:id:takeshiD:20151218024632p:plain

[Cygwin(lessをパイプでつなげた状態)]

python tree.py | less

f:id:takeshiD:20151218024635p:plain


lessをつなげたときだけなぜか特殊文字の♡まで表示出来たのはなんでだろう。しかもコマンドプロンプトの時だけ。
lessコマンドは双方ともCygwin内蔵のlessから持ってきているので文字コードの違いなんだろうなきっと。

コマンドとして使うときディレクトリ指定もできたらいいのですがコマンドライン引数のなんちゃらが面倒でした。
調べた感じだと組み込みでargparseというモジュールがあるらしいのでいずれいじってみたいです。
めんどうだからここまでで終わりにしました。

Bottle日本語ドキュメント(自分用) 記事一覧まとめ

チュートリアル

この記事は、下記の情報を訳したものです。
Tutorial — Bottle 0.13-dev documentation

 このチュートリアルでは、ボトルのWebフレームワークの概念と機能を紹介し、同様に基本的かつ高度なトピックをカバーしています。あなたが最初から最後までそれを読んで、またはそれ以降のリファレンスとして使用することができます。自動的に生成されたAPIリファレンスにも、あなたにとっては興味深いかも知れません。

 それ以上の詳細について説明しますが、このチュートリアル未満を説明しています。最も一般的な質問のためのソリューションは、当社のレシピ集やFAQページに記載されています。何か助けが必要な場合は、私たちのメーリングリストに参加したり、私たちのIRCチャン​​ネルで私たちをご覧ください。


※注意※これらの記事は自分用に翻訳したものですので翻訳ミスその他については一切責任は負いません。
また、翻訳も中途半端な状態です。今後修正していきます。(2015/12/16)

9.展開と用語集

展開

ボトルは、デフォルトでは、組み込みのwsgirefのWSGIServerで実行されます。この非スレッドHTTPサーバは、開発と早期の生産のための完全にいいのですが、ときに、サーバーの負荷が増大するパフォーマンスのボトルネックになる可能性があります。
パフォーマンスを向上させる最も簡単な方法は、ペーストまたはCherryPyにのようなマルチスレッド·サーバー·ライブラリをインストールし、その代わりに、シングルスレッド·サーバの使用するように瓶を伝えることです。

bottle.run(server='paste')

展開:これは、多くの他のデプロイメント·オプションは、別の資料に記載されている


用語集


callback

いくつかの外部アクションが発生したときに呼び出されることであるプログラマのコード。 Webフレームワークのコンテキストでは、URLのパスとアプリケーションコードの間のマッピングは、多くの場合、URLごとにコールバック関数を指定することで実現されています。

decorator
別の関数を返す関数は、通常@デコレータ構文を使用して関数変換として適用されます。デコレータについての詳細は、関数定義のためにPythonのドキュメントを参照してください。

environ

ルートの下のすべてのドキュメントに関する情報が保存され、相互参照に使用される構造。環境は、連続失点だけで新機能と変更されたドキュメントを読み、解析する必要がありますので、解析ステージの後のピクルスです。

handler function

いくつかの特定のイベントや状況を処理する関数。 Webフレームワークでは、アプリケーションはアプリケーションを構成するそれぞれの特定のURLのコールバックとしてハンドラ関数をアタッチすることにより開発されています。

source directory

そのサブディレクトリを含むディレクトリは、1つのスフィンクスプロジェクトのすべてのソースファイルが含まれています。



関連リンク

8.開発

開発

基礎を学び、独自のアプリケーションを書きたいかと思った時?あなたがより生産的に、役立つかもしれないいくつかのヒントはここにある。


デフォルトのアプリケーション

ボトルはボトルインスタンスのグローバル·スタックを維持し、モジュールレベルの関数やデコレータのいくつかの既定値としてスタックの一番上を使用しています。ルート()デコレータは、例えば、デフォルトのアプリケーションにBottle.route()を呼び出すためのショートカットです。

@route('/')
def hello():
    return 'Hello World'

この小さなアプリケーションのために非常に便利で、あなたにいくつかの入力を保存しますが、また、モジュールがインポートされるとすぐに、ルートがグローバルなアプリケーションにインストールされている、ことを意味します。インポートこの種の副作用を避けるために、ボトルには、アプリケーションを構築するための第二に、より明示的な方法を提供しています。

app = Bottle()
@app.route('/')
def hello():
    return 'Hello World'

アプリケーションオブジェクトを分離することも、再利用性、多くを向上させます。他の開発者が安全にモジュールからアプリケーションオブジェクトをインポートして、一緒にアプリケーションをマージするBottle.mount()を使用することができます。
代替手段として、あなたはまだ便利なショートカットを使用している間に経路を分離するためにアプリケーション·スタックの使用を行うことができます。

default_app.push()
@route('/')
def hello():
    return 'Hello World'
app = default_app.pop()

アプリ()とdefault_app()の両方がAppStackのインスタンスであるとスタックのようなAPIを実装しています。あなたから、必要に応じてスタックにプッシュアプ​​リケーションとポップすることができます。あなたが別のアプリケーションオブジェクトを提供していないサードパーティ製のモジュールをインポートしたい場合にも役立ちます。

default_app.push()
import some.module
app = default_app.pop()



モードのデバッグ

初期の開発中に、デバッグモードでは、非常に役立つことがあります。

bottle.debug(True)

このモードでは、ボトルははるかに冗長であり、エラーが発生するたびに有用なデバッグ情報を提供します。それはまたあなたの邪魔になるかもしれないいくつかの最適化を無効にし、可能な限り設定ミスについて警告をいくつかのチェックを追加します。
ここで、デバッグモードで変更するものの不完全なリストは、次のとおりです。
デフォルトのエラーページでは、トレースを示しています。
テンプレートはキャッシュされません。
プラグインはすぐに適用されます。
ただ本番サーバー上でデバッグモードを使用しないことを確認してください。


リロードオート

開発中に、あなたの最近の変更をテストするためにサーバーをたくさん起動する必要があります。自動リロード機能はあなたのためにこれを行うことができます。あなたは、モジュールファイルを編集するたびに、リロード機能·サーバー·プロセスが再起動し、コードの最新バージョンをロードします。

from bottle import run
run(reloader=True)

どのように動作します:メインプロセスはサーバを起動するが、メインプロセスを開始するために使用されているのと同じコマンドライン引数を使用して、新しい子プロセスを生成しません。すべてのモジュールレベルのコードは、少なくとも二回実行されます!注意してください。
子プロセスはos.environ BOTTLE_CHILD?がtrueに設定されていると通常の非リロードアプリケーションサーバとして起動します。すぐにロードされたモジュールの変更のいずれかと同様に、子プロセスは終了し、メイン·プロセスによって再生成された。テンプレートファイルの変更は、リロードをトリガしません。テンプレートキャッシュを無効にするデバッグモードを使用してください。
リロードは、子プロセスを停止する能力に依存している。あなたがsignal.SIGINTをサポートしていないWindowsまたは他のオペレーティングシステムPythonでとKeyboardInterruptが発生している)上で実行されている場合、signal.SIGTERMは子を殺すために使用されています。終了ハンドラやfinally節などは、SIGTERM後に実行されないことに注意してください。


コマンドラインインタフェース

バージョン0.10以降では、コマンドラインツールとしてボトルを使用することができます。

$ python -m bottle
Usage: bottle.py [options] package.module:app
Options:
-h, --help show this help message and exit
--version show version number.
-b ADDRESS, --bind=ADDRESS
bind socket to ADDRESS.
-s SERVER, --server=SERVER
use SERVER as backend.
-p PLUGIN, --plugin=PLUGIN
install additional plugin/s.
--debug start server in debug mode.
--reload auto-reload on file changes.

localhostにポートのペアとデフォルト値:8080 Addressフィールドは、IPアドレスまたはIPアドレスを取ります。他のパラメータは自己説明的であるべきです。
プラグインとアプリケーションの両方がインポート式を介して指定されています。これらは、コロンで区切られ、そのモジュールの名前空間に評価するのimportパス(例えばpackage.module)および式から構成されています。詳細については、load()を参照してください。ここではいくつかの例は、次のとおりです。

# Grab the 'app' object from the 'myapp.controller' module and
# start a paste server on port 80 on all interfaces.
python -m bottle -server paste -bind 0.0.0.0:80 myapp.controller:app
# Start a self-reloading development server and serve the global
# default application. The routes are defined in 'test.py'
python -m bottle --debug --reload test
# Install a custom debug plugin with some parameters
python -m bottle --debug --reload --plugin 'utils:DebugPlugin(exc=True)'' test
# Serve an application that is created with 'myapp.controller.make_app()'
# on demand.
python -m bottle 'myapp.controller:make_app()''



次の記事へ進む → 9.展開と用語集


関連リンク

7.プラグイン

プラグイン

バージョン0.9の新機能。
ボトルのコア機能は、ケースを使用する最も一般的なカバーですが、マイクロフレームワークとしてそれには限界がある。 "プラグイン"が遊びに来る場所です。プラグインは、フレームワークに欠けている機能を追加するサードパーティ製のライブラリを統合する、または単にいくつかの反復作業を自動化します。
我々は、使用可能なプラグインの成長のリストを持っており、ほとんどのプラグインは、アプリケーション間での移植性、再利用できるように設計されています。チャンスはあなたの問題がすでに解決されたことを高く、すぐに使用できるプラグインが存在しています。されていない場合は、プラグイン開発ガイドはあなたを助けるかもしれません。
エフェクトとプラグインAPIは、多様であり、特定のプラグインに依存しています。例えばSQLitePluginプラグインは、dbのキーワード引数を必要とするコールバックを検出し、新しいデータベース接続オブジェクトのコールバックが呼び出されるたびに作成されます。これは非常に便利なデータベースを使用できるようになります。

from bottle import route, install, template
from bottle_sqlite import SQLitePlugin
install(SQLitePlugin(dbfile='/tmp/test.db'))
@route('/show/<post_id:int>')
def show(db, post_id):
    c = db.execute('SELECT title, content FROM posts WHERE id = ?', (post_id,))
    row = c.fetchone()
    return template('show_post', title=row['title'], text=row['content'])
@route('/contact')
def contact_page():
''' This callback does not need a db connection. Because the 'db'
    keyword argument is missing, the sqlite plugin ignores this callback
    completely. '''
    return template('contact')

他のプラグインは、スレッドセーフなローカル·オブジェクトを移入し、要求オブジェクトの詳細を変更するには、コールバックによって返されるデータをフィルタ処理したり、完全にコールバックを回避する可能性があります。例えば、 "auth"というプラグインが有効なセッションをチェックし、元のコールバックを呼び出す代わりに、ログインページを返すことができます。正確には何が起こるかはプラグインに依存しています。


アプリケーション全体のインストール

プラグインは、アプリケーション全体または単に追加の機能を必要とするいくつかの特定のルートにインストールすることができます。ほとんどのプラグインは、安全にすべてのルートにインストールされ、その機能を必要としないコールバックにオーバーヘッドを追加しないように十分にスマートであることができます。
私たちは例えばSQLitePluginプラグインを見てみましょう。それだけでデータベース接続を必要とするルートのコールに影響を与えます。他のルートはそのままにしています。このため、我々は追加のオーバーヘッドがアプリケーション全体のプラグインをインストールすることができます。
プラグインをインストールするには、(インストール)を呼び出す最初の引数としてプラグインを使って:

from bottle_sqlite import SQLitePlugin
install(SQLitePlugin(dbfile='/tmp/test.db'))

プラグインはまだルートのコールには適用されません。これがないルートが失われていないことを確認するために延期されます。は、まずプラグインをインストールし、変更したい場合は、後でルートを追加することができます。インストールされているプラ​​グインの順序は、しかし、重要である。プラグインがデータベース接続を必要とする場合には、まずデータベースプラグインをインストールする必要があります。


プラグインのアンインストール

あなたは()以前にインストールしたプラグインをアンインストールするには、名前、クラス、またはインスタンスを使用することができます。

sqlite_plugin = SQLitePlugin(dbfile='/tmp/test.db')
install(sqlite_plugin)
uninstall(sqlite_plugin) # uninstall a specific plugin
uninstall(SQLitePlugin) # uninstall all plugins of that type
uninstall('sqlite') # uninstall all plugins with that name
uninstall(True) # uninstall all plugins at once

リクエストを処理しながら、プラグインがあっても、実行時に、いつでもインストールおよび削除することができます。これは、いくつかのきちんとしたトリックを(スロー·デバッグをインストールしたり、必要なプラグインだけをプロファイリング)を有効にしますが、使い古されてはいけません。たびにプラグインの変更点のリストは、ルートキャッシュがフラッシュされ、すべてのプラグインが再適用されます。

注意

モジュールレベルのインストール()とアンインストール()関数は、デフォルトのアプリケーションに影響を与えます。特定のアプリケーション用のプラグインを管理するには、ボトルのアプリケーションオブジェクト上の対応するメソッドを使用しています。


ルート固有のインストール

ルートの適用パラメータ()を使用すると、経路のごく少数にプラグインをインストールする場合は、デコレータに便利です。

sqlite_plugin = SQLitePlugin(dbfile='/tmp/test.db')
@route('/create', apply=[sqlite_plugin])
def create(db):
db.execute('INSERT INTO ...')



ブラックリストプラグイン

明示的にルートの数のプラグインを無効にすることができます。ルート()デコレータは、この目的のためのスキップ·パラメータがあります。

sqlite_plugin = SQLitePlugin(dbfile='/tmp/test.db')
install(sqlite_plugin)
@route('/open/<db>', skip=[sqlite_plugin])
def open_db(db):
    # The 'db' keyword argument is not touched by the plugin this time.

    # The plugin handle can be used for runtime configuration, too.
    if db in ('test', 'test2'):
        sqlite_plugin.dbfile = '/tmp/%s.db' % db
        return "Database File switched to: /tmp/%s.db" % db
    abort(404, "No such database.")

スキップ·パラメータは、単一の値または値のリストを受け入れます。あなたはスキップされるプラグインを識別するための名前、クラス、またはインスタンスを使用することができます。一度にすべてのプラグインをスキップする= Trueをスキップして設定します。


プラグインとサブアプリケーション

ほとんどのプラグインは、それらがインストールされたアプリケーションに固有のものです。その結果、彼らはBottle.mount()でマウントされたサブアプリケーションに影響を及ぼすべきではありません。次に例を示します。

root = Bottle()
root.mount('/blog', apps.blog)
@root.route('/contact', template='contact')
def contact():
    return {'email': 'contact@example.com'}
root.install(plugins.WTForms())

あなたがアプリケーションをマウントするたびに、ボトルには、サブアプリケーションに転送するすべての要求は、メインアプリケーションのプロキシ·ルートを作成します。プラグインはデフォルトでは、プロキシ経路この種の無効になっています。結果として、私たちの(架空の)WTFormsプラグインは/コンタクト経路に影響を与えますが、/ブログサブアプリケーションのルートには影響ありません。
この動作は、正気のデフォルトとして意図されていますが、オーバーライドすることができます。次の例は、再活性化し、特定のプロキシの経路のすべてのプラグイン
root.mount('/blog', apps.blog, skip=None)
しかし、思わぬ障害があります:プラグインは単一のルートとして、サブアプリケーション全体を見て、すなわち、プロキシルートは、上記の。サブアプリケーションの個々の経路に影響を与えるためには、明示的にマウントされたアプリケーションにプラグインをインストールする必要があります。


次の記事へ進む → 8.開発


関連リンク