durusmail: quixote-users: Re: rtemplate: utility for reloading changed PTL files
rtemplate: utility for reloading changed PTL files
2003-12-06
Re: rtemplate: utility for reloading changed PTL files
2003-12-06
2003-12-06
2003-12-06
2003-12-06
Re: rtemplate: utility for reloading changed PTL files
2003-12-07
Re: rtemplate: utility for reloading changed PTL files
2003-12-07
Re: rtemplate: utility for reloading changed PTL files
2003-12-08
2003-12-07
2003-12-08
2003-12-06
Re: rtemplate: utility for reloading changed PTL files
2003-12-08
Re: rtemplate: utility for reloading changed PTLfiles
2003-12-08
2003-12-08
Re: rtemplate: utility for reloading changed PTL files
Graham Fawcett
2003-12-08
Skip Montanaro wrote:
>     >> I would rename the module to quixote.reloadable.  (A module that
>     >> provides one primary class should be named after that class.)
>     >> (Although arguably the class could be better named: Reloader?
>     >> ReloadProxy?)
>
>     Graham> Yes, I'm not sure either. Originally it was ReloadableTemplate
>     Graham> (hence the module name, rtemplate), but that's not really
>     Graham> accurate either. I like Reloader, but perhaps it should be
>     Graham> something more specific to modules?
>
> How about ReloadableModule (or ModuleReloader or ModuleAutoloader)?  That's
> what it is, after all.

I think I like ReloadableModule. I guess quixote.reloadable_module would be the
most accurate name for the module, though it's a bit long. If Neil et. al. want
to include it in Quixote, they are welcome to modify the names as they please.

Attached is a renamed version with Greg's suggested doc changes. I also munged
some of the attribute/method names, to reduce the chances that names in the
proxy might be shadowing names in the target module.

> Are there other objects besides modules for which it
> can serve as a proxy?

As written, I can't think of any; it expects a source file, and compiles it;
that pretty much eliminates anything but modules.

Regards,

-- Graham
"""
reloadable_module
support for reloading changed PTL files (to aid debugging)

Graham Fawcett 
2003.12.06 -- first revision.
2003.12.07 -- added warnings to documentation, adjusted attribute
              names to reduce possible namespace collisions
"""

from quixote.ptl_compile import compile_template
import os
import sys
import time


class ReloadableModule:
    """
    A module proxy that reloads itself when its source file changes.

    The constructor takes an already-loaded PTL module, and acts as a proxy
    for that module. If the module's source changes, this class will
    recompile the PTL file and replace its copy of the original module with
    the modified one.

    Uses Quixote's ptl_compile module, which requires the Python compiler
    package. It seems to work for normal Python modules as well as PTL
    modules.

    This class is intended only as a debugging aid. Reloading modules can
    occasionally lead to unexpected behaviour, and is not guaranteed to
    work with all possible modules.

    Example usage::

        # ---- main.ptl ----
        from quixote.reloadable_module import ReloadableModule

        import buggy                        # load the module once
        buggy = ReloadableModule(buggy)     # wrap it in a reloader

        _q_exports = ['buggy', ... ]
    """

    def __init__(self, module):
        self._rm_filename = module.__file__
        self._rm_name = module.__name__
        self._rm_module = module.__dict__
        self._rm_mtime = self._rm_get_mtime()

    def _rm_get_mtime(self):
        mt = os.stat(self._rm_filename).st_mtime
        return mt

    def __getattr__(self, name):
        mtime = self._rm_get_mtime()
        if mtime > self._rm_mtime:
            self._rm_mtime = mtime
            self._rm_reload()
        try:
            return self._rm_module[name]
        except KeyError, msg:
            raise AttributeError, msg

    def _rm_reload(self):
        print >> sys.stderr, '%s: Reloading %s' % (time.ctime(), self._rm_name)
        f = open(self._rm_filename)
        try:
            codeobj = compile_template(f, self._rm_filename)
            m = {}
            m['__name__'] = self._rm_name
            m['__file__'] = self._rm_filename
            exec codeobj in m
        finally:
            f.close()
        self._rm_module = m

reply