在我的脚本之前制作 Python 运行 几行

Making Python run a few lines before my script

我需要 运行 脚本 foo.py,但我还需要在 foo.py 中的代码之前向 运行 插入一些调试行。目前我只是将这些行放在 foo.py 中,我很小心不要将其提交给 Git,但我不喜欢这个解决方案。

我想要的是一个单独的文件 bar.py,我不承诺 Git。那我要运行:

python /somewhere/bar.py /somewhere_else/foo.py

我想要做的是首先 运行 bar.py 中的一些代码行,然后 运行 foo.py 作为 __main__。它应该与 bar.py 行 运行 在同一个进程中,否则调试行将无济于事。

有没有办法让 bar.py 做到这一点?

有人建议:

import imp
import sys

# Debugging code here

fp, pathname, description = imp.find_module(sys.argv[1])
imp.load_module('__main__', fp, pathname, description)

问题在于,因为它使用进口机器,所以我需要与 foo.py 到 运行 位于同一文件夹中。我不想要那个。我想简单地输入 foo.py.

的完整路径

另外:该解决方案也需要与 .pyc 文件一起使用。

foo.py 不必与主文件夹位于同一文件夹中。使用

export PYTHONPATH=$HOME/dirWithFoo/:$PYTHONPATH

foo所在的路径添加到Python路径。现在你可以

from foo import myfunc

myfunc()

让 myfunc 做任何你想做的事

您可能有以下内容:

if __name__ == '__main__':
    # some code

相反,在 foo 中的函数 main() 中编写代码,然后执行:

if __name__ == '__main__':
    main()

然后,在 bar 中,您可以导入 foo 并调用 foo.main()

此外,如果您需要change the working directory,您可以使用os.chdir(path)方法,例如os.chdir('path/of/bar') .

如果文件是 .pyc,您可以使用 execfile() if the file is .py and uncompyle2

假设您的文件结构如下:

test|-- foo.py
    |-- bar
         |--bar.py   

foo.py

import sys

a = 1
print ('debugging...')

# run the other file
if sys.argv[1].endswith('.py'): # if .py run right away
    execfile(sys.argv[1], globals(), locals())
elif sys.argv[1].endswith('.pyc'): # if .pyc, first uncompyle, then run
    import uncompyle2
    from StringIO import StringIO
    f = StringIO()
    uncompyle2.uncompyle_file(sys.argv[1], f)
    f.seek(0)
    exec(f.read(), globals(), locals())

bar.py

print a
print 'real job'

并且在 test/ 中,如果您这样做:

$ python foo.py bar/bar.py
$ python foo.py bar/bar.pyc

两者,输出相同:

debugging...
1
real job

另请参阅此

你试过吗?

bar.py

imp.load_source('__main__', '../../foo.py')

对我来说,它运行 foo.py 中的任何内容(在 main 之前和内部)

之后,也许你在 bar.py 中做的事情可能比我的基本测试更棘手。

load_source 的好处是,如果 .pyc 已经存在,它会导入它,或者启动 .py 的 "compile",然后导入 .pyc。

bar.py 必须表现得像 Python 解释器本身,而 运行 foo.py(或 foo.pyc,正如你所要求的那样)就好像这是主要脚本。这出奇的难。我 90% 的解决方案如下所示:

def run_script_as_main(cmdline):
    # It is crucial to import locally what we need as we
    # later remove everything from __main__
    import sys, imp, traceback, os
    # Patch sys.argv
    sys.argv = cmdline
    # Clear the __main__ namespace and set it up to run
    # the secondary program
    maindict = sys.modules["__main__"].__dict__
    builtins = maindict['__builtins__']
    maindict.clear()
    maindict['__file__'] = cmdline[0]
    maindict['__builtins__'] = builtins
    maindict['__name__'] = "__main__"
    maindict['__doc__'] = None
    # Python prepends a script's location to sys.path
    sys.path[0] = os.path.dirname(os.path.abspath(cmdline[0]))
    # Treat everything as a Python source file, except it
    # ends in '.pyc'
    loader = imp.load_source
    if maindict["__file__"].endswith(".pyc"):
        loader = imp.load_compiled
    with open(cmdline[0], 'rb') as f:
        try:
            loader('__main__', maindict["__file__"], f)
        except Exception:
            # In case of an exception, remove this script from the
            # stack trace; if you don't care seeing some bar.py in
            # traceback, you can leave that out.
            ex_type, ex_value, ex_traceback = sys.exc_info()
            tb = traceback.extract_tb(ex_traceback, None)[1:]
            sys.stderr.write("Traceback (most recent call last):\n")
            for line in traceback.format_list(tb):
                sys.stderr.write(line)
            for line in traceback.format_exception_only(ex_type, ex_value):
                sys.stderr.write(line)

这模仿了 Python 解释器的默认行为 比较紧密。它设置系统全局变量 __name____file__sys.argv,将脚本位置插入 sys.path,清除全局命名空间等

您可以将该代码复制到 bar.py 或从某处导入它并像这样使用它:

if __name__ == "__main__":
    # You debugging setup goes here
    ...
    # Run the Python program given as argv[1]
    run_script_as_main(sys.argv[1:])

然后,鉴于此:

$ find so-foo-bar
so-foo-bar
so-foo-bar/debugaid
so-foo-bar/debugaid/bar.py
so-foo-bar/foo.py

foo.py 看起来像这样:

import sys

if __name__ == "__main__":
    print "My name is", __name__
    print "My file is", __file__
    print "My command line is", sys.argv
    print "First 2 path items", sys.path[:2]
    print "Globals", globals().keys()
    raise Exception("foo")

... 运行ning foo.py 通过 bar.py 给你:

    $ python so-foo-bar/debugaid/bar.py so-foo-bar/foo.py
    My name is __main__
    My file is so-foo-bar/foo.py
    My command line is ['so-foo-bar/foo.py']
    First 2 path items ['~/so-foo-bar', '/usr/local/...']
    Globals ['__builtins__', '__name__', '__file__', 'sys', '__package__', '__doc__']
    Traceback (most recent call last):
      File "so-foo-bar/foo.py", line 9, in 
        raise Exception("foo")
    Exception: foo

虽然我猜测 run_script_as_main 缺少一些有趣的细节,但它与 Python 的方式非常接近 运行 foo.py.

这个解决方案最终对我很有效:

#!/usr/bin/env python

import imp
import sys
import os.path

file_path = sys.argv.pop(1)
assert os.path.exists(file_path)

folder, file_name = os.path.split(file_path)

###############################################################################
#                                                                             #

# Debugging cruft here

#                                                                             #
###############################################################################


module_name, _ = os.path.splitext(file_name)
sys.path.append(folder)
imp.load_module('__main__', *imp.find_module(module_name))

Python 有一种在启动时 运行ning 代码的机制; site 模块。

"This module is automatically imported during initialization."

站点模块将尝试在导入 __main__ 之前导入名为 sitecustomize 的模块。 如果您的环境指示它,它还将尝试导入名为 usercustomize 的模块。

例如,您可以将 sitecustomize.py 文件放入包含以下内容的站点包文件夹中:

import imp

import os

if 'MY_STARTUP_FILE' in os.environ:
    try:
        file_path = os.environ['MY_STARTUP_FILE']
        folder, file_name = os.path.split(file_path)
        module_name, _ = os.path.splitext(file_name)
        fp, pathname, description = imp.find_module(module_name, [folder])
    except Exception as e:
        # Broad exception handling since sitecustomize exceptions are ignored
        print "There was a problem finding startup file", file_path
        print repr(e)
        exit()

    try:
        imp.load_module(module_name, fp, pathname, description)
    except Exception as e:
        print "There was a problem loading startup file: ", file_path
        print repr(e)
        exit()
    finally:
        # "the caller is responsible for closing the file argument" from imp docs
        if fp:
            fp.close()

然后你可以 运行 你的脚本是这样的:

MY_STARTUP_FILE=/somewhere/bar.py python /somewhere_else/foo.py
  • 您可以 运行 foo.py 之前的任何脚本,而无需添加代码来重新导入 __main__
  • 运行 export MY_STARTUP_FILE=/somewhere/bar.py 不需要每次都引用