Pythonライブラリパスをコントロールする

ネタが続くので連日投稿です

PYTHON_PATHとsys.path

さて、前回の話の中でPYTHONPATHを使ってインポートできるディレクトリを追加していたわけだが、これは最終的に sys.path に追加されて、インポート可能になっている。

PYTHONPATH=aodag.hoge python

とやれば、$PWD/aodag.hoge が sys.pathに追加されるのでインポートできるってわけさ。

もちろん、sys.pathにpythonコード内で直接パスを追加してしまっても、同じようにインポートできる。

import sys

import os

sys.path.append(os.path.join(os.getcwd(), 'aodag.hoge')

ってやれば、カレントディレクトリのaodag.hogeディレクトリ以下からインポート可能。

 

.pthファイルでライブラリパスに追加する

sys.pathに追加する方法はこれ以外にもあって、.pthファイルをsite-packagesかUSERSITEにおく方法もある。

hoge.pthとかの名前でファイルを作ってその中にファイルパスを書けば、sys.pathに追加される。

ファイルパスは / か ./ とかで始まる必要があって、それ以外の文字列で始まるとpythonコードとみなされて、実行される。

ここまでが、python自体の機能。

 

マルチバージョン

次はマルチバージョンな話。

setuptoolsとかdistributeに入ってるpkg_resourcesは、バージョンを指定してパッケージをアクティブにする機能を持っている。

まず、マルチバージョンでインストールされていないと、使う意味がないので、その方法から。

easy_install -m "bucho==0.1.0"

easy_install -m "bucho==0.0.5"

-m オプションを使うとマルチバージョンインストールとなる。

通常easy_install使うと、自動でeasy-install.pthに追加されてインポート可能になるけど、マルチバージョンインストールすると追加されない。

なので、そのままだと、せっかくインストールしたbuchoパッケージをインポートできない状態になっている。

ここで、pkg_resourcesで、使いたいバージョンのbuchoパッケージをアクティブにする。

import pkg_resources

pkg_resources.require("bucho==0.1.0")

import bucho

requireすることで、sys.pathに対象バージョンのパッケージへのパスが追加される。

そうした後でインポートすればいいわけだ。

 

setup.py develop とか pip でリポジトリからインストールとか egg-linkとか

ちなみにこの話をするきっかけとなった質問は、pipでhgリポジトリからインストールしたけど、libにソースがないという話。

この場合は、hg cloneしたソースを pip --editableしてるわけで、まあ、python setup.py develop も動きは同じ。

で、libにソースがないってことなんだけど、実はeasy-install.pthにソースへのパスが追加されるので、インポートが可能になってるんだね。

この話について回答したときに、.pthじゃなくて.egg-linkができると答えてしまったのだが、これは間違いだった。

では、.egg-linkってなんのためにあるの?っていうと、これはegg-infoのためにある。

egg-infoはライブラリパスとは別の話で、pkg_resourcesがとりあつかうパッケージメタデータのためにあって、普通インストールされると、site-packagesに、bucho-0.1.0.egg/PKG_INFO とか、 bucho/EGG_INFO ってファイルができるんだけど、developした場合はソースの方にこれらのファイルがあるので、そのポインタのためにegg-linkファイルを生成してる。

egg-linkを見ると、ソースへのパスが入ってるのが分かると思う。

pkg_resourcesはEGG_INFOとかを探すときにegg-linkを見つけたら、その先のソースパスにegg-infoを探しに行くってことだね。

わかったかな?ae35さん。

Pythonのnamespace

Pythonのnamespaceについて聞かれたので、おさらいとメモ

まずは、定義されているPEP

http://www.python.org/dev/peps/pep-0382/

namespaceの目的は別ディレクトリにあるパッケージ内のモジュールをそれぞれインポートできるようにすること。

具体的には、以下のような状況を考える。

aodag.hoge/aodag/__init__.py

 

aodag.hoge/aodag/hoge.py

aodag.fuga/aodag/fuga.py

aodag.fuga/aodag/__init__.py

といった構成があって、

PYTHON_PATH=aodag.hoge:aodag.fuga

とする。

で、pythonを実行してみると。

◯ import aodag

 

◯ import aodag.hoge

× import aodag.fuga

となる。

PYTHON_PATHの順番で、先にaodag.hogeディレクトリ以下のaodagパッケージが読み込まれてしまうので、aodag.fugaディレクトリのaodagパッケージが読み込まれない。

ここで aodag パッケージをnamespaceパッケージにすると、別ディレクトリにあるaodagパッケージ以下のモジュールもimportできるようになる。

それぞれのディレクトリの aodag/__init__.py 内にnamespece宣言を書く。

二種類方法があって、まずは、setuptools/distributeによる方法。

pkg_resources.declare_namespace(__name__)

次は、pkgutilを使う方法

__path__ = pkgutil.extend_path(__path__, __name__)

注意したいのは、どのaodagディレクトリでも __init__.py にnamespace宣言をしなきゃいけない。

なぜかというと、先にnamespace宣言のないパッケージがロードされてしまうと、他のディレクトリのパッケージがロードされないので、一番最初にロードされるパッケージにnamespace宣言が必要になる。そして、ロードの順序というのはインストールの順序や環境設定などで予測不能になってしまうからだ。

さて、これでどちらのディレクトリからもモジュールはロードできる。

上の例だと、aodag.hogeもaodag.fugaもロードできるってこと。

さて、同じaodag.*** のように扱えそうな物では、aodag/__init__.py モジュール(__init__ は名前なしモジュールみたいなものなので、import aodag といったようにパッケージ名でインポートする) の中に直接書いたオブジェクトがあり得る。

aodag/__init__.py で 関数funcを宣言したときに、from aodag import func ができるのだろうか?

これはできることもあるしできないこともある!?

なにを言っているのか?と思われそうだが、PYTHON_PATH=aodag.hoge:aodag.fuga ってなってるときは、aodag.hoge/aodag/__init__.py に書いた関数などは、インポート可能。

でも、aodag.fuga/aodag/__init__.py に書いた関数はインポート不可能。

ということでnamespace packageにしても、その直下の__init__.pyで宣言したオブジェクトは最初のパッケージのものしか使えない。

ようするにnamespace package以下には、モジュール以外のものを置いてはいけない。

さもなければ、環境毎にimportできたりできなかったりと不安定な状態になる。

あと、namespace packageの目的にあるように、別ディレクトリ間での同じパッケージをまとめるものであるので、全てのパッケージで、namespace宣言するのも間違いなんだね。

namespaceパッケージというのは、こういうもの。

feiz君、分かったかな?

 

 

Paste離れとかPyramid2動向とか

前回はPasteScriptの代わりにPaverでWSGIアプリを起動してみた。

ところで、pyramidはpyramid2でPython3対応を目標に掲げているのだが、そこでの検討事項の1つがPaste。

多分Pasteで使われているコードってauthtktくらいなんだよね。

ちなみにもう1つの検討事項がzope.component。

Pasteは、Python3対応するかどうかすら怪しい気配なので、代わりの物を考えないといけないわけだ。

有力っぽいのが、 pyramidのメイン開発者がこっそり?作っている wiseguy  

設定ファイルがYAMLになっているところが、なぜ?と思わせるが、設定内容がcolanderでスキーマ定義するところを見ると、スキーマにさえ適合するようにできれば、設定ファイルのフォーマットは交換可能になるんじゃないかと期待。

あと、 marrow っていうPasteをリファクタリングしてPython3とかWeb3(こっちもどうなってんの...)に対応させるプロジェクトもあって、marrow.blueprintが、paster createを置き換えるものになりそう。

pasterコマンドがだいたい create と serve しか使ってないような感じなので、まあそこだけ置き換えればいいよねってことなのか。setup.pyとかpaverとかfabricとかbuildoutレシピとか、細かいタスクのツールが充実してきていることもあるし、Pasteコマンドを作るのはそれほど重要視されていないようだ。

Scribefire

Bloggerによく書いてた頃は、Scribefire + Firefoxに大変お世話になった。

Chromeアプリケーションになっていたので試しに入れてみたら、posterousにも対応しているようだ。

最近posterousの投稿にはEvernoteで下書きしてそのままメール共有で投稿するって方法だったんだけど、これだと後から編集したときに面倒なんだよね。Posterousの使い方としてあんまり後から編集とかしないからそれほど問題に考えてなかったが、PyCon mini jp の資料あつめをしたときに気づいた。

Evernoteで下書きするのは、ボツにした場合でも資料として保存されるのがよいのでこれは継続したい。あとからの編集だけScrivefireでやるのかな。そうするとEvernoteに残らないんだよな。

どうしようねぇ。

pastescript離れを考える

From Evernote:

pastescript離れを考える

Pyramidも使ってるPasteScriptだけど、Pasteという結構大きめなライブラリに依存している。
そして、Pasteライブラリって大部分は既に使われていないんだよね。
ということで、WSGIに限らず細かなタスクをまとめるものとして、paverをもうちょっと使うことを考えた。
fabricでもいいかもしんない。

paverのタスクは、カレントディレクトリにあるpavement.py に定義します。
task デコレータで登録。

と、これが前回調べたときのおさらい。

今回はpaverからWSGIアプリケーションを実行させてみる。

WSGIアプリケーション実行

PasteScriptは内部でPasteDeployを使ってWSGIアプリをロードしている。
このとき、PasteDeployは指定された設定ファイルの内容を、app_factoryに渡す。

ということなので、WSGIアプリケーションをpkg_resourcesを使ってegg経由で呼び出す方法と、dotted_nameで呼び出す方法と二種類考える。

まずは、いきなり WSGIアプリケーションを取得する方法。
とりあえず、wsgiref.simple_server.demoapp を例にします。

文字列で渡されたパッケージ名からインポートするには、 __import__ 関数を使います。
また、インポート済のパッケージは、 sys.modules から取得できます。
オブジェクトは、getattr で取り出せばよいです。

from paver.easy import *

options(
    run_server={
        'port': '8080',
    },
)

@task
@cmdopts([
    ('appname=', 'a', 'wsgi application'),
    ('port=', 'p', 'port of httpd'),
])
def run_server(options, info):
    """ run wsgi application """
    dotted_name = options.appname
    port = int(options.port)
    mod, obj = dotted_name.split(":")
    __import__(mod)
    app = getattr(sys.modules[mod], obj)

    from wsgiref.simple_server import make_server
    httpd = make_server('', port, app)
    info('run wsgi application %s on port %d' % (dotted_name, port))
    httpd.serve_forever()

というところ。

しかし、これでは設定を切り替えたりできないので、 paste.app_factory エントリポイントを pkg_resources から取得して設定を渡したいところ。
appnameが、"egg:" で始まる場合は、pkg_resourcesで取得するようにします。
またコンフィグレーションファイルの内容をapp_factoryに渡してwsgiアプリを作成するようにします。
コンフィグレーションファイルのパーサーとして、configobjを利用することにします。

from paver.easy import *
import pkg_resources
import configobj

options(
    run_server={
        'port': '8080',
    },
)

@task
@cmdopts([
    ('appname=', 'a', 'wsgi application'),
    ('port=', 'p', 'port of httpd'),
    ('config=', 'c', 'configuration file'),
])
def run_server(options, info):
    """ run wsgi application """
    dotted_name = options.appname
    port = int(options.port)
    if dotted_name.startswith('egg:'):
        egg_name = dotted_name[len('egg:'):]
        distname, entryname = egg_name.split(':', 1)
        app_factory = pkg_resources.load_entry_point(distname, 'paste.app_factory', entryname)
        config = configobj.ConfigObj(options.config)
        app = app_factory(config.get('global', {}), **config[egg_name].dict())
    else:
        mod, obj = dotted_name.split(":")
        __import__(mod)
        app = getattr(sys.modules[mod], obj)

    from wsgiref.simple_server import make_server
    httpd = make_server('', port, app)
    info('run wsgi application %s on port %d' % (dotted_name, port))
    httpd.serve_forever()

PasteScript, PasteDeployの組み合わせだと、さらにWSGIミドルウェアの構成を変えられますが、そこまではさすがに手間なのでやらないことにします。

Python V!

From Evernote:

Python V!

地震ゆれゆれの中みなさまいかがおすごしでしょうか。
日本は大変な状況ですが、先週後半から昨日までPyCon USが開催されていましたね。
行きたかったです。(´・ω・`)

さて、pythonv という話がちらほらとDistutil SIG に流れています。
最近のPython開発環境では、ほぼ必須と思われる、virtualenv 相当の機能を本体にとりこむようです。
ひとまずの実装がbitbucketにあがっていたので、動かしてみました。
※ この確認時のリビジョンは、 e56f05883ceb でした。

まずは、ソースをとってきます。
本当にPythonのソースリポジトリをクローンしたものに、変更を入れてあるので、すごく時間がかかります。

$ cd cpythonv
$ hg up pythonv2
$ ./configure --prefix=/opt/python3.3a0-pythonv2
$ make
$ sudo make install

まだ、コマンドなどが用意されていないので、環境作成は自分でやらないといけません。

$ mkdir v2env
$ cd v2env
$ mkdir bin
$ cp /opt/python3.3a0-pythonv2/bin/python bin/
$ mkdir -p lib/python3.3/site-packages
$ touch pythonv.conf

これで、pythonv環境ができあがります。
site-packagesを確認してみます。

$ bin/python3
>>> import site
>>> site.getsitepackages()
['/Users/aodag/v2env/lib/python3.3/site-packages', '/Users/aodag/v2env/lib/site-python']

distributeをインストールします。

$ tar xvf distribute-0.6.15.tar.gz
$ cd distribute-0.6.15
$ ../bin/python3 setup.py install
$ ls ../bin
easy_install easy_install-3.3 python3

ちゃんと、pythonv環境のほうにインストールされてますね。
これが3.3にとりこまれるかどうかはまだ決まってません。
そのうちPEPが提出されることでしょう。

参考

PyCon mini JP リンク集 #pyconjp

From Evernote:

PyCon mini JP リンク集 #pyconjp

公式サイトにリンク集ができたようなので、このサイトでの更新を終了します。

PyCon mini JP発表者のかたがたのツイートやSlideShareを頼りに発表資料やプロジェクト、プロダクトのリンクを集めた。
網羅できてるわけじゃないので、のってないからって怒らないでね。
SlideShareが埋め込みになってるのは、Posterousの仕様です。
Google docsが埋め込みになっていないのも、Posterousの仕様です。

講演
基調講演 日本Pythonコミュニティの10年ちょっと http://www.slideshare.net/atsuoishimoto/slide-at-pycon-mini-jp-on-2011129
Python界隈の翻訳プロジェクト http://www.slideshare.net/t2y/python-6731903
NVDAの紹介と日本語化プロジェクトの活動状況 http://www.slideshare.net/nishimotz/nishimotz-pycon2011jan
テンプレートエンジンの高速化と失敗談について http://www.slideshare.net/kwatch/how-to-create-a-highspeed-template-engine-in-python
Pythonでフィジカルコンピューティング http://www.slideshare.net/irieda/python-physicalcomputing
ハードウェアを用いてPythonを学ぶ http://www.slideshare.net/yutakitagami/python-6744817

LT
App Engineと非同期とテストと私 http://www.slideshare.net/jbking/app-engine-6748831
グローバルPloneコミュニティと開発スタイル http://www.slideshare.net/terapyon/pycon-minijp-lt-terada-how-to-develop-on-plone

プロジェクト
Pythonドキュメント日本語翻訳プロジェクト http://code.google.com/p/python-doc-ja/

PyPI

映像資料

@takano32 提供のLT動画

#2以降もあるようなので、youtubeから辿ってみてください。

nosecolor作ったよ

From Evernote:

nosecolor作ったよ

やっぱりTDDはレッドグリーンのリズムだよね。
でもnoseとかTextTesutRunnerとか、白黒なんだよ。
なので、nose plugin で結果表示に色を付けたよ。
nosecolorをインストールすると、noseで --color オプションを使えるようになる。
このオプションをつけると、成功したテストは、失敗したテストはで表示されるようになる。
一覧性とかを考えると、トレースバックを出すかどうか結構悩ましい。

http://pypi.python.org/pypi/nosecolor に登録しておいた。

にょろろん。

git

From Evernote:

git

最近Pyramidをそれとなく調査したりしてるのですが、Pyramidはgithubにホスティングされてます。
仕事ではMercurial使ってるわけで、まあ、コマンド体系が違うだけでやることは一緒でしょと思ってました。

が、結構根本的な考え方が違う?
Mercurialは正しさを求めてて、そこからはずれる場合はプラグインでっていうのがあると思うんだけど、
gitは、gitだけでとにかくパワフルな感じ。

その辺は、もっと使い込んでから比較したいところだけど、日頃hg使ってる状態でgit使ったときにひっかかったこと。

  • gitのbranchコマンドは、切り替えまではやってくれない
  • gitでcommitするときは、明示的にそのcommitに含めるファイルを追加しないといけない(または、 -a オプション使う)

この2つはとにかく勝手が分からないまま、branch切ってパッチを作ろうとしたときに間違えまくった。
あやうくmasterにcommitしてupしそうになったぜ。(マニュアル読もうな。自分。)