1
2
3
4
5
6
7
8
9
10
11
12
13
14 import os
15 import sys
16 import threading
17 import time
18 import traceback
19
20 _SLEEP_TIME = 1
21
23 """When this function is run from the main thread, it will force other
24 threads to exit when any modules currently loaded change.
25
26 :param modification_callback: a function taking a single argument,
27 the modified file, which is called
28 every time a modification is
29 detected
30
31 :param loop_callback: a function taking no arguments, which is
32 called after every modification check
33
34 """
35 mtimes = {}
36 while True:
37 for filename in filter(None, [getattr(module, '__file__', None)
38 for module in sys.modules.values()]):
39 while not os.path.isfile(filename):
40 filename = os.path.dirname(filename)
41 if not filename:
42 break
43 if not filename:
44 continue
45
46 if filename.endswith(('.pyc', '.pyo')):
47 filename = filename[:-1]
48
49 if not os.path.isfile(filename):
50
51 continue
52
53 mtime = os.stat(filename).st_mtime
54 if filename not in mtimes:
55 mtimes[filename] = mtime
56 continue
57 if mtime != mtimes[filename]:
58 modification_callback(filename)
59 sys.exit(3)
60 loop_callback()
61 time.sleep(_SLEEP_TIME)
62
64 is_win32 = sys.platform == 'win32'
65 if is_win32:
66 can_exec = lambda path: os.path.isfile(path) and \
67 os.path.normpath(path).endswith('.exe')
68 else:
69 can_exec = lambda path: os.access(path, os.X_OK)
70
71 if os.path.isfile(sys.argv[0]):
72 args = sys.argv if can_exec(sys.argv[0]) else \
73 [sys.executable] + sys.argv
74 elif is_win32 and can_exec(sys.argv[0] + '.exe'):
75 args = [sys.argv[0] + '.exe'] + sys.argv[1:]
76 elif os.path.isfile(sys.argv[0] + '-script.py'):
77 args = [sys.executable, sys.argv[0] + '-script.py'] + sys.argv[1:]
78 else:
79 args = [sys.executable] + sys.argv
80 path = args[0]
81 if is_win32:
82 args = ['"%s"' % arg for arg in args]
83 new_environ = os.environ.copy()
84 new_environ['RUN_MAIN'] = 'true'
85
86 while True:
87
88
89 exit_code = os.spawnve(os.P_WAIT, path, args, new_environ)
90 if exit_code != 3:
91 return exit_code
92
93 -def main(func, modification_callback, *args, **kwargs):
94 """Run the given function and restart any time modules are changed."""
95 if os.environ.get('RUN_MAIN'):
96 exit_code = []
97 def main_thread():
98 try:
99 func(*args, **kwargs)
100 exit_code.append(None)
101 except SystemExit, e:
102 exit_code.append(e.code)
103 except:
104 traceback.print_exception(*sys.exc_info())
105 exit_code.append(1)
106 def check_exit():
107 if exit_code:
108 sys.exit(exit_code[0])
109
110 thread = threading.Thread(target=main_thread, name='Main thread')
111 thread.setDaemon(True)
112 thread.start()
113 try:
114
115 _reloader_thread(modification_callback, check_exit)
116 except KeyboardInterrupt:
117 pass
118 else:
119
120 try:
121 sys.exit(_restart_with_reloader())
122 except KeyboardInterrupt:
123 pass
124