diff options
| author | Christophe Simonis <chs@odoo.com> | 2019-03-26 10:33:26 +0100 |
|---|---|---|
| committer | Christophe Simonis <chs@odoo.com> | 2019-03-26 10:33:26 +0100 |
| commit | 5dbf7bf357f8f76a9e36f4707bd9e9bfc77e3436 (patch) | |
| tree | fecaa70fd89a26938b03ad124cb326b6523f47bc /odoo/service/server.py | |
| parent | 02f5b654c385772649534fe205d3328fb21354ba (diff) | |
| parent | f00c490be8688d80f211f6755b5ada541eefd602 (diff) | |
[MERGE] forward port branch saas-12.1 up to f00c490be86
Diffstat (limited to 'odoo/service/server.py')
| -rw-r--r-- | odoo/service/server.py | 122 |
1 files changed, 95 insertions, 27 deletions
diff --git a/odoo/service/server.py b/odoo/service/server.py index 68ea2c002ac..977cc0a3630 100644 --- a/odoo/service/server.py +++ b/odoo/service/server.py @@ -25,9 +25,25 @@ if os.name == 'posix': # Unix only for workers import fcntl import resource + try: + import inotify + from inotify.adapters import InotifyTrees + from inotify.constants import IN_MODIFY, IN_CREATE, IN_MOVED_TO + INOTIFY_LISTEN_EVENTS = IN_MODIFY | IN_CREATE | IN_MOVED_TO + except ImportError: + inotify = None else: # Windows shim signal.SIGHUP = -1 + inotify = None + +if not inotify: + try: + import watchdog + from watchdog.observers import Observer + from watchdog.events import FileCreatedEvent, FileModifiedEvent, FileMovedEvent + except ImportError: + watchdog = None # Optional process names for workers try: @@ -45,13 +61,6 @@ from odoo.tools import stripped_sys_argv, dumpstacks, log_ormcache_stats _logger = logging.getLogger(__name__) -try: - import watchdog - from watchdog.observers import Observer - from watchdog.events import FileCreatedEvent, FileModifiedEvent, FileMovedEvent -except ImportError: - watchdog = None - SLEEP_INTERVAL = 60 # 1 min def memory_info(process): @@ -181,7 +190,24 @@ class ThreadedWSGIServerReloadable(LoggingBaseWSGIServerMixIn, werkzeug.serving. #---------------------------------------------------------- # FileSystem Watcher for autoreload and cache invalidation #---------------------------------------------------------- -class FSWatcher(object): +class FSWatcherBase(object): + def handle_file(self, path): + if path.endswith('.py') and not os.path.basename(path).startswith('.~'): + try: + source = open(path, 'rb').read() + b'\n' + compile(source, path, 'exec') + except IOError: + _logger.error('autoreload: python code change detected, IOError for %s', path) + except SyntaxError: + _logger.error('autoreload: python code change detected, SyntaxError in %s', path) + else: + if not getattr(odoo, 'phoenix', False): + _logger.info('autoreload: python code updated, autoreload activated') + restart() + return True + + +class FSWatcherWatchdog(FSWatcherBase): def __init__(self): self.observer = Observer() for path in odoo.modules.module.ad_paths: @@ -192,27 +218,60 @@ class FSWatcher(object): if isinstance(event, (FileCreatedEvent, FileModifiedEvent, FileMovedEvent)): if not event.is_directory: path = getattr(event, 'dest_path', event.src_path) - if path.endswith('.py') and not os.path.basename(path).startswith('.~'): - try: - source = open(path, 'rb').read() + b'\n' - compile(source, path, 'exec') - except FileNotFoundError: - _logger.error('autoreload: python code change detected, FileNotFound for %s', path) - except SyntaxError: - _logger.error('autoreload: python code change detected, SyntaxError in %s', path) - else: - if not getattr(odoo, 'phoenix', False): - _logger.info('autoreload: python code updated, autoreload activated') - restart() + self.handle_file(path) def start(self): self.observer.start() - _logger.info('AutoReload watcher running') + _logger.info('AutoReload watcher running with watchdog') def stop(self): self.observer.stop() self.observer.join() + +class FSWatcherInotify(FSWatcherBase): + def __init__(self): + self.started = False + # ignore warnings from inotify in case we have duplicate addons paths. + inotify.adapters._LOGGER.setLevel(logging.ERROR) + # recreate a list as InotifyTrees' __init__ deletes the list's items + paths_to_watch = [] + for path in odoo.modules.module.ad_paths: + paths_to_watch.append(path) + _logger.info('Watching addons folder %s', path) + self.watcher = InotifyTrees(paths_to_watch, mask=INOTIFY_LISTEN_EVENTS, block_duration_s=.5) + + def run(self): + _logger.info('AutoReload watcher running with inotify') + dir_creation_events = set(('IN_MOVED_TO', 'IN_CREATE')) + while self.started: + for event in self.watcher.event_gen(timeout_s=0, yield_nones=False): + (_, type_names, path, filename) = event + if 'IN_ISDIR' not in type_names: + # despite not having IN_DELETE in the watcher's mask, the + # watcher sends these events when a directory is deleted. + if 'IN_DELETE' not in type_names: + full_path = os.path.join(path, filename) + if self.handle_file(full_path): + return + elif dir_creation_events.intersection(type_names): + full_path = os.path.join(path, filename) + for root, _, files in os.walk(full_path): + for file in files: + if self.handle_file(os.path.join(root, file)): + return + + def start(self): + self.started = True + self.thread = threading.Thread(target=self.run, name="odoo.service.autoreload.watcher") + self.thread.setDaemon(True) + self.thread.start() + + def stop(self): + self.started = False + self.thread.join() + + #---------------------------------------------------------- # Servers: Threaded, Gevented and Prefork #---------------------------------------------------------- @@ -883,6 +942,8 @@ class Worker(object): while self.alive: self.multi.pipe_ping(self.watchdog_pipe) self.sleep() + if not self.alive: + break self.process_work() self.check_limits() except: @@ -1113,21 +1174,28 @@ def start(preload=None, stop=False): server = ThreadedServer(odoo.service.wsgi_server.application) watcher = None - if 'reload' in config['dev_mode']: - if watchdog: - watcher = FSWatcher() + if 'reload' in config['dev_mode'] and not odoo.evented: + if inotify: + watcher = FSWatcherInotify() + watcher.start() + elif watchdog: + watcher = FSWatcherWatchdog() watcher.start() else: - _logger.warning("'watchdog' module not installed. Code autoreload feature is disabled") + if os.name == 'posix' and platform.system() != 'Darwin': + module = 'inotify' + else: + module = 'watchdog' + _logger.warning("'%s' module not installed. Code autoreload feature is disabled", module) if 'werkzeug' in config['dev_mode']: server.app = DebuggedApplication(server.app, evalex=True) rc = server.run(preload, stop) + if watcher: + watcher.stop() # like the legend of the phoenix, all ends with beginnings if getattr(odoo, 'phoenix', False): - if watcher: - watcher.stop() _reexec() return rc if rc else 0 |
