Daniel Reis
Daniel Reis

Reputation: 13342

Force python interpreter to reload a code module

The OpenERP python code development cycle is to edit your code, restart the server and test it. Restarting the server is necessary, because it's what makes your source code to be reloaded into memory, but it adds an annoying delay in your work pace.

Since python is such a dynamic language, I wonder if there is a way to force a running python interpreter (the app server ) to reload on the fly a code module, so that it can be tested without restarting the app server?

Update: Following down the reload path suggested by @ecatmur, I got the the code below, but it still doesnt work:

class module(osv.osv):
    _inherit = "ir.module.module"

    def action_reload(self, cr, uid, ids, context=None):
        for obj in self.browse(cr, uid, ids, context=context):
            modulename = 'openerp.addons.' + obj.name
            tmp = __import__(modulename)
            pycfile = tmp.__file__
            modulepath = string.replace(pycfile, ".pyc", ".py")
            code=open(modulepath, 'rU').read()
            compile(code, modulename, "exec")
            execfile(modulepath)
            reload( sys.modules[modulename] )
        openerp.modules.registry.RegistryManager.delete(cr.dbname)
        openerp.modules.registry.RegistryManager.new(cr.dbname)

Upvotes: 22

Views: 9138

Answers (5)

Don Kirkby
Don Kirkby

Reputation: 56620

UPDATE: since v8 the Odoo server provides an --auto-reload option to perform this.

Excellent question, I've often wondered the same. I think the main problem with the code you posted is that it only reloads the OpenERP module's __init__.py file, not all the individual files. The reimport module recommended by ecatmur takes care of that, and I also had to unregister the module's report parsers and model classes before reloading everything.

I've posted my module_reload module on Launchpad. It seems to work for changes to model classes, osv_memory wizards, and report parsers. It does not work for old-style wizards, and there may be other scenarios that don't work.

Here's the method that reloads the module.

def button_reload(self, cr, uid, ids, context=None):
    for module_record in self.browse(cr, uid, ids, context=context):
        #Remove any report parsers registered for this module.
        module_path = 'addons/' + module_record.name
        for service_name, service in Service._services.items():
            template = getattr(service, 'tmpl', '')
            if template.startswith(module_path):
                Service.remove(service_name)

        #Remove any model classes registered for this module
        MetaModel.module_to_models[module_record.name] = []                    

        #Reload all Python modules from the OpenERP module's directory.
        modulename = 'openerp.addons.' + module_record.name
        root = __import__(modulename)
        module = getattr(root.addons, module_record.name)

        reimport(module)
    RegistryManager.delete(cr.dbname)
    RegistryManager.new(cr.dbname)
    return {}

Upvotes: 9

asmeurer
asmeurer

Reputation: 91490

If you are just doing development, then reload is OK, but if you are in deployment, you should avoid such tricks, because they will never work 100% of the time. There will always be some subtleties, where the changes won't propagate. For example, if some code copies an object instead of just using references to it, then it will remain the same after reloading. Conversely, if a reference doesn't propagate forward correctly, then an is comparison will fail when it would be expected to work, because one of the objects will be from the "old" unreleased module. The only 100% sure fire method would be to reload everything, which is essentially what a server restart is.

Even if you are just doing development, you will occasionally run across false bugs that are just side effects of an incomplete reload. If it doesn't occur to you to try a clean restart, you can spend a long time trying to track the phantom bug down. So if you end up doing this, keep that in the back of your mind.

Upvotes: 1

rguillebert
rguillebert

Reputation: 286

ipython has a deepreload module, the documentation is here : http://ipython.org/ipython-doc/stable/api/generated/IPython.lib.deepreload.html#module-IPython.lib.deepreload

I think it's usable outside of the ipython REPL.

Upvotes: 1

gurney alex
gurney alex

Reputation: 13645

the reload built in function reloads a module or a package. In the context of OpenERP though, you need a bit more, since reloading an OpenERP addon requires processing the XML files etc. But I agree that having this in OpenERP would be nice.

Upvotes: 0

ecatmur
ecatmur

Reputation: 157344

The reload built-in function will reload a single module. There are various solutions to recursively reload updated packages; see How to re import an updated package while in Python Interpreter?

Part of the issue is that existing objects need to be adjusted to reference the new classes etc. from the reloaded modules; reimport does this reasonably well. In the IPython interactive console I use the autoreload extension, although it's not designed for use outside IPython.

Upvotes: 14

Related Questions