1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
6 # Copyright (C) 2010-2011 OpenERP s.a. (<http://openerp.com>).
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU Affero General Public License as
10 # published by the Free Software Foundation, either version 3 of the
11 # License, or (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU Affero General Public License for more details.
18 # You should have received a copy of the GNU Affero General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 ##############################################################################
24 from os.path import join as opj
30 import openerp.osv as osv
31 import openerp.tools as tools
32 import openerp.tools.osutil as osutil
33 from openerp.tools.safe_eval import safe_eval as eval
34 import openerp.pooler as pooler
35 from openerp.tools.translate import _
37 import openerp.netsvc as netsvc
40 import openerp.release as release
44 from zipfile import PyZipFile, ZIP_DEFLATED
45 from cStringIO import StringIO
49 import openerp.modules.db
50 import openerp.modules.graph
52 _ad = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'addons') # default addons path (base)
55 # Modules already loaded
58 logger = netsvc.Logger()
60 def initialize_sys_path():
66 ad_paths = map(lambda m: os.path.abspath(tools.ustr(m.strip())), tools.config['addons_path'].split(','))
68 sys.path.insert(1, _ad)
73 sys.path.insert(ad_cnt, adp)
76 ad_paths.append(_ad) # for get_module_path
79 def get_module_path(module, downloaded=False):
80 """Return the path of the given module.
82 Search the addons paths and return the first path where the given
83 module is found. If downloaded is True, return the default addons
84 path if nothing else is found.
89 if os.path.exists(opj(adp, module)) or os.path.exists(opj(adp, '%s.zip' % module)):
90 return opj(adp, module)
93 return opj(_ad, module)
94 logger.notifyChannel('init', netsvc.LOG_WARNING, 'module %s: module not found' % (module,))
98 def get_module_filetree(module, dir='.'):
99 path = get_module_path(module)
103 dir = os.path.normpath(dir)
106 if dir.startswith('..') or (dir and dir[0] == '/'):
107 raise Exception('Cannot access file outside the module')
109 if not os.path.isdir(path):
111 zip = zipfile.ZipFile(path + ".zip")
112 files = ['/'.join(f.split('/')[1:]) for f in zip.namelist()]
114 files = osutil.listdir(path, True)
118 if not f.startswith(dir):
122 f = f[len(dir)+int(not dir.endswith('/')):]
123 lst = f.split(os.sep)
126 current = current.setdefault(lst.pop(0), {})
127 current[lst.pop(0)] = None
131 def zip_directory(directory, b64enc=True, src=True):
132 """Compress a directory
134 @param directory: The directory to compress
135 @param base64enc: if True the function will encode the zip file with base64
136 @param src: Integrate the source files
138 @return: a string containing the zip file
141 RE_exclude = re.compile('(?:^\..+\.swp$)|(?:\.py[oc]$)|(?:\.bak$)|(?:\.~.~$)', re.I)
143 def _zippy(archive, path, src=True):
144 path = os.path.abspath(path)
145 base = os.path.basename(path)
146 for f in osutil.listdir(path, True):
147 bf = os.path.basename(f)
148 if not RE_exclude.search(bf) and (src or bf in ('__openerp__.py', '__terp__.py') or not bf.endswith('.py')):
149 archive.write(os.path.join(path, f), os.path.join(base, f))
151 archname = StringIO()
152 archive = PyZipFile(archname, "w", ZIP_DEFLATED)
154 # for Python 2.5, ZipFile.write() still expects 8-bit strings (2.6 converts to utf-8)
155 directory = tools.ustr(directory).encode('utf-8')
157 archive.writepy(directory)
158 _zippy(archive, directory, src=src)
160 archive_data = archname.getvalue()
164 return base64.encodestring(archive_data)
168 def get_module_as_zip(modulename, b64enc=True, src=True):
169 """Generate a module as zip file with the source or not and can do a base64 encoding
171 @param modulename: The module name
172 @param b64enc: if True the function will encode the zip file with base64
173 @param src: Integrate the source files
175 @return: a stream to store in a file-like object
178 ap = get_module_path(str(modulename))
180 raise Exception('Unable to find path for module %s' % modulename)
182 ap = ap.encode('utf8')
183 if os.path.isfile(ap + '.zip'):
184 val = file(ap + '.zip', 'rb').read()
186 val = base64.encodestring(val)
188 val = zip_directory(ap, b64enc, src)
193 def get_module_resource(module, *args):
194 """Return the full path of a resource of the given module.
196 @param module: the module
197 @param args: the resource path components
199 @return: absolute path to the resource
201 TODO name it get_resource_path
202 TODO make it available inside on osv object (self.get_resource_path)
204 a = get_module_path(module)
205 if not a: return False
206 resource_path = opj(a, *args)
207 if zipfile.is_zipfile( a +'.zip') :
208 zip = zipfile.ZipFile( a + ".zip")
209 files = ['/'.join(f.split('/')[1:]) for f in zip.namelist()]
210 resource_path = '/'.join(args)
211 if resource_path in files:
212 return opj(a, resource_path)
213 elif os.path.exists(resource_path):
218 def load_information_from_description_file(module):
220 :param module: The name of the module (sale, purchase, ...)
223 terp_file = get_module_resource(module, '__openerp__.py')
225 terp_file = get_module_resource(module, '__terp__.py')
226 mod_path = get_module_path(module)
229 if os.path.isfile(terp_file) or zipfile.is_zipfile(mod_path+'.zip'):
230 terp_f = tools.file_open(terp_file)
232 info = eval(terp_f.read())
234 logger.notifyChannel('modules', netsvc.LOG_ERROR,
235 'module %s: exception while evaluating file %s' %
240 # TODO the version should probably be mandatory
241 info.setdefault('version', '0')
242 info.setdefault('category', 'Uncategorized')
243 info.setdefault('depends', [])
244 info.setdefault('author', '')
245 info.setdefault('website', '')
246 info.setdefault('name', False)
247 info.setdefault('description', '')
248 info['certificate'] = info.get('certificate') or None
249 info['web'] = info.get('web') or False
250 info['license'] = info.get('license') or 'AGPL-3'
251 info.setdefault('installable', True)
252 info.setdefault('active', False)
253 for kind in ['data', 'demo', 'test',
254 'init_xml', 'update_xml', 'demo_xml']:
255 info.setdefault(kind, [])
258 #TODO: refactor the logger in this file to follow the logging guidelines
260 logging.getLogger('modules').debug('module %s: no descriptor file'
261 ' found: __openerp__.py or __terp__.py (deprecated)', module)
265 def init_module_models(cr, module_name, obj_list):
266 """ Initialize a list of models.
268 Call _auto_init and init on each model to create or update the
269 database tables supporting the models.
271 TODO better explanation of _auto_init and init.
275 logger.notifyChannel('init', netsvc.LOG_INFO,
276 'module %s: creating or updating database tables' % module_name)
279 result = obj._auto_init(cr, {'module': module_name})
282 if hasattr(obj, 'init'):
286 obj._auto_end(cr, {'module': module_name})
288 obj.vacuum(cr, openerp.SUPERUSER)
296 def load_module(module_name):
297 """ Load a Python module found on the addons paths."""
298 fm = imp.find_module(module_name, ad_paths)
300 imp.load_module(module_name, *fm)
306 def register_module_classes(m):
307 """ Register module named m, if not already registered.
309 This will load the module and register all of its models. (Actually, the
310 explicit constructor call of each of the models inside the module will
316 mt = isinstance(e, zipimport.ZipImportError) and 'zip ' or ''
317 msg = "Couldn't load %smodule %s" % (mt, m)
318 logger.notifyChannel('init', netsvc.LOG_CRITICAL, msg)
319 logger.notifyChannel('init', netsvc.LOG_CRITICAL, e)
324 logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: registering objects' % m)
325 mod_path = get_module_path(m)
327 initialize_sys_path()
329 zip_mod_path = mod_path + '.zip'
330 if not os.path.isfile(zip_mod_path):
333 zimp = zipimport.zipimporter(zip_mod_path)
343 """Returns the list of module names
347 name = os.path.basename(name)
348 if name[-4:] == '.zip':
352 def is_really_module(name):
353 name = opj(dir, name)
354 return os.path.isdir(name) or zipfile.is_zipfile(name)
355 return map(clean, filter(is_really_module, os.listdir(dir)))
358 initialize_sys_path()
360 plist.extend(listdir(ad))
361 return list(set(plist))
364 def get_modules_with_version():
365 modules = get_modules()
367 for module in modules:
369 info = load_information_from_description_file(module)
370 res[module] = "%s.%s" % (release.major_version, info['version'])
376 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: