Interrupt main-thread as soon as a changed module file is detected, the lockfile gets deleted or gets too old.
| 3736 | |
| 3737 | |
| 3738 | class FileCheckerThread(threading.Thread): |
| 3739 | """ Interrupt main-thread as soon as a changed module file is detected, |
| 3740 | the lockfile gets deleted or gets too old. """ |
| 3741 | |
| 3742 | def __init__(self, lockfile, interval): |
| 3743 | threading.Thread.__init__(self) |
| 3744 | self.daemon = True |
| 3745 | self.lockfile, self.interval = lockfile, interval |
| 3746 | #: Is one of 'reload', 'error' or 'exit' |
| 3747 | self.status = None |
| 3748 | |
| 3749 | def run(self): |
| 3750 | exists = os.path.exists |
| 3751 | mtime = lambda p: os.stat(p).st_mtime |
| 3752 | files = dict() |
| 3753 | |
| 3754 | for module in list(sys.modules.values()): |
| 3755 | path = getattr(module, '__file__', '') or '' |
| 3756 | if path[-4:] in ('.pyo', '.pyc'): path = path[:-1] |
| 3757 | if path and exists(path): files[path] = mtime(path) |
| 3758 | |
| 3759 | while not self.status: |
| 3760 | if not exists(self.lockfile)\ |
| 3761 | or mtime(self.lockfile) < time.time() - self.interval - 5: |
| 3762 | self.status = 'error' |
| 3763 | thread.interrupt_main() |
| 3764 | for path, lmtime in list(files.items()): |
| 3765 | if not exists(path) or mtime(path) > lmtime: |
| 3766 | self.status = 'reload' |
| 3767 | thread.interrupt_main() |
| 3768 | break |
| 3769 | time.sleep(self.interval) |
| 3770 | |
| 3771 | def __enter__(self): |
| 3772 | self.start() |
| 3773 | |
| 3774 | def __exit__(self, exc_type, *_): |
| 3775 | if not self.status: self.status = 'exit' # silent exit |
| 3776 | self.join() |
| 3777 | return exc_type is not None and issubclass(exc_type, KeyboardInterrupt) |
| 3778 | |
| 3779 | ############################################################################### |
| 3780 | # Template Adapters ############################################################ |