Add reload of modules if files have changed
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
* Add reload of modules if files have changed
|
||||
* Add salt to sha of password
|
||||
* Add strftime to ir.lang to handle locale's format
|
||||
* Add sqlite backend
|
||||
|
||||
@@ -73,3 +73,6 @@ db_type = postgresql
|
||||
# Enable psyco module
|
||||
# Need to have psyco installed http://psyco.sourceforge.net/
|
||||
#psyco = False
|
||||
|
||||
# Enable auto-reload of modules if changed
|
||||
#auto_reload = True
|
||||
|
||||
@@ -40,6 +40,7 @@ class ConfigManager(object):
|
||||
'multi_server': False,
|
||||
'session_timeout': 600,
|
||||
'psyco': False,
|
||||
'auto_reload': True,
|
||||
}
|
||||
|
||||
parser = optparse.OptionParser(version=VERSION)
|
||||
|
||||
+41
-13
@@ -16,13 +16,17 @@ MODULES_PATH = os.path.dirname(__file__)
|
||||
MODULES = []
|
||||
|
||||
EGG_MODULES = {}
|
||||
try:
|
||||
import pkg_resources
|
||||
for ep in pkg_resources.iter_entry_points('trytond.modules'):
|
||||
mod_name = ep.module_name.split('.')[-1]
|
||||
EGG_MODULES[mod_name] = ep
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
def update_egg_modules():
|
||||
global EGG_MODULES
|
||||
try:
|
||||
import pkg_resources
|
||||
for ep in pkg_resources.iter_entry_points('trytond.modules'):
|
||||
mod_name = ep.module_name.split('.')[-1]
|
||||
EGG_MODULES[mod_name] = ep
|
||||
except ImportError:
|
||||
pass
|
||||
update_egg_modules()
|
||||
|
||||
|
||||
class Graph(dict):
|
||||
@@ -303,6 +307,7 @@ def get_module_list():
|
||||
module_list.add(file)
|
||||
elif file[-4:] == '.zip':
|
||||
module_list.add(file[:-4])
|
||||
update_egg_modules()
|
||||
module_list.update(EGG_MODULES.keys())
|
||||
module_list.add('ir')
|
||||
module_list.add('workflow')
|
||||
@@ -311,12 +316,27 @@ def get_module_list():
|
||||
module_list.add('tests')
|
||||
return list(module_list)
|
||||
|
||||
def register_classes():
|
||||
import trytond.ir
|
||||
import trytond.workflow
|
||||
import trytond.res
|
||||
import trytond.webdav
|
||||
import trytond.tests
|
||||
def register_classes(reload_p=False):
|
||||
'''
|
||||
Import modules to register the classes in the Pool
|
||||
|
||||
:param reload_p: reload modules instead of import it
|
||||
'''
|
||||
if not reload_p:
|
||||
import trytond.ir
|
||||
import trytond.workflow
|
||||
import trytond.res
|
||||
import trytond.webdav
|
||||
import trytond.tests
|
||||
else:
|
||||
for module in ('trytond.model', 'trytond.report', 'trytond.wizard',
|
||||
'trytond.ir', 'trytond.workflow', 'trytond.res',
|
||||
'trytond.webdav', 'trytond.tests'):
|
||||
for i in sys.modules.keys():
|
||||
if i.startswith(module) \
|
||||
and i != module:
|
||||
del sys.modules[i]
|
||||
reload(sys.modules[module])
|
||||
|
||||
logger = logging.getLogger('modules')
|
||||
|
||||
@@ -328,6 +348,14 @@ def register_classes():
|
||||
MODULES.append(module)
|
||||
continue
|
||||
|
||||
if reload_p and 'trytond.modules.' + module in sys.modules:
|
||||
for i in sys.modules.keys():
|
||||
if i.startswith('trytond.modules.' + module) \
|
||||
and i != 'trytond.modules.' + module:
|
||||
del sys.modules[i]
|
||||
reload(sys.modules['trytond.modules.' + module])
|
||||
continue
|
||||
|
||||
if os.path.isfile(OPJ(MODULES_PATH, module + '.zip')):
|
||||
mod_path = OPJ(MODULES_PATH, module + '.zip')
|
||||
zimp = zipimport.zipimporter(mod_path)
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
#This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
from threading import Lock
|
||||
from trytond.modules import get_module_list
|
||||
|
||||
_lock = Lock()
|
||||
_times = {}
|
||||
_modules = None
|
||||
|
||||
def _modified(path):
|
||||
global _times
|
||||
global _lock
|
||||
_lock.acquire()
|
||||
try:
|
||||
try:
|
||||
if not os.path.isfile(path):
|
||||
return path in _times
|
||||
|
||||
mtime = os.stat(path).st_mtime
|
||||
if path not in _times:
|
||||
_times[path] = mtime
|
||||
|
||||
if mtime != _times[path]:
|
||||
_times[path] = mtime
|
||||
return True
|
||||
except:
|
||||
return True
|
||||
finally:
|
||||
_lock.release()
|
||||
return False
|
||||
|
||||
def monitor():
|
||||
'''
|
||||
Monitor module files for change
|
||||
|
||||
:return: True if at least one file has changed
|
||||
'''
|
||||
global _modules
|
||||
modified = False
|
||||
for module in sys.modules.keys():
|
||||
if not module.startswith('trytond.'):
|
||||
continue
|
||||
if not hasattr(sys.modules[module], '__file__'):
|
||||
continue
|
||||
path = getattr(sys.modules[module], '__file__')
|
||||
if not path:
|
||||
continue
|
||||
if os.path.splitext(path)[1] in ['.pyc', '.pyo', '.pyd']:
|
||||
path = path[:-1]
|
||||
if _modified(path):
|
||||
if subprocess.call((sys.executable, '-c', 'import %s' % module)):
|
||||
modified = False
|
||||
break
|
||||
modified = True
|
||||
modules = set(get_module_list())
|
||||
if _modules is None:
|
||||
_modules = modules
|
||||
for module in modules.difference(_modules):
|
||||
if subprocess.call((sys.executable, '-c',
|
||||
'import trytond.modules.%s' % module)):
|
||||
modified = False
|
||||
break
|
||||
modified = True
|
||||
_modules = modules
|
||||
return modified
|
||||
+23
-1
@@ -1,6 +1,6 @@
|
||||
#This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
from trytond.modules import load_modules
|
||||
from trytond.modules import load_modules, register_classes
|
||||
from threading import RLock
|
||||
import logging
|
||||
|
||||
@@ -54,6 +54,28 @@ class Pool(object):
|
||||
break
|
||||
Pool.classes[type].setdefault(module, []).append(cls)
|
||||
|
||||
@staticmethod
|
||||
def start():
|
||||
'''
|
||||
Start/restart the Pool
|
||||
'''
|
||||
Pool._lock.acquire()
|
||||
try:
|
||||
reload_p = False
|
||||
for type in Pool.classes:
|
||||
if Pool.classes[type]:
|
||||
reload_p = True
|
||||
Pool.classes[type] = {}
|
||||
try:
|
||||
register_classes(reload_p=reload_p)
|
||||
except:
|
||||
if not reload_p:
|
||||
raise
|
||||
for db_name in Pool.database_list():
|
||||
Pool(db_name).init()
|
||||
finally:
|
||||
Pool._lock.release()
|
||||
|
||||
@staticmethod
|
||||
def database_list():
|
||||
'''
|
||||
|
||||
@@ -7,6 +7,7 @@ from trytond.tools import Cache, find_language_context
|
||||
from trytond.backend import Database, DatabaseIntegrityError
|
||||
from trytond.config import CONFIG
|
||||
from trytond.version import VERSION
|
||||
from trytond.monitor import monitor
|
||||
import traceback
|
||||
import logging
|
||||
import time
|
||||
@@ -22,6 +23,9 @@ import pydoc
|
||||
def dispatch(host, port, protocol, database_name, user, session, object_type,
|
||||
object_name, method, *args, **kargs):
|
||||
|
||||
if CONFIG['auto_reload'] and monitor():
|
||||
Pool.start()
|
||||
|
||||
if object_type == 'common':
|
||||
if method == 'login':
|
||||
res = security.login(database_name, user, session)
|
||||
|
||||
+8
-4
@@ -12,7 +12,6 @@ import sys, os, signal
|
||||
import time
|
||||
from trytond.backend import Database, DatabaseOperationalError
|
||||
from config import CONFIG
|
||||
from trytond.modules import register_classes
|
||||
import mx.DateTime
|
||||
|
||||
if not hasattr(mx.DateTime, 'strptime'):
|
||||
@@ -29,6 +28,7 @@ import threading
|
||||
from pool import Pool
|
||||
import string
|
||||
import random
|
||||
from trytond.monitor import monitor
|
||||
|
||||
|
||||
class TrytonServer(object):
|
||||
@@ -98,7 +98,7 @@ class TrytonServer(object):
|
||||
elif not cursor.test():
|
||||
raise Exception("'%s' is not a Tryton database!" % db_name)
|
||||
|
||||
register_classes()
|
||||
Pool.start()
|
||||
|
||||
for db_name in CONFIG["db_name"]:
|
||||
cursor = Database(db_name).connect().cursor()
|
||||
@@ -175,8 +175,7 @@ class TrytonServer(object):
|
||||
def handler(signum, frame):
|
||||
if hasattr(signal, 'SIGUSR1'):
|
||||
if signum == signal.SIGUSR1:
|
||||
for db_name in Pool.database_list():
|
||||
Pool(db_name).init()
|
||||
Pool.start()
|
||||
return
|
||||
if CONFIG['netrpc']:
|
||||
netrpcd.stop()
|
||||
@@ -220,6 +219,11 @@ class TrytonServer(object):
|
||||
psyco.full()
|
||||
|
||||
while True:
|
||||
if CONFIG['auto_reload'] and monitor():
|
||||
try:
|
||||
Pool.start()
|
||||
except:
|
||||
pass
|
||||
for dbname in Pool.database_list():
|
||||
pool = Pool(dbname)
|
||||
if 'ir.cron' not in pool.object_name_list():
|
||||
|
||||
Reference in New Issue
Block a user