Mercurial > repos > shellac > sam_consensus_v3
comparison env/lib/python3.9/site-packages/galaxy/util/watcher.py @ 0:4f3585e2f14b draft default tip
"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author | shellac |
---|---|
date | Mon, 22 Mar 2021 18:12:50 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4f3585e2f14b |
---|---|
1 # TODO: this is largely copied from galaxy.tools.toolbox.galaxy and generalized, the tool-oriented watchers in that | |
2 # module should probably be updated to use this where possible | |
3 | |
4 | |
5 import logging | |
6 import os.path | |
7 import time | |
8 | |
9 | |
10 try: | |
11 from watchdog.events import FileSystemEventHandler | |
12 from watchdog.observers import Observer | |
13 from watchdog.observers.polling import PollingObserver | |
14 can_watch = True | |
15 except ImportError: | |
16 Observer = None | |
17 FileSystemEventHandler = object | |
18 PollingObserver = None | |
19 can_watch = False | |
20 | |
21 from galaxy.util.hash_util import md5_hash_file | |
22 | |
23 log = logging.getLogger(__name__) | |
24 | |
25 | |
26 def get_observer_class(config_name, config_value, default, monitor_what_str): | |
27 """ | |
28 """ | |
29 config_value = config_value or default | |
30 config_value = str(config_value).lower() | |
31 if config_value in ("true", "yes", "on", "auto"): | |
32 expect_observer = True | |
33 observer_class = Observer | |
34 elif config_value == "polling": | |
35 expect_observer = True | |
36 observer_class = PollingObserver | |
37 elif config_value in ('false', 'no', 'off'): | |
38 expect_observer = False | |
39 observer_class = None | |
40 else: | |
41 message = f"Unrecognized value for {config_name} config option: {config_value}" | |
42 raise Exception(message) | |
43 | |
44 if expect_observer and observer_class is None: | |
45 message = "Watchdog library unavailable, cannot monitor %s." % monitor_what_str | |
46 if config_value == "auto": | |
47 log.info(message) | |
48 else: | |
49 raise Exception(message) | |
50 | |
51 return observer_class | |
52 | |
53 | |
54 def get_watcher(config, config_name, default="False", monitor_what_str=None, watcher_class=None, | |
55 event_handler_class=None, **kwargs): | |
56 config_value = getattr(config, config_name, None) | |
57 observer_class = get_observer_class(config_name, config_value, default=default, monitor_what_str=monitor_what_str) | |
58 if observer_class is not None: | |
59 watcher_class = watcher_class or Watcher | |
60 event_handler_class = event_handler_class or EventHandler | |
61 return watcher_class(observer_class, event_handler_class, **kwargs) | |
62 else: | |
63 return NullWatcher() | |
64 | |
65 | |
66 class BaseWatcher: | |
67 | |
68 def __init__(self, observer_class, even_handler_class, **kwargs): | |
69 self.observer = None | |
70 self.observer_class = observer_class | |
71 self.event_handler = even_handler_class(self) | |
72 self.monitored_dirs = {} | |
73 | |
74 def start(self): | |
75 if self.observer is None: | |
76 self.observer = self.observer_class() | |
77 self.observer.start() | |
78 self.resume_watching() | |
79 | |
80 def monitor(self, dir_path, recursive=False): | |
81 self.monitored_dirs[dir_path] = recursive | |
82 if self.observer is not None: | |
83 self.observer.schedule(self.event_handler, dir_path, recursive=recursive) | |
84 | |
85 def resume_watching(self): | |
86 for dir_path, recursive in self.monitored_dirs.items(): | |
87 self.monitor(dir_path, recursive) | |
88 | |
89 def shutdown(self): | |
90 if self.observer is not None: | |
91 self.observer.stop() | |
92 self.observer.join() | |
93 self.observer = None | |
94 | |
95 | |
96 class Watcher(BaseWatcher): | |
97 | |
98 def __init__(self, observer_class, event_handler_class, **kwargs): | |
99 super().__init__(observer_class, event_handler_class, **kwargs) | |
100 self.path_hash = {} | |
101 self.file_callbacks = {} | |
102 self.dir_callbacks = {} | |
103 self.ignore_extensions = {} | |
104 self.require_extensions = {} | |
105 self.event_handler = event_handler_class(self) | |
106 | |
107 def watch_file(self, file_path, callback=None): | |
108 file_path = os.path.abspath(file_path) | |
109 dir_path = os.path.dirname(file_path) | |
110 if dir_path not in self.monitored_dirs: | |
111 if callback is not None: | |
112 self.file_callbacks[file_path] = callback | |
113 self.monitor(dir_path) | |
114 log.debug("Watching for changes to file: %s", file_path) | |
115 | |
116 def watch_directory(self, dir_path, callback=None, recursive=False, ignore_extensions=None, require_extensions=None): | |
117 dir_path = os.path.abspath(dir_path) | |
118 if dir_path not in self.monitored_dirs: | |
119 if callback is not None: | |
120 self.dir_callbacks[dir_path] = callback | |
121 if ignore_extensions: | |
122 self.ignore_extensions[dir_path] = ignore_extensions | |
123 if require_extensions: | |
124 self.require_extensions[dir_path] = require_extensions | |
125 self.monitor(dir_path, recursive=recursive) | |
126 log.debug("Watching for changes in directory%s: %s", ' (recursively)' if recursive else '', dir_path) | |
127 | |
128 | |
129 class EventHandler(FileSystemEventHandler): | |
130 | |
131 def __init__(self, watcher): | |
132 self.watcher = watcher | |
133 | |
134 def on_any_event(self, event): | |
135 self._handle(event) | |
136 | |
137 def _extension_check(self, key, path): | |
138 required_extensions = self.watcher.require_extensions.get(key) | |
139 if required_extensions: | |
140 return any(filter(path.endswith, required_extensions)) | |
141 return not any(filter(path.endswith, self.watcher.ignore_extensions.get(key, []))) | |
142 | |
143 def _handle(self, event): | |
144 # modified events will only have src path, move events will | |
145 # have dest_path and src_path but we only care about dest. So | |
146 # look at dest if it exists else use src. | |
147 path = getattr(event, 'dest_path', None) or event.src_path | |
148 path = os.path.abspath(path) | |
149 callback = self.watcher.file_callbacks.get(path) | |
150 if os.path.basename(path).startswith('.'): | |
151 return | |
152 if callback: | |
153 ext_ok = self._extension_check(path, path) | |
154 else: | |
155 # reversed sort for getting the most specific dir first | |
156 for key in reversed(sorted(self.watcher.dir_callbacks.keys())): | |
157 if os.path.commonprefix([path, key]) == key: | |
158 callback = self.watcher.dir_callbacks[key] | |
159 ext_ok = self._extension_check(key, path) | |
160 break | |
161 if not callback or not ext_ok: | |
162 return | |
163 cur_hash = md5_hash_file(path) | |
164 if cur_hash: | |
165 if self.watcher.path_hash.get(path) == cur_hash: | |
166 return | |
167 else: | |
168 time.sleep(0.5) | |
169 if cur_hash != md5_hash_file(path): | |
170 # We're still modifying the file, it'll be picked up later | |
171 return | |
172 self.watcher.path_hash[path] = cur_hash | |
173 callback(path=path) | |
174 | |
175 | |
176 class NullWatcher: | |
177 | |
178 def start(self): | |
179 pass | |
180 | |
181 def shutdown(self): | |
182 pass | |
183 | |
184 def watch_file(self, *args, **kwargs): | |
185 pass | |
186 | |
187 def watch_directory(self, *args, **kwargs): | |
188 pass |