Files
tryton/trytond/modules/__init__.py
T

437 lines
15 KiB
Python
Raw Normal View History

2008-09-13 19:41:02 +02:00
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
2009-02-11 14:52:22 +01:00
from trytond.backend import Database
2008-09-13 19:41:02 +02:00
import os, sys, imp
import itertools
from sets import Set
from trytond.config import CONFIG
import trytond.tools as tools
import zipfile
import zipimport
import traceback
import logging
OPJ = os.path.join
MODULES_PATH = os.path.dirname(__file__)
sys.path.insert(1, MODULES_PATH)
MODULES = []
2008-11-18 17:40:30 +01:00
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
2008-09-13 19:41:02 +02:00
class Graph(dict):
def add_node(self, name, deps):
for i in [Node(x, self) for x in deps]:
i.add_child(name)
if not deps:
Node(name, self)
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
def __str__(self):
res = ''
for i in self:
res += str(i)
res += '\n'
return res
class Singleton(object):
def __new__(cls, name, graph):
if name in graph:
inst = graph[name]
else:
inst = object.__new__(cls)
graph[name] = inst
return inst
class Node(Singleton):
def __init__(self, name, graph):
super(Node, self).__init__()
self.name = name
self.graph = graph
if not hasattr(self, 'datas'):
self.datas = None
if not hasattr(self, 'childs'):
self.childs = []
if not hasattr(self, 'depth'):
self.depth = 0
def add_child(self, name):
node = Node(name, self.graph)
node.depth = max(self.depth + 1, node.depth)
if node not in self.all_childs():
self.childs.append(node)
for attr in ('init', 'update'):
if hasattr(self, attr):
setattr(node, attr, True)
self.childs.sort(lambda x, y: cmp(x.name, y.name))
def all_childs(self):
res = []
for child in self.childs:
res.append(child)
res += child.all_childs()
return res
def has_child(self, name):
return Node(name, self.graph) in self.childs or \
bool([c for c in self.childs if c.has_child(name)])
def __setattr__(self, name, value):
super(Node, self).__setattr__(name, value)
if name in ('init', 'update'):
for child in self.childs:
setattr(child, name, value)
if name == 'depth':
for child in self.childs:
setattr(child, name, value + 1)
def __iter__(self):
return itertools.chain(iter(self.childs),
*[iter(x) for x in self.childs])
def __str__(self):
return self.pprint()
def pprint(self, depth=0):
res = '%s\n' % self.name
for child in self.childs:
res += '%s`-> %s' % (' ' * depth, child.pprint(depth + 1))
return res
def create_graph(module_list, force=None):
if force is None:
force = []
graph = Graph()
packages = []
2009-02-11 14:52:22 +01:00
logger = logging.getLogger('modules')
2008-09-13 19:41:02 +02:00
for module in module_list:
tryton_file = OPJ(MODULES_PATH, module, '__tryton__.py')
mod_path = OPJ(MODULES_PATH, module)
if module in ('ir', 'workflow', 'res', 'webdav', 'tests'):
2008-09-13 19:41:02 +02:00
root_path = os.path.dirname(os.path.dirname(__file__))
tryton_file = OPJ(root_path, module, '__tryton__.py')
mod_path = OPJ(root_path, module)
2008-11-18 17:40:30 +01:00
elif module in EGG_MODULES:
ep = EGG_MODULES[module]
tryton_file = OPJ(ep.dist.location, 'trytond', 'modules', module,
'__tryton__.py')
mod_path = OPJ(ep.dist.location, 'trytond', 'modules', module)
2008-09-13 19:41:02 +02:00
if os.path.isfile(tryton_file) or zipfile.is_zipfile(mod_path+'.zip'):
try:
2008-11-18 17:40:30 +01:00
info = eval(tools.file_open(tryton_file, subdir='').read())
2008-09-13 19:41:02 +02:00
except:
2009-02-11 14:52:22 +01:00
logger.error('%s:eval file %s' % (module, tryton_file))
2008-09-13 19:41:02 +02:00
raise
2008-11-07 11:23:57 +01:00
packages.append((module, info.get('depends', []), info))
2008-09-13 19:41:02 +02:00
elif module != 'all':
2009-02-11 14:52:22 +01:00
logger.error('%s:Module not found!' % (module,))
2008-09-13 19:41:02 +02:00
current, later = Set([x[0] for x in packages]), Set()
while packages and current > later:
package, deps, datas = 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 not package in current:
packages.pop(0)
continue
later.clear()
current.remove(package)
graph.add_node(package, deps)
node = Node(package, graph)
node.datas = datas
for kind in ('init', 'update'):
if (package in CONFIG[kind]) \
or ('all' in CONFIG[kind]) \
or (kind in force):
setattr(node, kind, True)
else:
later.add(package)
packages.append((package, deps, datas))
packages.pop(0)
for package, deps, datas in packages:
if package not in later:
continue
missings = [x for x in deps if x not in graph]
2009-02-11 14:52:22 +01:00
logger.error('%s:Unmet dependency %s' % (package, missings))
2008-09-13 19:41:02 +02:00
return graph, packages, later
2009-02-11 14:52:22 +01:00
def load_module_graph(cursor, graph, pool, lang=None):
2008-09-13 19:41:02 +02:00
if lang is None:
lang = ['en_US']
modules_todo = []
2009-02-11 14:52:22 +01:00
logger = logging.getLogger('modules')
modules = [x.name for x in graph]
cursor.execute('SELECT name, state FROM ir_module_module ' \
'WHERE name in (' + ','.join(['%s' for x in modules]) + ')',
modules)
module2state = {}
for name, state in cursor.fetchall():
module2state[name] = state
2008-09-13 19:41:02 +02:00
for package in graph:
module = package.name
if module not in MODULES:
continue
2009-02-11 14:52:22 +01:00
logger.info(module)
2008-09-13 19:41:02 +02:00
sys.stdout.flush()
2009-02-11 14:52:22 +01:00
objects = pool.instanciate(module)
package_state = module2state.get(module, 'uninstalled')
2008-09-13 19:41:02 +02:00
idref = {}
if hasattr(package, 'init') \
or hasattr(package, 'update') \
or (package_state in ('to install', 'to upgrade')):
2009-02-11 14:52:22 +01:00
for type in objects.keys():
for obj in objects[type]:
logger.info('%s:init %s' % (module, obj._name))
obj.init(cursor, module)
2008-09-13 19:41:02 +02:00
#Instanciate a new parser for the package:
tryton_parser = tools.TrytondXmlHandler(
cursor=cursor,
2008-09-30 11:55:07 +02:00
pool=pool,
2008-09-13 19:41:02 +02:00
module=module,)
for filename in package.datas.get('xml', []):
mode = 'update'
if hasattr(package, 'init') or package_state == 'to install':
2008-09-13 19:41:02 +02:00
mode = 'init'
2009-02-11 14:52:22 +01:00
logger.info('%s:loading %s' % (module, filename))
2008-09-13 19:41:02 +02:00
ext = os.path.splitext(filename)[1]
if ext == '.sql':
if mode == 'init':
queries = tools.file_open(OPJ(module,
filename)).read().split(';')
for query in queries:
new_query = ' '.join(query.split())
if new_query:
cursor.execute(new_query)
else:
# Feed the parser with xml content:
tryton_parser.parse_xmlstream(
tools.file_open(OPJ(module, filename)))
2009-01-26 16:18:48 +01:00
modules_todo.append((module, list(tryton_parser.to_delete)))
2008-09-13 19:41:02 +02:00
for filename in package.datas.get('translation', []):
lang2 = os.path.splitext(filename)[0]
if lang2 not in lang:
continue
try:
trans_file = tools.file_open(OPJ(module, filename))
except IOError:
2009-02-11 14:52:22 +01:00
logger.error('%s:file %s not found!' % (module, filename))
2008-09-13 19:41:02 +02:00
continue
2009-02-11 14:52:22 +01:00
logger.info('%s:loading %s' % (module, filename))
2008-09-13 19:41:02 +02:00
translation_obj = pool.get('ir.translation')
2008-12-07 23:01:45 +01:00
translation_obj.translation_import(cursor, 0, lang2, module,
trans_file)
2008-09-13 19:41:02 +02:00
cursor.execute("UPDATE ir_module_module SET state = 'installed' " \
"WHERE name = %s", (package.name,))
module2state[package.name] = 'installed'
2008-09-13 19:41:02 +02:00
2009-02-11 14:52:22 +01:00
# Create missing reports
from trytond.report import Report
report_obj = pool.get('ir.action.report')
report_ids = report_obj.search(cursor, 0, [
('module', '=', module),
])
report_names = pool.object_name_list(type='report')
2009-02-11 14:52:22 +01:00
for report in report_obj.browse(cursor, 0, report_ids):
report_name = report.report_name
if report_name not in report_names:
report = object.__new__(Report)
report._name = report_name
pool.add(report, type='report')
cursor.commit()
2008-09-13 19:41:02 +02:00
# Vacuum :
while modules_todo:
(module, to_delete) = modules_todo.pop()
2008-09-30 11:55:07 +02:00
tools.post_import(cursor, pool, module, to_delete)
2008-09-13 19:41:02 +02:00
cursor.commit()
def get_module_list():
2008-12-01 20:25:09 +01:00
module_list = set()
2008-09-13 19:41:02 +02:00
if os.path.exists(MODULES_PATH) and os.path.isdir(MODULES_PATH):
for file in os.listdir(MODULES_PATH):
if os.path.isdir(OPJ(MODULES_PATH, file)):
2008-12-01 20:25:09 +01:00
module_list.add(file)
2008-09-13 19:41:02 +02:00
elif file[-4:] == '.zip':
2008-12-01 20:25:09 +01:00
module_list.add(file[-4:])
2008-11-18 17:40:30 +01:00
for ep in pkg_resources.iter_entry_points('trytond.modules'):
mod_name = ep.module_name.split('.')[-1]
2008-12-01 20:25:09 +01:00
module_list.add(mod_name)
module_list.add('ir')
module_list.add('workflow')
module_list.add('res')
module_list.add('webdav')
module_list.add('tests')
2008-12-01 20:25:09 +01:00
return list(module_list)
2008-09-13 19:41:02 +02:00
def register_classes():
import trytond.ir
import trytond.workflow
import trytond.res
import trytond.webdav
import trytond.tests
2008-11-18 17:40:30 +01:00
2009-02-11 14:52:22 +01:00
logger = logging.getLogger('modules')
2008-09-13 19:41:02 +02:00
for package in create_graph(get_module_list())[0]:
module = package.name
2009-02-11 14:52:22 +01:00
logger.info('%s:registering classes' % module)
2008-09-13 19:41:02 +02:00
if module in ('ir', 'workflow', 'res', 'webdav', 'tests'):
2008-09-13 19:41:02 +02:00
MODULES.append(module)
continue
2008-11-18 17:40:30 +01:00
if os.path.isfile(OPJ(MODULES_PATH, module + '.zip')):
mod_path = OPJ(MODULES_PATH, module + '.zip')
2008-09-13 19:41:02 +02:00
try:
zimp = zipimport.zipimporter(mod_path)
zimp.load_module(module)
except zipimport.ZipImportError:
tb_s = ''
for line in traceback.format_exception(*sys.exc_info()):
try:
line = line.encode('utf-8', 'ignore')
except:
continue
tb_s += line
for path in sys.path:
tb_s = tb_s.replace(path, '')
if CONFIG['debug_mode']:
import pdb
traceb = sys.exc_info()[2]
pdb.post_mortem(traceb)
2009-02-11 14:52:22 +01:00
logger.error('Couldn\'t import module %s:\n%s' % (module, tb_s))
2008-09-13 19:41:02 +02:00
break
2008-11-18 17:40:30 +01:00
elif os.path.isdir(OPJ(MODULES_PATH, module)):
try:
mod_file, pathname, description = imp.find_module(module,
[MODULES_PATH])
try:
imp.load_module(module, mod_file, pathname, description)
finally:
if mod_file is not None:
mod_file.close()
2008-11-18 17:40:30 +01:00
except ImportError:
tb_s = ''
for line in traceback.format_exception(*sys.exc_info()):
try:
line = line.encode('utf-8', 'ignore')
except:
continue
tb_s += line
for path in sys.path:
tb_s = tb_s.replace(path, '')
if CONFIG['debug_mode']:
import pdb
traceb = sys.exc_info()[2]
pdb.post_mortem(traceb)
2009-02-11 14:52:22 +01:00
logger.error('Couldn\'t import module %s:\n%s' % (module, tb_s))
2008-11-18 17:40:30 +01:00
break
elif module in EGG_MODULES:
ep = EGG_MODULES[module]
mod_path = os.path.join(ep.dist.location,
*ep.module_name.split('.')[:-1])
mod_file, pathname, description = imp.find_module(module,
[mod_path])
try:
imp.load_module(module, mod_file, pathname, description)
finally:
if mod_file is not None:
mod_file.close()
2008-11-18 17:40:30 +01:00
else:
2009-02-11 14:52:22 +01:00
logger.error('Couldn\'t find module %s' % module)
2008-11-18 17:40:30 +01:00
break
2008-09-13 19:41:02 +02:00
MODULES.append(module)
2009-02-11 14:52:22 +01:00
def load_modules(database_name, pool, update=False, lang=None):
2008-09-30 11:55:07 +02:00
res = True
2009-02-11 14:52:22 +01:00
database = Database(database_name).connect()
2008-09-13 19:41:02 +02:00
cursor = database.cursor()
try:
force = []
2009-02-11 14:52:22 +01:00
if update:
2008-09-13 19:41:02 +02:00
if 'all' in CONFIG['init']:
2008-11-07 11:23:57 +01:00
cursor.execute("SELECT name FROM ir_module_module")
2008-09-13 19:41:02 +02:00
else:
cursor.execute("SELECT name FROM ir_module_module " \
"WHERE state IN ('installed', 'to install', " \
"'to upgrade', 'to remove')")
else:
cursor.execute("SELECT name FROM ir_module_module " \
"WHERE state IN ('installed', 'to upgrade', 'to remove')")
module_list = [name for (name,) in cursor.fetchall()]
2009-02-11 14:52:22 +01:00
if update:
2008-09-13 19:41:02 +02:00
for module in CONFIG['init'].keys():
if CONFIG['init'][module]:
module_list.append(module)
for module in CONFIG['update'].keys():
if CONFIG['update'][module]:
module_list.append(module)
graph = create_graph(module_list, force)[0]
try:
2009-02-11 14:52:22 +01:00
load_module_graph(cursor, graph, pool, lang)
2008-09-13 19:41:02 +02:00
except:
cursor.rollback()
raise
2009-02-11 14:52:22 +01:00
if update:
2008-09-13 19:41:02 +02:00
cursor.execute("SELECT name FROM ir_module_module " \
"WHERE state IN ('to remove')")
if cursor.rowcount:
for (mod_name,) in cursor.fetchall():
#TODO check if ressource not updated by the user
cursor.execute('SELECT model, db_id FROM ir_model_data ' \
'WHERE module = %s ' \
'ORDER BY id DESC', (mod_name,))
for rmod, rid in cursor.fetchall():
pool.get(rmod).delete(cursor, 0, rid)
cursor.commit()
cursor.execute("UPDATE ir_module_module SET state = %s " \
"WHERE state IN ('to remove')", ('uninstalled',))
cursor.commit()
2008-09-30 11:55:07 +02:00
res = False
2008-09-13 19:41:02 +02:00
module_obj = pool.get('ir.module.module')
module_obj.update_list(cursor, 0)
cursor.commit()
finally:
cursor.close()
2008-09-30 11:55:07 +02:00
return res