[英]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
__main__
. __main__
。 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
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.