Запуск Python за несколько строк до моего script
Мне нужно запустить 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
.
Ответы
Ответ 1
Python имеет механизм запуска кода при запуске; site.
"This module is automatically imported during initialization."
Модуль сайта попытается импортировать модуль с именем sitecustomize
до импорта __main__
.
Он также попытается импортировать модуль с именем 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()
Затем вы можете запустить script следующим образом:
MY_STARTUP_FILE=/somewhere/bar.py python /somewhere_else/foo.py
- Вы можете запустить любой script перед foo.py без необходимости добавлять код в reimport
__main__
.
- Запустите
export MY_STARTUP_FILE=/somewhere/bar.py
и не нужно ссылаться на него каждый раз
Ответ 2
Вы можете использовать execfile()
, если файл .py
и uncompyle2, если файл .pyc
.
Скажем, у вас есть файловая структура вроде:
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
Также см. ответ .
Ответ 3
Вероятно, у вас есть что-то вроде:
if __name__ == '__main__':
# some code
Вместо этого напишите свой код в функции main()
в foo
, а затем выполните:
if __name__ == '__main__':
main()
Затем в баре вы можете импортировать foo и вызвать foo.main()
.
Дополнительно, если вам нужно изменить рабочий каталог, вы можете использовать метод os.chdir(path)
, например. os.chdir('path/of/bar')
.
Ответ 4
bar.py
должен вести себя как сам интерпретатор Python и запускать foo.py
(или foo.pyc
, как вы просили об этом), как если бы это был основной script. Это удивительно сложно. Мое 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 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
, вставляет расположение script в 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")
... running 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
.
Ответ 5
foo.py
не обязательно должна находиться в той же папке, что и в основной папке. Используйте
export PYTHONPATH=$HOME/dirWithFoo/:$PYTHONPATH
Чтобы добавить путь, который foo
находится в пути Python. Теперь вы можете
from foo import myfunc
myfunc()
И сделайте myfunc делать все, что захочет
Ответ 6
Вы пробовали это?
bar.py
imp.load_source('__main__', '../../foo.py')
для меня он запускает все, что находится в foo.py(до и внутри main)
После, может быть, что вы делаете в bar.py, может быть более сложным, чем мой основной тест.
Хорошая вещь с load_source заключается в том, что она импортирует .pyc, если она уже существует, или инициирует "компиляцию".py, а затем импортирует .pyc.
Ответ 7
Это решение для меня хорошо работает:
#!/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))
Ответ 8
Предполагая, что вы используете Bash, как насчет:
python <(cat /somewhere/bar.py /somewhere_else/foo.py)