简体   繁体   English

让Python在我的脚本之前运行几行

[英]Making Python run a few lines before my script

I need to run a script foo.py , but I need to also insert some debugging lines to run before the code in foo.py . 我需要运行脚本foo.py ,但我还需要在foo.py的代码之前插入一些调试行。 Currently I just put those lines in foo.py and I'm careful not to commit that to Git, but I don't like this solution. 目前我只是将这些行放在foo.py ,我小心不要将它提交给Git,但我不喜欢这个解决方案。

What I want is a separate file bar.py that I don't commit to Git. 我想要的是一个单独的文件bar.py ,我不承诺给Git。 Then I want to run: 然后我想跑:

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

What I want this to do is first run some lines of code in bar.py , and then run foo.py as __main__ . 我想要做的是首先在bar.py运行一些代码bar.py ,然后运行foo.py作为__main__ It should be in the same process that the bar.py lines ran in, otherwise the debugging lines won't help. 它应该在bar.py行运行的相同过程中,否则调试行将无济于事。

Is there a way to make bar.py do this? 有没有办法让bar.py这样做?

Someone suggested this: 有人建议:

import imp
import sys

# Debugging code here

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

The problem with that is that because it uses import machinery, I need to be on the same folder as foo.py to run that. 问题是,因为它使用导入机制,我需要与foo.py在同一个文件夹上运行它。 I don't want that. 我不希望这样。 I want to simply put in the full path to foo.py . 我想简单地介绍一下foo.py的完整路径。

Also: The solution needs to work with .pyc files as well. 另外:解决方案也需要使用.pyc文件。

Python has a mechanism for running code at startup; Python有一种在启动时运行代码的机制; the site module. 网站模块。

"This module is automatically imported during initialization."

The site module will attempt to import a module named sitecustomize before __main__ is imported. 在导入__main__之前,站点模块将尝试导入名为sitecustomize __main__ It will also attempt to import a module named usercustomize if your environment instructs it to. 如果您的环境指示它,它还将尝试导入名为usercustomize的模块。

For example, you could put a sitecustomize.py file in your site-packages folder that contains this: 例如,您可以将sitecustomize.py文件放在包含以下内容的site-packages文件夹中:

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()

Then you could run your script like this: 然后你可以像这样运行你的脚本:

MY_STARTUP_FILE=/somewhere/bar.py python /somewhere_else/foo.py
  • You could run any script before foo.py without needing to add code to reimport __main__ . 您可以在foo.py之前运行任何脚本,而无需添加代码来重新导入__main__
  • Run export MY_STARTUP_FILE=/somewhere/bar.py and not need to reference it every time 运行export MY_STARTUP_FILE=/somewhere/bar.py ,不需要每次都引用它

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

Let's say you have your file structure like: 假设您的文件结构如下:

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

foo.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 bar.py

print a
print 'real job'

And in test/ , if you do: test/ ,如果你这样做:

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

Both, outputs the same: 两者,输出相同:

debugging...
1
real job

Please also see this answer . 还请看这个答案

You probably have something along the lines of: 你可能有以下几点:

if __name__ == '__main__':
    # some code

Instead, write your code in a function main() in foo and then do: 相反,在foo函数main()中编写代码,然后执行:

if __name__ == '__main__':
    main()

Then, in bar, you can import foo and call foo.main() . 然后,在bar中,您可以导入foo并调用foo.main()

Additionaly, if you need to change the working directory , you can use the os.chdir(path) method, eg os.chdir('path/of/bar') . 另外,如果需要更改工作目录 ,可以使用os.chdir(path)方法,例如os.chdir('path/of/bar')

bar.py would have to behave like the Python interpreter itself and run foo.py (or foo.pyc , as you asked for that) as if it was the main script. bar.py必须像Python解释器本身一样运行foo.py (或foo.pyc ,就像你要求的那样),好像它是主脚本一样。 This is surprisingly hard. 这非常难。 My 90% solution to do that looks like so: 我这样做的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)

This mimics the default behavior of the Python interpreter relatively closely. 这相对密切地模仿了Python解释器的默认行为。 It sets system globals __name__ , __file__ , sys.argv , inserts the script location into sys.path , clears the global namespace etc. 它集系统全局__name____file__sys.argv ,插入脚本位置到sys.path ,清除全局命名空间等。

You would copy that code to bar.py or import it from somewhere and use it like so: 您可以将该代码复制到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:])

Then, given this: 然后,鉴于此:

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

and foo.py looking like this: 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")

... running foo.py via bar.py gives you that: ...通过bar.py运行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

While I'd guess that run_script_as_main is lacking in some interesting details, it's pretty close to the way Python would run foo.py . 虽然我猜想run_script_as_main缺少一些有趣的细节,但它与Python运行foo.py的方式非常接近。

The foo.py need not be on the same folder as the main folder. foo.py不必与主文件夹位于同一文件夹中。 Use 采用

export PYTHONPATH=$HOME/dirWithFoo/:$PYTHONPATH

To add the path that foo resides to Python path. 添加foo所在的路径到Python路径。 Now you can 现在你可以

from foo import myfunc

myfunc()

And make myfunc do whatever u want 并让myfunc做你想做的事

Have you tried this? 你试过这个吗?

bar.py bar.py

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

for me it runs whatever is in foo.py (before and inside main ) 对我来说它运行无论是在foo.py(之前和内部

After, maybe what you do in bar.py might be more tricky that my basic test. 之后,也许你在bar.py中所做的事情可能比我的基本测试更棘手。

The good thing with load_source is that it imports the .pyc if it already exists or initiates a "compile" of the .py and then imports the .pyc. load_source的好处是它导入.pyc(如果它已经存在)或者启动.py的“编译”然后导入.pyc。

This solution is what ended up working well for me: 这个解决方案最终适合我:

#!/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))

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

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