web-dev-qa-db-ja.com

Pythonで「ホットライブラリ」開発用にsys.pathを設定する最良の方法は?

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つ覚えておくべきこと(または、可能性としては、忘れる可能性が高い)なので、できればこれを避けたいと思います。

4
William Payne

OK、私は PEP 302 インポートフックを含むソリューションの作成を開始しました。複雑すぎる。

私はあなたがあなた自身の質問に次のように答えたと思います:

この「ゲートウェイ」スクリプトは、あらゆる種類のブートストラップ機能を実装できます。たとえば、virtualenvが正しく設定され、現在のブランチに対してアクティブ化されていることを確認します。

virtualenvを既に使用している場合は、既にあるロジックを「〜/ venv/or-whatever-path/bin/activate」スクリプトに入れます。エントリポイントを指定しない場合は、可能なエントリポイントのリストを表示し、応答を要求することをお勧めします(番号付きリストとraw_input()ステートメントでできます)。

また、ターミナルのタブ名またはウィンドウタイトルに表示されるように、ブランチの名前を$ PS1に入れます。私は似たようなものを選び、プロンプトの色を変えて、間違った場所で正しいことをしないようにします。

2
Phil Cooper

virtualenv を使用する必要があります。隔離されたpython環境を作成します。製品のバージョンに応じてサードパーティパッケージの異なるバージョンが必要な場合にも推奨されます。

3
KangOl

私のワークフローはあなたのワークフローにいくらか似ています。私は複数のブランチを異なるディレクトリでチェックアウトしています。ブランチごとにvirtualenvを作成してみましたが、面倒です。

$PWDに対して実行可能なコードしかないため、PYTHONPATH=$PWD/.. python code-to-run.pyに対してPYTHONPATHを設定しました。これは、現在のディレクトリのソースツリーを使用し、その後、virtualenvおよびシステムモジュールを使用します。

これは明らかにWindowsでは機能しません。

私が試したもう1つの方法は、「自己変更」.pthファイルでした。含まれていた

import sys, os; sys.path.insert(0. os.path.abspath('..'))

必要に応じて、より複雑なロジックを使用できます。

1
9000

リビジョンコントロールでは、sync/clone/updateでスクリプトを実行できますか?その場合は、ソースツリーの各ディレクトリに適切なファイルを構成して、ルートディレクトリが明示的に参照されるようにすることができます。

たとえば、Mercurialを使用している場合、これは http://Mercurial.selenic.com/wiki/TipsAndTricks から役立つかもしれません:

  1. 自動生成(バイナリ)ファイルのマージを回避(PDF)
  2. その場でPDFを作成する

これは、常に使用できる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

1
codeshot

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())
0
William Payne