| OLD | NEW |
| 1 # -*- coding: utf-8 -*- | 1 # -*- coding: utf-8 -*- |
| 2 """ | 2 """ |
| 3 jinja2.loaders | 3 jinja2.loaders |
| 4 ~~~~~~~~~~~~~~ | 4 ~~~~~~~~~~~~~~ |
| 5 | 5 |
| 6 Jinja loader classes. | 6 Jinja loader classes. |
| 7 | 7 |
| 8 :copyright: (c) 2010 by the Jinja Team. | 8 :copyright: (c) 2010 by the Jinja Team. |
| 9 :license: BSD, see LICENSE for more details. | 9 :license: BSD, see LICENSE for more details. |
| 10 """ | 10 """ |
| 11 import os | 11 import os |
| 12 import sys | 12 import sys |
| 13 import weakref | 13 import weakref |
| 14 from types import ModuleType | 14 from types import ModuleType |
| 15 from os import path | 15 from os import path |
| 16 try: | 16 from hashlib import sha1 |
| 17 from hashlib import sha1 | |
| 18 except ImportError: | |
| 19 from sha import new as sha1 | |
| 20 from jinja2.exceptions import TemplateNotFound | 17 from jinja2.exceptions import TemplateNotFound |
| 21 from jinja2.utils import LRUCache, open_if_exists, internalcode | 18 from jinja2.utils import open_if_exists, internalcode |
| 19 from jinja2._compat import string_types, iteritems |
| 22 | 20 |
| 23 | 21 |
| 24 def split_template_path(template): | 22 def split_template_path(template): |
| 25 """Split a path into segments and perform a sanity check. If it detects | 23 """Split a path into segments and perform a sanity check. If it detects |
| 26 '..' in the path it will raise a `TemplateNotFound` error. | 24 '..' in the path it will raise a `TemplateNotFound` error. |
| 27 """ | 25 """ |
| 28 pieces = [] | 26 pieces = [] |
| 29 for piece in template.split('/'): | 27 for piece in template.split('/'): |
| 30 if path.sep in piece \ | 28 if path.sep in piece \ |
| 31 or (path.altsep and path.altsep in piece) or \ | 29 or (path.altsep and path.altsep in piece) or \ |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 146 given order: | 144 given order: |
| 147 | 145 |
| 148 >>> loader = FileSystemLoader('/path/to/templates') | 146 >>> loader = FileSystemLoader('/path/to/templates') |
| 149 >>> loader = FileSystemLoader(['/path/to/templates', '/other/path']) | 147 >>> loader = FileSystemLoader(['/path/to/templates', '/other/path']) |
| 150 | 148 |
| 151 Per default the template encoding is ``'utf-8'`` which can be changed | 149 Per default the template encoding is ``'utf-8'`` which can be changed |
| 152 by setting the `encoding` parameter to something else. | 150 by setting the `encoding` parameter to something else. |
| 153 """ | 151 """ |
| 154 | 152 |
| 155 def __init__(self, searchpath, encoding='utf-8'): | 153 def __init__(self, searchpath, encoding='utf-8'): |
| 156 if isinstance(searchpath, basestring): | 154 if isinstance(searchpath, string_types): |
| 157 searchpath = [searchpath] | 155 searchpath = [searchpath] |
| 158 self.searchpath = list(searchpath) | 156 self.searchpath = list(searchpath) |
| 159 self.encoding = encoding | 157 self.encoding = encoding |
| 160 | 158 |
| 161 def get_source(self, environment, template): | 159 def get_source(self, environment, template): |
| 162 pieces = split_template_path(template) | 160 pieces = split_template_path(template) |
| 163 for searchpath in self.searchpath: | 161 for searchpath in self.searchpath: |
| 164 filename = path.join(searchpath, *pieces) | 162 filename = path.join(searchpath, *pieces) |
| 165 f = open_if_exists(filename) | 163 f = open_if_exists(filename) |
| 166 if f is None: | 164 if f is None: |
| (...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 267 | 265 |
| 268 Because auto reloading is rarely useful this is disabled per default. | 266 Because auto reloading is rarely useful this is disabled per default. |
| 269 """ | 267 """ |
| 270 | 268 |
| 271 def __init__(self, mapping): | 269 def __init__(self, mapping): |
| 272 self.mapping = mapping | 270 self.mapping = mapping |
| 273 | 271 |
| 274 def get_source(self, environment, template): | 272 def get_source(self, environment, template): |
| 275 if template in self.mapping: | 273 if template in self.mapping: |
| 276 source = self.mapping[template] | 274 source = self.mapping[template] |
| 277 return source, None, lambda: source != self.mapping.get(template) | 275 return source, None, lambda: source == self.mapping.get(template) |
| 278 raise TemplateNotFound(template) | 276 raise TemplateNotFound(template) |
| 279 | 277 |
| 280 def list_templates(self): | 278 def list_templates(self): |
| 281 return sorted(self.mapping) | 279 return sorted(self.mapping) |
| 282 | 280 |
| 283 | 281 |
| 284 class FunctionLoader(BaseLoader): | 282 class FunctionLoader(BaseLoader): |
| 285 """A loader that is passed a function which does the loading. The | 283 """A loader that is passed a function which does the loading. The |
| 286 function becomes the name of the template passed and has to return either | 284 function becomes the name of the template passed and has to return either |
| 287 an unicode string with the template source, a tuple in the form ``(source, | 285 an unicode string with the template source, a tuple in the form ``(source, |
| (...skipping 11 matching lines...) Expand all Loading... |
| 299 return value. | 297 return value. |
| 300 """ | 298 """ |
| 301 | 299 |
| 302 def __init__(self, load_func): | 300 def __init__(self, load_func): |
| 303 self.load_func = load_func | 301 self.load_func = load_func |
| 304 | 302 |
| 305 def get_source(self, environment, template): | 303 def get_source(self, environment, template): |
| 306 rv = self.load_func(template) | 304 rv = self.load_func(template) |
| 307 if rv is None: | 305 if rv is None: |
| 308 raise TemplateNotFound(template) | 306 raise TemplateNotFound(template) |
| 309 elif isinstance(rv, basestring): | 307 elif isinstance(rv, string_types): |
| 310 return rv, None, None | 308 return rv, None, None |
| 311 return rv | 309 return rv |
| 312 | 310 |
| 313 | 311 |
| 314 class PrefixLoader(BaseLoader): | 312 class PrefixLoader(BaseLoader): |
| 315 """A loader that is passed a dict of loaders where each loader is bound | 313 """A loader that is passed a dict of loaders where each loader is bound |
| 316 to a prefix. The prefix is delimited from the template by a slash per | 314 to a prefix. The prefix is delimited from the template by a slash per |
| 317 default, which can be changed by setting the `delimiter` argument to | 315 default, which can be changed by setting the `delimiter` argument to |
| 318 something else:: | 316 something else:: |
| 319 | 317 |
| 320 loader = PrefixLoader({ | 318 loader = PrefixLoader({ |
| 321 'app1': PackageLoader('mypackage.app1'), | 319 'app1': PackageLoader('mypackage.app1'), |
| 322 'app2': PackageLoader('mypackage.app2') | 320 'app2': PackageLoader('mypackage.app2') |
| 323 }) | 321 }) |
| 324 | 322 |
| 325 By loading ``'app1/index.html'`` the file from the app1 package is loaded, | 323 By loading ``'app1/index.html'`` the file from the app1 package is loaded, |
| 326 by loading ``'app2/index.html'`` the file from the second. | 324 by loading ``'app2/index.html'`` the file from the second. |
| 327 """ | 325 """ |
| 328 | 326 |
| 329 def __init__(self, mapping, delimiter='/'): | 327 def __init__(self, mapping, delimiter='/'): |
| 330 self.mapping = mapping | 328 self.mapping = mapping |
| 331 self.delimiter = delimiter | 329 self.delimiter = delimiter |
| 332 | 330 |
| 333 def get_source(self, environment, template): | 331 def get_loader(self, template): |
| 334 try: | 332 try: |
| 335 prefix, name = template.split(self.delimiter, 1) | 333 prefix, name = template.split(self.delimiter, 1) |
| 336 loader = self.mapping[prefix] | 334 loader = self.mapping[prefix] |
| 337 except (ValueError, KeyError): | 335 except (ValueError, KeyError): |
| 338 raise TemplateNotFound(template) | 336 raise TemplateNotFound(template) |
| 337 return loader, name |
| 338 |
| 339 def get_source(self, environment, template): |
| 340 loader, name = self.get_loader(template) |
| 339 try: | 341 try: |
| 340 return loader.get_source(environment, name) | 342 return loader.get_source(environment, name) |
| 341 except TemplateNotFound: | 343 except TemplateNotFound: |
| 342 # re-raise the exception with the correct fileame here. | 344 # re-raise the exception with the correct fileame here. |
| 343 # (the one that includes the prefix) | 345 # (the one that includes the prefix) |
| 344 raise TemplateNotFound(template) | 346 raise TemplateNotFound(template) |
| 345 | 347 |
| 348 @internalcode |
| 349 def load(self, environment, name, globals=None): |
| 350 loader, local_name = self.get_loader(name) |
| 351 try: |
| 352 return loader.load(environment, local_name) |
| 353 except TemplateNotFound: |
| 354 # re-raise the exception with the correct fileame here. |
| 355 # (the one that includes the prefix) |
| 356 raise TemplateNotFound(name) |
| 357 |
| 346 def list_templates(self): | 358 def list_templates(self): |
| 347 result = [] | 359 result = [] |
| 348 for prefix, loader in self.mapping.iteritems(): | 360 for prefix, loader in iteritems(self.mapping): |
| 349 for template in loader.list_templates(): | 361 for template in loader.list_templates(): |
| 350 result.append(prefix + self.delimiter + template) | 362 result.append(prefix + self.delimiter + template) |
| 351 return result | 363 return result |
| 352 | 364 |
| 353 | 365 |
| 354 class ChoiceLoader(BaseLoader): | 366 class ChoiceLoader(BaseLoader): |
| 355 """This loader works like the `PrefixLoader` just that no prefix is | 367 """This loader works like the `PrefixLoader` just that no prefix is |
| 356 specified. If a template could not be found by one loader the next one | 368 specified. If a template could not be found by one loader the next one |
| 357 is tried. | 369 is tried. |
| 358 | 370 |
| (...skipping 10 matching lines...) Expand all Loading... |
| 369 self.loaders = loaders | 381 self.loaders = loaders |
| 370 | 382 |
| 371 def get_source(self, environment, template): | 383 def get_source(self, environment, template): |
| 372 for loader in self.loaders: | 384 for loader in self.loaders: |
| 373 try: | 385 try: |
| 374 return loader.get_source(environment, template) | 386 return loader.get_source(environment, template) |
| 375 except TemplateNotFound: | 387 except TemplateNotFound: |
| 376 pass | 388 pass |
| 377 raise TemplateNotFound(template) | 389 raise TemplateNotFound(template) |
| 378 | 390 |
| 391 @internalcode |
| 392 def load(self, environment, name, globals=None): |
| 393 for loader in self.loaders: |
| 394 try: |
| 395 return loader.load(environment, name, globals) |
| 396 except TemplateNotFound: |
| 397 pass |
| 398 raise TemplateNotFound(name) |
| 399 |
| 379 def list_templates(self): | 400 def list_templates(self): |
| 380 found = set() | 401 found = set() |
| 381 for loader in self.loaders: | 402 for loader in self.loaders: |
| 382 found.update(loader.list_templates()) | 403 found.update(loader.list_templates()) |
| 383 return sorted(found) | 404 return sorted(found) |
| 384 | 405 |
| 385 | 406 |
| 386 class _TemplateModule(ModuleType): | 407 class _TemplateModule(ModuleType): |
| 387 """Like a normal module but with support for weak references""" | 408 """Like a normal module but with support for weak references""" |
| 388 | 409 |
| (...skipping 12 matching lines...) Expand all Loading... |
| 401 """ | 422 """ |
| 402 | 423 |
| 403 has_source_access = False | 424 has_source_access = False |
| 404 | 425 |
| 405 def __init__(self, path): | 426 def __init__(self, path): |
| 406 package_name = '_jinja2_module_templates_%x' % id(self) | 427 package_name = '_jinja2_module_templates_%x' % id(self) |
| 407 | 428 |
| 408 # create a fake module that looks for the templates in the | 429 # create a fake module that looks for the templates in the |
| 409 # path given. | 430 # path given. |
| 410 mod = _TemplateModule(package_name) | 431 mod = _TemplateModule(package_name) |
| 411 if isinstance(path, basestring): | 432 if isinstance(path, string_types): |
| 412 path = [path] | 433 path = [path] |
| 413 else: | 434 else: |
| 414 path = list(path) | 435 path = list(path) |
| 415 mod.__path__ = path | 436 mod.__path__ = path |
| 416 | 437 |
| 417 sys.modules[package_name] = weakref.proxy(mod, | 438 sys.modules[package_name] = weakref.proxy(mod, |
| 418 lambda x: sys.modules.pop(package_name, None)) | 439 lambda x: sys.modules.pop(package_name, None)) |
| 419 | 440 |
| 420 # the only strong reference, the sys.modules entry is weak | 441 # the only strong reference, the sys.modules entry is weak |
| 421 # so that the garbage collector can remove it once the | 442 # so that the garbage collector can remove it once the |
| (...skipping 19 matching lines...) Expand all Loading... |
| 441 mod = __import__(module, None, None, ['root']) | 462 mod = __import__(module, None, None, ['root']) |
| 442 except ImportError: | 463 except ImportError: |
| 443 raise TemplateNotFound(name) | 464 raise TemplateNotFound(name) |
| 444 | 465 |
| 445 # remove the entry from sys.modules, we only want the attribute | 466 # remove the entry from sys.modules, we only want the attribute |
| 446 # on the module object we have stored on the loader. | 467 # on the module object we have stored on the loader. |
| 447 sys.modules.pop(module, None) | 468 sys.modules.pop(module, None) |
| 448 | 469 |
| 449 return environment.template_class.from_module_dict( | 470 return environment.template_class.from_module_dict( |
| 450 environment, mod.__dict__, globals) | 471 environment, mod.__dict__, globals) |
| OLD | NEW |