Mercurial > repos > bcclaywell > argo_navis
comparison venv/lib/python2.7/site-packages/jinja2/loaders.py @ 0:d67268158946 draft
planemo upload commit a3f181f5f126803c654b3a66dd4e83a48f7e203b
| author | bcclaywell |
|---|---|
| date | Mon, 12 Oct 2015 17:43:33 -0400 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:d67268158946 |
|---|---|
| 1 # -*- coding: utf-8 -*- | |
| 2 """ | |
| 3 jinja2.loaders | |
| 4 ~~~~~~~~~~~~~~ | |
| 5 | |
| 6 Jinja loader classes. | |
| 7 | |
| 8 :copyright: (c) 2010 by the Jinja Team. | |
| 9 :license: BSD, see LICENSE for more details. | |
| 10 """ | |
| 11 import os | |
| 12 import sys | |
| 13 import weakref | |
| 14 from types import ModuleType | |
| 15 from os import path | |
| 16 from hashlib import sha1 | |
| 17 from jinja2.exceptions import TemplateNotFound | |
| 18 from jinja2.utils import open_if_exists, internalcode | |
| 19 from jinja2._compat import string_types, iteritems | |
| 20 | |
| 21 | |
| 22 def split_template_path(template): | |
| 23 """Split a path into segments and perform a sanity check. If it detects | |
| 24 '..' in the path it will raise a `TemplateNotFound` error. | |
| 25 """ | |
| 26 pieces = [] | |
| 27 for piece in template.split('/'): | |
| 28 if path.sep in piece \ | |
| 29 or (path.altsep and path.altsep in piece) or \ | |
| 30 piece == path.pardir: | |
| 31 raise TemplateNotFound(template) | |
| 32 elif piece and piece != '.': | |
| 33 pieces.append(piece) | |
| 34 return pieces | |
| 35 | |
| 36 | |
| 37 class BaseLoader(object): | |
| 38 """Baseclass for all loaders. Subclass this and override `get_source` to | |
| 39 implement a custom loading mechanism. The environment provides a | |
| 40 `get_template` method that calls the loader's `load` method to get the | |
| 41 :class:`Template` object. | |
| 42 | |
| 43 A very basic example for a loader that looks up templates on the file | |
| 44 system could look like this:: | |
| 45 | |
| 46 from jinja2 import BaseLoader, TemplateNotFound | |
| 47 from os.path import join, exists, getmtime | |
| 48 | |
| 49 class MyLoader(BaseLoader): | |
| 50 | |
| 51 def __init__(self, path): | |
| 52 self.path = path | |
| 53 | |
| 54 def get_source(self, environment, template): | |
| 55 path = join(self.path, template) | |
| 56 if not exists(path): | |
| 57 raise TemplateNotFound(template) | |
| 58 mtime = getmtime(path) | |
| 59 with file(path) as f: | |
| 60 source = f.read().decode('utf-8') | |
| 61 return source, path, lambda: mtime == getmtime(path) | |
| 62 """ | |
| 63 | |
| 64 #: if set to `False` it indicates that the loader cannot provide access | |
| 65 #: to the source of templates. | |
| 66 #: | |
| 67 #: .. versionadded:: 2.4 | |
| 68 has_source_access = True | |
| 69 | |
| 70 def get_source(self, environment, template): | |
| 71 """Get the template source, filename and reload helper for a template. | |
| 72 It's passed the environment and template name and has to return a | |
| 73 tuple in the form ``(source, filename, uptodate)`` or raise a | |
| 74 `TemplateNotFound` error if it can't locate the template. | |
| 75 | |
| 76 The source part of the returned tuple must be the source of the | |
| 77 template as unicode string or a ASCII bytestring. The filename should | |
| 78 be the name of the file on the filesystem if it was loaded from there, | |
| 79 otherwise `None`. The filename is used by python for the tracebacks | |
| 80 if no loader extension is used. | |
| 81 | |
| 82 The last item in the tuple is the `uptodate` function. If auto | |
| 83 reloading is enabled it's always called to check if the template | |
| 84 changed. No arguments are passed so the function must store the | |
| 85 old state somewhere (for example in a closure). If it returns `False` | |
| 86 the template will be reloaded. | |
| 87 """ | |
| 88 if not self.has_source_access: | |
| 89 raise RuntimeError('%s cannot provide access to the source' % | |
| 90 self.__class__.__name__) | |
| 91 raise TemplateNotFound(template) | |
| 92 | |
| 93 def list_templates(self): | |
| 94 """Iterates over all templates. If the loader does not support that | |
| 95 it should raise a :exc:`TypeError` which is the default behavior. | |
| 96 """ | |
| 97 raise TypeError('this loader cannot iterate over all templates') | |
| 98 | |
| 99 @internalcode | |
| 100 def load(self, environment, name, globals=None): | |
| 101 """Loads a template. This method looks up the template in the cache | |
| 102 or loads one by calling :meth:`get_source`. Subclasses should not | |
| 103 override this method as loaders working on collections of other | |
| 104 loaders (such as :class:`PrefixLoader` or :class:`ChoiceLoader`) | |
| 105 will not call this method but `get_source` directly. | |
| 106 """ | |
| 107 code = None | |
| 108 if globals is None: | |
| 109 globals = {} | |
| 110 | |
| 111 # first we try to get the source for this template together | |
| 112 # with the filename and the uptodate function. | |
| 113 source, filename, uptodate = self.get_source(environment, name) | |
| 114 | |
| 115 # try to load the code from the bytecode cache if there is a | |
| 116 # bytecode cache configured. | |
| 117 bcc = environment.bytecode_cache | |
| 118 if bcc is not None: | |
| 119 bucket = bcc.get_bucket(environment, name, filename, source) | |
| 120 code = bucket.code | |
| 121 | |
| 122 # if we don't have code so far (not cached, no longer up to | |
| 123 # date) etc. we compile the template | |
| 124 if code is None: | |
| 125 code = environment.compile(source, name, filename) | |
| 126 | |
| 127 # if the bytecode cache is available and the bucket doesn't | |
| 128 # have a code so far, we give the bucket the new code and put | |
| 129 # it back to the bytecode cache. | |
| 130 if bcc is not None and bucket.code is None: | |
| 131 bucket.code = code | |
| 132 bcc.set_bucket(bucket) | |
| 133 | |
| 134 return environment.template_class.from_code(environment, code, | |
| 135 globals, uptodate) | |
| 136 | |
| 137 | |
| 138 class FileSystemLoader(BaseLoader): | |
| 139 """Loads templates from the file system. This loader can find templates | |
| 140 in folders on the file system and is the preferred way to load them. | |
| 141 | |
| 142 The loader takes the path to the templates as string, or if multiple | |
| 143 locations are wanted a list of them which is then looked up in the | |
| 144 given order:: | |
| 145 | |
| 146 >>> loader = FileSystemLoader('/path/to/templates') | |
| 147 >>> loader = FileSystemLoader(['/path/to/templates', '/other/path']) | |
| 148 | |
| 149 Per default the template encoding is ``'utf-8'`` which can be changed | |
| 150 by setting the `encoding` parameter to something else. | |
| 151 | |
| 152 To follow symbolic links, set the *followlinks* parameter to ``True``:: | |
| 153 | |
| 154 >>> loader = FileSystemLoader('/path/to/templates', followlinks=True) | |
| 155 | |
| 156 .. versionchanged:: 2.8+ | |
| 157 The *followlinks* parameter was added. | |
| 158 """ | |
| 159 | |
| 160 def __init__(self, searchpath, encoding='utf-8', followlinks=False): | |
| 161 if isinstance(searchpath, string_types): | |
| 162 searchpath = [searchpath] | |
| 163 self.searchpath = list(searchpath) | |
| 164 self.encoding = encoding | |
| 165 self.followlinks = followlinks | |
| 166 | |
| 167 def get_source(self, environment, template): | |
| 168 pieces = split_template_path(template) | |
| 169 for searchpath in self.searchpath: | |
| 170 filename = path.join(searchpath, *pieces) | |
| 171 f = open_if_exists(filename) | |
| 172 if f is None: | |
| 173 continue | |
| 174 try: | |
| 175 contents = f.read().decode(self.encoding) | |
| 176 finally: | |
| 177 f.close() | |
| 178 | |
| 179 mtime = path.getmtime(filename) | |
| 180 | |
| 181 def uptodate(): | |
| 182 try: | |
| 183 return path.getmtime(filename) == mtime | |
| 184 except OSError: | |
| 185 return False | |
| 186 return contents, filename, uptodate | |
| 187 raise TemplateNotFound(template) | |
| 188 | |
| 189 def list_templates(self): | |
| 190 found = set() | |
| 191 for searchpath in self.searchpath: | |
| 192 walk_dir = os.walk(searchpath, followlinks=self.followlinks) | |
| 193 for dirpath, dirnames, filenames in walk_dir: | |
| 194 for filename in filenames: | |
| 195 template = os.path.join(dirpath, filename) \ | |
| 196 [len(searchpath):].strip(os.path.sep) \ | |
| 197 .replace(os.path.sep, '/') | |
| 198 if template[:2] == './': | |
| 199 template = template[2:] | |
| 200 if template not in found: | |
| 201 found.add(template) | |
| 202 return sorted(found) | |
| 203 | |
| 204 | |
| 205 class PackageLoader(BaseLoader): | |
| 206 """Load templates from python eggs or packages. It is constructed with | |
| 207 the name of the python package and the path to the templates in that | |
| 208 package:: | |
| 209 | |
| 210 loader = PackageLoader('mypackage', 'views') | |
| 211 | |
| 212 If the package path is not given, ``'templates'`` is assumed. | |
| 213 | |
| 214 Per default the template encoding is ``'utf-8'`` which can be changed | |
| 215 by setting the `encoding` parameter to something else. Due to the nature | |
| 216 of eggs it's only possible to reload templates if the package was loaded | |
| 217 from the file system and not a zip file. | |
| 218 """ | |
| 219 | |
| 220 def __init__(self, package_name, package_path='templates', | |
| 221 encoding='utf-8'): | |
| 222 from pkg_resources import DefaultProvider, ResourceManager, \ | |
| 223 get_provider | |
| 224 provider = get_provider(package_name) | |
| 225 self.encoding = encoding | |
| 226 self.manager = ResourceManager() | |
| 227 self.filesystem_bound = isinstance(provider, DefaultProvider) | |
| 228 self.provider = provider | |
| 229 self.package_path = package_path | |
| 230 | |
| 231 def get_source(self, environment, template): | |
| 232 pieces = split_template_path(template) | |
| 233 p = '/'.join((self.package_path,) + tuple(pieces)) | |
| 234 if not self.provider.has_resource(p): | |
| 235 raise TemplateNotFound(template) | |
| 236 | |
| 237 filename = uptodate = None | |
| 238 if self.filesystem_bound: | |
| 239 filename = self.provider.get_resource_filename(self.manager, p) | |
| 240 mtime = path.getmtime(filename) | |
| 241 def uptodate(): | |
| 242 try: | |
| 243 return path.getmtime(filename) == mtime | |
| 244 except OSError: | |
| 245 return False | |
| 246 | |
| 247 source = self.provider.get_resource_string(self.manager, p) | |
| 248 return source.decode(self.encoding), filename, uptodate | |
| 249 | |
| 250 def list_templates(self): | |
| 251 path = self.package_path | |
| 252 if path[:2] == './': | |
| 253 path = path[2:] | |
| 254 elif path == '.': | |
| 255 path = '' | |
| 256 offset = len(path) | |
| 257 results = [] | |
| 258 def _walk(path): | |
| 259 for filename in self.provider.resource_listdir(path): | |
| 260 fullname = path + '/' + filename | |
| 261 if self.provider.resource_isdir(fullname): | |
| 262 _walk(fullname) | |
| 263 else: | |
| 264 results.append(fullname[offset:].lstrip('/')) | |
| 265 _walk(path) | |
| 266 results.sort() | |
| 267 return results | |
| 268 | |
| 269 | |
| 270 class DictLoader(BaseLoader): | |
| 271 """Loads a template from a python dict. It's passed a dict of unicode | |
| 272 strings bound to template names. This loader is useful for unittesting: | |
| 273 | |
| 274 >>> loader = DictLoader({'index.html': 'source here'}) | |
| 275 | |
| 276 Because auto reloading is rarely useful this is disabled per default. | |
| 277 """ | |
| 278 | |
| 279 def __init__(self, mapping): | |
| 280 self.mapping = mapping | |
| 281 | |
| 282 def get_source(self, environment, template): | |
| 283 if template in self.mapping: | |
| 284 source = self.mapping[template] | |
| 285 return source, None, lambda: source == self.mapping.get(template) | |
| 286 raise TemplateNotFound(template) | |
| 287 | |
| 288 def list_templates(self): | |
| 289 return sorted(self.mapping) | |
| 290 | |
| 291 | |
| 292 class FunctionLoader(BaseLoader): | |
| 293 """A loader that is passed a function which does the loading. The | |
| 294 function receives the name of the template and has to return either | |
| 295 an unicode string with the template source, a tuple in the form ``(source, | |
| 296 filename, uptodatefunc)`` or `None` if the template does not exist. | |
| 297 | |
| 298 >>> def load_template(name): | |
| 299 ... if name == 'index.html': | |
| 300 ... return '...' | |
| 301 ... | |
| 302 >>> loader = FunctionLoader(load_template) | |
| 303 | |
| 304 The `uptodatefunc` is a function that is called if autoreload is enabled | |
| 305 and has to return `True` if the template is still up to date. For more | |
| 306 details have a look at :meth:`BaseLoader.get_source` which has the same | |
| 307 return value. | |
| 308 """ | |
| 309 | |
| 310 def __init__(self, load_func): | |
| 311 self.load_func = load_func | |
| 312 | |
| 313 def get_source(self, environment, template): | |
| 314 rv = self.load_func(template) | |
| 315 if rv is None: | |
| 316 raise TemplateNotFound(template) | |
| 317 elif isinstance(rv, string_types): | |
| 318 return rv, None, None | |
| 319 return rv | |
| 320 | |
| 321 | |
| 322 class PrefixLoader(BaseLoader): | |
| 323 """A loader that is passed a dict of loaders where each loader is bound | |
| 324 to a prefix. The prefix is delimited from the template by a slash per | |
| 325 default, which can be changed by setting the `delimiter` argument to | |
| 326 something else:: | |
| 327 | |
| 328 loader = PrefixLoader({ | |
| 329 'app1': PackageLoader('mypackage.app1'), | |
| 330 'app2': PackageLoader('mypackage.app2') | |
| 331 }) | |
| 332 | |
| 333 By loading ``'app1/index.html'`` the file from the app1 package is loaded, | |
| 334 by loading ``'app2/index.html'`` the file from the second. | |
| 335 """ | |
| 336 | |
| 337 def __init__(self, mapping, delimiter='/'): | |
| 338 self.mapping = mapping | |
| 339 self.delimiter = delimiter | |
| 340 | |
| 341 def get_loader(self, template): | |
| 342 try: | |
| 343 prefix, name = template.split(self.delimiter, 1) | |
| 344 loader = self.mapping[prefix] | |
| 345 except (ValueError, KeyError): | |
| 346 raise TemplateNotFound(template) | |
| 347 return loader, name | |
| 348 | |
| 349 def get_source(self, environment, template): | |
| 350 loader, name = self.get_loader(template) | |
| 351 try: | |
| 352 return loader.get_source(environment, name) | |
| 353 except TemplateNotFound: | |
| 354 # re-raise the exception with the correct fileame here. | |
| 355 # (the one that includes the prefix) | |
| 356 raise TemplateNotFound(template) | |
| 357 | |
| 358 @internalcode | |
| 359 def load(self, environment, name, globals=None): | |
| 360 loader, local_name = self.get_loader(name) | |
| 361 try: | |
| 362 return loader.load(environment, local_name, globals) | |
| 363 except TemplateNotFound: | |
| 364 # re-raise the exception with the correct fileame here. | |
| 365 # (the one that includes the prefix) | |
| 366 raise TemplateNotFound(name) | |
| 367 | |
| 368 def list_templates(self): | |
| 369 result = [] | |
| 370 for prefix, loader in iteritems(self.mapping): | |
| 371 for template in loader.list_templates(): | |
| 372 result.append(prefix + self.delimiter + template) | |
| 373 return result | |
| 374 | |
| 375 | |
| 376 class ChoiceLoader(BaseLoader): | |
| 377 """This loader works like the `PrefixLoader` just that no prefix is | |
| 378 specified. If a template could not be found by one loader the next one | |
| 379 is tried. | |
| 380 | |
| 381 >>> loader = ChoiceLoader([ | |
| 382 ... FileSystemLoader('/path/to/user/templates'), | |
| 383 ... FileSystemLoader('/path/to/system/templates') | |
| 384 ... ]) | |
| 385 | |
| 386 This is useful if you want to allow users to override builtin templates | |
| 387 from a different location. | |
| 388 """ | |
| 389 | |
| 390 def __init__(self, loaders): | |
| 391 self.loaders = loaders | |
| 392 | |
| 393 def get_source(self, environment, template): | |
| 394 for loader in self.loaders: | |
| 395 try: | |
| 396 return loader.get_source(environment, template) | |
| 397 except TemplateNotFound: | |
| 398 pass | |
| 399 raise TemplateNotFound(template) | |
| 400 | |
| 401 @internalcode | |
| 402 def load(self, environment, name, globals=None): | |
| 403 for loader in self.loaders: | |
| 404 try: | |
| 405 return loader.load(environment, name, globals) | |
| 406 except TemplateNotFound: | |
| 407 pass | |
| 408 raise TemplateNotFound(name) | |
| 409 | |
| 410 def list_templates(self): | |
| 411 found = set() | |
| 412 for loader in self.loaders: | |
| 413 found.update(loader.list_templates()) | |
| 414 return sorted(found) | |
| 415 | |
| 416 | |
| 417 class _TemplateModule(ModuleType): | |
| 418 """Like a normal module but with support for weak references""" | |
| 419 | |
| 420 | |
| 421 class ModuleLoader(BaseLoader): | |
| 422 """This loader loads templates from precompiled templates. | |
| 423 | |
| 424 Example usage: | |
| 425 | |
| 426 >>> loader = ChoiceLoader([ | |
| 427 ... ModuleLoader('/path/to/compiled/templates'), | |
| 428 ... FileSystemLoader('/path/to/templates') | |
| 429 ... ]) | |
| 430 | |
| 431 Templates can be precompiled with :meth:`Environment.compile_templates`. | |
| 432 """ | |
| 433 | |
| 434 has_source_access = False | |
| 435 | |
| 436 def __init__(self, path): | |
| 437 package_name = '_jinja2_module_templates_%x' % id(self) | |
| 438 | |
| 439 # create a fake module that looks for the templates in the | |
| 440 # path given. | |
| 441 mod = _TemplateModule(package_name) | |
| 442 if isinstance(path, string_types): | |
| 443 path = [path] | |
| 444 else: | |
| 445 path = list(path) | |
| 446 mod.__path__ = path | |
| 447 | |
| 448 sys.modules[package_name] = weakref.proxy(mod, | |
| 449 lambda x: sys.modules.pop(package_name, None)) | |
| 450 | |
| 451 # the only strong reference, the sys.modules entry is weak | |
| 452 # so that the garbage collector can remove it once the | |
| 453 # loader that created it goes out of business. | |
| 454 self.module = mod | |
| 455 self.package_name = package_name | |
| 456 | |
| 457 @staticmethod | |
| 458 def get_template_key(name): | |
| 459 return 'tmpl_' + sha1(name.encode('utf-8')).hexdigest() | |
| 460 | |
| 461 @staticmethod | |
| 462 def get_module_filename(name): | |
| 463 return ModuleLoader.get_template_key(name) + '.py' | |
| 464 | |
| 465 @internalcode | |
| 466 def load(self, environment, name, globals=None): | |
| 467 key = self.get_template_key(name) | |
| 468 module = '%s.%s' % (self.package_name, key) | |
| 469 mod = getattr(self.module, module, None) | |
| 470 if mod is None: | |
| 471 try: | |
| 472 mod = __import__(module, None, None, ['root']) | |
| 473 except ImportError: | |
| 474 raise TemplateNotFound(name) | |
| 475 | |
| 476 # remove the entry from sys.modules, we only want the attribute | |
| 477 # on the module object we have stored on the loader. | |
| 478 sys.modules.pop(module, None) | |
| 479 | |
| 480 return environment.template_class.from_module_dict( | |
| 481 environment, mod.__dict__, globals) |
