Added some better error reporting and path handling when creating template paths.
[django.git] / django / template / loaders / app_directories.py
blob24a1beec16c5161427fb9b53faeaa37a17d4221e
1 """
2 Wrapper for loading templates from "template" directories in INSTALLED_APPS
3 packages.
4 """
6 import os
8 from django.conf import settings
9 from django.core.exceptions import ImproperlyConfigured
10 from django.template import TemplateDoesNotExist
11 from django.utils._os import safe_join
13 # At compile time, cache the directories to search.
14 app_template_dirs = []
15 for app in settings.INSTALLED_APPS:
16 i = app.rfind('.')
17 if i == -1:
18 m, a = app, None
19 else:
20 m, a = app[:i], app[i+1:]
21 try:
22 if a is None:
23 mod = __import__(m, {}, {}, [])
24 else:
25 mod = getattr(__import__(m, {}, {}, [a]), a)
26 except ImportError, e:
27 raise ImproperlyConfigured, 'ImportError %s: %s' % (app, e.args[0])
28 template_dir = os.path.join(os.path.dirname(mod.__file__), 'templates')
29 if os.path.isdir(template_dir):
30 app_template_dirs.append(template_dir)
32 # It won't change, so convert it to a tuple to save memory.
33 app_template_dirs = tuple(app_template_dirs)
35 def get_template_sources(template_name, template_dirs=None):
36 """
37 Returns the absolute paths to "template_name", when appended to each
38 directory in "template_dirs". Any paths that don't lie inside one of the
39 template dirs are excluded from the result set, for security reasons.
40 """
41 if not template_dirs:
42 template_dirs = app_template_dirs
43 for template_dir in template_dirs:
44 try:
45 yield safe_join(template_dir, template_name)
46 except UnicodeDecodeError:
47 # The template dir name was a bytestring that wasn't valid UTF-8.
48 raise
49 except ValueError:
50 # The joined path was located outside of template_dir.
51 pass
53 def load_template_source(template_name, template_dirs=None):
54 for filepath in get_template_sources(template_name, template_dirs):
55 try:
56 return (open(filepath).read().decode(settings.FILE_CHARSET), filepath)
57 except IOError:
58 pass
59 raise TemplateDoesNotExist, template_name
60 load_template_source.is_usable = True