diff --git a/django/template/__init__.py b/django/template/__init__.py index 5c4ab30..1f91d33 100644 --- a/django/template/__init__.py +++ b/django/template/__init__.py @@ -49,6 +49,7 @@ u'

Hello

' u'' """ import re +import imp from inspect import getargspec from django.conf import settings from django.template.context import Context, RequestContext, ContextPopException @@ -59,6 +60,7 @@ from django.utils.encoding import smart_unicode, force_unicode from django.utils.translation import ugettext as _ from django.utils.safestring import SafeData, EscapeData, mark_safe, mark_for_escaping from django.utils.html import escape +from django.templatetags import get_templatetags_modules __all__ = ('Template', 'Context', 'RequestContext', 'compile_string') @@ -913,22 +915,48 @@ class Library(object): return func return dec -def get_library(module_name): - lib = libraries.get(module_name, None) +def import_library(templatetag_module, library_name): + try: + components = templatetag_module.split('.') + mod = __import__(templatetag_module, {}, {}, components[-1]) + imp.find_module(library_name, mod.__path__) + except ImportError: + return None + library_module = '%s.%s' % (templatetag_module, library_name) + components = library_module.split('.') + mod = __import__(library_module, {}, {}, components[-1]) + try: + return mod.register + except AttributeError: + raise InvalidTemplateLibrary("Template library %s does not have a variable named 'register'" % module_name) + +def get_library(library_name): + lib = libraries.get(library_name, None) if not lib: - try: - mod = __import__(module_name, {}, {}, ['']) - except ImportError, e: - raise InvalidTemplateLibrary("Could not load template library from %s, %s" % (module_name, e)) - try: - lib = mod.register - libraries[module_name] = lib - except AttributeError: - raise InvalidTemplateLibrary("Template library %s does not have a variable named 'register'" % module_name) + + """ + If library is not already loaded loop over all templatetags modules to locate it. + + {% load somelib %} and {% load someotherlib %} loops twice. + + Subsequent loads eg. {% load somelib %} in the same thread will grab the cached + module from libraries. + """ + templatetags_modules = get_templatetags_modules() + tried_modules = [] + for module in templatetags_modules: + taglib_module = '%s.%s' % (module, library_name) + tried_modules.append(taglib_module) + lib = import_library(module, library_name) + if lib: + libraries[library_name] = lib + break + if not lib: + raise InvalidTemplateLibrary("Template library %s not found, tried %s" % (library_name, ','.join(tried_modules))) return lib -def add_to_builtins(module_name): - builtins.append(get_library(module_name)) +def add_to_builtins(module, library_name): + builtins.append(import_library(module, library_name)) -add_to_builtins('django.template.defaulttags') -add_to_builtins('django.template.defaultfilters') +add_to_builtins('django.templatetags', 'defaulttags') +add_to_builtins('django.templatetags', 'defaultfilters') diff --git a/django/template/defaultfilters.py b/django/template/defaultfilters.py deleted file mode 100644 index cef3143..0000000 --- a/django/template/defaultfilters.py +++ /dev/null @@ -1,851 +0,0 @@ -"""Default variable filters.""" - -import re -import random as random_module -try: - from functools import wraps -except ImportError: - from django.utils.functional import wraps # Python 2.3, 2.4 fallback. - -from django.template import Variable, Library -from django.conf import settings -from django.utils.translation import ugettext, ungettext -from django.utils.encoding import force_unicode, iri_to_uri -from django.utils.safestring import mark_safe, SafeData - -register = Library() - -####################### -# STRING DECORATOR # -####################### - -def stringfilter(func): - """ - Decorator for filters which should only receive unicode objects. The object - passed as the first positional argument will be converted to a unicode - object. - """ - def _dec(*args, **kwargs): - if args: - args = list(args) - args[0] = force_unicode(args[0]) - if isinstance(args[0], SafeData) and getattr(func, 'is_safe', False): - return mark_safe(func(*args, **kwargs)) - return func(*args, **kwargs) - - # Include a reference to the real function (used to check original - # arguments by the template parser). - _dec._decorated_function = getattr(func, '_decorated_function', func) - for attr in ('is_safe', 'needs_autoescape'): - if hasattr(func, attr): - setattr(_dec, attr, getattr(func, attr)) - return wraps(func)(_dec) - -################### -# STRINGS # -################### - - -def addslashes(value): - """ - Adds slashes before quotes. Useful for escaping strings in CSV, for - example. Less useful for escaping JavaScript; use the ``escapejs`` - filter instead. - """ - return value.replace('\\', '\\\\').replace('"', '\\"').replace("'", "\\'") -addslashes.is_safe = True -addslashes = stringfilter(addslashes) - -def capfirst(value): - """Capitalizes the first character of the value.""" - return value and value[0].upper() + value[1:] -capfirst.is_safe=True -capfirst = stringfilter(capfirst) - -_js_escapes = ( - ('\\', '\\\\'), - ('"', '\\"'), - ("'", "\\'"), - ('\n', '\\n'), - ('\r', '\\r'), - ('\b', '\\b'), - ('\f', '\\f'), - ('\t', '\\t'), - ('\v', '\\v'), - ('``) and a new line - followed by a blank line becomes a paragraph break (``

``). - """ - from django.utils.html import linebreaks - autoescape = autoescape and not isinstance(value, SafeData) - return mark_safe(linebreaks(value, autoescape)) -linebreaks.is_safe = True -linebreaks.needs_autoescape = True -linebreaks = stringfilter(linebreaks) - -def linebreaksbr(value, autoescape=None): - """ - Converts all newlines in a piece of plain text to HTML line breaks - (``
``). - """ - if autoescape and not isinstance(value, SafeData): - from django.utils.html import escape - value = escape(value) - return mark_safe(value.replace('\n', '
')) -linebreaksbr.is_safe = True -linebreaksbr.needs_autoescape = True -linebreaksbr = stringfilter(linebreaksbr) - -def safe(value): - """ - Marks the value as a string that should not be auto-escaped. - """ - from django.utils.safestring import mark_safe - return mark_safe(value) -safe.is_safe = True -safe = stringfilter(safe) - -def removetags(value, tags): - """Removes a space separated list of [X]HTML tags from the output.""" - tags = [re.escape(tag) for tag in tags.split()] - tags_re = u'(%s)' % u'|'.join(tags) - starttag_re = re.compile(ur'<%s(/?>|(\s+[^>]*>))' % tags_re, re.U) - endtag_re = re.compile(u'' % tags_re) - value = starttag_re.sub(u'', value) - value = endtag_re.sub(u'', value) - return value -removetags.is_safe = True -removetags = stringfilter(removetags) - -def striptags(value): - """Strips all [X]HTML tags.""" - from django.utils.html import strip_tags - return strip_tags(value) -striptags.is_safe = True -striptags = stringfilter(striptags) - -################### -# LISTS # -################### - -def dictsort(value, arg): - """ - Takes a list of dicts, returns that list sorted by the property given in - the argument. - """ - var_resolve = Variable(arg).resolve - decorated = [(var_resolve(item), item) for item in value] - decorated.sort() - return [item[1] for item in decorated] -dictsort.is_safe = False - -def dictsortreversed(value, arg): - """ - Takes a list of dicts, returns that list sorted in reverse order by the - property given in the argument. - """ - var_resolve = Variable(arg).resolve - decorated = [(var_resolve(item), item) for item in value] - decorated.sort() - decorated.reverse() - return [item[1] for item in decorated] -dictsortreversed.is_safe = False - -def first(value): - """Returns the first item in a list.""" - try: - return value[0] - except IndexError: - return u'' -first.is_safe = False - -def join(value, arg): - """Joins a list with a string, like Python's ``str.join(list)``.""" - try: - data = arg.join(map(force_unicode, value)) - except AttributeError: # fail silently but nicely - return value - safe_args = reduce(lambda lhs, rhs: lhs and isinstance(rhs, SafeData), - value, True) - if safe_args: - return mark_safe(data) - else: - return data -join.is_safe = True - -def last(value): - "Returns the last item in a list" - try: - return value[-1] - except IndexError: - return u'' -last.is_safe = True - -def length(value): - """Returns the length of the value - useful for lists.""" - return len(value) -length.is_safe = True - -def length_is(value, arg): - """Returns a boolean of whether the value's length is the argument.""" - return len(value) == int(arg) -length_is.is_safe = True - -def random(value): - """Returns a random item from the list.""" - return random_module.choice(value) -random.is_safe = True - -def slice_(value, arg): - """ - Returns a slice of the list. - - Uses the same syntax as Python's list slicing; see - http://diveintopython.org/native_data_types/lists.html#odbchelper.list.slice - for an introduction. - """ - try: - bits = [] - for x in arg.split(u':'): - if len(x) == 0: - bits.append(None) - else: - bits.append(int(x)) - return value[slice(*bits)] - - except (ValueError, TypeError): - return value # Fail silently. -slice_.is_safe = True - -def unordered_list(value, autoescape=None): - """ - Recursively takes a self-nested list and returns an HTML unordered list -- - WITHOUT opening and closing