Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
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.

cached.py 3.6KB

1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. """
  2. Wrapper class that takes a list of template loaders as an argument and attempts
  3. to load templates from them in order, caching the result.
  4. """
  5. import hashlib
  6. from django.template import TemplateDoesNotExist
  7. from django.template.backends.django import copy_exception
  8. from .base import Loader as BaseLoader
  9. class Loader(BaseLoader):
  10. def __init__(self, engine, loaders):
  11. self.get_template_cache = {}
  12. self.loaders = engine.get_template_loaders(loaders)
  13. super().__init__(engine)
  14. def get_dirs(self):
  15. for loader in self.loaders:
  16. if hasattr(loader, "get_dirs"):
  17. yield from loader.get_dirs()
  18. def get_contents(self, origin):
  19. return origin.loader.get_contents(origin)
  20. def get_template(self, template_name, skip=None):
  21. """
  22. Perform the caching that gives this loader its name. Often many of the
  23. templates attempted will be missing, so memory use is of concern here.
  24. To keep it in check, caching behavior is a little complicated when a
  25. template is not found. See ticket #26306 for more details.
  26. With template debugging disabled, cache the TemplateDoesNotExist class
  27. for every missing template and raise a new instance of it after
  28. fetching it from the cache.
  29. With template debugging enabled, a unique TemplateDoesNotExist object
  30. is cached for each missing template to preserve debug data. When
  31. raising an exception, Python sets __traceback__, __context__, and
  32. __cause__ attributes on it. Those attributes can contain references to
  33. all sorts of objects up the call chain and caching them creates a
  34. memory leak. Thus, unraised copies of the exceptions are cached and
  35. copies of those copies are raised after they're fetched from the cache.
  36. """
  37. key = self.cache_key(template_name, skip)
  38. cached = self.get_template_cache.get(key)
  39. if cached:
  40. if isinstance(cached, type) and issubclass(cached, TemplateDoesNotExist):
  41. raise cached(template_name)
  42. elif isinstance(cached, TemplateDoesNotExist):
  43. raise copy_exception(cached)
  44. return cached
  45. try:
  46. template = super().get_template(template_name, skip)
  47. except TemplateDoesNotExist as e:
  48. self.get_template_cache[key] = (
  49. copy_exception(e) if self.engine.debug else TemplateDoesNotExist
  50. )
  51. raise
  52. else:
  53. self.get_template_cache[key] = template
  54. return template
  55. def get_template_sources(self, template_name):
  56. for loader in self.loaders:
  57. yield from loader.get_template_sources(template_name)
  58. def cache_key(self, template_name, skip=None):
  59. """
  60. Generate a cache key for the template name and skip.
  61. If skip is provided, only origins that match template_name are included
  62. in the cache key. This ensures each template is only parsed and cached
  63. once if contained in different extend chains like:
  64. x -> a -> a
  65. y -> a -> a
  66. z -> a -> a
  67. """
  68. skip_prefix = ""
  69. if skip:
  70. matching = [
  71. origin.name for origin in skip if origin.template_name == template_name
  72. ]
  73. if matching:
  74. skip_prefix = self.generate_hash(matching)
  75. return "-".join(s for s in (str(template_name), skip_prefix) if s)
  76. def generate_hash(self, values):
  77. return hashlib.sha1("|".join(values).encode()).hexdigest()
  78. def reset(self):
  79. "Empty the template cache."
  80. self.get_template_cache.clear()