Skip to content
Permalink
Browse files

[MERGE] forward port branch saas-14 up to cded89b

  • Loading branch information...
KangOl committed Mar 22, 2019
2 parents c635394 + cded89b commit 48e4c18fc411df7db5ae703d767324016ac21e88
Showing with 116 additions and 30 deletions.
  1. +22 −3 odoo/addons/base/ir/ir_logging.py
  2. +94 −27 odoo/service/server.py
@@ -1,13 +1,27 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models
from odoo import api, fields, models


class IrLogging(models.Model):
_name = 'ir.logging'
_order = 'id DESC'

create_date = fields.Datetime(readonly=True)
create_uid = fields.Integer(string='Uid', readonly=True) # Integer not m2o is intentionnal
# The _log_access fields are defined manually for the following reasons:
#
# - The entries in ir_logging are filled in with sql queries bypassing the orm. As the --log-db
# cli option allows to insert ir_logging entries into a remote database, the one2many *_uid
# fields make no sense in the first place but we will keep it for backward compatibility.
#
# - Also, when an ir_logging entry is triggered by the orm (when using --log-db) at the moment
# it is making changes to the res.users model, the ALTER TABLE will aquire an exclusive lock
# on res_users, preventing the ir_logging INSERT to be processed, hence the ongoing module
# install/update will hang forever as the orm is blocked by the ir_logging query that will
# never occur.
create_uid = fields.Integer(string='Created by', readonly=True)
create_date = fields.Datetime(string='Created on', readonly=True)
write_uid = fields.Integer(string='Last Updated by', readonly=True)
write_date = fields.Datetime(string='Last Updated on', readonly=True)

name = fields.Char(required=True)
type = fields.Selection([('client', 'Client'), ('server', 'Server')], required=True, index=True)
dbname = fields.Char(string='Database Name', index=True)
@@ -16,3 +30,8 @@ class IrLogging(models.Model):
path = fields.Char(required=True)
func = fields.Char(string='Function', required=True)
line = fields.Char(required=True)

@api.model_cr
def init(self):
super(IrLogging, self).init()
self._cr.execute("ALTER TABLE ir_logging DROP CONSTRAINT IF EXISTS ir_logging_write_uid_fkey")
@@ -25,9 +25,25 @@
import fcntl
import resource
import psutil
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:
@@ -44,13 +60,6 @@

_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):
@@ -120,7 +129,25 @@ def server_activate(self):
#----------------------------------------------------------
# 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:
# Forward-ports: watch out PY3 compatibility!
source = open(path, 'rb').read() + '\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:
@@ -131,27 +158,60 @@ def dispatch(self, event):
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() + '\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
#----------------------------------------------------------
@@ -956,21 +1016,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

0 comments on commit 48e4c18

Please sign in to comment.
You can’t perform that action at this time.