root/trunk/kid/importer.py

Revision 503, 7.5 kB (checked in by cito, 1 year ago)

Improved serialization of unicode characters as decimal character references or character entity references with symbolic names. Renamed escape_map to code_names, now only available via Format. Corrected author email addresses.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Rev
Line 
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)
Note: See TracBrowser for help on using the browser.