Add reload of modules if files have changed

This commit is contained in:
Cédric Krier
2009-08-07 10:42:23 +02:00
parent 8235418015
commit c81c4f99a8
8 changed files with 149 additions and 18 deletions
+1
View File
@@ -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
+3
View File
@@ -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
+1
View File
@@ -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
View File
@@ -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)
+68
View File
@@ -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
View File
@@ -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():
'''
+4
View File
@@ -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
View File
@@ -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():