Skip to content

Commit 22ea81e

Browse files
committed
Reload watched (sub)modules automatically
Adds a new step, ReloadModules, which reloads all watched modules except for the init module. This step needs to know which modules have changed, which is currently only available in the main loops variables (new_changed, last_changed), so I pass this information to the step if it's named "ReloadModules". Other alternatives could be some global state, or passing some general state to all the steps.
1 parent 8ec6330 commit 22ea81e

1 file changed

Lines changed: 33 additions & 3 deletions

File tree

hotload.py

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,21 @@ def _file_changed(f):
4646
def _all_file_changes(filepaths):
4747
return {path: _file_changed(path) for path in filepaths}
4848

49+
def _changed_modules(new_changed, last_changed):
50+
list = []
51+
if last_changed is None:
52+
return list
53+
modules = {}
54+
for module in sys.modules.copy().values():
55+
file = getattr(module, '__file__', None)
56+
if file is not None:
57+
modules[file] = module
58+
for path in new_changed:
59+
if new_changed[path] != last_changed[path]:
60+
module = modules.get(path, None)
61+
if module is not None:
62+
list.append(module)
63+
return list
4964

5065
def listfiles(folder, ext=""):
5166
fs = list()
@@ -135,6 +150,13 @@ class ClearTerminal(Runnable):
135150
def run(self):
136151
os.system("cls" if os.name == "nt" else "clear")
137152

153+
class ReloadModules(Runnable):
154+
def __init__(self, module):
155+
self.main_module = module
156+
def run(self, modules):
157+
for module in modules:
158+
if module != self.main_module:
159+
_reload_module(module)
138160

139161
def hotload(watch, steps, waittime_ms=1.0 / 144):
140162
"""Hotload that code!"""
@@ -148,18 +170,25 @@ def hotload(watch, steps, waittime_ms=1.0 / 144):
148170
# Take note of when files were last changed before we start reloading
149171
last_changed = None
150172

173+
is_module_reloader = lambda step: type(step).__name__ == "ReloadModules"
174+
do_reload_modules = bool(list(filter(is_module_reloader, steps)))
175+
151176
# Begin the loop! Each Runner is responsible for handling its own exceptions.
152177
while True:
153178
new_changed = _all_file_changes(watchfiles)
154179
if last_changed == new_changed:
155180
time.sleep(waittime_ms)
156181
else:
157182
reload_begin_ms = time.time() * 1000
183+
changed_modules = _changed_modules(new_changed, last_changed) if do_reload_modules else []
158184
last_changed = new_changed
159185
try:
160186
for step in steps:
161187
try:
162-
step.run()
188+
if is_module_reloader(step):
189+
step.run(changed_modules)
190+
else:
191+
step.run()
163192
except KeyboardInterrupt:
164193
raise
165194
except:
@@ -210,7 +239,7 @@ def main():
210239
print()
211240
print(" ls *py | hotload hello.py")
212241

213-
watchfiles = [f.strip() for f in sys.stdin.readlines()]
242+
watchfiles = [os.path.normpath(os.path.join(os.getcwd(), f.strip())) for f in sys.stdin.readlines()]
214243

215244
if not watchfiles:
216245
print("Error: no watch files specified.")
@@ -227,7 +256,7 @@ def main():
227256

228257
conf = {
229258
"watch": [watchfiles],
230-
"steps": [ClearTerminal(), reloaded_module],
259+
"steps": [ClearTerminal(), ReloadModules(reloaded_module.module), reloaded_module],
231260
}
232261

233262
hotload(**conf)
@@ -237,3 +266,4 @@ def main():
237266

238267
if __name__ == "__main__":
239268
main()
269+

0 commit comments

Comments
 (0)