.. Python Project Howto Created 2009 Sep 12 John Kleint .. role:: cmd(samp) .. |pphurl| replace:: http://infinitemonkeycorps.net/docs/pph/ :tocdepth: 2 =============================== Python Project Howto (日本語訳) =============================== | Version |version| | |today| | この文章は「 `Python Project Howto`_ 」の日本語訳です。 | Translated by Hideo Hattori .. _`Python Project Howto`: http://infinitemonkeycorps.net/docs/pph/ 目標 =========== あなたはPythonでいくつかのコードを生み出しました。 あなたはこう考えます、「これは誰かの役に立つかも」と。 オープソースプロジェクトとしてそれをリリースしたいと思いました。 とても良いところに訪れましたね。 このドキュメントはPythonプロジェクトをリリースするあなたの手助けします。 あなたが作成することになるPythonプロジェクトのファイル構成は以下のようになっています。 私のプロジェクトである googlemaps_ モジュールを例に使いながら説明していきます。 .. _googlemaps: http://sourceforge.net/projects/py-googlemaps/ .. _googlemaps source: http://py-googlemaps.svn.sourceforge.net/viewvc/py-googlemaps/trunk/ .. This is a really horrible way to have to get a class on a hyperlink .. role:: dir .. role:: plainfile .. role:: pyfile .. role:: comment .. |googlemaps-top/| replace:: :dir:`googlemaps/` .. _googlemaps-top/: `プロジェクトのホスティング`_ .. |# Project Hosting| replace:: :comment:`# プロジェクトのホスティング` .. _# project hosting: `プロジェクトのホスティング`_ .. |.svn/| replace:: :dir:`.svn/` .. _.svn/: `バージョン管理`_ .. |# Version Control| replace:: :comment:`# バージョン管理` .. _# version control: `バージョン管理`_ .. |googlemaps-qual/| replace:: :dir:`googlemaps/` .. _googlemaps-qual/: `コードの品質`_ .. |# Quality Code| replace:: :comment:`# コードの品質` .. _# quality code: `コードの品質`_ .. |googlemaps.py| replace:: :pyfile:`googlemaps.py` .. _googlemaps.py: `コードの品質`_ .. |test/| replace:: :dir:`test/` .. _test/: `ユニットテスト`_ .. |# Unit Testing| replace:: :comment:`# ユニットテスト` .. _# unit testing: `ユニットテスト`_ .. |test_googlemaps.py| replace:: :pyfile:`test_googlemaps.py` .. _test_googlemaps.py: `ユニットテスト`_ .. |doc/| replace:: :dir:`doc/` .. _doc/: `ドキュメンテーション`_ .. |# Documentation| replace:: :comment:`# ドキュメンテーション` .. _# documentation: `ドキュメンテーション`_ .. |index.rst| replace:: :plainfile:`index.rst` .. _index.rst: `Sphinxを使ってドキュメントを生成する`_ .. |html/| replace:: :dir:`html/` .. _html/: `Sphinxを使ってドキュメントを生成する`_ .. |index.html| replace:: :plainfile:`index.html` .. _index.html: `Sphinxを使ってドキュメントを生成する`_ .. |README.txt| replace:: :plainfile:`README.txt` .. _README.txt: `README`_ .. |LICENSE.txt| replace:: :plainfile:`LICENSE.txt` .. _LICENSE.txt: `ライセンス`_ .. |# Licensing| replace:: :comment:`# ライセンス` .. _# Licensing: `ライセンス`_ .. |setup.py-file| replace:: :pyfile:`setup.py` .. _setup.py-file: `setup.py`_ .. |# Packaging| replace:: :comment:`# パッケージング` .. _# Packaging: `パッケージング`_ .. |MANIFEST.in| replace:: :plainfile:`MANIFEST.in` .. _MANIFEST.in: `setup.py`_ .. container:: dirlisting-title Pythonプロジェクトの構成 .. container:: dirlisting .. cssclass:: dirline indent0 |googlemaps-top/|_ |# Project Hosting|_ .. cssclass:: dirline indent1 |.svn/|_ |# version control|_ .. cssclass:: dirline indent1 |googlemaps-qual/|_ |# Quality Code|_ .. cssclass:: dirline indent2 |googlemaps.py|_ .. cssclass:: dirline indent1 |test/|_ |# Unit Testing|_ .. cssclass:: dirline indent2 |test_googlemaps.py|_ .. cssclass:: dirline indent1 |doc/|_ |# Documentation|_ .. cssclass:: dirline indent2 |index.rst|_ .. cssclass:: dirline indent2 |html/|_ .. cssclass:: dirline indent3 |index.html|_ .. cssclass:: dirline indent1 |README.txt|_ .. cssclass:: dirline indent1 |LICENSE.txt|_ |# Licensing|_ .. cssclass:: dirline indent1 |setup.py-file|_ |# Packaging|_ .. cssclass:: dirline indent1 |MANIFEST.in|_ .. Here's what that's supposed to look like. googlemaps/ # Project hosting .svn/ # version control googlemaps/ # Quality code googlemaps.py test/ # Unit testing test_googlemaps.py doc/ # Documentation index.rst html/ index.html README.txt LICENSE.txt # Licensing setup.py # Packaging MANIFEST.in 目標としてはリリースの前にこれらのファイルを作成することです。 コードやコマンドを例に使いながら、順を追ってよいPythonソフトウェアを どうやったら作れるのかを説明していきます。 各リンクをクリックすることで、その章にジャンプします。 `googlemaps source`_ を見てください。このプロジェクトはひとつの モジュールから構成されていますが、フルパッケージ(バンドルモジュール)の モノよりは簡単です。それでははじめていきましょう。 .. _projecthosting: プロジェクトのホスティング ========================== ホストの選択 ------------- ユーザがあなたのソフトウェアをダウンロードし、どのように使用するのかを知るために、 そしてよいフィードバックを得るためにインターネット上で公開する必要があります。 いくつかのウェブサイトは無料でプロジェクトをホストできます。 一般的なホストは一覧を見てください。 基本的な要件としては、ウェブページ、バージョン管理システム、 そしてフィードバックシステムが使えることです。 フィードバックの仕組みとしては、バグトラッカやフォーラム、 メーリングリスト等があります。 ホスト先をもし決めかねているなら、 一番古くからあり多くの人に知られているSourceforgeを使うのが良いと思います。 今回はSourceforgeを使って説明していきます。 プロジェクト名の選択 --------------------- あなたは登録にあたってプロジェクト名を決める必要があります。 プロジェク名は重要ですが、あまり多くの時間をかけすぎない方が良いと思います。 `Google `_ や `The Python Package Index `_ 、 `freshmeat `_ 等で同じ名前がないか確認してみてください。 (商標はまた別ですが)ほとんど関係ないものであれば大丈夫でしょう。 もしあなたが考えた名前がすでに使われていた場合は"Py"か"Python"をつける、 または `Monty Python `_ からヒントを得ることを検討してみてください。 .. note:: ホスト先でプロジェクト名または"省略表記名"を取得するかもしれません。 ホームページのURLがこれらに影響されて、"ファインダビリティ"を損なうことになるかもしれません。 プロジェクトの実際の名前はホスト上使用される表記と異なっていても問題ありませんが、 できるだけあわせた方がよいでしょう。 このことを重要なことだと考えるのなら、 プロジェクトの名前を変えるか、ミスマッチした名前を受け入れるか、 あなたの名前が使えるホスティングサービスを探すかしてください。 .. note:: あなたは自分のウェブサイト上でプロジェクトをホストすることができますが、 それにはより多くの手間がかかります。 あなたが選択したホストのアカウントでサインアップします。 プロジェクトを登録し、プロジェクトのメタデータを入力します。 概要、カテゴリ、タグかキーワード、プログラミング言語、等が含まれます。 これらによってソフトウェアを簡単に探し出し、どのようなソフトウェアなのかというのを 理解してもらえるようになります。 (選択したホストによっては、この時点でソフトウェアのライセンスを選択する必要があります。 詳しくは `ライセンス`_ の章を参照してください。) オープンソースプロジェクトのホスト一覧 -------------------------------------- * Bitbucket: http://bitbucket.org/ * CodePlex: http://www.codeplex.com/ * Freepository: https://freepository.com/ * GitHub: http://github.com/ * Gitorious: http://gitorious.org/ * Google Code: http://code.google.com/projecthosting/ * Launchpad: https://launchpad.net/ * Sourceforge: http://www.sourceforge.net/ * Tuxfamily: http://www.tuxfamily.org/ .. _versioncontrol: バージョン管理 ==================== リポジトリ作成 ------------------- コードをインターネット上で管理するために、 バージョン管理システム(VCSはリビジョン管理、ソースコード管理システムとも呼ばれます) の設定をおこなう必要があります。 Subversionは広く知られたバージョン管理システムで、最も使われています。 多くのホストでサポートされていて、すべてのプラットフォーム(OS)で使うことができます。 この文章ではSubversionを使って説明しますが、 もちろん他のバージョン管理システムを選択してもかまいません。 機能としてはそれらは基本的に同じであり、 後々変更したいと思ったらexportしてからimportすればよいのです。 ホストの選択によってバージョン管理システムの選択にも制限があることに注意してください、 逆もまたしかりです。 プロジェクトのウェブサイトでSVN(git、hg、bzr...)リポジトリを作成してください。 コードのバックアップ --------------------- 何か起こった時のために、バックアップとしていつでもtarボールを作成しておくのは良い心がけです。 .. note:: この文章ではUnixコマンドとパスを使っていますが、他のOSでも概念は共有できます。 Pythonのソースが :file:`{/path/to/googlemaps/googlemaps.py}` にあるとします。 :file:`{/path/to/}` はシステムに、 :samp:`{googlemaps}` はプロジェクト名によって必要に応じて置き換えてください。 tarボールを作成するには以下のようにします: :cmd:`$ cd {/path/to/}` :cmd:`$ tar czvf {googlemaps}.tgz {googlemaps/}` このとき、( :file:`*.pyc` のような)コンパイル済みファイル、バイナリファイル、 隠れ設定ファイルやディレクトリ、そして公開によって共有したくない個別ファイルは消去すべきです。 編集用の新しいディレクトリを作成するために、コードがあるディレクトリ自体を移動させます。 :cmd:`$ mv {googlemaps}/ {googlemaps}-backup/` リポジトリのチェックアウト -------------------------- URLかコマンドによってリポジトリをチェックアウトします。 :command:`svn` コマンドでSourceforgeからチェックアウトを行うには以下を実行します: :cmd:`$ cd {/path/to/googlemaps}` :cmd:`$ svn co https://{googlemaps}.svn.sourceforge.net/svnroot/{googlemaps} {googlemaps}` 新たに作成された :file:`googlemaps` ディレクトリには :file:`.svn` ディレクトリ(VCSによって異なります)が含まれています。 ワーキングリポジトリにコードをコピーする ------------------------------------------ 新しく作成された :file:`googlemaps` ディレクトリ(これがプロジェクトです!)に ソースコード用に新しいサブディレクトリを作成します。 ソースコードはプロジェクトかパッケージと同じ名前のサブディレクトリに格納します。 :cmd:`$ svn mkdir {/path/to/googlemaps/googlemaps}` (これは :command:`svn` の例!) :cmd:`$ cp -a {/path/to/googlemaps}-backup/* {/path/to/googlemaps}/` リポジトリにコードを追加する ------------------------------- :cmd:`$ cd {/path/to/googlemaps/}` :cmd:`$ svn add *` 変更をコミットする ------------------- :cmd:`$ svn commit -m "Initial import."` プロジェクトホストのサーバにマスターファイルが作成されました。 以降、自分のマシン上のワーキングディレクトリですべての変更を行います。 最新のバージョン管理システムを使っているのなら、適宜リンクを参照してください。 ただ基本的には、どのバージョン管理システムもソースコードの追加、移動、削除ができます。 :command:`svn` での基本的なコマンドは以下です: | :command:`svn stat` - 変更と追加状態の表示 | :command:`svn commit` - 変更の保存 | :command:`svn add` - 新規ファイルをバージョン管理下におく | :command:`svn mv` - ファイルの移動したいときに使用 ( :command:`/bin/mv` の代わりに使用) | :command:`svn rm` - ファイルの削除を通知 ( :command:`/bin/rm` の代わりに使用) | :command:`svn mkdir` - ワーキングコピーに新たなディレクトリを作成 ( :command:`/bin/mkdir` の代わりに使用) | :command:`svn update` - リポジトリからワーキングコピー変更を反映 バージョン管理システム一覧 --------------------------- * Bazaar (bzr): http://bazaar-vcs.org/ * Git (git): http://git-scm.com/ * Mercurial (hg): http://mercurial.selenic.com/wiki/ * Subversion (svn): http://subversion.tigris.org/ コードの品質 ==================== 良いコードを書くこととは長い旅のようなものです。 Andrew HuntとDavid Thomasによって書かれた「 `達人プログラマー`_ 」 (原書:The Pragmatic Programmer)は良い足がかりとなります。 Steve McConnell著「 `Code Complete`_ 」も良いでしょう。 良いデザインと実装について学び、そしてそれを使ってみてください。 たぶん一番の近道は他のハッカーにあなたのコードを読んでもらい、使ってもらうことです。 他の人、つまり同僚や顧客、潜在的な雇用者が読めるようなコードを書いてください、 そうすればプロジェクトは評判になってきます。 学んだことを活かして良いコードを書いてください。 (##それがあなたの落胆と時間の節約になるとだけ言っておきます。) 良いコードを書くこととは芸術のようなものです。 美意識は主観的なものですがPythonでは `PEP 8`_ と呼ばれる標準スタイルガイドがあります。 PEP 8を読んでください。 pep8_ と呼ばれるツールがあるので、それを対象となるコードに使用して構文をチェックしてください。 良いコードを書くこととは科学のようなものです。 コードの品質は客観的な数値として計測することができるのです。 まずは `ユニットテスト`_ が正しく実施できていることを確認してください。 次に Pylint_ でチェックして、 よく `コードの臭い `_ ( `en `_ )を確認してください。 Pylint ------ :cmd:`$ sudo easy_install pylint` :cmd:`$ pylint mymodule.py` Pylintはコードの多くの潜在的な問題を警告してくれます。 あなたはまず命名規則に関して気づかされる部分があることでしょう。 キャメルケースである ``ClassNames`` (クラス名)と ``ExceptionNames`` (例外名)は例外として(それは `エラー` とされます)、 普通はすべての名前は ``lowercase_with_underscores`` (小文字とアンダースコア)にすべきです。 "定数" は ``UPPER_CASE_UNDERSCORE`` (大文字とアンダースコア)にします。 ``_private_identifiers`` (プライベート変数名)はアンダースコアで始めます。 ただしモジュールの場合、パブリックなクラス、関数、データは ``__all__`` と呼ばれるグローバルスコープのリストで定義するのが良い方法です:: __all__ = ['GoogleMaps', 'GoogleMapsError'] 以下に要点を簡単に書きます。 1文字や2文字の変数名は短すぎます。字下げは4つのスペースです。 オペレータ(訳注:+や-等)の前後やカンマの後ろにはホワイトスペースを入れます。 ドキュメンテーション文字列を書きます。 シャドウ変数(訳注:同じ名前の変数を使用して変数を再定義すること)は使ってはいけません。 ``None`` 、 ``True`` 、 ``False`` との比較は ``is`` を使います。 :exc:`Exception` のサブクラスでモジュール例外を使ってください。 ``try`` はできるだけ限定した使い方をしてください、 ( :exc:`Exception` を使わないときは)可能な限り例外を捉えるようにしてください。 コードをシンプルに保ってください。最後に重要なことですが、グローバル変数を使わないでください。 Pylintが出力する警告について学んでください。 フェールスポジティブ(訳注:ツール側の誤判定)であれば無視してもかまいません。 もしPylintが誤っていて本当にあなたのコードが正しいものであれば、 以下のようにしてPylintにわかるようにコードに明示してください:: def fetch_json(query_url, params={}, headers={}): # pylint: disable-msg=W0102 ... 少なくとも8以上のスコアをとれるようにしてください。 個人的には、1行あたりの最大長が79文字以内になっているかどうかの警告についてはなおすようにしています (ユーザが対話的なヘルプを使えるように、ドキュメンテーション文字列を書くようにしています)。 将来に対する準備 ---------------------- Pythonは Python 2 と下位互換の無い `Python 3`_ に移行し始めています、 将来に備えて、Python 2.6以降で ``-3`` オプションを指定して `ユニットテスト <#unittesting>`_ を実行しておきましょう: :samp:`$ python -3 test/test_mymodule.py` Python 3に変更したときに出力される警告を参照できます。 もし本気でPython 3上で動かしたいと思うのであれば、 2to3_ ツールを使ってPython 3のコードに変換してください、 そして、Python 3をインストールし(Python 2もまだ置いておいて!)、 ユニットテストを実行してください。 両方のバージョンをメンテナンスしないでください、 :command:`2to3` を実行することでバージョン2.xのソースコードでも動かせますので。 コードの品質はオープンソースプロジェクトにおいて最も重要な事柄のひとつです。 いったんパッケージの準備が整えば、コードの品質向上に多くの時間を注ぎ込むことになるでしょう。 Pythonのコード品質関連ツール一覧 -------------------------------- .. _`達人プログラマー`: http://www.pragprog.com/the-pragmatic-programmer .. _Code Complete: http://cc2e.com/ .. _Python 3: http://docs.python.org/dev/3.0/whatsnew/3.0.html .. |PEP 8| replace:: http://python.org/dev/peps/pep-0008/ .. _PEP 8: http://python.org/dev/peps/pep-0008/ .. |pylint| replace:: http://www.logilab.org/857 .. _Pylint: http://www.logilab.org/857 .. |pep8| replace:: http://pypi.python.org/pypi/pep8/ .. _pep8: http://pypi.python.org/pypi/pep8/ .. |pyntch| replace:: http://www.unixuser.org/~euske/python/pyntch/index.html .. _Pyntch: http://www.unixuser.org/~euske/python/pyntch/index.html .. |pymetrics| replace:: http://sourceforge.net/projects/pymetrics/ .. _PyMetrics: http://sourceforge.net/projects/pymetrics/ .. |coverage.py| replace:: http://nedbatchelder.com/code/coverage/ .. _coverage.py: http://nedbatchelder.com/code/coverage/ .. |2to3| replace:: http://docs.python.org/library/2to3.html .. _2to3: http://docs.python.org/library/2to3.html .. |pyflakes| replace:: http://divmod.org/trac/wiki/DivmodPyflakes .. _pyflakes: http://divmod.org/trac/wiki/DivmodPyflakes .. |pychecker| replace:: http://pychecker.sourceforge.net/ .. _pychecker: http://pychecker.sourceforge.net/ * Pylint, `lint `_ for Python: |pylint| * PEP 8, Style Guide for Python Code: |PEP 8| * pep8, code formatting checker: |pep8| * Pyntch, a static type checker (for a dynamic language!): |pyntch| * PyFlakes, a fast lint-like tool for Python: |pyflakes| * PyChecker, a Python source code checking tool: |pychecker| * PyMetrics, code statistics including cyclomatic complexity: |pymetrics| * 2to3, Automated Python 2 to 3 code translation: |2to3| .. _unittesting: ユニットテスト ==================== 良い方法はユニットテスト実行して常にコードをチェックすることです。 変更のたびに自動的に行わせるようにすればなお良いです。 コードを書いたらテストを行うのが `ユニットテスト `__ の前提です。 そうすることで容易にコードを変更することができるようになります。 ここで言う `ユニット` とは、モジュール、クラス、または関数のことで、部分的な機能毎にわけてテストします。 そうすることで全体の動作が保証されるのです。 doctest -------- Pythonは2つのテスト用標準モジュールを持っています、 :mod:`doctest` と :mod:`unittest` (こちらが重要!)です。 :mod:`doctest` は一石二鳥なモジュールで、コード例で使い方を説明しますが まさにあなたが予想した通りの答えになります。 Pythonインタラクティブシェルで実行し、それをチェックして、 ドキュメンテーション文字列に貼付ければよいのです:: def local_search(self, query, numresults=_LOCAL_RESULTS_PER_PAGE, **kwargs): """ Searches Google Local for the string `query` and returns a dictionary of the results. >>> gmaps = GoogleMaps() >>> local = gmaps.local_search('sushi san francisco, ca') >>> result = local['responseData']['results'][0] >>> print result['titleNoFormatting'] Sushi Groove """ そして以下のようにメインルーチンを追加すれば良いのです:: if __name__ == "__main__": import doctest doctest.testmod() スクリプトとして実行すれば、 :mod:`doctest` モジュールは自動的にドキュメンテーション文字列からテストコードを取り出し、 実行し、ドキュメンテーション文字列上の出力結果と比較してテスト結果を出力します。 テストコードを書くことで `良いドキュメント <#id25>`_ の一部となり、 :mod:`doctest` を使うことで大した労力を伴うことなくテストが行えるのです。 .. id25 dirty... unittest --------- :mod:`unittest` モジュールはPython標準の "重量級" ユニットテストフレームワークです。 :mod:`doctest` より少しだけ融通が利き、機能が充実していますが、 使いこなすには少し時間をかけて学ぶ必要があります。 以下は :mod:`unittest` モジュールを使ったテストです:: import unittest from googlemaps import GoogleMaps class Test(unittest.TestCase): """Unit tests for googlemaps.""" def test_local_search(self): """Test googlemaps local_search().""" gmaps = GoogleMaps(GMAPS_API_KEY, referrer_url='http://www.google.com/') local = gmaps.local_search('sushi san francisco, ca') result = local['responseData']['results'][0] self.assertEqual(result['titleNoFormatting'], 'Sushi Groove') if __name__ == "__main__": unittest.main() :file:`test/test_googlemaps.py` を呼び出します、一般的に :file:`test` と言う名前でディレクトリの中に格納しておきます、そして :file:`test_{modulename}.py` というファイル名にします。 `modulename` モジュールのためのテストはすべてこのファイルで行い、 `modulename` のすべての機能をテストするようにします。 doctestを使いたいのなら :file:`test/test_{modulename}.py` を以下のようにして実行してください:: import unittest import doctest import googlemaps class Test(unittest.TestCase): """Unit tests for googlemaps.""" def test_doctests(self): """Run googlemaps doctests""" doctest.testmod(googlemaps) if __name__ == "__main__": unittest.main() Pythonのユニットテストのための標準が :mod:`unittest` モジュールです。 ##多くのツールやIDEで使われており、 :mod:`unittest` のテストがインターフェースとなっています。 テストを作成する ----------------- VCSを使ってプロジェクトのルートディレクトリに ``test`` ディレクトリを作成してください: :cmd:`$ cd {/path/to/googlemaps/}` :cmd:`$ svn mkdir test` :file:`test_{modulename}.py` ファイルを作成し、 :mod:`unittest` モジュールをインポートしてください。 テストモジュールはプロジェクト本体のコードとディレクトリを分けてください。 :envvar:`PYTHONPATH` 環境変数にその親ディレクトリを追加してください。実行までの手順は以下です: :cmd:`$ cd {/path/to/googlemaps}` :cmd:`$ export PYTHONPATH=$PYTHONPATH:/path/to/{googlemaps}/{googlemaps}` :cmd:`$ python test/test_{googlemaps}.py` 最後に、Pythonで人気のあるユニットテストフレームワークを紹介します。 `nose`_ です。 :mod:`nose` モジュールはシンプルでありながら、 :mod:`unittest` モジュールを拡張したフレームワークです (例えば、自動であなたのテストコードを見つけたり、 :envvar:`PYTHONPATH` を設定してくれたり、ということができます)。 ただしPythonの標準モジュールには含まれていませんので注意してください。 まとめると、ユニットテストを書き、それを実行します。いつでもです。 そうすることで、コードをリリースに関して自身が湧くようになってきます。 .. |unittest| replace:: http://docs.python.org/library/unittest.html .. _unittest: http://docs.python.org/library/unittest.html .. |doctest| replace:: http://docs.python.org/library/doctest.html .. _doctest: http://docs.python.org/library/doctest.html .. |nose| replace:: http://somethingaboutorange.com/mrl/projects/nose/ .. _nose: http://somethingaboutorange.com/mrl/projects/nose/ .. |pttt| replace:: http://pycheesecake.org/wiki/PythonTestingToolsTaxonomy .. _pttt: http://pycheesecake.org/wiki/PythonTestingToolsTaxonomy ユニットテストに関する資料 -------------------------- * unittest, Python's builtin unit testing framework: |unittest| * doctest, Test interactive Python examples in docstrings: |doctest| * nose, "is nicer testing for python": |nose| * coverage.py, test coverage measurement: |coverage.py| * Python Testing Tools Taxonomy, including web app and GUI testing tools: |pttt| ドキュメンテーション ==================== 良いドキュメントはユーザにコードの使用方法を示すことと、 *あなたが* 信頼できるものを作成していると思ってもらうために重要なものになります。 :mod:`doctest` モジュールを使えば、ドキュメントを使ったテストができるのです。 Pythonでは docstrings_ モジュールを使ってソースコードの中にドキュメントを 入れることができます、##よく3つのコメントの意味を担っています。 ドキュメンテーション文字列の拡張フォーマッティング、見栄えの良いドキュメントの印刷または ウェブページの生成を自動的に行うことが可能です。 私はコードを書く *前に* (または並行して)ドキュメントを書くことを提案します。 これはユーザ中心のデザインとして用いられているすばらしい方法です。 あなたはいくつかのポイントについて使用方法を書くべきです、## そうすることで、より簡単に実装でき、テストも保守も楽になります、賭けてもよいです。 コードをコミットする前にドキュメントを書くことで、 どのようにそれが動作するのか理解し易くなります。 あなたが高レベルのモジュールを書くときにはうまく働きます。 自分自身に尋ねてみてください、 `XYZ` というモジュールを見つけ出したとして、 それに何を求めるでしょうか?どうやって使うの?まず何から調べればいいの? どんな例を見たい? 良いドキュメント(とコード)を *あなた* のために書いてください。 このすばらしい架空のモジュールのためにドキュメントを書いてください。 ドキュメンテーション文字列内でreStructuredTextを使う ----------------------------------------------------- Pythonコミュニティはドキュメンテーション文字列のマークアップのために reStructuredText_ を使用することに興味が向いているようです。 Javadocのような感じです。使用例を示します:: def reverse_geocode(self, lat, lng, sensor='false', oe='utf8', ll='', spn='', gl=''): """ Converts a (latitude, longitude) pair to an address. Interesting bits: >>> gmaps = GoogleMaps() >>> reverse = gmaps.reverse_geocode(38.887563, -77.019929) >>> address = reverse['Placemark'][0]['address'] >>> print address 200 6th St SW, Washington, DC 20024, USA >>> accuracy = reverse['Placemark'][0]['AddressDetails']['Accuracy'] >>> print accuracy 8 :param lat: latitude :type lat: float :param lng: longitude :type lng: float :return: `Reverse geocoder return value`_ dictionary giving closest address(es) to `(lat, lng)` :rtype: dict :raises GoogleMapsError: If the coordinates could not be reverse geocoded. Keyword arguments and return value are identical to those of :meth:`geocode()`. .. _`Reverse geocoder return value`: http://code.google.com/apis/maps/documentation/geocoding/index.html#ReverseGeocoding """ 以下のような感じで自動的にHTMLを生成できます: .. automethod:: googlemaps.GoogleMaps.reverse_geocode キーポイント: * 通常のドキュメンテーション文字列のフォーマットは `PEP 257`_ を参照してください。 * 識別子への参照は `\`バッククオート`\` で囲みます。 * ``:param lat: latutide`` は引数の説明です。 * ``:type lat: float`` は引数の型についてです。 * ``:return: dictionary giving closest addresses...`` は戻り値の説明です。 * ``:rtype: dict`` は戻り値の型についてです。 * ``:raises GoogleMapsError: If coordinates could not...`` は例外についてです。 * ``>>>`` はdoctestの始まりを示し、自動的にコードを整形します。 ``::`` か4つのスペースで字下げし、空行で分離します。 * 他のメソッド、関数、クラス、モジュールは :samp:`:meth:\`{mymethod}\`` 、 :samp:`:func:\`{myfunc}\`` 、 :samp:`:class:\`{myclass}\`` 、 :samp:`:mod:\`{mymodule}\`` のようにします。 * ハイパーリンクは :samp:`\`Google\`_` のようにバッククオート(`)で囲ってアンダーバー(_)を単語末尾につけます。 リンク先を指定したい場合は ``.. _Google: http://www.google.com/`` のようにしてください。 * 詳しくは reStructuredText_ のドキュメントを参照するようにしてください。 Sphinxを使ってドキュメントを生成する ------------------------------------ Sphinx_ を使ってドキュメントを作成する方法をお教えしましょう。 SphinxはPython自身の公式ドキュメンテーションツールであり、Pythonのプロジェクトで多数使われています。 http://docs.python.org/ で作成されたものを見ることができます。 Sphinxであなたのモジュール用に自動的なドキュメント生成するためのセットアップは簡単です。 プロジェクトのルートディレクトリ(ドキュメント作成の場所としては一般的な場所です)にVCSを使って :file:`doc/` ディレクトリを作成し、 :command:`sphinx-quickstart` コマンドを打ちます: :cmd:`$ cd {/path/to/googlemaps}` :cmd:`$ svn mkdir doc` :cmd:`$ cd doc` :cmd:`$ sudo easy_install sphinx` :cmd:`$ sphinx-quickstart` いくつかの質問に答えます(デフォルトが良いでしょう。ただし、 `autodoc` モジュールは'yes'と答えておきましょう)。 答え終わったら、 :file:`index.rst` ファイルに以下を追加しましょう: :cmd:`.. automodule:: {googlemaps}` ##ドキュメンテーション文字列の中には含まないでください。 もちろん :file:`index.rst` と Sphinxの :file:`conf.py` ファイルはバージョン管理しておきましょう: :cmd:`$ svn add index.rst conf.py` :file:`doc/` ディレクトリ上で :command:`sphinx-build` コマンドを実行し、ドキュメントを生成しましょう: :cmd:`$ sphinx-build . html/` (動作には、環境変数 :envvar:`PYTHONPATH` に :file:`{/path/to/googlemaps/googlemaps}` を追加する必要があるかもしれません。) ``doc/html/`` にHTMLドキュメントが作成されます。Sphinxは非常に美しいドキュメントを生成します。 ウェブブラウザでそれを見てみてください。 ##あなたが書いたものが美しく仕上げられるので、 より良いドキュメントを作成しようと思うようになるでしょう。 README ------- コードのドキュメンテーション文字列とHTMLドキュメントに加えて、 :file:`README.txt` ファイルをディレクトリのトップレベルに作成すべきです。 もちろんバージョン管理も行いましょう。 :file:`README.txt` ファイルは以下のものを含むべきです: * パッケージ名、バージョン、リリースの日付 * パッケージの説明文 * 依存しているPythonのバージョン * パッケージのインストール方法( :samp:`easy_install {packagename}` で十分でしょう) * もしあなたのパッケージにスクリプトが含まれているのであればその使用方法 * パッケージの作者名と連絡手段## * コピーライトと `ライセンス`_ について * ドキュメントへのリンク 中にはこれらの内容を複数のファイルに分けて ( :file:`INSTALL` 、 :file:`AUTHORS` 、 :file:`ANNOUNCE` 等)作成する人がいますが、 個人的にはできるだけシンプルにするほうが好みです。 もしすでにドキュメンテーション文字列かその他のものに上記のような情報を書いているのなら、 必ず `don't repeat yourself `_ [05]_: Sphinxを使ってプレーン(reStructured)テキストを生成するか、スクリプトを使って ``README.txt`` ファイルを生成してください。 :samp:`{mymodule}.__doc__` のようにすれば、モジュールやクラス、関数等から ドキュメンテーション文字列を取得することができます。 Sphinxでプレーン(reStructured)テキストを :file:`text/` ディレクトリに作成する方法は以下になります: :cmd:`$ sphinx-build -b text . text/` .. |docstrings| replace:: http://docs.python.org/tutorial/controlflow.html#documentation-strings .. _docstrings: http://docs.python.org/tutorial/controlflow.html#documentation-strings .. |reStructuredText| replace:: http://docutils.sourceforge.net/rst.html .. _reStructuredText: http://docutils.sourceforge.net/rst.html .. |PEP 257| replace:: http://www.python.org/dev/peps/pep-0257/ .. _PEP 257: http://www.python.org/dev/peps/pep-0257/ .. |Sphinx| replace:: http://sphinx.pocoo.org/ .. _Sphinx: http://sphinx.pocoo.org/ ドキュメンテーションに関する資料 -------------------------------- * PEP 257, Docstring Conventions: |PEP 257| * reStructuredText markup: |reStructuredText| * Sphinx, Python Documentation Generator: |Sphinx| ライセンス ================== あなたはコードのライセンスを決める必要があります。 一般的なオープンソースライセンスはGPLやLGPL、BSDライセンス、 MIT/X11ライセンス、Apapcheライセンス、Eclipseライセンス、Mozillaライセンスです。 ここではあなたのライセンス選択のガイドとなるように、いくつかのリソースを示します。 あなたのライセンスの選択によって、どのオープンソースプロジェクトに取り入れられるのかが変わってきます。 プロジェクトホストの選択によっても、ホストが使っているソフトウェアが決まっていますので、 ライセンス選択に関係するかもしれません。 ライセンスについて学んで、コードがどう使われるのかを考えて、ライセンスを選択してください。 ライセンスを選択する際は、 `Open Source Initiative`_ でライセンスの全文を見ることができます。 ライセンスをよく読んで、それに同意してください。 その全文をコピーして、プロジェクトのトップディレクトリの :file:`LICENSE.txt` ファイルを作成してそれを 張り付けてください(VCSで管理もおこなってください)。 すべてのソースコードの一番上の部分にコメントとしてライセンス情報を記入しておいてください。 参照先情報を含む、要約した文章を含むようにしてください。 あなたのソースコードにライセンスの全文を書く必要はありません、 以下で示した情報を参照するようにしてください。 もしかなりの量のドキュメントがあるなら、 `フリーなドキュメント向けライセンス`_ を選択して ドキュメントだけわけてライセンス設定すると良いでしょう。 .. |Open Source Initiative| replace:: http://www.opensource.org/licenses .. _Open Source Initiative: http://www.opensource.org/licenses .. _`フリーなドキュメント向けライセンス`: http://www.dreamsongs.com/IHE/IHE-50.html ライセンスに関する資料 ---------------------- * Open Source Licenses at OSI: |Open Source Initiative| * Open Source Licenses on Wikipedia: http://en.wikipedia.org/wiki/Open_source_license * zooko Quick Reference for Choosing a Free Software License: http://zooko.com/licence_quick_ref.html * KDE Open Source License Comparison: http://developer.kde.org/documentation/licensing/licenses_summary.html * New Media Rights' Open Source Licensing Guide: http://www.newmediarights.org/open_source/new_media_rights_open_source_licensing_guide * Groklaw - Understanding Open Source Software: http://www.groklaw.net/article.php?story=20031231092027900 * Innovation Happens Elsewhere's Licenses for Docmentation: http://www.dreamsongs.com/IHE/IHE-50.html * Creative Commons 'Choose a License' (for documentation): http://creativecommons.org/choose/ * Free Software Foundation's Various Licenses and Comments About Them: http://www.gnu.org/philosophy/license-list.html パッケージング ==================== パッケージングはディレクトリ構成を整えて、ユーザがダウンロードして使ってもらうようにすることです。 Pythonでは distutils_ を使うのが一般的です。 :file:`setup.py` ファイルを作成し、 :command:`easy_install` コマンドを実行します。 setup.py -------- 以下が :file:`setup.py` の例です:: from distutils.core import setup import sys sys.path.append('googlemaps') import googlemaps setup(name='googlemaps', version='1.0', author='John Kleint', author_email='py-googlemaps-general@lists.sourceforge.net', url='http://sourceforge.net/projects/py-googlemaps/', download_url='https://sourceforge.net/projects/py-googlemaps/files/', description='Easy geocoding, reverse geocoding, driving directions, and local search in Python via Google.', long_description=googlemaps.GoogleMaps.__doc__, package_dir={'': 'googlemaps'}, py_modules=['googlemaps'], provides=['googlemaps'], keywords='google maps local search ajax api geocode geocoding directions navigation json', license='Lesser Affero General Public License v3', classifiers=['Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'Natural Language :: English', 'Operating System :: OS Independent', 'Programming Language :: Python :: 2', 'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)', 'License :: OSI Approved :: GNU Affero General Public License v3', 'Topic :: Internet', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Scientific/Engineering :: GIS', ], ) もし ``download_url`` をまだ書いていなくても心配しないでください。 自分モジュールの ``__doc__`` 文字列を ``long_description`` に使っています、 どんなテキストでも使用することができます。 パッケージにモジュールを含んでいるのであれば ``py_modules`` と ``packages`` を入れ替えて ``package_dir`` を削除してください。 PyPIの `trove classifiers`_ リストから ``classifiers`` の項目を見つけてください。 もしサードパーティのパッケージかモジュールに依存しているのなら、 ``required`` キーワードを設定してください。 以下は、 パッケージルートディレクトリに作成した :file:`MANIFEST.in` ファイルです、これは ドキュメントやその他のファイルをパッケージに含める時に必要です:: recursive-include doc/html * prune doc/html/.doctrees/ exclude doc/html/.buildinfo include LICENSE.txt では :command:`setup.py sdist` コマンドでパッケージをビルドしてみましょう: :cmd:`$ python setup.py sdist` エラーが発生していなければ :file:`dist/{googlemaps}-1.0.tar.gz` が作成されているはずです。 :command:`cheesecake_index` コマンドを使って、インストールに問題が無いことと パッケージ提供に問題が無いかを確認しましょう: :cmd:`$ sudo easy_install cheesecake` :cmd:`$ cheesecake_index --path=dist/{googlemaps}-1.0.tar.gz` パッケージがインストールできることをすぐに確認できます。 個人的にはインストールのし易さとドキュメントの整備具合にやや比重が置かれているように思います、 `cheesecake index`_ によると70%位を目指すと良いそうです。 .. |trove classifiers| replace:: http://pypi.python.org/pypi?%3Aaction=list_classifiers .. _trove classifiers: http://pypi.python.org/pypi?%3Aaction=list_classifiers .. |distutils| replace:: http://docs.python.org/distutils/index.html .. _distutils: http://docs.python.org/distutils/index.html .. |setuptools| replace:: http://peak.telecommunity.com/DevCenter/setuptools .. _setuptools: http://peak.telecommunity.com/DevCenter/setuptools .. |cheesecake| replace:: http://pycheesecake.org/ .. _cheesecake: http://pycheesecake.org/ .. _cheesecake index: http://pycheesecake.org/#algorithm-for-computing-the-cheesecake-index パッケージングに関する資料 -------------------------- * distutils, the standard Python Distribution Utilities: |distutils| * setuptools, an enhanced, extended distutils and home of :command:`easy_install`: |setuptools| * Cheesecake, package "kwalitee" checker: |cheesecake| * PyPI's list of trove classifiers: |trove classifiers| リリース ===================== :file:`dist/{googlemaps}-1.0.tar.gz` ファイルをビルドできました、 さっそくアップロードしてみましょう。プロジェクトホストはリリースのためにファイルを アップロードをするように言ってきます、たいていのホスト先はウェブかSCP/SFTPかFTP経由で 行うことになるでしょう。ではパッケージをアップロードしましょう、 ``docs/html`` ディレクトリに ドキュメントをアップロードしてください(サブディレクトリを忘れないでください)。 ライセンス等の情報が変わっているのであれば、アップロード後にプロジェクト情報を変更してください。 あなたのプログラムがGUIであれば、いくつかスクリーンショットを撮って、 プロジェクトのウェブページに加えると良いでしょう。 パッケージをダウンロードできるURLをコピーして(リンクを含んだページが良いでしょう)、 ``setup.py`` の ``download_url`` に設定してください。 PyPI ----------- あなたはおそらく `Python Package Index`_ にパッケージを登録したくなるでしょう。 PyPIは、 easy_install_ や pip_ を使って、自動でダウンロードとインストールをおこなえます。 PyPIアカウントを取得する必要があります(無料です)。 取得はこのような感じでパスワードを使うことによってPyPIのウェブサイトか コマンドライン経由でおこなうことができます: :cmd:`$ python setup.py sdist register upload` これでパッケージのビルド、ユーザネームとパスワードの問い合わせ、 PyPIへのパッケージ(とメタデータの)登録、アップロードができます。 パッケージのアップロードは必ず必要というわけではありませんが、 パッケージ自体を探し出すのが簡単になります。 また、Windowsユーザ向けのインストーラを作成することもできます: :cmd:`$ python setup.py bdist_wininst upload` これで :file:`dist/{googlemaps}-1.0.win32.exe` のようなファイルが作成され、 アップロードも行えます。(Python2.6では変な名前がつくバグがありますが、リネームするだけで解決できます。) PyPIで :samp:`http://pypi.python.org/pypi/{projectname}/` プロジェクトページを訪れてみてください、 :file:`setup.py` の ``long_description`` の文がウェブページのはじめの方にきているのに気づいたでしょう。 PyPIは reStructuredText_ をうまく理解して、あなたが書いた ``long_description`` を抜き出して 表示してくれるのです。 PyPIはreStructuredTextを :samp:`sphinx-build -b text . text/` と実行したときのような "シンプルな" テキスト形式にします。 バージョンナンバーを変えずに再登録することで修正することができます。 また、PyPIのウェブサイト経由でもプロジェクトのメタデータを編集することができます。 .. |pypi| replace:: http://pypi.python.org/pypi .. _Python Package Index: http://pypi.python.org/pypi .. |easy_install| replace:: http://peak.telecommunity.com/DevCenter/EasyInstall .. _easy_install: http://peak.telecommunity.com/DevCenter/EasyInstall .. |pip| replace:: http://pip.openplans.org/ .. _pip: http://pip.openplans.org/ * PyPI, the Python Package Index: |pypi| * Registering with the Package Index: http://docs.python.org/distutils/packageindex.html * Easy Install, automatically download and install Python packages: |easy_install| * pip, "pip installs packages": |pip| * freshmeat.net, Open source project index: http://freshmeat.net/ 最後に -------- Pythonパッケージのリリース、おめでとう! あなたのコードは成長し、よい土台ができ、 Pythonのオープソースプロジェクトのメカニズムについて知ることができたのです。 最後になりましたが、オープソースプロジェクトの運営については Karl Fogelが執筆した無料の書籍「 `Producing Open Source Software`_ 」(訳注: [10]_ ) を読むとよいでしょう。それでは幸運を。 | *人生の楽しい面だけをみていようぜ...* | *人生の楽しい面だけをみていようぜ...* .. _Producing Open Source Software: http://www.producingoss.com/ .. [02] 訳注:原書名「The Pragmatic Programmer」 .. [03] 訳注:原書名「Code Complete」 .. [05] `日本語リンク `_ .. [10] 日本語訳「 `オープンソースソフトウェアの育て方 `_ 」 :Author: John Kleint ** :Version: |version| :License: `Creative Commons Attribution-Share Alike 3.0 `_ ( `ja `_ ) :URL: |pphurl| 翻訳について ============ *##* がついている箇所はほとんど意味読み取れなかったところです。 翻訳についての質問・改訳点等ありましたら服部 までご連絡いただければさいわいです。