Odoo运行机制(三)----模块加载

Odoo URL解析

openerp.http.Root

Root类是 OpenERP Web客户端的WSGI应用,处理HTTP请求的接口为__call__()

def __call__(self, environ, start_response):
        """ Handle a WSGI request
        """
        if not self._loaded:
            self._loaded = True
            self.load_addons()
        return self.dispatch(environ, start_response)

可见,dispatch()是处理HTTP请求的核心方法

def dispatch(self, environ, start_response):
        """
        Performs the actual WSGI dispatching for the application.
        """
        try:
            httprequest = werkzeug.wrappers.Request(environ)
            httprequest.app = self

            explicit_session = self.setup_session(httprequest)
            self.setup_db(httprequest)
            self.setup_lang(httprequest)

            request = self.get_request(httprequest)

            def _dispatch_nodb():
                try:
                    func, arguments = self.nodb_routing_map.bind_to_environ(request.httprequest.environ).match()
                except werkzeug.exceptions.HTTPException, e:
                    return request._handle_exception(e)
                request.set_handler(func, arguments, "none")
                result = request.dispatch()
                return result

            with request:
                db = request.session.db
                if db:
                    openerp.modules.registry.RegistryManager.check_registry_signaling(db)
                    try:
                        with openerp.tools.mute_logger('openerp.sql_db'):
                            ir_http = request.registry['ir.http']
                    except (AttributeError, psycopg2.OperationalError):
                        # psycopg2 error or attribute error while constructing
                        # the registry. That means the database probably does
                        # not exists anymore or the code doesnt match the db.
                        # Log the user out and fall back to nodb
                        request.session.logout()
                        result = _dispatch_nodb()
                    else:
                        result = ir_http._dispatch()
                        openerp.modules.registry.RegistryManager.signal_caches_change(db)
                else:
                    result = _dispatch_nodb()

                response = self.get_response(httprequest, result, explicit_session)
            return response(environ, start_response)

        except werkzeug.exceptions.HTTPException, e:
            return e(environ, start_response)

dispatch将通过setup_session()方法恢复或者创建sessionsession保存在openerp.tools.config.session_dir所指明的目录下。
dispatch()中,通过 httprequest = werkzeug.wrappers.Request(environ)得到WSGIRequest对象,并对werkzeug.wrappers.Request类通过HttpRequest类进行包装。
通过bind_to_environ函数,得到当前Http请求对应的回调函数。并将回调函数通过request.set_handler(func, arguments, "none")传递给request。紧接着通过ir_http = request.registry['ir.http']得到一个ir_http对象

Odoo 模块加载

request.registry

request.registry定义如下:

class WebRequest(object):
    ...
    @property
    def registry(self):
        """
        The registry to the database linked to this request. Can be ``None``
        if the current request uses the ``none`` authentication.

        .. deprecated:: 8.0

            use :attr:`.env`
        """
        return openerp.modules.registry.RegistryManager.get(self.db) if self.db else None

RegistryManager.get()从指定的数据库名称中返回一个registry对象,其定义如下。

@classmethod
    def get(cls, db_name, force_demo=False, status=None, update_module=False):
        """ Return a registry for a given database name."""
        with cls.lock():
            try:
                return cls.registries[db_name]
            except KeyError:
                return cls.new(db_name, force_demo, status,
                               update_module)
            finally:
                # set db tracker - cleaned up at the WSGI
                # dispatching phase in openerp.service.wsgi_server.application
                threading.current_thread().dbname = db_name

新建Registry的代码如下,此时应当为加载一个新的数据库。

    @classmethod
    def new(cls, db_name, force_demo=False, status=None,
            update_module=False):
        """ Create and return a new registry for a given database name.

        The (possibly) previous registry for that database name is discarded.

        """
        import openerp.modules
        with cls.lock():
            with openerp.api.Environment.manage():
                registry = Registry(db_name)

                # Initializing a registry will call general code which will in
                # turn call registries.get (this object) to obtain the registry
                # being initialized. Make it available in the registries
                # dictionary then remove it if an exception is raised.
                cls.delete(db_name)
                cls.registries[db_name] = registry
                try:
                    with registry.cursor() as cr:
                        seq_registry, seq_cache = Registry.setup_multi_process_signaling(cr)
                        registry.base_registry_signaling_sequence = seq_registry
                        registry.base_cache_signaling_sequence = seq_cache
                    # This should be a method on Registry
                    openerp.modules.load_modules(registry._db, force_demo, status, update_module)
                except Exception:
                    del cls.registries[db_name]
                    raise

                # load_modules() above can replace the registry by calling
                # indirectly new() again (when modules have to be uninstalled).
                # Yeah, crazy.
                registry = cls.registries[db_name]

                cr = registry.cursor()
                try:
                    registry.do_parent_store(cr)
                    cr.commit()
                finally:
                    cr.close()

        registry.ready = True

        if update_module:
            # only in case of update, otherwise we'll have an infinite reload loop!
            cls.signal_registry_change(db_name)
        return registry

openerp.modules.load_modules

加载模块

#openerp.modules.load_modules(registry._db, force_demo, status, update_module)
def load_modules(db, force_demo=False, status=None, update_module=False):
    ...
        # STEP 1: LOAD BASE (must be done before module dependencies can be computed for later steps) 
        graph = openerp.modules.graph.Graph()
        graph.add_module(cr, 'base', force)
        ...
        loaded_modules, processed_modules = load_module_graph(cr, graph, status, perform_checks=update_module, report=report)        

        # STEP 2: Mark other modules to be loaded/updated
        ...
        # STEP 3: Load marked modules (skipping base which was done in STEP 1)
        # IMPORTANT: this is done in two parts, first loading all installed or
        #            partially installed modules (i.e. installed/to upgrade), to
        #            offer a consistent system to the second part: installing
        #            newly selected modules.
        #            We include the modules 'to remove' in the first step, because
        #            they are part of the "currently installed" modules. They will
        #            be dropped in STEP 6 later, before restarting the loading
        #            process.
        # IMPORTANT 2: We have to loop here until all relevant modules have been
        #              processed, because in some rare cases the dependencies have
        #              changed, and modules that depend on an uninstalled module
        #              will not be processed on the first pass.
        #              It's especially useful for migrations.
            ...
            if update_module:
                processed_modules += load_marked_modules(cr, graph,
                    ['to install'], force, status, report,
                    loaded_modules, update_module)          
            ...
        # STEP 4: Finish and cleanup installations
        ...
        # STEP 5: Cleanup menus 
        # Remove menu items that are not referenced by any of other
        # (child) menu item, ir_values, or ir_model_data.
        # TODO: This code could be a method of ir_ui_menu. Remove menu without actions of children
        ...
        # STEP 6: Uninstall modules to remove
        # Remove records referenced from ir_model_data for modules to be
        # removed (and removed the references from ir_model_data).
           ...

        # STEP 7: verify custom views on every model
        ...
        # STEP 8: call _register_hook on every model
        ...
        # STEP 9: Run the post-install tests
      ...

opernerp.modules.load_module_graph()

for index, package in enumerate(graph):
        module_name = package.name
        module_id = package.id
        ...
        load_openerp_module(package.name)
        new_install = package.installed_version is None
        if new_install:
            py_module = sys.modules['openerp.addons.%s' % (module_name,)]
            pre_init = package.info.get('pre_init_hook')
            if pre_init:
                getattr(py_module, pre_init)(cr)

        models = registry.load(cr, package)#加载模块
        loaded_modules.append(package.name)
        if hasattr(package, 'init') or hasattr(package, 'update') or package.state in ('to install', 'to upgrade'):
            registry.setup_models(cr, partial=True)
            init_module_models(cr, package.name, models)

        # Can't put this line out of the loop: ir.module.module will be
        # registered by init_module_models() above.
        modobj = registry['ir.module.module']

       ...

openerp.modules.load_openerp_module()

def load_openerp_module(module_name):
    global loaded
    if module_name in loaded:
        return

    initialize_sys_path()
    try:
        mod_path = get_module_path(module_name)
        __import__('openerp.addons.' + module_name)       
        info = load_information_from_description_file(module_name)
        if info['post_load']:
            getattr(sys.modules['openerp.addons.' + module_name], info['post_load'])()

    except Exception, e:
        msg = "Couldn't load module %s" % (module_name)
        _logger.critical(msg)
        _logger.critical(e)
        raise
    else:
        loaded.append(module_name)

openerp.modules.init_module_models()

def init_module_models(cr, module_name, obj_list):
    _logger.info('module %s: creating or updating database tables', module_name)
    todo = []
    for obj in obj_list:
        result = obj._auto_init(cr, {'module': module_name})
        ...
    cr.commit()

通过obj._auto_end(cr, {'module': module_name})初始化数据库表

 def _auto_init(self, cr, context=None):
    ...
        self._field_create(cr, context=context)#添加ir_model、ir_model_fields和ir_model_data数据项
        create = not self._table_exist(cr)
        ...
             self._create_table(cr)
                ...

openerp.modules.registry.load()

def load(self, cr, module):
        ...
        from .. import models

        models_to_load = [] # need to preserve loading order
        lazy_property.reset_all(self)
        for cls in models.MetaModel.module_to_models.get(module.name, []):
            model = cls._build_model(self, cr)#实例化模块
            if model._name not in models_to_load:               
                models_to_load.append(model._name)
        return [self.models[m] for m in models_to_load]

openrp.Models.build_model()

openerp.modules.registry将自身作为pool传递给_build_model

 @classmethod
    def _build_model(cls, pool, cr):       
        ...
        # instantiate the model, and initialize it
        model = object.__new__(cls)
        model.__init__(pool, cr)
        return model

你可能感兴趣的:(Odoo)