如果代码发生变化,我想重启我的Python Web应用程序。但是可能会有大量文件可以更改,因为导入模块中的文件可能会发生变化......
如何从导入的软件包/模块中获取实际文件名?
如何有效地检测修改后的Python文件?有没有图书馆可以做到这一点?
答案 0 :(得分:6)
无耻的插头。还有http://github.com/gorakhargosh/watchdog我正在努力做到这一点。
HTH。
答案 1 :(得分:1)
gamin是另一种选择,略低于Linux特定。
答案 2 :(得分:1)
我不确定如何在您的环境中实施“重新加载应用程序”操作;使用内置reload
重新加载已更改的模块可能不会削减它。
但就检测是否有变化而言,以下是接近它的一种方法。
__file__
属性。 sys.modules
。sys.modules
,并依次查找每个模块的磁盘更改有时__file__
指向.pyc
文件而不是.py
文件,因此您可能需要删除尾随c。有时存在.pyc
文件,但.py
不存在;在一个强大的系统中,你必须允许这样做。
执行此操作的代码概念证明(不健全):
_module_timestamps = {}
_checking = False
def run_checker():
global _checking
_checking = True
while _checking:
for name, module in sys.modules.iteritems():
if hasattr(module, '__file__'):
filename = module.__file__
if filename.endswith('.pyc'):
filename = filename[:-1]
mtime = os.stat(filename).st_mtime
if name not in _module_timestamps:
_module_timestamps[name] = mtime
else:
if mtime > _module_timestamps[name]:
do_reload(name)
else:
'module %r has no file attribute' % (name,)
time.sleep(1)
def do_reload(modname):
print 'I would reload now, because of %r' % (modname,)
check_thread = threading.Thread(target=run_checker)
check_thread.daemon = True
check_thread.start()
try:
while 1:
time.sleep(0.1)
except KeyboardInterrupt:
print '\nexiting...'
答案 3 :(得分:1)
这是一个如何使用pyinotify实现这一点的例子(即,在Linux上)。
from importlib import import_module
class RestartingLauncher:
def __init__(self, module_name, start_function, stop_function, path="."):
self._module_name = module_name
self._filename = '%s.py' % module_name
self._start_function = start_function
self._stop_function = stop_function
self._path = path
self._setup()
def _setup(self):
import pyinotify
self._wm = pyinotify.WatchManager()
self._notifier = pyinotify.ThreadedNotifier(
self._wm, self._on_file_modified)
self._notifier.start()
# We monitor the directory (instead of just the file) because
# otherwise inotify gets confused by editors such a Vim.
flags = pyinotify.EventsCodes.OP_FLAGS['IN_MODIFY']
wdd = self._wm.add_watch(self._path, flags)
def _on_file_modified(self, event):
if event.name == self._filename:
print "File modification detected. Restarting application..."
self._reload_request = True
getattr(self._module, self._stop_function)()
def run(self):
self._module = import_module(self._module_name)
self._reload_request = True
while self._reload_request:
self._reload_request = False
reload(self._module)
getattr(self._module, self._start_function)()
print 'Bye!'
self._notifier.stop()
def launch_app(module_name, start_func, stop_func):
try:
import pyinotify
except ImportError:
print 'Pyinotify not found. Launching app anyway...'
m = import_module(self._module_name)
getattr(m, start_func)()
else:
RestartingLauncher(module_name, start_func, stop_func).run()
if __name__ == '__main__':
launch_app('example', 'main', 'force_exit')
launch_app调用中的参数是文件名(不带“.py”),开始执行的函数和以某种方式停止执行的函数。
这是一个愚蠢的“app”示例,可以使用之前的代码(重新)启动:
run = True
def main():
print 'in...'
while run: pass
print 'out'
def force_exit():
global run
run = False
在您想要使用它的典型应用程序中,您可能有某种主循环。对于基于GLib / GTK +的应用程序,这是一个更实际的例子:
from gi.repository import GLib
GLib.threads_init()
loop = GLib.MainLoop()
def main():
print "running..."
loop.run()
def force_exit():
print "stopping..."
loop.quit()
相同的概念适用于大多数其他循环(Clutter,Qt等)。
监视多个代码文件(即,属于应用程序的所有文件)和错误恢复(例如,打印异常并在空闲循环中等待,直到代码被修复,然后再次启动它)留作练习读者:)。
注意:此答案中的所有代码均在ISC许可下发布(除了知识共享)。
答案 4 :(得分:0)
这是特定于操作系统的。对于Linux,有inotify,参见例如http://github.com/rvoicilas/inotify-tools/