You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

engine.py 6.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. import functools
  2. from django.core.exceptions import ImproperlyConfigured
  3. from django.utils.functional import cached_property
  4. from django.utils.module_loading import import_string
  5. from .base import Context, Template
  6. from .context import _builtin_context_processors
  7. from .exceptions import TemplateDoesNotExist
  8. from .library import import_library
  9. class Engine:
  10. default_builtins = [
  11. 'django.template.defaulttags',
  12. 'django.template.defaultfilters',
  13. 'django.template.loader_tags',
  14. ]
  15. def __init__(self, dirs=None, app_dirs=False, context_processors=None,
  16. debug=False, loaders=None, string_if_invalid='',
  17. file_charset='utf-8', libraries=None, builtins=None, autoescape=True):
  18. if dirs is None:
  19. dirs = []
  20. if context_processors is None:
  21. context_processors = []
  22. if loaders is None:
  23. loaders = ['django.template.loaders.filesystem.Loader']
  24. if app_dirs:
  25. loaders += ['django.template.loaders.app_directories.Loader']
  26. if not debug:
  27. loaders = [('django.template.loaders.cached.Loader', loaders)]
  28. else:
  29. if app_dirs:
  30. raise ImproperlyConfigured(
  31. "app_dirs must not be set when loaders is defined.")
  32. if libraries is None:
  33. libraries = {}
  34. if builtins is None:
  35. builtins = []
  36. self.dirs = dirs
  37. self.app_dirs = app_dirs
  38. self.autoescape = autoescape
  39. self.context_processors = context_processors
  40. self.debug = debug
  41. self.loaders = loaders
  42. self.string_if_invalid = string_if_invalid
  43. self.file_charset = file_charset
  44. self.libraries = libraries
  45. self.template_libraries = self.get_template_libraries(libraries)
  46. self.builtins = self.default_builtins + builtins
  47. self.template_builtins = self.get_template_builtins(self.builtins)
  48. @staticmethod
  49. @functools.lru_cache()
  50. def get_default():
  51. """
  52. Return the first DjangoTemplates backend that's configured, or raise
  53. ImproperlyConfigured if none are configured.
  54. This is required for preserving historical APIs that rely on a
  55. globally available, implicitly configured engine such as:
  56. >>> from django.template import Context, Template
  57. >>> template = Template("Hello {{ name }}!")
  58. >>> context = Context({'name': "world"})
  59. >>> template.render(context)
  60. 'Hello world!'
  61. """
  62. # Since Engine is imported in django.template and since
  63. # DjangoTemplates is a wrapper around this Engine class,
  64. # local imports are required to avoid import loops.
  65. from django.template import engines
  66. from django.template.backends.django import DjangoTemplates
  67. for engine in engines.all():
  68. if isinstance(engine, DjangoTemplates):
  69. return engine.engine
  70. raise ImproperlyConfigured('No DjangoTemplates backend is configured.')
  71. @cached_property
  72. def template_context_processors(self):
  73. context_processors = _builtin_context_processors
  74. context_processors += tuple(self.context_processors)
  75. return tuple(import_string(path) for path in context_processors)
  76. def get_template_builtins(self, builtins):
  77. return [import_library(x) for x in builtins]
  78. def get_template_libraries(self, libraries):
  79. loaded = {}
  80. for name, path in libraries.items():
  81. loaded[name] = import_library(path)
  82. return loaded
  83. @cached_property
  84. def template_loaders(self):
  85. return self.get_template_loaders(self.loaders)
  86. def get_template_loaders(self, template_loaders):
  87. loaders = []
  88. for template_loader in template_loaders:
  89. loader = self.find_template_loader(template_loader)
  90. if loader is not None:
  91. loaders.append(loader)
  92. return loaders
  93. def find_template_loader(self, loader):
  94. if isinstance(loader, (tuple, list)):
  95. loader, *args = loader
  96. else:
  97. args = []
  98. if isinstance(loader, str):
  99. loader_class = import_string(loader)
  100. return loader_class(self, *args)
  101. else:
  102. raise ImproperlyConfigured(
  103. "Invalid value in template loaders configuration: %r" % loader)
  104. def find_template(self, name, dirs=None, skip=None):
  105. tried = []
  106. for loader in self.template_loaders:
  107. try:
  108. template = loader.get_template(name, skip=skip)
  109. return template, template.origin
  110. except TemplateDoesNotExist as e:
  111. tried.extend(e.tried)
  112. raise TemplateDoesNotExist(name, tried=tried)
  113. def from_string(self, template_code):
  114. """
  115. Return a compiled Template object for the given template code,
  116. handling template inheritance recursively.
  117. """
  118. return Template(template_code, engine=self)
  119. def get_template(self, template_name):
  120. """
  121. Return a compiled Template object for the given template name,
  122. handling template inheritance recursively.
  123. """
  124. template, origin = self.find_template(template_name)
  125. if not hasattr(template, 'render'):
  126. # template needs to be compiled
  127. template = Template(template, origin, template_name, engine=self)
  128. return template
  129. def render_to_string(self, template_name, context=None):
  130. """
  131. Render the template specified by template_name with the given context.
  132. For use in Django's test suite.
  133. """
  134. if isinstance(template_name, (list, tuple)):
  135. t = self.select_template(template_name)
  136. else:
  137. t = self.get_template(template_name)
  138. # Django < 1.8 accepted a Context in `context` even though that's
  139. # unintended. Preserve this ability but don't rewrap `context`.
  140. if isinstance(context, Context):
  141. return t.render(context)
  142. else:
  143. return t.render(Context(context))
  144. def select_template(self, template_name_list):
  145. """
  146. Given a list of template names, return the first that can be loaded.
  147. """
  148. if not template_name_list:
  149. raise TemplateDoesNotExist("No template names provided")
  150. not_found = []
  151. for template_name in template_name_list:
  152. try:
  153. return self.get_template(template_name)
  154. except TemplateDoesNotExist as exc:
  155. if exc.args[0] not in not_found:
  156. not_found.append(exc.args[0])
  157. continue
  158. # If we get here, none of the templates could be loaded
  159. raise TemplateDoesNotExist(', '.join(not_found))