import logging
import openerp.modules.db
+import openerp.modules.graph
logger = netsvc.Logger()
if k.startswith('openerp.') and sys.modules.get(k[8:]) is None:
sys.modules[k[8:]] = v
-class Graph(dict):
- """ Modules dependency graph.
-
- The graph is a mapping from module name to Nodes.
-
- """
-
- def addNode(self, name, deps):
- max_depth, father = 0, None
- for n in [Node(x, self) for x in deps]:
- if n.depth >= max_depth:
- father = n
- max_depth = n.depth
- if father:
- father.addChild(name)
- else:
- Node(name, self)
-
- def update_from_db(self, cr):
- if not len(self):
- return
- # update the graph with values from the database (if exist)
- ## First, we set the default values for each package in graph
- additional_data = dict.fromkeys(self.keys(), {'id': 0, 'state': 'uninstalled', 'dbdemo': False, 'installed_version': None})
- ## Then we get the values from the database
- cr.execute('SELECT name, id, state, demo AS dbdemo, latest_version AS installed_version'
- ' FROM ir_module_module'
- ' WHERE name IN %s',(tuple(additional_data),)
- )
-
- ## and we update the default values with values from the database
- additional_data.update(dict([(x.pop('name'), x) for x in cr.dictfetchall()]))
-
- for package in self.values():
- for k, v in additional_data[package.name].items():
- setattr(package, k, v)
-
- def __iter__(self):
- level = 0
- done = set(self.keys())
- while done:
- level_modules = [(name, module) for name, module in self.items() if module.depth==level]
- for name, module in level_modules:
- done.remove(name)
- yield module
- level += 1
-
-class Singleton(object):
- def __new__(cls, name, graph):
- if name in graph:
- inst = graph[name]
- else:
- inst = object.__new__(cls)
- inst.name = name
- graph[name] = inst
- return inst
-
-
-class Node(Singleton):
- """ One module in the modules dependency graph.
-
- Node acts as a per-module singleton.
-
- """
-
- def __init__(self, name, graph):
- self.graph = graph
- if not hasattr(self, 'children'):
- self.children = []
- if not hasattr(self, 'depth'):
- self.depth = 0
-
- def addChild(self, name):
- node = Node(name, self.graph)
- node.depth = self.depth + 1
- if node not in self.children:
- self.children.append(node)
- for attr in ('init', 'update', 'demo'):
- if hasattr(self, attr):
- setattr(node, attr, True)
- self.children.sort(lambda x, y: cmp(x.name, y.name))
-
- def __setattr__(self, name, value):
- super(Singleton, self).__setattr__(name, value)
- if name in ('init', 'update', 'demo'):
- tools.config[name][self.name] = 1
- for child in self.children:
- setattr(child, name, value)
- if name == 'depth':
- for child in self.children:
- setattr(child, name, value + 1)
-
- def __iter__(self):
- return itertools.chain(iter(self.children), *map(iter, self.children))
-
- def __str__(self):
- return self._pprint()
-
- def _pprint(self, depth=0):
- s = '%s\n' % self.name
- for c in self.children:
- s += '%s`-> %s' % (' ' * depth, c._pprint(depth+1))
- return s
-
def get_module_path(module, downloaded=False):
"""Return the path of the given module.
continue
return res
-def create_graph(cr, module_list, force=None):
- graph = Graph()
- upgrade_graph(graph, cr, module_list, force)
- return graph
-
-def upgrade_graph(graph, cr, module_list, force=None):
- if force is None:
- force = []
- packages = []
- len_graph = len(graph)
- for module in module_list:
- # This will raise an exception if no/unreadable descriptor file.
- # NOTE The call to load_information_from_description_file is already
- # done by db.initialize, so it is possible to not do it again here.
- info = load_information_from_description_file(module)
- if info['installable']:
- packages.append((module, info)) # TODO directly a dict, like in get_modules_with_version
- else:
- logger.notifyChannel('init', netsvc.LOG_WARNING, 'module %s: not installable, skipped' % (module))
-
- dependencies = dict([(p, info['depends']) for p, info in packages])
- current, later = set([p for p, info in packages]), set()
-
- while packages and current > later:
- package, info = packages[0]
- deps = info['depends']
-
- # 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 not package in current:
- packages.pop(0)
- continue
- later.clear()
- current.remove(package)
- graph.addNode(package, deps)
- node = Node(package, graph)
- node.data = info
- for kind in ('init', 'demo', 'update'):
- if package in tools.config[kind] or 'all' in tools.config[kind] or kind in force:
- setattr(node, kind, True)
- else:
- later.add(package)
- packages.append((package, info))
- packages.pop(0)
-
- graph.update_from_db(cr)
-
- 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)))
-
- result = len(graph) - len_graph
- if result != len(module_list):
- logger.notifyChannel('init', netsvc.LOG_WARNING, 'Not all modules have loaded.')
- return result
-
def init_module_models(cr, module_name, obj_list):
""" Initialize a list of models.
cr.execute("update ir_module_module set state=%s where name=%s and state=%s", ('to upgrade', 'base', 'installed'))
# STEP 1: LOAD BASE (must be done before module dependencies can be computed for later steps)
- graph = create_graph(cr, ['base'], force)
+ graph = openerp.modules.graph.Graph()
+ graph.add_module(cr, 'base', force)
if not graph:
logger.notifyChannel('init', netsvc.LOG_CRITICAL, 'module base cannot be loaded! (hint: verify addons-path)')
raise osv.osv.except_osv(_('Could not load base module'), _('module base cannot be loaded! (hint: verify addons-path)'))
if not module_list:
break
- new_modules_in_graph = upgrade_graph(graph, cr, module_list, force)
+ new_modules_in_graph = graph.add_modules(cr, module_list, force)
if new_modules_in_graph == 0:
# nothing to load
break
--- /dev/null
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
+# Copyright (C) 2010-2011 OpenERP s.a. (<http://openerp.com>).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+import os, sys, imp
+from os.path import join as opj
+import itertools
+import zipimport
+
+import openerp
+
+import openerp.osv as osv
+import openerp.tools as tools
+import openerp.tools.osutil as osutil
+from openerp.tools.safe_eval import safe_eval as eval
+import openerp.pooler as pooler
+from openerp.tools.translate import _
+
+import openerp.netsvc as netsvc
+
+import zipfile
+import openerp.release as release
+
+import re
+import base64
+from zipfile import PyZipFile, ZIP_DEFLATED
+from cStringIO import StringIO
+
+import logging
+
+import openerp.modules.db
+
+logger = netsvc.Logger()
+
+
+class Graph(dict):
+ """ Modules dependency graph.
+
+ The graph is a mapping from module name to Nodes.
+
+ """
+
+ def add_node(self, name, deps):
+ max_depth, father = 0, None
+ for n in [Node(x, self) for x in deps]:
+ if n.depth >= max_depth:
+ father = n
+ max_depth = n.depth
+ if father:
+ return father.add_child(name)
+ else:
+ return Node(name, self)
+
+ def update_from_db(self, cr):
+ if not len(self):
+ return
+ # update the graph with values from the database (if exist)
+ ## First, we set the default values for each package in graph
+ additional_data = dict.fromkeys(self.keys(), {'id': 0, 'state': 'uninstalled', 'dbdemo': False, 'installed_version': None})
+ ## Then we get the values from the database
+ cr.execute('SELECT name, id, state, demo AS dbdemo, latest_version AS installed_version'
+ ' FROM ir_module_module'
+ ' WHERE name IN %s',(tuple(additional_data),)
+ )
+
+ ## and we update the default values with values from the database
+ additional_data.update(dict([(x.pop('name'), x) for x in cr.dictfetchall()]))
+
+ for package in self.values():
+ for k, v in additional_data[package.name].items():
+ setattr(package, k, v)
+
+ def add_module(self, cr, module, force=None):
+ self.add_modules(cr, [module], force)
+
+ def add_modules(self, cr, module_list, force=None):
+ if force is None:
+ force = []
+ packages = []
+ len_graph = len(self)
+ for module in module_list:
+ # This will raise an exception if no/unreadable descriptor file.
+ # NOTE The call to load_information_from_description_file is already
+ # done by db.initialize, so it is possible to not do it again here.
+ info = openerp.modules.load_information_from_description_file(module)
+ if info['installable']:
+ packages.append((module, info)) # TODO directly a dict, like in get_modules_with_version
+ else:
+ logger.notifyChannel('init', netsvc.LOG_WARNING, 'module %s: not installable, skipped' % (module))
+
+ dependencies = dict([(p, info['depends']) for p, info in packages])
+ current, later = set([p for p, info in packages]), set()
+
+ while packages and current > later:
+ package, info = packages[0]
+ deps = info['depends']
+
+ # if all dependencies of 'package' are already in the graph, add 'package' in the graph
+ if reduce(lambda x, y: x and y in self, deps, True):
+ if not package in current:
+ packages.pop(0)
+ continue
+ later.clear()
+ current.remove(package)
+ node = self.add_node(package, deps)
+ node.data = info
+ for kind in ('init', 'demo', 'update'):
+ if package in tools.config[kind] or 'all' in tools.config[kind] or kind in force:
+ setattr(node, kind, True)
+ else:
+ later.add(package)
+ packages.append((package, info))
+ packages.pop(0)
+
+ self.update_from_db(cr)
+
+ for package in later:
+ unmet_deps = filter(lambda p: p not in self, dependencies[package])
+ logger.notifyChannel('init', netsvc.LOG_ERROR, 'module %s: Unmet dependencies: %s' % (package, ', '.join(unmet_deps)))
+
+ result = len(self) - len_graph
+ if result != len(module_list):
+ logger.notifyChannel('init', netsvc.LOG_WARNING, 'Not all modules have loaded.')
+ return result
+
+
+ def __iter__(self):
+ level = 0
+ done = set(self.keys())
+ while done:
+ level_modules = [(name, module) for name, module in self.items() if module.depth==level]
+ for name, module in level_modules:
+ done.remove(name)
+ yield module
+ level += 1
+
+
+class Singleton(object):
+ def __new__(cls, name, graph):
+ if name in graph:
+ inst = graph[name]
+ else:
+ inst = object.__new__(cls)
+ inst.name = name
+ graph[name] = inst
+ return inst
+
+
+class Node(Singleton):
+ """ One module in the modules dependency graph.
+
+ Node acts as a per-module singleton.
+
+ """
+
+ def __init__(self, name, graph):
+ self.graph = graph
+ if not hasattr(self, 'children'):
+ self.children = []
+ if not hasattr(self, 'depth'):
+ self.depth = 0
+
+ def add_child(self, name):
+ node = Node(name, self.graph)
+ node.depth = self.depth + 1
+ if node not in self.children:
+ self.children.append(node)
+ for attr in ('init', 'update', 'demo'):
+ if hasattr(self, attr):
+ setattr(node, attr, True)
+ self.children.sort(lambda x, y: cmp(x.name, y.name))
+ return node
+
+ def __setattr__(self, name, value):
+ super(Singleton, self).__setattr__(name, value)
+ if name in ('init', 'update', 'demo'):
+ tools.config[name][self.name] = 1
+ for child in self.children:
+ setattr(child, name, value)
+ if name == 'depth':
+ for child in self.children:
+ setattr(child, name, value + 1)
+
+ def __iter__(self):
+ return itertools.chain(iter(self.children), *map(iter, self.children))
+
+ def __str__(self):
+ return self._pprint()
+
+ def _pprint(self, depth=0):
+ s = '%s\n' % self.name
+ for c in self.children:
+ s += '%s`-> %s' % (' ' * depth, c._pprint(depth+1))
+ return s
+
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: