parent
9acb5b8ca4
commit
b01f041047
|
@ -56,6 +56,7 @@ if ad != _ad:
|
|||
# Modules already loaded
|
||||
loaded = []
|
||||
|
||||
|
||||
class Graph(dict):
|
||||
|
||||
def addNode(self, name, deps):
|
||||
|
@ -79,6 +80,7 @@ class Graph(dict):
|
|||
yield module
|
||||
level += 1
|
||||
|
||||
|
||||
class Singleton(object):
|
||||
|
||||
def __new__(cls, name, graph):
|
||||
|
@ -90,6 +92,7 @@ class Singleton(object):
|
|||
graph[name] = inst
|
||||
return inst
|
||||
|
||||
|
||||
class Node(Singleton):
|
||||
|
||||
def __init__(self, name, graph):
|
||||
|
@ -107,7 +110,7 @@ class Node(Singleton):
|
|||
for attr in ('init', 'update', 'demo'):
|
||||
if hasattr(self, attr):
|
||||
setattr(node, attr, True)
|
||||
self.childs.sort(lambda x,y: cmp(x.name, y.name))
|
||||
self.childs.sort(lambda x, y: cmp(x.name, y.name))
|
||||
|
||||
def hasChild(self, name):
|
||||
return Node(name, self.graph) in self.childs or \
|
||||
|
@ -135,9 +138,9 @@ class Node(Singleton):
|
|||
s += '%s`-> %s' % (' ' * depth, c._pprint(depth+1))
|
||||
return s
|
||||
|
||||
|
||||
def get_module_path(module):
|
||||
"""Return the path of the given module.
|
||||
"""
|
||||
"""Return the path of the given module."""
|
||||
|
||||
if os.path.exists(opj(ad, module)) or os.path.exists(opj(ad, '%s.zip' % module)):
|
||||
return opj(ad, module)
|
||||
|
@ -148,11 +151,12 @@ def get_module_path(module):
|
|||
logger.notifyChannel('init', netsvc.LOG_WARNING, 'module %s: module not found' % (module,))
|
||||
return False
|
||||
|
||||
|
||||
def get_module_filetree(module, dir='.'):
|
||||
path = get_module_path(module)
|
||||
if not path:
|
||||
return False
|
||||
|
||||
|
||||
dir = os.path.normpath(dir)
|
||||
if dir == '.':
|
||||
dir = ''
|
||||
|
@ -165,11 +169,12 @@ def get_module_filetree(module, dir='.'):
|
|||
files = ['/'.join(f.split('/')[1:]) for f in zip.namelist()]
|
||||
else:
|
||||
files = tools.osutil.listdir(path, True)
|
||||
|
||||
|
||||
tree = {}
|
||||
for f in files:
|
||||
if not f.startswith(dir):
|
||||
continue
|
||||
|
||||
if dir:
|
||||
f = f[len(dir)+int(not dir.endswith('/')):]
|
||||
lst = f.split(os.sep)
|
||||
|
@ -177,14 +182,14 @@ def get_module_filetree(module, dir='.'):
|
|||
while len(lst) != 1:
|
||||
current = current.setdefault(lst.pop(0), {})
|
||||
current[lst.pop(0)] = None
|
||||
|
||||
|
||||
return tree
|
||||
|
||||
|
||||
def get_module_as_zip(modulename, b64enc=True, src=True):
|
||||
|
||||
|
||||
RE_exclude = re.compile('(?:^\..+\.swp$)|(?:\.py[oc]$)|(?:\.bak$)|(?:\.~.~$)', re.I)
|
||||
|
||||
|
||||
def _zippy(archive, path, src=True):
|
||||
path = os.path.abspath(path)
|
||||
base = os.path.basename(path)
|
||||
|
@ -192,12 +197,12 @@ def get_module_as_zip(modulename, b64enc=True, src=True):
|
|||
bf = os.path.basename(f)
|
||||
if not RE_exclude.search(bf) and (src or bf == '__terp__.py' or not path.endswith('.py')):
|
||||
archive.write(os.path.join(path, f), os.path.join(base, f))
|
||||
|
||||
|
||||
ap = get_module_path(str(modulename))
|
||||
if not ap:
|
||||
raise Exception('Unable to find path for module %s' % modulename)
|
||||
|
||||
ap = ap.encode('utf8')
|
||||
|
||||
ap = ap.encode('utf8')
|
||||
if os.path.isfile(ap + '.zip'):
|
||||
val = file(ap + '.zip', 'rb').read()
|
||||
else:
|
||||
|
@ -225,6 +230,7 @@ def get_module_resource(module, *args):
|
|||
a = get_module_path(module)
|
||||
return a and opj(a, *args) or False
|
||||
|
||||
|
||||
def get_modules():
|
||||
"""Returns the list of module names
|
||||
"""
|
||||
|
@ -242,9 +248,10 @@ def get_modules():
|
|||
|
||||
return list(set(listdir(ad) + listdir(_ad)))
|
||||
|
||||
|
||||
def create_graph(module_list, force=None):
|
||||
if not force:
|
||||
force=[]
|
||||
force = []
|
||||
graph = Graph()
|
||||
packages = []
|
||||
|
||||
|
@ -256,7 +263,8 @@ def create_graph(module_list, force=None):
|
|||
except IOError:
|
||||
continue
|
||||
terp_file = get_module_resource(module, '__terp__.py')
|
||||
if not terp_file: continue
|
||||
if not terp_file:
|
||||
continue
|
||||
if os.path.isfile(terp_file) or zipfile.is_zipfile(mod_path+'.zip'):
|
||||
try:
|
||||
info = eval(tools.file_open(terp_file).read())
|
||||
|
@ -265,14 +273,14 @@ def create_graph(module_list, force=None):
|
|||
raise
|
||||
if info.get('installable', True):
|
||||
packages.append((module, info.get('depends', []), info))
|
||||
|
||||
|
||||
dependencies = dict([(p, deps) for p, deps, data in packages])
|
||||
current, later = Set([p for p, dep, data in packages]), Set()
|
||||
while packages and current > later:
|
||||
package, deps, data = packages[0]
|
||||
|
||||
# if all dependencies of 'package' are already in the graph, add 'package' in the graph
|
||||
if reduce(lambda x,y: x and y in graph, deps, True):
|
||||
if reduce(lambda x, y: x and y in graph, deps, True):
|
||||
if not package in current:
|
||||
packages.pop(0)
|
||||
continue
|
||||
|
@ -288,15 +296,15 @@ def create_graph(module_list, force=None):
|
|||
later.add(package)
|
||||
packages.append((package, deps, data))
|
||||
packages.pop(0)
|
||||
|
||||
|
||||
for package in later:
|
||||
unmet_deps = filter(lambda p: p not in graph, dependencies[package])
|
||||
logger.notifyChannel('init', netsvc.LOG_ERROR, 'module %s: Unmet dependencies: %s' % (package, ', '.join(unmet_deps)))
|
||||
|
||||
return graph
|
||||
|
||||
|
||||
def init_module_objects(cr, module_name, obj_list):
|
||||
pool = pooler.get_pool(cr.dbname)
|
||||
logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: creating or updating database tables' % module_name)
|
||||
todo = []
|
||||
for obj in obj_list:
|
||||
|
@ -311,6 +319,7 @@ def init_module_objects(cr, module_name, obj_list):
|
|||
t[1](cr, *t[2])
|
||||
cr.commit()
|
||||
|
||||
|
||||
def register_class(m):
|
||||
"""
|
||||
Register module named m, if not already registered
|
||||
|
@ -351,8 +360,8 @@ class MigrationManager(object):
|
|||
This class manage the migration of modules
|
||||
Migrations files must be python files containing a "migrate(cr, installed_version)" function.
|
||||
Theses files must respect a directory tree structure: A 'migrations' folder which containt a
|
||||
folder by version. Version can be 'module' version or 'server.module' version (in this case,
|
||||
the files will only be processed by this version of the server). Python file names must start
|
||||
folder by version. Version can be 'module' version or 'server.module' version (in this case,
|
||||
the files will only be processed by this version of the server). Python file names must start
|
||||
by 'pre' or 'post' and will be executed, respectively, before and after the module initialisation
|
||||
Example:
|
||||
|
||||
|
@ -368,7 +377,7 @@ class MigrationManager(object):
|
|||
| `-- post-clean-data.py
|
||||
`-- foo.py # not processed
|
||||
|
||||
This similar structure is generated by the maintenance module with the migrations files get by
|
||||
This similar structure is generated by the maintenance module with the migrations files get by
|
||||
the maintenance contract
|
||||
|
||||
"""
|
||||
|
@ -393,7 +402,6 @@ class MigrationManager(object):
|
|||
self.migrations[pkg.name]['module'] = get_module_filetree(pkg.name, 'migrations') or {}
|
||||
self.migrations[pkg.name]['maintenance'] = get_module_filetree('base', 'maintenance/migrations/' + pkg.name) or {}
|
||||
|
||||
|
||||
def migrate_module(self, pkg, stage):
|
||||
assert stage in ('pre', 'post')
|
||||
stageformat = {'pre': '[>%s]',
|
||||
|
@ -402,7 +410,7 @@ class MigrationManager(object):
|
|||
|
||||
if not (hasattr(pkg, 'update') or pkg.state == 'to upgrade'):
|
||||
return
|
||||
|
||||
|
||||
def convert_version(version):
|
||||
if version.startswith(release.major_version) and version != release.major_version:
|
||||
return version # the version number already containt the server version
|
||||
|
@ -413,7 +421,7 @@ class MigrationManager(object):
|
|||
return [d for d in tree if tree[d] is not None]
|
||||
|
||||
versions = list(set(
|
||||
__get_dir(self.migrations[pkg.name]['module']) +
|
||||
__get_dir(self.migrations[pkg.name]['module']) +
|
||||
__get_dir(self.migrations[pkg.name]['maintenance'])
|
||||
))
|
||||
versions.sort(key=lambda k: parse_version(convert_version(k)))
|
||||
|
@ -424,7 +432,7 @@ class MigrationManager(object):
|
|||
"""
|
||||
m = self.migrations[pkg.name]
|
||||
lst = []
|
||||
|
||||
|
||||
mapping = {'module': {'module': pkg.name, 'rootdir': opj('migrations')},
|
||||
'maintenance': {'module': 'base', 'rootdir': opj('maintenance', 'migrations', pkg.name)},
|
||||
}
|
||||
|
@ -439,7 +447,7 @@ class MigrationManager(object):
|
|||
lst.append((mapping[x]['module'], opj(mapping[x]['rootdir'], version, f)))
|
||||
return lst
|
||||
|
||||
def mergedict(a,b):
|
||||
def mergedict(a, b):
|
||||
a = a.copy()
|
||||
a.update(b)
|
||||
return a
|
||||
|
@ -448,7 +456,7 @@ class MigrationManager(object):
|
|||
|
||||
parsed_installed_version = parse_version(pkg.installed_version or '')
|
||||
current_version = parse_version(convert_version(pkg.data.get('version', '0')))
|
||||
|
||||
|
||||
versions = _get_migration_versions(pkg)
|
||||
|
||||
for version in versions:
|
||||
|
@ -458,7 +466,7 @@ class MigrationManager(object):
|
|||
'stage': stage,
|
||||
'version': stageformat[stage] % version,
|
||||
}
|
||||
|
||||
|
||||
for modulename, pyfile in _get_migration_files(pkg, version, stage):
|
||||
name, ext = os.path.splitext(os.path.basename(pyfile))
|
||||
if ext.lower() != '.py':
|
||||
|
@ -466,18 +474,18 @@ class MigrationManager(object):
|
|||
mod = fp = fp2 = None
|
||||
try:
|
||||
fp = tools.file_open(opj(modulename, pyfile))
|
||||
|
||||
# imp.load_source need a real file object, so we create
|
||||
|
||||
# imp.load_source need a real file object, so we create
|
||||
# on from the file-like object we get from file_open
|
||||
fp2 = os.tmpfile()
|
||||
fp2.write(fp.read())
|
||||
fp2.seek(0)
|
||||
try:
|
||||
mod = imp.load_source(name, pyfile, fp2)
|
||||
logger.notifyChannel('migration', netsvc.LOG_INFO, 'module %(addon)s: Running migration %(version)s %(name)s"' % mergedict({'name': mod.__name__},strfmt))
|
||||
logger.notifyChannel('migration', netsvc.LOG_INFO, 'module %(addon)s: Running migration %(version)s %(name)s"' % mergedict({'name': mod.__name__}, strfmt))
|
||||
mod.migrate(self.cr, pkg.installed_version)
|
||||
except ImportError:
|
||||
logger.notifyChannel('migration', netsvc.LOG_ERROR, 'module %(addon)s: Unable to load %(stage)s-migration file %(file)s' % mergedict({'file': opj(modulename,pyfile)}, strfmt))
|
||||
logger.notifyChannel('migration', netsvc.LOG_ERROR, 'module %(addon)s: Unable to load %(stage)s-migration file %(file)s' % mergedict({'file': opj(modulename, pyfile)}, strfmt))
|
||||
raise
|
||||
except AttributeError:
|
||||
logger.notifyChannel('migration', netsvc.LOG_ERROR, 'module %(addon)s: Each %(stage)s-migration file must have a "migrate(cr, installed_version)" function' % strfmt)
|
||||
|
@ -490,12 +498,12 @@ class MigrationManager(object):
|
|||
fp2.close()
|
||||
if mod:
|
||||
del mod
|
||||
|
||||
|
||||
|
||||
def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs):
|
||||
# **kwargs is passed directly to convert_xml_import
|
||||
if not status:
|
||||
status={}
|
||||
status = {}
|
||||
|
||||
status = status.copy()
|
||||
package_todo = []
|
||||
|
@ -519,14 +527,14 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs):
|
|||
for package in graph:
|
||||
for k, v in additional_data[package.name].items():
|
||||
setattr(package, k, v)
|
||||
|
||||
|
||||
migrations = MigrationManager(cr, graph)
|
||||
|
||||
check_rules = False
|
||||
modobj = None
|
||||
|
||||
for package in graph:
|
||||
status['progress'] = (float(statusi)+0.1)/len(graph)
|
||||
status['progress'] = (float(statusi)+0.1) / len(graph)
|
||||
m = package.name
|
||||
mid = package.id
|
||||
|
||||
|
@ -543,14 +551,14 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs):
|
|||
modobj.check(cr, 1, [mid])
|
||||
|
||||
idref = {}
|
||||
status['progress'] = (float(statusi)+0.4)/len(graph)
|
||||
status['progress'] = (float(statusi)+0.4) / len(graph)
|
||||
if hasattr(package, 'init') or hasattr(package, 'update') or package.state in ('to install', 'to upgrade'):
|
||||
check_rules = True
|
||||
init_module_objects(cr, m, modules)
|
||||
for kind in ('init', 'update'):
|
||||
for filename in package.data.get('%s_xml' % kind, []):
|
||||
mode = 'update'
|
||||
if hasattr(package, 'init') or package.state=='to install':
|
||||
if hasattr(package, 'init') or package.state == 'to install':
|
||||
mode = 'init'
|
||||
logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: loading %s' % (m, filename))
|
||||
name, ext = os.path.splitext(filename)
|
||||
|
@ -567,7 +575,7 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs):
|
|||
tools.convert_xml_import(cr, m, fp, idref, mode=mode, **kwargs)
|
||||
fp.close()
|
||||
if hasattr(package, 'demo') or (package.dbdemo and package.state != 'installed'):
|
||||
status['progress'] = (float(statusi)+0.75)/len(graph)
|
||||
status['progress'] = (float(statusi)+0.75) / len(graph)
|
||||
for xml in package.data.get('demo_xml', []):
|
||||
name, ext = os.path.splitext(xml)
|
||||
logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: loading %s' % (m, xml))
|
||||
|
@ -584,7 +592,7 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs):
|
|||
#cr.execute("update ir_module_module set state='installed', latest_version=%s where id=%s", (ver, mid,))
|
||||
|
||||
# Set new modules and dependencies
|
||||
modobj.write(cr, 1, [mid], {'state':'installed', 'latest_version':ver})
|
||||
modobj.write(cr, 1, [mid], {'state': 'installed', 'latest_version': ver})
|
||||
cr.commit()
|
||||
|
||||
# Update translations for all installed languages
|
||||
|
@ -593,12 +601,12 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs):
|
|||
cr.commit()
|
||||
migrations.migrate_module(package, 'post')
|
||||
|
||||
statusi+=1
|
||||
statusi += 1
|
||||
|
||||
if perform_checks and check_rules:
|
||||
cr.execute("""select model,name from ir_model where id not in (select model_id from ir_model_access)""")
|
||||
for (model,name) in cr.fetchall():
|
||||
logger.notifyChannel('init', netsvc.LOG_WARNING, 'object %s (%s) has no access rules!' % (model,name))
|
||||
for (model, name) in cr.fetchall():
|
||||
logger.notifyChannel('init', netsvc.LOG_WARNING, 'object %s (%s) has no access rules!' % (model, name))
|
||||
|
||||
|
||||
cr.execute('select model from ir_model where state=%s', ('manual',))
|
||||
|
@ -608,9 +616,10 @@ def load_module_graph(cr, graph, status=None, perform_checks=True, **kwargs):
|
|||
pool.get('ir.model.data')._process_end(cr, 1, package_todo)
|
||||
cr.commit()
|
||||
|
||||
|
||||
def load_modules(db, force_demo=False, status=None, update_module=False):
|
||||
if not status:
|
||||
status={}
|
||||
status = {}
|
||||
|
||||
cr = db.cursor()
|
||||
force = []
|
||||
|
@ -632,20 +641,20 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
|
|||
ids = modobj.search(cr, 1, ['&', ('state', '=', 'uninstalled'), ('name', 'in', mods)])
|
||||
if ids:
|
||||
modobj.button_install(cr, 1, ids)
|
||||
|
||||
|
||||
mods = [k for k in tools.config['update'] if tools.config['update'][k]]
|
||||
if mods:
|
||||
ids = modobj.search(cr, 1, ['&',('state', '=', 'installed'), ('name', 'in', mods)])
|
||||
ids = modobj.search(cr, 1, ['&', ('state', '=', 'installed'), ('name', 'in', mods)])
|
||||
if ids:
|
||||
modobj.button_upgrade(cr, 1, ids)
|
||||
|
||||
|
||||
cr.execute("update ir_module_module set state=%s where name=%s", ('installed', 'base'))
|
||||
cr.execute("select name from ir_module_module where state in ('installed', 'to install', 'to upgrade')")
|
||||
else:
|
||||
cr.execute("select name from ir_module_module where state in ('installed', 'to upgrade')")
|
||||
module_list = [name for (name,) in cr.fetchall()]
|
||||
graph = create_graph(module_list, force)
|
||||
|
||||
|
||||
# the 'base' module has already been updated
|
||||
base = graph['base']
|
||||
base.state = 'installed'
|
||||
|
@ -658,7 +667,7 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
|
|||
logger.notifyChannel('init', netsvc.LOG_INFO, report)
|
||||
|
||||
for kind in ('init', 'demo', 'update'):
|
||||
tools.config[kind]={}
|
||||
tools.config[kind] = {}
|
||||
|
||||
cr.commit()
|
||||
if update_module:
|
||||
|
@ -666,7 +675,7 @@ def load_modules(db, force_demo=False, status=None, update_module=False):
|
|||
for mod_id, mod_name in cr.fetchall():
|
||||
pool = pooler.get_pool(cr.dbname)
|
||||
cr.execute('select model,res_id from ir_model_data where noupdate=%s and module=%s order by id desc', (False, mod_name,))
|
||||
for rmod,rid in cr.fetchall():
|
||||
for rmod, rid in cr.fetchall():
|
||||
uid = 1
|
||||
pool.get(rmod).unlink(cr, uid, [rid])
|
||||
cr.execute('delete from ir_model_data where noupdate=%s and module=%s', (False, mod_name,))
|
||||
|
|
Loading…
Reference in New Issue