2

我将函数调用包装在我的初始化模块中,并使用修补 PYTHONPATH 以将当前目录添加到其中的装饰器,这样我就可以在我的模块中使用相对导入,而不必担心将包显式添加到 PYTHONPATH(根据@abarnert 的评论进行编辑)

def patch_python_path(f):
    @wraps(f)       
    def wrap(*args, **kwargs):
        ROOT = os.pathsep.join([os.path.abspath(os.path.dirname(__file__))])
        if not os.environ.has_key("PYTHONPATH"):
            os.environ["PYTHONPATH"] = ""
        if not (ROOT in os.environ["PYTHONPATH"].split(":")):
            os.environ["PYTHONPATH"] = "%s:%s" % (os.environ["PYTHONPATH"], ROOT)
        if not ROOT in sys.path:
            sys.path.append(ROOT)
        return f(*args, **kwargs)
    return wrap

这是我如何使用它:

@patch_python_path
def initialize():
    #at this point any code being run has access to local modules through relative imports
    pass

这种方法是否存在我不知道的重大问题?


我的目标如下:

  • 我想要一个用户可以立即使用的自包含引导程序,而无需任何额外的环境操作
  • 我还假设用户可以更改引导程序包的名称,因此所有导入都必须保持相对

[编辑] 我实际上意识到我面临的问题更多地与 Fabric 运行任务的方式有关,而不是纯 Python 模块导入。如果我尝试从 python shell(而不是 fab task1)运行任务,则所有导入都可以正确解析,而无需任何修补。通过 fab 运行任务会导致导入错误

4

2 回答 2

2
  1. os.environ是全球性的。您正在修改它,而不是在之后修改它。因此,在@patch_python_path完成任何函数之后,您现在已经为之后定义的所有其他内容完成了等价的操作——包括在其他模块和顶级脚本中。
  2. sys.path也是全局的,你再次修改它而不是恢复它。
  3. 您永远不需要同时修改PYTHONPATHsys.path。(尤其是你通常不需要的前者。)
  4. 您要添加.PYTHONPATH,但要添加os.getcwd()sys.path。如果您确实需要同时修改两者,那么一切都将在 之后停止工作os.chdir(),这将有效地改变PYTHONPATH而不是sys.path.
  5. 任何包装的函数都将丢失其文档字符串、名称等;@functools.wraps在您的wrap功能上使用。

这些都只是你实施的问题,而不是首先考虑这是否是一个好主意。

人们想要这样的最常见原因是 (a) 使 Python XY 包像 Python VW 一样工作,以及 (b) 允许从源代码树导入包与在安装后导入包一样工作,即使从解释器 shell 中也是如此。前者可能是个坏主意;后者很方便,但还有其他方法可以完成它。如果您有一些不同的更高级别的目标,您必须先告诉我们该目标是什么,然后其他人才能告诉您这是否是实现目标的最佳方式。

于 2012-11-13T01:22:35.653 回答
1

您可以简单地将当前路径添加到sys.path

import sys
def patch_python_path(f):
    def wrap(*args, **kwargs):
        if not '.' in sys.path:
            sys.path.append('.')
        return f(*args, **kwargs)

    return wrap
于 2012-11-13T01:18:19.223 回答