if config["stop_after_init"]:
sys.exit(0)
+ for m in openerp.conf.server_wide_modules:
+ __import__(m)
+ # Register a WSGI entry point if any.
+ info = openerp.modules.module.load_information_from_description_file(m)
+ if info['wsgi']:
+ openerp.wsgi.register_wsgi_handler(getattr(sys.modules[m], info['wsgi']))
+
+ openerp.wsgi.serve()
+
+
setup_pid_file()
setup_signal_handlers()
start_services()
""" Addons module.
-This module only serves to contain OpenERP addons. For the code to
-manage those addons, see openerp.modules. This module conveniently
-reexports some symbols from openerp.modules. Importing them from here
-is deprecated.
+This module serves to contain all OpenERP addons, across all configured addons
+paths. For the code to manage those addons, see openerp.modules.
+
+Addons are made available here (i.e. under openerp.addons) after
+openerp.tools.config.parse_config() is called (so that the addons paths
+are known).
+
+This module also conveniently reexports some symbols from openerp.modules.
+Importing them from here is deprecated.
"""
import publisher_warranty
import report
+def YEAH(environ, start_response):
+ response = 'YEAH.\n'
+ start_response('200 OK', [('Content-Type', 'text/plain'), ('Content-Length', str(len(response)))])
+ return [response]
+
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
'test/test_ir_rule.yml', # <-- These tests modify/add/delete ir_rules.
'test/test_ir_values.yml',
],
+ 'wsgi': 'YEAH',
'installable': True,
'active': True,
'certificate': '0076807797149',
and provide real Python variables, e.g. addons_paths is really a list
of paths.
+To initialize properly this module, openerp.tools.config.parse_config()
+must be used.
+
"""
import deprecation
+# Paths to search for OpenERP addons.
+addons_paths = []
+
+# List of server-wide modules to load. Those modules are supposed to provide
+# features not necessarily tied to a particular database. This is in contrast
+# to modules that are always bound to a specific database when they are
+# installed (i.e. the majority of OpenERP addons). This is set with the --load
+# command-line option.
+server_wide_modules = []
+
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
"""
- def add_node(self, name, deps):
+ def add_node(self, name, info):
max_depth, father = 0, None
- for n in [Node(x, self) for x in deps]:
+ for n in [Node(x, self, None) for x in info['depends']]:
if n.depth >= max_depth:
father = n
max_depth = n.depth
if father:
- return father.add_child(name)
+ return father.add_child(name, info)
else:
- return Node(name, self)
+ return Node(name, self, info)
def update_from_db(self, cr):
if not len(self):
continue
later.clear()
current.remove(package)
- node = self.add_node(package, deps)
+ node = self.add_node(package, info)
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:
class Singleton(object):
- def __new__(cls, name, graph):
+ def __new__(cls, name, graph, info):
if name in graph:
inst = graph[name]
else:
inst = object.__new__(cls)
inst.name = name
+ inst.info = info
graph[name] = inst
return inst
class Node(Singleton):
""" One module in the modules dependency graph.
- Node acts as a per-module singleton.
+ Node acts as a per-module singleton. A node is constructed via
+ Graph.add_module() or Graph.add_modules(). Some of its fields are from
+ ir_module_module (setted by Graph.update_from_db()).
"""
- def __init__(self, name, graph):
+ def __init__(self, name, graph, info):
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)
+ def add_child(self, name, info):
+ node = Node(name, self.graph, info)
node.depth = self.depth + 1
if node not in self.children:
self.children.append(node)
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
logger = netsvc.Logger()
def initialize_sys_path():
+ """ Add all addons paths in sys.path.
+
+ This ensures something like ``import crm`` works even if the addons are
+ not in the PYTHONPATH.
+ """
global ad_paths
if ad_paths:
info['license'] = info.get('license') or 'AGPL-3'
info.setdefault('installable', True)
info.setdefault('active', False)
+ info.setdefault('wsgi', None) # WSGI entry point, given as a string
for kind in ['data', 'demo', 'test',
'init_xml', 'update_xml', 'demo_xml']:
info.setdefault(kind, [])
t[1](cr, *t[2])
cr.commit()
+# Import hook to write a addon m in both sys.modules['m'] and
+# sys.modules['openerp.addons.m']. Otherwise it could be loaded twice
+# if imported twice using different names.
+#class MyImportHook(object):
+# def find_module(self, module_name, package_path):
+# print ">>>", module_name, package_path
+# def load_module(self, module_name):
+# raise ImportError("Restricted")
-def load_module(module_name):
- """ Load a Python module found on the addons paths."""
- fm = imp.find_module(module_name, ad_paths)
- try:
- imp.load_module(module_name, *fm)
- finally:
- if fm[0]:
- fm[0].close()
-
+#sys.meta_path.append(MyImportHook())
def register_module_classes(m):
""" Register module named m, if not already registered.
- This will load the module and register all of its models. (Actually, the
- explicit constructor call of each of the models inside the module will
- register them.)
+ This loads the module and register all of its models, thanks to either
+ the MetaModel metaclass, or the explicit instantiation of the model.
"""
try:
zip_mod_path = mod_path + '.zip'
if not os.path.isfile(zip_mod_path):
- load_module(m)
+ __import__(m)
else:
zimp = zipimport.zipimporter(zip_mod_path)
zimp.load_module(m)
import os
import sys
import openerp
+import openerp.conf
import openerp.loglevels as loglevels
import logging
import openerp.release as release
group.add_option("-P", "--import-partial", dest="import_partial", my_default='',
help="Use this for big data importation, if it crashes you will be able to continue at the current state. Provide a filename to store intermediate importation states.")
group.add_option("--pidfile", dest="pidfile", help="file where the server pid will be stored")
+ group.add_option("--load", dest="server_wide_modules", help="Comma-separated list of server-wide modules")
parser.add_option_group(group)
+ # XML-RPC / HTTP
group = optparse.OptionGroup(parser, "XML-RPC Configuration")
group.add_option("--xmlrpc-interface", dest="xmlrpc_interface", my_default='',
help="Specify the TCP IP address for the XML-RPC protocol. The empty string binds to all interfaces.")
help="disable the XML-RPC protocol")
parser.add_option_group(group)
+ # XML-RPC / HTTPS
title = "XML-RPC Secure Configuration"
if not self.has_ssl:
title += " (disabled as ssl is unavailable)"
self.options[option.dest] = option.my_default
self.casts[option.dest] = option
- self.parse_config()
+ self.parse_config(None, False)
- def parse_config(self, args=None):
+ def parse_config(self, args=None, complete=True):
+ """ Parse the configuration file (if any) and the command-line
+ arguments.
+
+ This method initializes openerp.tools.config and openerp.conf (the
+ former should be removed in the furture) with library-wide
+ configuration values.
+
+ This method must be called before proper usage of this library can be
+ made.
+
+ Typical usage of this method:
+
+ openerp.tools.config.parse_config(sys.argv[1:])
+
+ :param complete: this is a hack used in __init__(), leave it to True.
+
+ """
if args is None:
args = []
opt, args = self.parser.parse_args(args)
if opt.save:
self.save()
+ openerp.conf.addons_paths = self.options['addons_path'].split(',')
+ openerp.conf.server_wide_modules = \
+ map(lambda m: m.strip(), opt.server_wide_modules.split(',')) if \
+ opt.server_wide_modules else []
+ if complete:
+ openerp.modules.module.initialize_sys_path()
+ openerp.modules.loading.open_openerp_namespace()
+ # openerp.addons.__path__.extend(openerp.conf.addons_paths) # This
+ # is not compatible with initialize_sys_path(): import crm and
+ # import openerp.addons.crm load twice the module.
+
def _generate_pgpassfile(self):
"""
Generate the pgpass file with the parameters from the command line (db_host, db_user,
def wsgi_jsonrpc(environ, start_response):
pass
+def wsgi_modules(environ, start_response):
+ """ WSGI handler dispatching to addons-provided entry points."""
+ pass
+
+# WSGI handlers provided by modules loaded with the --load command-line option.
+module_handlers = []
+
+def register_wsgi_handler(handler):
+ """ Register a WSGI handler.
+
+ Handlers are tried in the order they are added. We might provide a way to
+ register a handler for specific routes later.
+ """
+ module_handlers.append(handler)
+
def application(environ, start_response):
""" WSGI entry point."""
# Try all handlers until one returns some result (i.e. not None).
- wsgi_handlers = [wsgi_xmlrpc, wsgi_jsonrpc, legacy_wsgi_xmlrpc]
+ wsgi_handlers = [
+ wsgi_xmlrpc,
+ wsgi_jsonrpc,
+ legacy_wsgi_xmlrpc,
+ wsgi_modules,
+ ] + module_handlers
for handler in wsgi_handlers:
result = handler(environ, start_response)
if result is None: