| 1 |
# -*- coding: utf-8 -*- |
|---|
| 2 |
|
|---|
| 3 |
"""Kid Import Hooks. |
|---|
| 4 |
|
|---|
| 5 |
When installed, these hooks allow importing .kid files as if they were |
|---|
| 6 |
Python modules. |
|---|
| 7 |
|
|---|
| 8 |
Notes: |
|---|
| 9 |
|
|---|
| 10 |
We use new import hooks instead of the old ihooks module, because ihooks is |
|---|
| 11 |
incompatible with Python eggs. We allow importing from one or more specified |
|---|
| 12 |
paths for Kid templates, or importing from sys.path. In the latter case, we |
|---|
| 13 |
use an importer on meta_path because importers on path_hook do not fall back |
|---|
| 14 |
to the built-in import in Python >= 2.5 (this worked in Python 2.3 and 2.4). |
|---|
| 15 |
|
|---|
| 16 |
""" |
|---|
| 17 |
|
|---|
| 18 |
__revision__ = "$Rev$" |
|---|
| 19 |
__date__ = "$Date$" |
|---|
| 20 |
__author__ = "Ryan Tomayko <rtomayko@gmail.com>; Christoph Zwerschke <cito@online.de>" |
|---|
| 21 |
__copyright__ = "Copyright 2004-2005, Ryan Tomayko; 2006 Christoph Zwerschke" |
|---|
| 22 |
__license__ = "MIT <http://www.opensource.org/licenses/mit-license.php>" |
|---|
| 23 |
|
|---|
| 24 |
import sys |
|---|
| 25 |
import time |
|---|
| 26 |
import new |
|---|
| 27 |
from os import environ, extsep, pathsep |
|---|
| 28 |
from os.path import exists, join as joinpath, isdir |
|---|
| 29 |
|
|---|
| 30 |
from kid import __version__ |
|---|
| 31 |
from kid.codewriter import raise_template_error |
|---|
| 32 |
from kid.compiler import KidFile, KID_EXT |
|---|
| 33 |
from kid.template_util import TemplateImportError |
|---|
| 34 |
|
|---|
| 35 |
__all__ = ['install', 'uninstall', 'import_template', 'get_template_name'] |
|---|
| 36 |
|
|---|
| 37 |
|
|---|
| 38 |
def install(ext=None, path=None): |
|---|
| 39 |
"""Install importer for Kid templates. |
|---|
| 40 |
|
|---|
| 41 |
ext can be one or more extensions as list or comma separated string |
|---|
| 42 |
path can be one or more paths as list or pathsep separated string |
|---|
| 43 |
|
|---|
| 44 |
""" |
|---|
| 45 |
if ext: |
|---|
| 46 |
if isinstance(ext, basestring): |
|---|
| 47 |
exts = ext.split(',') |
|---|
| 48 |
else: |
|---|
| 49 |
exts = list(ext) |
|---|
| 50 |
for ext in exts: |
|---|
| 51 |
if not ext.startswith(extsep): |
|---|
| 52 |
raise Exception, "Illegal exception: " + ext |
|---|
| 53 |
if KID_EXT in exts: |
|---|
| 54 |
exts.remove(KID_EXT) |
|---|
| 55 |
else: |
|---|
| 56 |
exts = [] |
|---|
| 57 |
exts.insert(0, KID_EXT) |
|---|
| 58 |
if path: # install path hook |
|---|
| 59 |
if isinstance(path, basestring): |
|---|
| 60 |
paths = path.split(pathsep) |
|---|
| 61 |
else: |
|---|
| 62 |
paths = list(path) |
|---|
| 63 |
# Install special Kid template paths, because since Python 2.5, |
|---|
| 64 |
# path hooks do not fall back to the built-in import any more. |
|---|
| 65 |
ext = ','.join(exts) |
|---|
| 66 |
kidpaths = [] |
|---|
| 67 |
syspath = sys.path |
|---|
| 68 |
for path in paths: |
|---|
| 69 |
kidpath = 'kid::%s::' % path |
|---|
| 70 |
syspath = [path for path in syspath |
|---|
| 71 |
if not path.startswith(kidpath)] |
|---|
| 72 |
kidpaths.append(kidpath + ext) |
|---|
| 73 |
sys.path = kidpaths + syspath |
|---|
| 74 |
if kidpaths: |
|---|
| 75 |
if not KidImporter in sys.path_hooks: |
|---|
| 76 |
sys.path_hooks.insert(0, KidImporter) |
|---|
| 77 |
else: # install meta hook for all sys paths |
|---|
| 78 |
for importer in sys.meta_path: |
|---|
| 79 |
if isinstance(importer, KidImporter): |
|---|
| 80 |
importer.exts = exts |
|---|
| 81 |
break |
|---|
| 82 |
else: |
|---|
| 83 |
importer = KidImporter(ext=exts) |
|---|
| 84 |
sys.meta_path.insert(0, importer) |
|---|
| 85 |
|
|---|
| 86 |
def uninstall(path=None): |
|---|
| 87 |
"""Uninstall importer for Kid templates. |
|---|
| 88 |
|
|---|
| 89 |
path can be one or more paths as list or pathsep separated string |
|---|
| 90 |
|
|---|
| 91 |
""" |
|---|
| 92 |
if path: # uninstall path hook |
|---|
| 93 |
if isinstance(path, basestring): |
|---|
| 94 |
paths = path.split(pathsep) |
|---|
| 95 |
else: |
|---|
| 96 |
paths = list(path) |
|---|
| 97 |
syspath = [] |
|---|
| 98 |
remove_hook = True |
|---|
| 99 |
for path in sys.path: |
|---|
| 100 |
p = path.split(':') |
|---|
| 101 |
if len(p) >= 5 and \ |
|---|
| 102 |
p[0] == 'kid' and not p[1] and not p[-2]: |
|---|
| 103 |
if ':'.join(p[2:-2]) in paths: |
|---|
| 104 |
continue |
|---|
| 105 |
remove_hook = False |
|---|
| 106 |
syspath.append(path) |
|---|
| 107 |
sys.path = syspath |
|---|
| 108 |
if remove_hook: |
|---|
| 109 |
if KidImporter in sys.path_hooks: |
|---|
| 110 |
sys.path_hooks = [hook for hook in sys.path_hooks |
|---|
| 111 |
if hook != KidImporter] |
|---|
| 112 |
sys.path_importer_cache.clear() |
|---|
| 113 |
else: # uninstall meta hook for all sys paths |
|---|
| 114 |
sys.meta_path = [importer for importer in sys.meta_path |
|---|
| 115 |
if not isinstance(importer, KidImporter)] |
|---|
| 116 |
|
|---|
| 117 |
def import_template(name, filename, force=False): |
|---|
| 118 |
if not force and name and sys.modules.has_key(name): |
|---|
| 119 |
return sys.modules[name] |
|---|
| 120 |
template = KidFile(filename) |
|---|
| 121 |
code = template.compile(dump_source=environ.get('KID_OUTPUT_PY')) |
|---|
| 122 |
module = _create_module(code, name, filename) |
|---|
| 123 |
return module |
|---|
| 124 |
|
|---|
| 125 |
def get_template_name(name, filename): |
|---|
| 126 |
if name: |
|---|
| 127 |
return name |
|---|
| 128 |
else: |
|---|
| 129 |
return 'kid.util.template_%x' % (hash(filename) + sys.maxint + 1) |
|---|
| 130 |
|
|---|
| 131 |
def _create_module(code, name, filename, |
|---|
| 132 |
store=True, ns={}, exec_module=None): |
|---|
| 133 |
for recompiled in range(2): |
|---|
| 134 |
name = get_template_name(name, filename) |
|---|
| 135 |
mod = new.module(name) |
|---|
| 136 |
mod.__file__ = filename |
|---|
| 137 |
mod.__ctime__ = time.time() |
|---|
| 138 |
mod.__dict__.update(ns) |
|---|
| 139 |
try: |
|---|
| 140 |
if exec_module: |
|---|
| 141 |
exec_module(mod, code) |
|---|
| 142 |
else: |
|---|
| 143 |
exec code in mod.__dict__ |
|---|
| 144 |
except Exception: |
|---|
| 145 |
if store: |
|---|
| 146 |
sys.modules[name] = mod |
|---|
| 147 |
raise_template_error(module=name, filename=filename) |
|---|
| 148 |
if getattr(mod, 'kid_version', None) == __version__: |
|---|
| 149 |
break |
|---|
| 150 |
# the module has been compiled against an old Kid version, |
|---|
| 151 |
# recompile to ensure compatibility and best performance |
|---|
| 152 |
if recompiled: # already tried recompiling, to no avail |
|---|
| 153 |
raise TemplateImportError('Cannot recompile template file' |
|---|
| 154 |
' %r for Kid version %s' % (filename, __version__)) |
|---|
| 155 |
template = KidFile(filename) |
|---|
| 156 |
template.stale = True |
|---|
| 157 |
template._python = template._code = None |
|---|
| 158 |
code = template.compile(dump_source=environ.get('KID_OUTPUT_PY')) |
|---|
| 159 |
if store: |
|---|
| 160 |
sys.modules[name] = mod |
|---|
| 161 |
return mod |
|---|
| 162 |
|
|---|
| 163 |
|
|---|
| 164 |
class KidImporter(object): |
|---|
| 165 |
"""Importer for Kid templates via sys.path_hooks or sys.meta_path.""" |
|---|
| 166 |
|
|---|
| 167 |
def __init__(self, path=None, ext=None): |
|---|
| 168 |
if path: # initialized via sys.path_hooks |
|---|
| 169 |
# check for special path format: |
|---|
| 170 |
# path = kid::/path/to/templates::.ext1,.ext2 |
|---|
| 171 |
p = path.split(':') |
|---|
| 172 |
if len(p) >= 5 and \ |
|---|
| 173 |
p[0] == 'kid' and not p[1] and not p[-2]: |
|---|
| 174 |
path = ':'.join(p[2:-2]) |
|---|
| 175 |
exts = p[-1].split(',') |
|---|
| 176 |
if exts: |
|---|
| 177 |
for ext in exts: |
|---|
| 178 |
if not ext.startswith(extsep): |
|---|
| 179 |
break |
|---|
| 180 |
else: |
|---|
| 181 |
if isdir(path): |
|---|
| 182 |
self.path = path |
|---|
| 183 |
self.exts = exts |
|---|
| 184 |
return |
|---|
| 185 |
raise ImportError |
|---|
| 186 |
else: # initialize for use via sys.meta_path |
|---|
| 187 |
if ext: |
|---|
| 188 |
if isinstance(ext, basestring): |
|---|
| 189 |
exts = ext.split(',') |
|---|
| 190 |
else: |
|---|
| 191 |
exts = list(ext) |
|---|
| 192 |
for ext in exts: |
|---|
| 193 |
if not ext.startswith(extsep): |
|---|
| 194 |
raise ImportError |
|---|
| 195 |
else: |
|---|
| 196 |
raise ImportError |
|---|
| 197 |
self.path = None |
|---|
| 198 |
self.exts = exts |
|---|
| 199 |
|
|---|
| 200 |
def find_module(self, fullname, path=None): |
|---|
| 201 |
name = fullname.split('.')[-1] |
|---|
| 202 |
if self.path: |
|---|
| 203 |
if path: |
|---|
| 204 |
raise ImportError |
|---|
| 205 |
else: |
|---|
| 206 |
paths = [self.path] |
|---|
| 207 |
else: |
|---|
| 208 |
paths = sys.path |
|---|
| 209 |
if path: |
|---|
| 210 |
paths = path + paths |
|---|
| 211 |
for path in paths: |
|---|
| 212 |
if isdir(path): |
|---|
| 213 |
path = joinpath(path, name) |
|---|
| 214 |
for ext in self.exts: |
|---|
| 215 |
if exists(path + ext): |
|---|
| 216 |
self.filename = path + ext |
|---|
| 217 |
return self |
|---|
| 218 |
return None |
|---|
| 219 |
|
|---|
| 220 |
def load_module(self, fullname): |
|---|
| 221 |
return import_template(fullname, self.filename, force=True) |
|---|