Pythonソースが次のように構成されています:
+-branchname/
+-dst/
+-src/
| +-library/
| | +-cleese/
| | | +-test/
| | | | +-__init__.py
| | | | +-test_cleese.py
| | | +-__init__.py
| | | +-cleese.py
| | +-palin/
| | +-test/
| | | +-__init__.py
| | | +-test_palin.py
| | +-__init__.py
| | +-palin.py
| +-productline/
| +-circus/
| | +-test/
| | | +-__init__.py
| | | +-test_circus.py
| | +-__init__.py
| | +-circus.py
| +-grail/
| +-test/
| | +-__init__.py
| | +-test_grail.py
| +-__init__.py
| +-grail.py
+-branch_root_marker
エントリポイントは、テストの実装方法に応じて、circus /およびgrail /と、(場合によっては)各test /ディレクトリにあります。
このソースツリーの複数のコピーがローカルストレージに存在しているため(さまざまなメンテナンスや機能の分岐に対応)、シェルにPYTHONPATHを設定するのに苦労することはありません。 (別のブランチで作業するように切り替えるたびに変更することを覚えておく必要があり、私はとても忘れています)
代わりに、「現在の」ファイルの場所から始めて、リーフからルートに向かって移動し、branch_root_markerを探して、ファイルツリーを上るロジックがあります。現在の作業コピーのルートディレクトリが見つかると、library /およびproductline /がsys.pathに追加されます。システムの各エントリポイントからこの関数を呼び出します。
"""Add working copy (branch) to sys.path"""
import os
import sys
def _setpath():
"""Add working copy (branch) to sys.path"""
markerfile = "branch_root_marker"
path = ""
if ("__file__" in locals() or globals()) and __file__ != "__main__":
path = __file__
Elif sys.argv and sys.argv[0] and not sys.argv[0] == "-c":
path = sys.argv[0]
path = os.path.realpath(os.path.expandvars(path))
while os.path.exists(path):
if os.path.exists(os.path.join(path, markerfile)):
break
else:
path = os.path.dirname(path)
errormsg = " ".join(("Could not find", markerfile))
assert os.path.exists(path) and path != os.path.dirname(path), errormsg
path = os.path.join(path, "src")
(_, subdir_list, _) = os.walk(path).next()
for subdir in subdir_list:
if subdir.startswith("."):
continue
subdir_path = os.path.join(path, subdir)
if subdir_path in sys.path:
continue
sys.path.append(subdir_path)
_setpath()
現在、私は各エントリポイントでこの関数の個別の同一のコピーを保持する必要があります。これは非常に短い関数ですが、DRYの原則がこのアプローチによっていかに香り高く違反されているかにかなり悩んでおり、sys.path変更ロジックを維持する方法を見つけたいと思います1つの場所で。
注:-思い浮かぶことの1つは、sys.path変更ロジックを、常にPYTHONPATHにある共通の場所にインストールすることです。これはひどい考えではありませんが、新しい環境に移動するたびに実行する必要があるインストール手順を導入することを意味します。もう1つ覚えておくべきこと(または、可能性としては、忘れる可能性が高い)なので、できればこれを避けたいと思います。
OK、私は PEP 302 インポートフックを含むソリューションの作成を開始しました。複雑すぎる。
私はあなたがあなた自身の質問に次のように答えたと思います:
この「ゲートウェイ」スクリプトは、あらゆる種類のブートストラップ機能を実装できます。たとえば、virtualenvが正しく設定され、現在のブランチに対してアクティブ化されていることを確認します。
virtualenvを既に使用している場合は、既にあるロジックを「〜/ venv/or-whatever-path/bin/activate」スクリプトに入れます。エントリポイントを指定しない場合は、可能なエントリポイントのリストを表示し、応答を要求することをお勧めします(番号付きリストとraw_input()ステートメントでできます)。
また、ターミナルのタブ名またはウィンドウタイトルに表示されるように、ブランチの名前を$ PS1に入れます。私は似たようなものを選び、プロンプトの色を変えて、間違った場所で正しいことをしないようにします。
virtualenv を使用する必要があります。隔離されたpython環境を作成します。製品のバージョンに応じてサードパーティパッケージの異なるバージョンが必要な場合にも推奨されます。
私のワークフローはあなたのワークフローにいくらか似ています。私は複数のブランチを異なるディレクトリでチェックアウトしています。ブランチごとにvirtualenvを作成してみましたが、面倒です。
$PWD
に対して実行可能なコードしかないため、PYTHONPATH=$PWD/.. python code-to-run.py
に対してPYTHONPATH
を設定しました。これは、現在のディレクトリのソースツリーを使用し、その後、virtualenvおよびシステムモジュールを使用します。
これは明らかにWindowsでは機能しません。
私が試したもう1つの方法は、「自己変更」.pthファイルでした。含まれていた
import sys, os; sys.path.insert(0. os.path.abspath('..'))
必要に応じて、より複雑なロジックを使用できます。
リビジョンコントロールでは、sync/clone/updateでスクリプトを実行できますか?その場合は、ソースツリーの各ディレクトリに適切なファイルを構成して、ルートディレクトリが明示的に参照されるようにすることができます。
たとえば、Mercurialを使用している場合、これは http://Mercurial.selenic.com/wiki/TipsAndTricks から役立つかもしれません:
これは、常に使用できるPDFが必要であることを前提としていますが、それらを追跡する必要はなく、そのコンテンツのみ(およびそれらはtexファイルで定義されています)です。
このため、リビジョンに更新するたびにPDFを作成する更新フックを追加します。
.hg/hgrcを編集して、フックセクションに更新フックを含めます。
[フック] update.create_pdfs = latex your_tex_file.texこれをさらに簡単にするために、すべてのPDFを作成するバージョン付きスクリプトを使用できます。そうすれば、スクリプトを呼び出すだけでよく、テキストファイルを追加したり、呼び出しを変更したりするときに.hg/hgrcを編集する必要はありません。
私はpythonスクリプトをプラットフォーム互換性のために使用します:
parse_latex.py:
行番号を切り替え1#!/ usr/bin/env python 2 from subprocess import call 3 for i in( "file1.tex"、 "file2.tex"):4 call(["ラテックス "、i]).hg/hgrc:
[フック] update.create = ./parse_latex.py
OK、それでゲートウェイスクリプトを使いました。現時点では、最小限の機能、パスのキャッシュ、virtualenv制御などはありません。
import os
import sys
import fnmatch
OK = 0
ERROR = 1
_EXCLUDE_FROM_SYSPATH = [".svn", ".hg", ".", ".."]
# -----------------------------------------------------------------------------
def _root_path_of_repo_branch():
"""Returns working copy root path."""
branch_root_marker = "branch_root_marker"
path = ""
if ("__file__" in locals() or globals()) and "__main__" != __file__:
path = __file__
Elif sys.argv and sys.argv[0] and not sys.argv[0] == "-c":
path = sys.argv[0]
path = os.path.realpath(os.path.expandvars(path))
while os.path.exists(path):
if os.path.exists(os.path.join(path, branch_root_marker)):
break
else:
path = os.path.dirname(path)
assert os.path.exists(path), " ".join("Could not find", branch_root_marker)
return path
# -----------------------------------------------------------------------------
def _pythondirs_in_branch(root_path_of_branch):
"""Find pythondirs in the specified branch."""
srcpath = os.path.join(root_path_of_branch, "src")
srctree = os.walk(srcpath)
pythondirs = list()
for (dirpath, dirnames, filenames) in srctree:
for name in _EXCLUDE_FROM_SYSPATH:
if name in dirnames:
dirnames.remove(name)
if any((fnmatch.fnmatch(name, "*.py") for name in filenames)):
pythondirs.append(dirpath)
return pythondirs
# -----------------------------------------------------------------------------
def _add_dirs_to_syspath(dirlist):
"""Adds specified directories to the sys path."""
for dirname in dirlist:
sys.path.append(dirname)
# -----------------------------------------------------------------------------
def _add_pythondirs_in_branch_to_syspath():
"""Adds pythondirs in the current branch to the sys path"""
branch_path = _root_path_of_repo_branch()
pythondirs = _pythondirs_in_branch(branch_path)
_add_dirs_to_syspath(pythondirs)
# -----------------------------------------------------------------------------
def main(argv=None):
"""Read config & start app."""
if argv is None:
argv = sys.argv[1:]
command_name = argv[0]
if "test" in command_name:
pass
_add_pythondirs_in_branch_to_syspath()
try:
command = __import__(command_name)
except ImportError, err:
print "IMPORT ERROR : " + err.message
return ERROR
return command.main(argv)
if __name__ == "__main__":
sys.exit(main())