46 u'<html><h1>Hello</h1></html>' |
46 u'<html><h1>Hello</h1></html>' |
47 >>> c = template.Context({'test':False, 'varvalue': 'Hello'}) |
47 >>> c = template.Context({'test':False, 'varvalue': 'Hello'}) |
48 >>> t.render(c) |
48 >>> t.render(c) |
49 u'<html></html>' |
49 u'<html></html>' |
50 """ |
50 """ |
|
51 import imp |
51 import re |
52 import re |
52 from inspect import getargspec |
53 from inspect import getargspec |
53 |
54 |
54 from django.conf import settings |
55 from django.conf import settings |
55 from django.template.context import Context, RequestContext, ContextPopException |
56 from django.template.context import Context, RequestContext, ContextPopException |
56 from django.utils.importlib import import_module |
57 from django.utils.importlib import import_module |
57 from django.utils.itercompat import is_iterable |
58 from django.utils.itercompat import is_iterable |
58 from django.utils.functional import curry, Promise |
59 from django.utils.functional import curry, Promise |
59 from django.utils.text import smart_split, unescape_string_literal |
60 from django.utils.text import smart_split, unescape_string_literal, get_text_list |
60 from django.utils.encoding import smart_unicode, force_unicode, smart_str |
61 from django.utils.encoding import smart_unicode, force_unicode, smart_str |
61 from django.utils.translation import ugettext as _ |
62 from django.utils.translation import ugettext as _ |
62 from django.utils.safestring import SafeData, EscapeData, mark_safe, mark_for_escaping |
63 from django.utils.safestring import SafeData, EscapeData, mark_safe, mark_for_escaping |
|
64 from django.utils.formats import localize |
63 from django.utils.html import escape |
65 from django.utils.html import escape |
|
66 from django.utils.module_loading import module_has_submodule |
64 |
67 |
65 __all__ = ('Template', 'Context', 'RequestContext', 'compile_string') |
68 __all__ = ('Template', 'Context', 'RequestContext', 'compile_string') |
66 |
69 |
67 TOKEN_TEXT = 0 |
70 TOKEN_TEXT = 0 |
68 TOKEN_VAR = 1 |
71 TOKEN_VAR = 1 |
84 |
87 |
85 ALLOWED_VARIABLE_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.' |
88 ALLOWED_VARIABLE_CHARS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.' |
86 |
89 |
87 # what to report as the origin for templates that come from non-loader sources |
90 # what to report as the origin for templates that come from non-loader sources |
88 # (e.g. strings) |
91 # (e.g. strings) |
89 UNKNOWN_SOURCE="<unknown source>" |
92 UNKNOWN_SOURCE = '<unknown source>' |
90 |
93 |
91 # match a variable or block tag and capture the entire tag, including start/end delimiters |
94 # match a variable or block tag and capture the entire tag, including start/end delimiters |
92 tag_re = re.compile('(%s.*?%s|%s.*?%s|%s.*?%s)' % (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END), |
95 tag_re = re.compile('(%s.*?%s|%s.*?%s|%s.*?%s)' % (re.escape(BLOCK_TAG_START), re.escape(BLOCK_TAG_END), |
93 re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END), |
96 re.escape(VARIABLE_TAG_START), re.escape(VARIABLE_TAG_END), |
94 re.escape(COMMENT_TAG_START), re.escape(COMMENT_TAG_END))) |
97 re.escape(COMMENT_TAG_START), re.escape(COMMENT_TAG_END))) |
171 def __iter__(self): |
161 def __iter__(self): |
172 for node in self.nodelist: |
162 for node in self.nodelist: |
173 for subnode in node: |
163 for subnode in node: |
174 yield subnode |
164 yield subnode |
175 |
165 |
|
166 def _render(self, context): |
|
167 return self.nodelist.render(context) |
|
168 |
176 def render(self, context): |
169 def render(self, context): |
177 "Display stage -- can be called many times" |
170 "Display stage -- can be called many times" |
178 return self.nodelist.render(context) |
171 context.render_context.push() |
|
172 try: |
|
173 return self._render(context) |
|
174 finally: |
|
175 context.render_context.pop() |
179 |
176 |
180 def compile_string(template_string, origin): |
177 def compile_string(template_string, origin): |
181 "Compiles template_string into NodeList ready for rendering" |
178 "Compiles template_string into NodeList ready for rendering" |
182 if settings.TEMPLATE_DEBUG: |
179 if settings.TEMPLATE_DEBUG: |
183 from debug import DebugLexer, DebugParser |
180 from debug import DebugLexer, DebugParser |
329 raise self.error(token, "Empty variable tag") |
326 raise self.error(token, "Empty variable tag") |
330 |
327 |
331 def empty_block_tag(self, token): |
328 def empty_block_tag(self, token): |
332 raise self.error(token, "Empty block tag") |
329 raise self.error(token, "Empty block tag") |
333 |
330 |
334 def invalid_block_tag(self, token, command): |
331 def invalid_block_tag(self, token, command, parse_until=None): |
|
332 if parse_until: |
|
333 raise self.error(token, "Invalid block tag: '%s', expected %s" % (command, get_text_list(["'%s'" % p for p in parse_until]))) |
335 raise self.error(token, "Invalid block tag: '%s'" % command) |
334 raise self.error(token, "Invalid block tag: '%s'" % command) |
336 |
335 |
337 def unclosed_block_tag(self, parse_until): |
336 def unclosed_block_tag(self, parse_until): |
338 raise self.error(None, "Unclosed tags: %s " % ', '.join(parse_until)) |
337 raise self.error(None, "Unclosed tags: %s " % ', '.join(parse_until)) |
339 |
338 |
409 |
408 |
410 def value(self): |
409 def value(self): |
411 "A microparser that parses for a value: some string constant or variable name." |
410 "A microparser that parses for a value: some string constant or variable name." |
412 subject = self.subject |
411 subject = self.subject |
413 i = self.pointer |
412 i = self.pointer |
|
413 |
|
414 def next_space_index(subject, i): |
|
415 "Increment pointer until a real space (i.e. a space not within quotes) is encountered" |
|
416 while i < len(subject) and subject[i] not in (' ', '\t'): |
|
417 if subject[i] in ('"', "'"): |
|
418 c = subject[i] |
|
419 i += 1 |
|
420 while i < len(subject) and subject[i] != c: |
|
421 i += 1 |
|
422 if i >= len(subject): |
|
423 raise TemplateSyntaxError("Searching for value. Unexpected end of string in column %d: %s" % (i, subject)) |
|
424 i += 1 |
|
425 return i |
|
426 |
414 if i >= len(subject): |
427 if i >= len(subject): |
415 raise TemplateSyntaxError("Searching for value. Expected another value but found end of string: %s" % subject) |
428 raise TemplateSyntaxError("Searching for value. Expected another value but found end of string: %s" % subject) |
416 if subject[i] in ('"', "'"): |
429 if subject[i] in ('"', "'"): |
417 p = i |
430 p = i |
418 i += 1 |
431 i += 1 |
419 while i < len(subject) and subject[i] != subject[p]: |
432 while i < len(subject) and subject[i] != subject[p]: |
420 i += 1 |
433 i += 1 |
421 if i >= len(subject): |
434 if i >= len(subject): |
422 raise TemplateSyntaxError("Searching for value. Unexpected end of string in column %d: %s" % (i, subject)) |
435 raise TemplateSyntaxError("Searching for value. Unexpected end of string in column %d: %s" % (i, subject)) |
423 i += 1 |
436 i += 1 |
|
437 |
|
438 # Continue parsing until next "real" space, so that filters are also included |
|
439 i = next_space_index(subject, i) |
|
440 |
424 res = subject[p:i] |
441 res = subject[p:i] |
425 while i < len(subject) and subject[i] in (' ', '\t'): |
442 while i < len(subject) and subject[i] in (' ', '\t'): |
426 i += 1 |
443 i += 1 |
427 self.backout.append(self.pointer) |
444 self.backout.append(self.pointer) |
428 self.pointer = i |
445 self.pointer = i |
429 return res |
446 return res |
430 else: |
447 else: |
431 p = i |
448 p = i |
432 while i < len(subject) and subject[i] not in (' ', '\t'): |
449 i = next_space_index(subject, i) |
433 if subject[i] in ('"', "'"): |
|
434 c = subject[i] |
|
435 i += 1 |
|
436 while i < len(subject) and subject[i] != c: |
|
437 i += 1 |
|
438 if i >= len(subject): |
|
439 raise TemplateSyntaxError("Searching for value. Unexpected end of string in column %d: %s" % (i, subject)) |
|
440 i += 1 |
|
441 s = subject[p:i] |
450 s = subject[p:i] |
442 while i < len(subject) and subject[i] in (' ', '\t'): |
451 while i < len(subject) and subject[i] in (' ', '\t'): |
443 i += 1 |
452 i += 1 |
444 self.backout.append(self.pointer) |
453 self.backout.append(self.pointer) |
445 self.pointer = i |
454 self.pointer = i |
529 if constant_arg: |
536 if constant_arg: |
530 args.append((False, Variable(constant_arg).resolve({}))) |
537 args.append((False, Variable(constant_arg).resolve({}))) |
531 elif var_arg: |
538 elif var_arg: |
532 args.append((True, Variable(var_arg))) |
539 args.append((True, Variable(var_arg))) |
533 filter_func = parser.find_filter(filter_name) |
540 filter_func = parser.find_filter(filter_name) |
534 self.args_check(filter_name,filter_func, args) |
541 self.args_check(filter_name, filter_func, args) |
535 filters.append( (filter_func,args)) |
542 filters.append((filter_func, args)) |
536 upto = match.end() |
543 upto = match.end() |
537 if upto != len(token): |
544 if upto != len(token): |
538 raise TemplateSyntaxError("Could not parse the remainder: '%s' from '%s'" % (token[upto:], token)) |
545 raise TemplateSyntaxError("Could not parse the remainder: '%s' from '%s'" % (token[upto:], token)) |
539 |
546 |
540 self.filters = filters |
547 self.filters = filters |
799 return "<Text Node: '%s'>" % smart_str(self.s[:25], 'ascii', |
816 return "<Text Node: '%s'>" % smart_str(self.s[:25], 'ascii', |
800 errors='replace') |
817 errors='replace') |
801 |
818 |
802 def render(self, context): |
819 def render(self, context): |
803 return self.s |
820 return self.s |
804 |
821 |
805 def _render_value_in_context(value, context): |
822 def _render_value_in_context(value, context): |
806 """ |
823 """ |
807 Converts any value to a string to become part of a rendered template. This |
824 Converts any value to a string to become part of a rendered template. This |
808 means escaping, if required, and conversion to a unicode object. If value |
825 means escaping, if required, and conversion to a unicode object. If value |
809 is a string, it is expected to have already been translated. |
826 is a string, it is expected to have already been translated. |
810 """ |
827 """ |
|
828 value = localize(value) |
811 value = force_unicode(value) |
829 value = force_unicode(value) |
812 if (context.autoescape and not isinstance(value, SafeData)) or isinstance(value, EscapeData): |
830 if (context.autoescape and not isinstance(value, SafeData)) or isinstance(value, EscapeData): |
813 return escape(value) |
831 return escape(value) |
814 else: |
832 else: |
815 return value |
833 return value |
940 if not isinstance(file_name, basestring) and is_iterable(file_name): |
958 if not isinstance(file_name, basestring) and is_iterable(file_name): |
941 t = select_template(file_name) |
959 t = select_template(file_name) |
942 else: |
960 else: |
943 t = get_template(file_name) |
961 t = get_template(file_name) |
944 self.nodelist = t.nodelist |
962 self.nodelist = t.nodelist |
945 return self.nodelist.render(context_class(dict, |
963 new_context = context_class(dict, autoescape=context.autoescape) |
946 autoescape=context.autoescape)) |
964 # Copy across the CSRF token, if present, because inclusion |
|
965 # tags are often used for forms, and we need instructions |
|
966 # for using CSRF protection to be as simple as possible. |
|
967 csrf_token = context.get('csrf_token', None) |
|
968 if csrf_token is not None: |
|
969 new_context['csrf_token'] = csrf_token |
|
970 return self.nodelist.render(new_context) |
947 |
971 |
948 compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, InclusionNode) |
972 compile_func = curry(generic_tag_compiler, params, defaults, getattr(func, "_decorated_function", func).__name__, InclusionNode) |
949 compile_func.__doc__ = func.__doc__ |
973 compile_func.__doc__ = func.__doc__ |
950 self.tag(getattr(func, "_decorated_function", func).__name__, compile_func) |
974 self.tag(getattr(func, "_decorated_function", func).__name__, compile_func) |
951 return func |
975 return func |
952 return dec |
976 return dec |
953 |
977 |
954 def get_library(module_name): |
978 def import_library(taglib_module): |
955 lib = libraries.get(module_name, None) |
979 """Load a template tag library module. |
|
980 |
|
981 Verifies that the library contains a 'register' attribute, and |
|
982 returns that attribute as the representation of the library |
|
983 """ |
|
984 app_path, taglib = taglib_module.rsplit('.',1) |
|
985 app_module = import_module(app_path) |
|
986 try: |
|
987 mod = import_module(taglib_module) |
|
988 except ImportError, e: |
|
989 # If the ImportError is because the taglib submodule does not exist, that's not |
|
990 # an error that should be raised. If the submodule exists and raised an ImportError |
|
991 # on the attempt to load it, that we want to raise. |
|
992 if not module_has_submodule(app_module, taglib): |
|
993 return None |
|
994 else: |
|
995 raise InvalidTemplateLibrary("ImportError raised loading %s: %s" % (taglib_module, e)) |
|
996 try: |
|
997 return mod.register |
|
998 except AttributeError: |
|
999 raise InvalidTemplateLibrary("Template library %s does not have a variable named 'register'" % taglib_module) |
|
1000 |
|
1001 templatetags_modules = [] |
|
1002 |
|
1003 def get_templatetags_modules(): |
|
1004 """Return the list of all available template tag modules. |
|
1005 |
|
1006 Caches the result for faster access. |
|
1007 """ |
|
1008 global templatetags_modules |
|
1009 if not templatetags_modules: |
|
1010 _templatetags_modules = [] |
|
1011 # Populate list once per thread. |
|
1012 for app_module in ['django'] + list(settings.INSTALLED_APPS): |
|
1013 try: |
|
1014 templatetag_module = '%s.templatetags' % app_module |
|
1015 import_module(templatetag_module) |
|
1016 _templatetags_modules.append(templatetag_module) |
|
1017 except ImportError: |
|
1018 continue |
|
1019 templatetags_modules = _templatetags_modules |
|
1020 return templatetags_modules |
|
1021 |
|
1022 def get_library(library_name): |
|
1023 """ |
|
1024 Load the template library module with the given name. |
|
1025 |
|
1026 If library is not already loaded loop over all templatetags modules to locate it. |
|
1027 |
|
1028 {% load somelib %} and {% load someotherlib %} loops twice. |
|
1029 |
|
1030 Subsequent loads eg. {% load somelib %} in the same process will grab the cached |
|
1031 module from libraries. |
|
1032 """ |
|
1033 lib = libraries.get(library_name, None) |
956 if not lib: |
1034 if not lib: |
957 try: |
1035 templatetags_modules = get_templatetags_modules() |
958 mod = import_module(module_name) |
1036 tried_modules = [] |
959 except ImportError, e: |
1037 for module in templatetags_modules: |
960 raise InvalidTemplateLibrary("Could not load template library from %s, %s" % (module_name, e)) |
1038 taglib_module = '%s.%s' % (module, library_name) |
961 try: |
1039 tried_modules.append(taglib_module) |
962 lib = mod.register |
1040 lib = import_library(taglib_module) |
963 libraries[module_name] = lib |
1041 if lib: |
964 except AttributeError: |
1042 libraries[library_name] = lib |
965 raise InvalidTemplateLibrary("Template library %s does not have a variable named 'register'" % module_name) |
1043 break |
|
1044 if not lib: |
|
1045 raise InvalidTemplateLibrary("Template library %s not found, tried %s" % (library_name, ','.join(tried_modules))) |
966 return lib |
1046 return lib |
967 |
1047 |
968 def add_to_builtins(module_name): |
1048 def add_to_builtins(module): |
969 builtins.append(get_library(module_name)) |
1049 builtins.append(import_library(module)) |
970 |
1050 |
971 add_to_builtins('django.template.defaulttags') |
1051 add_to_builtins('django.template.defaulttags') |
972 add_to_builtins('django.template.defaultfilters') |
1052 add_to_builtins('django.template.defaultfilters') |