简体   繁体   English

自动修补 PYTHONPATH(使用 Fabric 任务时)

[英]Automatically patching PYTHONPATH (when using Fabric tasks)

I wrap function calls within my initialization module with a decorator that patches PYTHONPATH to add current directory to it so I can use relative imports within my modules without worrying about adding the package to PYTHONPATH explicitly (edited based on @abarnert's comments)我在我的初始化模块中包装函数调用,装饰器修补 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

Here's how I use it:这是我如何使用它:

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

Are there any major issues with this approach that I am not aware of?这种方法有什么我不知道的重大问题吗?


My objectives with this are the following:我的目标如下:

  • i want to have a self contained bootstrap that a user can immediately use without any additional env manipulations我想要一个自包含的引导程序,用户可以立即使用它而无需任何额外的 env 操作
  • i also assume that a user can change the name of the bootstrap package so it's imperative that all the imports remain relative我还假设用户可以更改引导程序包的名称,因此所有导入都必须保持相对

[EDIT] I'm actually realizing that the problem I am facing has more to do with the way Fabric runs tasks as opposed to pure Python module importing. [编辑] 我实际上意识到我面临的问题更多地与 Fabric 运行任务的方式有关,而不是纯 Python 模块导入。 If I attempt to run a task from a python shell (as opposed to fab task1) all the imports are resolved correctly without any patching required.如果我尝试从 python shell(而不是 fab task1)运行任务,则所有导入都会正确解析,无需任何修补。 Running a task through fab causes import errors通过 fab 运行任务会导致导入错误

  1. os.environ is global. os.environ是全球性的。 You're modifying it, and not modifying it back afterward.你正在修改它,之后不会再修改它。 So, after doing @patch_python_path on any function, you've now done the equivalent for everything else you define after that—including in other modules and in the top-level script.因此,在对任何函数执行@patch_python_path之后,您现在已经为之后定义的所有其他内容执行了等效操作——包括在其他模块和顶级脚本中。
  2. sys.path is also global, and again you're modifying it and not restoring it. sys.path也是全局的,您再次修改它而不是恢复它。
  3. You shouldn't ever need to modify both PYTHONPATH and sys.path .您永远不需要同时修改PYTHONPATHsys.path (In particular, it's the former that you usually shouldn't need.) (特别是,您通常不需要前者。)
  4. You're adding .您正在添加. to PYTHONPATH , but os.getcwd() to sys.path .PYTHONPATH ,但os.getcwd()sys.path If you ever do need to modify both, everything will stop working after an os.chdir() , which will effectively change the PYTHONPATH but not the sys.path .如果您确实需要修改两者,那么在os.chdir()之后一切都将停止工作,这将有效地更改PYTHONPATH而不是sys.path
  5. Any wrapped function will lose its docstring, name, etc.;任何包装的函数都将丢失其文档字符串、名称等; use @functools.wraps on your wrap function.在你的wrap函数上使用@functools.wraps

Those are all just issues with your implementation, without getting into whether it's a good idea in the first place.这些都只是你的实现的问题,而没有首先考虑它是否是一个好主意。

The most common reasons people want something like this are (a) make Python XY packages work like Python VW, and (b) allow importing a package from the source tree to work the same as importing it after install, even from the interpreter shell.人们想要这样的东西的最常见原因是 (a) 使 Python XY 包像 Python VW 一样工作,以及 (b) 允许从源树导入包与在安装后导入包一样工作,即使是从解释器 shell。 The former is probably a bad idea;前者可能是个坏主意; the latter is handy, but there are other ways to accomplish it.后者很方便,但还有其他方法可以实现。 If you have some different higher-level goal, you'll have to tell us what that goal is before anyone can tell you whether this is the best way to accomplish it.如果你有一些不同的更高层次的目标,你必须先告诉我们那个目标是什么,然后任何人才能告诉你这是否是实现它的最佳方式。

You might simply add current path to sys.path :您可以简单地将当前路径添加到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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM