デコレータの基本について理解したのでまとめる

Pythonのデコレータに関して、理解していることをまとめてみる。

@を使ったデコレータの書き方はただのシンタックスシュガー (読み書きのしやすさのために導入される構文)。

それでは、何に対するシンタックスシュガーなのか。

まず、以下のようなプログラムがあるとする。

def egg():
    return "egg"

if __name__ == '__main__':
    print(egg()) # egg

実行すると、当たり前だが以下の様な結果になる。

egg

次に、先ほどのファイルにscrambleというメソッドを追加する。

def scramble(egg):
    def _scramble():
        return "scramble " + egg() + "!"
    return _scramble

def egg():
    return "egg"

if __name__ == '__main__':
    scramble_egg = scramble(egg)
    print(scramble_egg()) # scramble egg!

scrambleメソッドは、オブジェクトを1つ受け取り、内部に定義されている_scrambleメソッドを返している。 つまり、scrambleメソッド自体は引数に対して何かを行うことはなく、 ただ内部のメソッドを返しているだけの機能しか持っていない。

返される内部メソッドの_scrambleは、scrambleメソッドが受け取ったの返り値に、 "scramble "と"!"という文字列を装飾(デコレート)して返している。

if __name__ == '__main__'では、scrambleメソッドにeggメソッド渡し、 返り値をscramble_eggという変数に格納している。 scrambleメソッドの返り値は_scrambleのため、scramble_egg変数は _scrambleメソッドの内容と同じ挙動をする。

そのため、ファイルを実行すると以下の様な実行結果になる。

scramble egg!

さて、if __name__ == '__main__'の中ではscrambleメソッドの返り値はscramble_eggという変数に格納していた。 ここで、すでに定義されているeggメソッドにscrambleの返り値を格納してみる。 Pythonはメソッドも変数も全てがオブジェクトのため、こういうことができる。

def scramble(egg):
    def _scramble():
        return "scramble " + egg() + "!"
    return _scramble

def egg():
    return "egg"

if __name__ == '__main__':
    egg = scramble(egg) # この部分と
    print(egg())        # この部分

実行結果は上記と同じscramble egg!

eggに対してscrambleメソッドの返り値を格納しているため、eggメソッドは 元の「"egg"という文字列を返す」という機能が上書きされている。 しかし、ただ上書きされたわけではなく、scrambleメソッドに元の機能を引数として渡しているため、 「"egg"という文字列を返す」機能は、_scramble内で使われることになる。

この状態におけるscrambleメソッドがデコレータとなる。 文字通り別のメソッドを装飾することができる。

これまでの手順を@を使って書き換えると、以下のようなコードになる。

def scramble(egg):
    def _scramble():
        return "scramble " + egg() + "!"
    return _scramble

@scramble
def egg():
    return "egg"

if __name__ == '__main__':
    print(egg()) # scramble egg!

Webサーバをapacheからnginxに移行

今までapacheで動かしていたWebサーバをnginxにようやく切り替えた。 その中でしたnginxの設定とか詰まったところとかメモ。

nginxの設定

全体に関わる設定

http://www.nametake.infoのように、特に設定をしていないサブドメインに アクセスがあっても、全部nametake.infoのページと同じ内容が表示されるようにしたい。 ということで、以下のようにdefault.confファイルを書いて、デフォルトのバーチャルホストを設定する。 また、server_name_にすることで、catch-allサーバとして設定。

default.conf
server {
    listen          80 default_server;
    server_name     _;
    root            /var/www/html;
    ~~~略~~~
}

あとは、nginxのバージョン番号とかOSの情報をレスポンスヘッダに含めたくないので、以下をnginx.confに追記。

nginx.conf
http {
    ~~~略~~~
    server_tokens off;
}

SSLに関わる設定

やりたかったこと

  • nametake.infoサブドメインで動かしているownCloudとかampacheの通信経路を暗号化したい
  • 設定ファイルはサービスごとに分割したい
  • owncloud.confとかampache.confとかに証明書の情報をいちいち書きたくない

やったこと

以下のようにSSL全体の設定も記述(と言ってもほとんどexample_ssl.confのまんま)。 サーバ証明書秘密鍵は、apacheを設定した時に作成したオレオレ証明書があるので、それをそのまま流用。

変更したのはlistenの部分のdefault_serverと、server_nameの部分のバーチャルホスト。

ssl.conf
server {
    listen       443 ssl default_server;
    server_name  .nametake.info;

    ssl_certificate         /my/cert/file;
    ssl_certificate_key     /my/cert/key/file;

    ~~~略~~~
}

上記のようにssl.confとしてSSL全体の設定ファイルを作成することで、それぞれのサービスで SSLを使用するときは、以下のように記述するだけでOK。

owncloud.conf
server {
    # 80番ポートでアクセスしてきたら443番ポートにリダイレクト
    listen 80;
    server_name cloud.nametake.info;
    # enforce https
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl;
    server_name cloud.nametake.info;
    ~~~略~~~
}

SSL通信したいサーバでlisten 443 sslと書くだけ。これで個別のファイルに証明書の情報を書かなくてよくなる。

サービスごとの細かいnginxの設定は[nginx (サービス名)]とかで検索して出てきた結果をコピペ。

詰まったところ

ownCloudのログイン画面でリダイレクトループする

原因

/var/lib/php/session/パーミッションapache:apacheのままになっていた。

解決策

/var/lib/php/session/パーミッションnginx:nginxに変更。

こんなところのパーミッションの設定もapacheになっていたのは想定外だった…… (serverのrootに指定したディレクトリの所有者がapacheのままになっていたのに気付かなくて30分ぐらい悩んだのは内緒)

.gitignoreの範囲の指定方法

過去に書いた記事をそのまま移行します。

前提

$HOMEディレクトリ上でドットファイルをgitを管理している。 .gitignoreはホワイトリスト方式で記述している。

やりたかったこと

$HOME
└── .vim
     ├── bundle
     ├── snippet
     ├── syntax
     ├── template
     └── userautoload

上記の構造になっているvimの設定ファイルのbundleディレクトリ以外をgitの管理下に置きたい。 (bundleディレクトリだけはgitで管理したくない)

やったこと

ホワイトリスト方式で.gitignoreを設定したが、階層構造をとっている時の設定でつまずいた。

ダメだったパターン

以下のように記述したら$HOME/.vim/以下のディレクトリ内部のファイルが読み込まれなかった。

# all file ignore
/*
/.*

# target not ignore
!/.gitignore
!/.vimrc
!/.vim/template/template.*
!/.vim/userautoload/*.vim
!/.vim/snippet/*.vim
!/.vim/syntax/*.vim

よく考えると、上の3行ですべてのファイルとディレクトリを場外しているため、 $HOME/.vimディレクトリがそもそもgitから見えない。 そのため、!/.vim/userautoload/*.vimのようにファイルを指定しても、 !/存在しないディレクトリ/userautoload/*.vimとなっているので、 読み込まれることがなかった。

成功したパターン

以下のように記述したら成功した。

# all file ignore
/*
/.*

# target not ignore
!/.gitignore
!/.vimrc

# .vim directry
!/.vim/
/.vim/*

!/.vim/template/
!template.*

!/.vim/syntax/
!*.vim

!/.vim/snippet/
!*.vim

!/.vim/userautoload/
!*.vim

やっていることは、

  1. !/.vim/$HOME/.vim/ディレクトリをホワイトリストに追加
  2. /.vim/*$HOME/.vim/以下のファイルとディレクトリを除外
  3. !/.vim/userautoload/で該当のディレクトリをホワイトリストに追加
  4. !*.vim*.vimファイルをホワイトリストに追加

という感じ。

ただ、一つのファイルで管理すると見栄えが悪かったので以下のように分割。

# $HOME/.gitignore
# all file ignore
/*
/.*

# target not ignore
!/.gitignore
!/.vimrc
!/.vim/
# $HOME/.vim/.gitignore
# all file ignore
/*
/.*

!/.gitignore

# target not ignore
!/template/
!template.*

!/syntax/
!*.vim

!/snippet/
!*.vim

!/userautoload/
!*.vim

これで管理しやすくなった。