"""quixote.imphooks

Import hooks; when installed, these hooks allow importing .ptl files
as if they were Python modules.

TODO:

    - cache compiled code in .pyc files
    - change priority of .ptl to be higher than .pyc
    - use imputil instead of ihooks?

Note: there's some unpleasant incompatibility between ZODB's import
trickery and the import hooks here.  Bottom line: if you're using ZODB,
import it *before* installing the Quixote/PTL import hooks.
"""

__revision__ = "$Id: ptl_import.py,v 1.21 2001/08/29 21:43:37 nascheme Exp $"


import sys
import os.path
import imp, ihooks, new, string
import cgi, urllib
import struct
import marshal
import stat

from ptl_compile import compile_template, PTL_EXT, PTLC_EXT, PTLC_MAGIC

assert sys.hexversion >= 0x20000b1, "need Python 2.0b1 or later"

def _new_module(code, name, filename):
    new_mod = new.module(name)
    new_mod.__file__ = filename
    new_mod.__name__ = name
    sys.modules[name] = new_mod

    exec code in new_mod.__dict__

    return new_mod

def _load_ptlc(name, filename, file=None):
    if not file:
        try:
            file = open(filename, "rb")
        except IOError:
            return None
    path, ext = os.path.splitext(filename)
    ptl_filename = path + PTL_EXT
    magic = file.read(len(PTLC_MAGIC))
    if magic != PTLC_MAGIC:
        return _load_ptl(name, ptl_filename)
    ptlc_mtime = marshal.load(file)
    try:
        mtime = os.stat(ptl_filename)[stat.ST_MTIME]
    except OSError:
        mtime = ptlc_mtime
    if mtime != ptlc_mtime:
        return _load_ptl(name, ptl_filename)
    code = marshal.load(file)
    return _new_module(code, name, filename)
    
def _load_ptl(name, filename, file=None):
    if not file:
        try:
            file = open(filename, "rb")
        except IOError:
            return None
    path, ext = os.path.splitext(filename)
    ptlc_filename = path + PTLC_EXT
    try:
        output = open(ptlc_filename, "wb")
    except IOError, msg:
        output = None
    try:
        code = compile_template(file, filename, output)
    except:
        # don't leave a corrupt .ptlc file around
        if output:
            output.close()
            os.unlink(ptlc_filename)
        raise
    return _new_module(code, name, filename)


# Constant used to signal a PTL files
PTLC_FILE = 128
PTL_FILE = 129

class PTLHooks (ihooks.Hooks):

    def get_suffixes (self):
        # add our suffixes
        L = imp.get_suffixes()
        return L + [(PTLC_EXT, 'rb', PTLC_FILE), (PTL_EXT, 'r', PTL_FILE)]

class PTLLoader (ihooks.ModuleLoader):
    
    def load_module (self, name, stuff):
        file, filename, info = stuff
        (suff, mode, type) = info

        # If it's a PTL file, load it specially.
        if type == PTLC_FILE:
            return _load_ptlc(name, filename, file)

        elif type == PTL_FILE:
            return _load_ptl(name, filename, file)

        else:
            # Otherwise, use the default handler for loading
            return ihooks.ModuleLoader.load_module( self, name, stuff)


_installed = 0

def install ():
    global _installed
    if not _installed:
        hooks = PTLHooks()
        loader = PTLLoader(hooks)
        ihooks.install(ihooks.ModuleImporter(loader))
        _installed = 1

if __name__ == '__main__':
    import foo
    print repr(foo.f1(1,2,3))
