diff --git a/addons/base_import_module/bin/oe_module_deploy.py b/addons/base_import_module/bin/oe_module_deploy.py deleted file mode 100755 index 2412382cba5..00000000000 --- a/addons/base_import_module/bin/oe_module_deploy.py +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env python -import argparse -import os -import sys -import tempfile -import zipfile - - -try: - import requests -except ImportError: - # no multipart encoding in stdlib and this script is temporary - sys.exit("This script requires the 'requests' module. ( pip install requests )") - -session = requests.session() - -def deploy_module(module_path, url, login, password, db=''): - url = url.rstrip('/') - authenticate(url, login, password, db) - module_file = zip_module(module_path) - try: - return upload_module(url, module_file) - finally: - os.remove(module_file) - -def upload_module(server, module_file): - print("Uploading module file...") - url = server + '/base_import_module/upload' - files = dict(mod_file=open(module_file, 'rb')) - res = session.post(url, files=files) - if res.status_code != 200: - raise Exception("Could not authenticate on server '%s'" % server) - return res.text - -def authenticate(server, login, password, db=''): - print("Authenticating on server '%s' ..." % server) - - # Fixate session with a given db if any - session.get(server + '/web/login', params=dict(db=db)) - - args = dict(login=login, password=password, db=db) - res = session.post(server + '/base_import_module/login', args) - if res.status_code == 404: - raise Exception("The server '%s' does not have the 'base_import_module' installed." % server) - elif res.status_code != 200: - raise Exception(res.text) - -def zip_module(path): - path = os.path.abspath(path) - if not os.path.isdir(path): - raise Exception("Could not find module directory '%s'" % path) - container, module_name = os.path.split(path) - temp = tempfile.mktemp(suffix='.zip') - try: - print("Zipping module directory...") - with zipfile.ZipFile(temp, 'w') as zfile: - for root, dirs, files in os.walk(path): - for file in files: - file_path = os.path.join(root, file) - zfile.write(file_path, file_path.split(container).pop()) - return temp - except Exception: - os.remove(temp) - raise - -if __name__ == '__main__': - parser = argparse.ArgumentParser(description='Deploy a module on an OpenERP server.') - parser.add_argument('path', help="Path of the module to deploy") - parser.add_argument('--url', dest='url', help='Url of the server (default=http://localhost:8069)', default="http://localhost:8069") - parser.add_argument('--db', dest='db', help='Database to use if server does not use db-filter.') - parser.add_argument('--login', dest='login', default="admin", help='Login (default=admin)') - parser.add_argument('--password', dest='password', default="admin", help='Password (default=admin)') - parser.add_argument('--no-ssl-check', dest='no_ssl_check', action='store_true', help='Do not check ssl cert') - if len(sys.argv) == 1: - sys.exit(parser.print_help()) - - args = parser.parse_args() - - if args.no_ssl_check: - session.verify = False - - try: - result = deploy_module(args.path, args.url, args.login, args.password, args.db) - print(result) - except Exception, e: - sys.exit("ERROR: %s" % e) diff --git a/openerp/cli/__init__.py b/openerp/cli/__init__.py index 27428369cbc..31582d95292 100644 --- a/openerp/cli/__init__.py +++ b/openerp/cli/__init__.py @@ -1,5 +1,6 @@ import logging import sys +import os import openerp from openerp import tools @@ -25,12 +26,18 @@ class Command(object): pass class Help(Command): + """Display the list of available commands""" def run(self, args): print "Available commands:\n" + padding = max([len(k) for k in commands.keys()]) + 2 for k, v in commands.items(): - print " %s" % k + print " %s%s" % (k.ljust(padding, ' '), v.__doc__ or '') + print "\nUse '%s --help' for individual command help." % sys.argv[0].split(os.path.sep)[-1] import server +import deploy +import scaffold +import start def main(): args = sys.argv[1:] @@ -47,6 +54,7 @@ def main(): # Subcommand discovery if len(args) and not args[0].startswith("-"): + logging.disable(logging.CRITICAL) for m in module.get_modules(): m = 'openerp.addons.' + m __import__(m) @@ -54,6 +62,7 @@ def main(): #except Exception, e: # raise # print e + logging.disable(logging.NOTSET) command = args[0] args = args[1:] diff --git a/openerp/cli/deploy.py b/openerp/cli/deploy.py new file mode 100644 index 00000000000..accbef81f46 --- /dev/null +++ b/openerp/cli/deploy.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import argparse +import os +import requests +import sys +import tempfile +import zipfile + +from . import Command + +class Deploy(Command): + """Deploy a module on an Odoo instance""" + def __init__(self): + super(Deploy, self).__init__() + self.session = requests.session() + + def deploy_module(self, module_path, url, login, password, db=''): + url = url.rstrip('/') + self.authenticate(url, login, password, db) + module_file = self.zip_module(module_path) + try: + return self.upload_module(url, module_file) + finally: + os.remove(module_file) + + def upload_module(self, server, module_file): + print("Uploading module file...") + url = server + '/base_import_module/upload' + files = dict(mod_file=open(module_file, 'rb')) + res = self.session.post(url, files=files) + if res.status_code != 200: + raise Exception("Could not authenticate on server '%s'" % server) + return res.text + + def authenticate(self, server, login, password, db=''): + print("Authenticating on server '%s' ..." % server) + + # Fixate session with a given db if any + self.session.get(server + '/web/login', params=dict(db=db)) + + args = dict(login=login, password=password, db=db) + res = self.session.post(server + '/base_import_module/login', args) + if res.status_code == 404: + raise Exception("The server '%s' does not have the 'base_import_module' installed." % server) + elif res.status_code != 200: + raise Exception(res.text) + + def zip_module(self, path): + path = os.path.abspath(path) + if not os.path.isdir(path): + raise Exception("Could not find module directory '%s'" % path) + container, module_name = os.path.split(path) + temp = tempfile.mktemp(suffix='.zip') + try: + print("Zipping module directory...") + with zipfile.ZipFile(temp, 'w') as zfile: + for root, dirs, files in os.walk(path): + for file in files: + file_path = os.path.join(root, file) + zfile.write(file_path, file_path.split(container).pop()) + return temp + except Exception: + os.remove(temp) + raise + + def run(self, cmdargs): + parser = argparse.ArgumentParser( + prog="%s deploy" % sys.argv[0].split(os.path.sep)[-1], + description=self.__doc__ + ) + parser.add_argument('path', help="Path of the module to deploy") + parser.add_argument('url', nargs='?', help='Url of the server (default=http://localhost:8069)', default="http://localhost:8069") + parser.add_argument('--db', dest='db', help='Database to use if server does not use db-filter.') + parser.add_argument('--login', dest='login', default="admin", help='Login (default=admin)') + parser.add_argument('--password', dest='password', default="admin", help='Password (default=admin)') + parser.add_argument('--no-ssl-check', dest='no_ssl_check', action='store_true', help='Do not check ssl cert') + if not cmdargs: + sys.exit(parser.print_help()) + + args = parser.parse_args(args=cmdargs) + + if args.no_ssl_check: + self.session.verify = False + + try: + if not args.url.startswith('http://'): + args.url = 'https://%s' % args.url + result = self.deploy_module(args.path, args.url, args.login, args.password, args.db) + print(result) + except Exception, e: + sys.exit("ERROR: %s" % e) diff --git a/openerp/cli/scaffold.py b/openerp/cli/scaffold.py new file mode 100644 index 00000000000..23c20b52542 --- /dev/null +++ b/openerp/cli/scaffold.py @@ -0,0 +1,249 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import argparse +import ast +import functools +import keyword +import logging +import os +import re +import simplejson +import sys + +import jinja2 + +from . import Command + +from openerp.modules.module import (get_module_root, MANIFEST, load_information_from_description_file as load_manifest) + + +class Scaffold(Command): + "Generate an Odoo module skeleton." + + def run(self, cmdargs): + # TODO: bash completion file + parser = argparse.ArgumentParser( + prog="%s scaffold" % sys.argv[0].split(os.path.sep)[-1], + description=self.__doc__ + ) + parser.add_argument('--init', type=identifier, help='Initialize a new Odoo module') + + parser.add_argument('--dest', default=".", + help='Directory where the module should be created/updated (default to current directory)') + + parser.add_argument('--model', type=identifier, help="Name of the model to add") + + parser.add_argument('--controller', type=identifier, help="Name of the controller to add") + + parser.add_argument('--web', action='store_true', default=False, + help="Generate structure for a webclient module") + + parser.add_argument('--theme', action='store_true', default=False, + help="Generate structure for a Website theme") + + if not cmdargs: + sys.exit(parser.print_help()) + args = parser.parse_args(args=cmdargs) + + dest = directory(args.dest) + if args.init: + dest = os.path.join(dest, args.init) + if os.path.exists(dest): + die("Can't initialize module in `%s`: Directory already exists." % dest) + if get_module_root(dest): + die("Can't init a new module in another Odoo module, you probably want to run this " + "command from your project's root") + else: + mroot = get_module_root(dest) + if not mroot: + die("The path `%s` provided does not point to an existing Odoo module. " + "Forgot to `--init` ?" % dest) + dest = mroot + + logging.disable(logging.CRITICAL) + scaffold = ScaffoldModule(dest) + if args.model: + scaffold.add_model(snake(args.model)) + if args.controller: + scaffold.add_controller(args.controller) + if args.web: + scaffold.add_webclient_structure() + if args.theme: + scaffold.add_theme_structure() + +class ScaffoldModule(object): + """ + Object for scaffolding existing or new Odoo modules + + @param path: Path of an existing module or path of module to create + """ + def __init__(self, path): + env = jinja2.Environment(loader=jinja2.PackageLoader( + 'openerp.cli', 'scaffold')) + env.filters['snake'] = snake + self.env = env + self.path = functools.partial(os.path.join, directory(path)) + self.created = not os.path.exists(self.path()) + directory(path, create=True) + self.module = self.path().split(os.path.sep)[-1] + if self.created: + manifest_base = os.path.splitext(MANIFEST)[0] + self.render_file('%s.jinja2' % manifest_base, self.path('%s.py' % manifest_base)) + + def add_model(self, model): + model_module = snake(model) + model_file = self.path('models', '%s.py' % model_module) + if os.path.exists(model_file): + die("Model `%s` already exists !" % model_file) + self.add_init_import(self.path('__init__.py'), 'models') + self.add_init_import(self.path('models', '__init__.py'), model_module) + + self.render_file('models.jinja2', model_file, model=model) + self.render_file('ir.model.access.jinja2', self.path('security', 'ir.model.access.csv'), + if_exists='append', model=model) + self.append_manifest_list('data', 'security/ir.model.access.csv') + + demo_file = 'data/%s_demo.xml' % self.module + self.append_xml_data('record.jinja2', self.path(demo_file), + model=model) + self.append_manifest_list('demo', demo_file) + + def add_controller(self, controller): + controller_module = snake(controller) + controller_file = self.path('controllers', '%s.py' % controller_module) + if os.path.exists(controller_file): + die("Controller `%s` already exists !" % controller_file) + self.add_init_import(self.path('__init__.py'), 'controllers') + # Check if the controller name correspond to a model and expose result to templates + has_model = self.has_import(self.path('models', '__init__.py'), controller_module) + self.add_init_import(self.path('controllers', '__init__.py'), controller_module) + self.render_file('controllers.jinja2', controller_file, controller=controller, + has_model=has_model) + + def add_webclient_structure(self): + self.append_manifest_list('depends', 'web') + prefix = '%s.%%s' % self.module + for ext in ('js', 'css', 'xml'): + self.render_file('webclient_%s.jinja2' % ext, + self.path('static', 'src', ext, prefix % ext)) + + def add_theme_structure(self): + self.append_manifest_list('depends', 'website') + css_file = '%s_theme.css' % self.module + self.render_file('theme_css.jinja2', self.path('static', 'src', 'css', css_file)) + theme_file = '%s_theme.xml' % self.module + self.append_xml_data('theme_xml.jinja2', self.path('views', theme_file), skip_if_exist=True) + + def has_import(self, initfile, module): + if not os.path.isfile(initfile): + return False + with open(initfile, 'r') as f: + for imp in ast.parse(f.read()).body: + if isinstance(imp, ast.Import): + if module in [mod.name for mod in imp.names]: + return True + return False + + def get_manifest(self, key=None, default=None): + manifest = load_manifest(self.module, self.path()) + if key: + return manifest.get(key, default) + else: + return manifest + + def append_manifest_list(self, key, value, unique=True): + # TODO: append value without altering serialized formatting + vals = self.get_manifest(key, []) + if unique and value in vals: + return + vals.append(value) + self.change_manifest_key(key, vals) + + def change_manifest_key(self, key, value): + value = simplejson.dumps(value) + with open(self.path(MANIFEST), 'r') as f: + data = f.read() + sdata = re.split('["\']%s["\']\s?:\s?\[[^\]]*\]' % key, data) + add = "'%s': %s" % (key, value) + if len(sdata) != 2: + warn("Could not update `%s` key in manifest. You should add this by yourself:" + "\n\n%s\n" % (key, add)) + else: + with open(self.path(MANIFEST), 'w') as f: + f.write(add.join(sdata)) + + def add_init_import(self, initfile, module): + if not(os.path.exists(initfile) and self.has_import(initfile, module)): + self.render_file('__init__.jinja2', initfile, if_exists='append', modules=[module]) + + def render_file(self, template, dest, if_exists='skip', **kwargs): + mode = 'a' + if os.path.exists(dest): + if if_exists == 'replace': + mode = 'w' + elif if_exists != 'append': + warn("File `%s` already exists. Skipping it..." % dest) + return + else: + kwargs['file_created'] = True + outdir = os.path.dirname(dest) + if not os.path.exists(outdir): + os.makedirs(outdir) + content = self.env.get_template(template).render(module=self.module, **kwargs) + with open(dest, mode) as f: + f.write(content) + + def append_xml_data(self, template, dest, skip_if_exist=False, **kwargs): + if not os.path.exists(dest): + self.render_file('xmldata.jinja2', dest, **kwargs) + elif skip_if_exist: + warn("File `%s` already exists. Skipping it..." % dest) + with open(dest, 'r') as f: + data = f.read() + m = re.search('(^\s*)?', data, re.MULTILINE) + content = self.env.get_template(template).render(module=self.module, **kwargs) + if not m: + warn("Could not add data in `%s`. You should add this by yourself:" + "\n\n%s\n" % (dest, content)) + else: + data = data[:m.start()] + content + m.group() + data[m.end():] + with open(self.path(dest), 'w') as f: + f.write(data) + +def snake(s): + """ snake cases ``s`` + + :param str s: + :return: str + """ + # insert a space before each uppercase character preceded by a + # non-uppercase letter + s = re.sub(r'(?<=[^A-Z])\B([A-Z])', r' \1', s) + # lowercase everything, split on whitespace and join + return '_'.join(s.lower().split()) + +def identifier(s): + if keyword.iskeyword(s): + die("%s is a Python keyword and can not be used as a name" % s) + if not re.match('[A-Za-z_][A-Za-z0-9_]*', s): + die("%s is not a valid Python identifier" % s) + return s + +def directory(p, create=False): + expanded = os.path.abspath( + os.path.expanduser( + os.path.expandvars(p))) + if create and not os.path.exists(expanded): + os.makedirs(expanded) + if create and not os.path.isdir(expanded): + die("%s exists but is not a directory" % p) + return expanded + +def die(message, code=1): + print >>sys.stderr, message + sys.exit(code) + +def warn(message): + # ASK: shall we use logger ? + print "WARNING: " + message + diff --git a/openerp/cli/scaffold/__init__.jinja2 b/openerp/cli/scaffold/__init__.jinja2 new file mode 100644 index 00000000000..e8b1efb3fc7 --- /dev/null +++ b/openerp/cli/scaffold/__init__.jinja2 @@ -0,0 +1,3 @@ +{% if file_created -%}# -*- coding: utf-8 -*-{%- endif %} +{% for module in modules if module -%}import {{ module }} +{%- endfor %} diff --git a/openerp/cli/scaffold/__openerp__.jinja2 b/openerp/cli/scaffold/__openerp__.jinja2 new file mode 100644 index 00000000000..838386a1ecb --- /dev/null +++ b/openerp/cli/scaffold/__openerp__.jinja2 @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +{ + 'name': "{{ module }}", + + 'summary': """ + Short (1 phrase/line) summary of the module's purpose, used as + subtitle on modules listing or apps.openerp.com""", + + 'description': """ + Long description of module's purpose + """, + + 'author': "Your Company", + 'website': "http://www.yourcompany.com", + + # Categories can be used to filter modules in modules listing + # Check /addons/base/module/module_data.xml of the full list + 'category': 'Uncategorized', + 'version': '0.1', + + # any module necessary for this one to work correctly + 'depends': ['base','website'], + 'data': [ + ], + + 'demo': [ + ], + + 'tests': [ + ], +} diff --git a/openerp/cli/scaffold/controllers.jinja2 b/openerp/cli/scaffold/controllers.jinja2 new file mode 100644 index 00000000000..dfed6fb9bc0 --- /dev/null +++ b/openerp/cli/scaffold/controllers.jinja2 @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +from openerp import http +from openerp.addons.web.controllers import main + +class {{ controller }}(main.Home): + @http.route('/{{ module }}/{{ controller }}', auth='public') + def index(self): + return "Hello, world!" +{% if has_model %} + @http.route('/{{ module }}/{{ controller }}/'], type='http', auth='public') + def {{ controller }}(self, {{ controller }}, **kw): + return "Hello, %r!" % {{ controller }} +{% endif %} diff --git a/openerp/cli/scaffold/ir.model.access.jinja2 b/openerp/cli/scaffold/ir.model.access.jinja2 new file mode 100644 index 00000000000..af558a3b625 --- /dev/null +++ b/openerp/cli/scaffold/ir.model.access.jinja2 @@ -0,0 +1,6 @@ +{% if file_created -%}id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink{%- endif %} +{% if model -%} +access_{{ module }}_{{ model }},{{- '' -}} +access_{{ module }}_{{ model }},{{- '' -}} +model_{{ module }}_{{ model }},,1,0,0,0 +{%- endif %} diff --git a/openerp/cli/scaffold/models.jinja2 b/openerp/cli/scaffold/models.jinja2 new file mode 100644 index 00000000000..36155a2566d --- /dev/null +++ b/openerp/cli/scaffold/models.jinja2 @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +from openerp.osv import orm, fields + +class {{ model }}(orm.Model): + _name = "{{ module }}.{{ model }}" + + _columns = { + 'name': fields.char(), + } diff --git a/openerp/cli/scaffold/record.jinja2 b/openerp/cli/scaffold/record.jinja2 new file mode 100644 index 00000000000..53e4ff22383 --- /dev/null +++ b/openerp/cli/scaffold/record.jinja2 @@ -0,0 +1,4 @@ + + My first record for model '{{ module }}.{{ model }}' + + diff --git a/openerp/cli/scaffold/theme_css.jinja2 b/openerp/cli/scaffold/theme_css.jinja2 new file mode 100644 index 00000000000..cd42d262c53 --- /dev/null +++ b/openerp/cli/scaffold/theme_css.jinja2 @@ -0,0 +1,4 @@ +@charset "utf-8"; +.{{ module }} { + background: white; +} diff --git a/openerp/cli/scaffold/theme_xml.jinja2 b/openerp/cli/scaffold/theme_xml.jinja2 new file mode 100644 index 00000000000..2e71368a347 --- /dev/null +++ b/openerp/cli/scaffold/theme_xml.jinja2 @@ -0,0 +1,26 @@ + + + + + + diff --git a/openerp/cli/scaffold/webclient_css.jinja2 b/openerp/cli/scaffold/webclient_css.jinja2 new file mode 100644 index 00000000000..dadce53503b --- /dev/null +++ b/openerp/cli/scaffold/webclient_css.jinja2 @@ -0,0 +1,4 @@ +@charset "utf-8"; +.openerp .oe_{{ module }} { + background: white; +} diff --git a/openerp/cli/scaffold/webclient_js.jinja2 b/openerp/cli/scaffold/webclient_js.jinja2 new file mode 100644 index 00000000000..9845e6f0662 --- /dev/null +++ b/openerp/cli/scaffold/webclient_js.jinja2 @@ -0,0 +1,14 @@ +openerp.{{ module }} = function (instance) { + +var _t = instance.web._t, + _lt = instance.web._lt; +var QWeb = instance.web.qweb; + +instance.web.WebClient.include({ + show_application: function () { + console.log("Show application"); + return this._super.apply(this, arguments); + }; +}); + +}; diff --git a/openerp/cli/scaffold/webclient_xml.jinja2 b/openerp/cli/scaffold/webclient_xml.jinja2 new file mode 100644 index 00000000000..98da7f73133 --- /dev/null +++ b/openerp/cli/scaffold/webclient_xml.jinja2 @@ -0,0 +1,7 @@ + diff --git a/openerp/cli/scaffold/xmldata.jinja2 b/openerp/cli/scaffold/xmldata.jinja2 new file mode 100644 index 00000000000..9a29774d6bf --- /dev/null +++ b/openerp/cli/scaffold/xmldata.jinja2 @@ -0,0 +1,5 @@ + + + + + diff --git a/openerp/cli/server.py b/openerp/cli/server.py index 5d731a9ad41..70602634efc 100644 --- a/openerp/cli/server.py +++ b/openerp/cli/server.py @@ -169,6 +169,7 @@ def main(args): sys.exit(rc) class Server(Command): + """Start the odoo server (default command)""" def run(self, args): main(args) diff --git a/openerp/cli/start.py b/openerp/cli/start.py new file mode 100644 index 00000000000..b3bd47ede63 --- /dev/null +++ b/openerp/cli/start.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import argparse +import glob +import os +import sys + +from . import Command +from .server import main +from openerp.modules.module import get_module_root, MANIFEST +from openerp.service.db import _create_empty_database, DatabaseExists + + +class Start(Command): + """Quick start the Odoo server for your project""" + + def get_module_list(self, path): + mods = glob.glob(os.path.join(path, '*/%s' % MANIFEST)) + return [mod.split(os.path.sep)[-2] for mod in mods] + + + def run(self, cmdargs): + parser = argparse.ArgumentParser( + prog="%s start" % sys.argv[0].split(os.path.sep)[-1], + description=self.__doc__ + ) + parser.add_argument('--path', default=".", + help="Directory where your project's modules are stored (will autodetect from current dir)") + parser.add_argument("-d", "--database", dest="db_name", default=None, + help="Specify the database name (default to project's directory name") + + + args, unknown = parser.parse_known_args(args=cmdargs) + + project_path = os.path.abspath(os.path.expanduser(os.path.expandvars(args.path))) + module_root = get_module_root(project_path) + db_name = None + if module_root: + # started in a module so we choose this module name for database + db_name = project_path.split(os.path.sep)[-1] + # go to the parent's directory of the module root + project_path = os.path.abspath(os.path.join(project_path, os.pardir)) + + # check if one of the subfolders has at least one module + mods = self.get_module_list(project_path) + if mods and '--addons-path' not in cmdargs: + cmdargs.append('--addons-path=%s' % project_path) + + if not args.db_name: + args.db_name = db_name or project_path.split(os.path.sep)[-1] + # TODO: forbid some database names ? eg template1, ... + try: + _create_empty_database(args.db_name) + except DatabaseExists, e: + pass + except Exception, e: + die("Could not create database `%s`. (%s)" % (args.db_name, e)) + cmdargs.extend(('-d', args.db_name)) + + if '--db-filter' not in cmdargs: + cmdargs.append('--db-filter=^%s$' % args.db_name) + + main(cmdargs) + +def die(message, code=1): + print >>sys.stderr, message + sys.exit(code) diff --git a/openerp/modules/module.py b/openerp/modules/module.py index 0ccb2865796..8f34a74f882 100644 --- a/openerp/modules/module.py +++ b/openerp/modules/module.py @@ -37,10 +37,13 @@ import openerp.tools as tools import openerp.release as release from openerp.tools.safe_eval import safe_eval as eval +MANIFEST = '__openerp__.py' + _logger = logging.getLogger(__name__) # addons path as a list -ad_paths = [] +ad_paths = [tools.config.addons_data_dir] +hooked = False # Modules already loaded loaded = [] @@ -83,17 +86,21 @@ def initialize_sys_path(): PYTHONPATH. """ global ad_paths - if ad_paths: - return + global hooked - ad_paths = [tools.config.addons_data_dir] - ad_paths += map(lambda m: os.path.abspath(tools.ustr(m.strip())), tools.config['addons_path'].split(',')) + for ad in tools.config['addons_path'].split(','): + ad = os.path.abspath(tools.ustr(ad.strip())) + if ad not in ad_paths: + ad_paths.append(ad) # add base module path base_path = os.path.abspath(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'addons')) - ad_paths += [base_path] + if base_path not in ad_paths: + ad_paths.append(base_path) - sys.meta_path.append(AddonsImportHook()) + if not hooked: + sys.meta_path.append(AddonsImportHook()) + hooked = True def get_module_path(module, downloaded=False, display_warning=True): """Return the path of the given module. @@ -169,6 +176,33 @@ def get_module_icon(module): return ('/' + module + '/') + '/'.join(iconpath) return '/base/' + '/'.join(iconpath) +def get_module_root(path): + """ + Get closest module's root begining from path + + # Given: + # /foo/bar/module_dir/static/src/... + + get_module_root('/foo/bar/module_dir/static/') + # returns '/foo/bar/module_dir' + + get_module_root('/foo/bar/module_dir/') + # returns '/foo/bar/module_dir' + + get_module_root('/foo/bar') + # returns None + + @param path: Path from which the lookup should start + + @return: Module root path or None if not found + """ + while not os.path.exists(os.path.join(path, MANIFEST)): + new_path = os.path.abspath(os.path.join(path, os.pardir)) + if path == new_path: + return None + path = new_path + return path + def load_information_from_description_file(module, mod_path=None): """ :param module: The name of the module (sale, purchase, ...) @@ -177,7 +211,7 @@ def load_information_from_description_file(module, mod_path=None): if not mod_path: mod_path = get_module_path(module) - terp_file = mod_path and opj(mod_path, '__openerp__.py') or False + terp_file = mod_path and opj(mod_path, MANIFEST) or False if terp_file: info = {} if os.path.isfile(terp_file): @@ -219,7 +253,7 @@ def load_information_from_description_file(module, mod_path=None): #TODO: refactor the logger in this file to follow the logging guidelines # for 6.0 - _logger.debug('module %s: no __openerp__.py file found.', module) + _logger.debug('module %s: no %s file found.', module, MANIFEST) return {} def init_module_models(cr, module_name, obj_list): @@ -291,7 +325,7 @@ def get_modules(): return name def is_really_module(name): - manifest_name = opj(dir, name, '__openerp__.py') + manifest_name = opj(dir, name, MANIFEST) zipfile_name = opj(dir, name) return os.path.isfile(manifest_name) return map(clean, filter(is_really_module, os.listdir(dir))) diff --git a/openerp/service/db.py b/openerp/service/db.py index bb97e72f992..de083f5b8ed 100644 --- a/openerp/service/db.py +++ b/openerp/service/db.py @@ -13,6 +13,7 @@ import psycopg2 import openerp from openerp import SUPERUSER_ID +from openerp.exceptions import Warning import openerp.release import openerp.sql_db import openerp.tools @@ -25,6 +26,9 @@ self_actions = {} self_id = 0 self_id_protect = threading.Semaphore() +class DatabaseExists(Warning): + pass + # This should be moved to openerp.modules.db, along side initialize(). def _initialize_db(id, db_name, demo, lang, user_password): try: @@ -81,7 +85,7 @@ def _create_empty_database(name): cr.execute("SELECT datname FROM pg_database WHERE datname = %s", (name,)) if cr.fetchall(): - raise openerp.exceptions.Warning("database %r already exists!" % (name,)) + raise DatabaseExists("database %r already exists!" % (name,)) else: cr.autocommit(True) # avoid transaction block cr.execute("""CREATE DATABASE "%s" ENCODING 'unicode' TEMPLATE "%s" """ % (name, chosen_template))