a class for registering template tags and filters compiled filter and template tag functions are stored in the filters and tags attributes the filter simpletag and inclusiontag methods provide a convenient way to register callables as tags register tag register tag register tag somename or register tagname somename register tag somename somefunc register a callable as a template filter example register filter def lowervalue return value lower register filter register filter register filter somename or register filtername somename register filter somename somefunc set the flag on the filter for filterexpression resolve set the flag on the innermost decorated function for decorators that need it e g stringfilter register a callable as a compiled template tag example register simpletag def helloargs kwargs return world register simpletag register simpletag register a callable as an inclusion tag register inclusiontag results html def showresultspoll choices poll choiceset all return choices choices base class for tag helper nodes such as simplenode and inclusionnode manages the positional and keyword arguments to be passed to the decorated function render the specified template and context cache the template object in rendercontext to avoid reparsing and loading when used in a for loop copy across the csrf token if present because inclusion tags are often used for forms and we need instructions for using csrf protection to be as simple as possible parse bits for template tag helpers simpletag and inclusiontag in particular by detecting syntax errors and by extracting positional and keyword arguments first we try to extract a potential kwarg from the bit the kwarg was successfully extracted an unexpected keyword argument was supplied the keyword argument has already been supplied once all good record the keyword argument if using the keyword syntax for a positional arg then consume it same for keywordonly arguments record the positional argument consume from the list of expected positional arguments consider the last n params handled where n is the number of defaults some positional arguments were not supplied load a library object from a template tag module a class for registering template tags and filters compiled filter and template tag functions are stored in the filters and tags attributes the filter simple_tag and inclusion_tag methods provide a convenient way to register callables as tags register tag register tag register tag somename or register tag name somename register tag somename somefunc register a callable as a template filter example register filter def lower value return value lower register filter register filter register filter somename or register filter name somename register filter somename somefunc set the flag on the filter for filterexpression resolve set the flag on the innermost decorated function for decorators that need it e g stringfilter register a callable as a compiled template tag example register simple_tag def hello args kwargs return world register simple_tag register simple_tag register a callable as an inclusion tag register inclusion_tag results html def show_results poll choices poll choice_set all return choices choices base class for tag helper nodes such as simplenode and inclusionnode manages the positional and keyword arguments to be passed to the decorated function render the specified template and context cache the template object in render_context to avoid reparsing and loading when used in a for loop copy across the csrf token if present because inclusion tags are often used for forms and we need instructions for using csrf protection to be as simple as possible parse bits for template tag helpers simple_tag and inclusion_tag in particular by detecting syntax errors and by extracting positional and keyword arguments first we try to extract a potential kwarg from the bit the kwarg was successfully extracted an unexpected keyword argument was supplied the keyword argument has already been supplied once all good record the keyword argument if using the keyword syntax for a positional arg then consume it same for keyword only arguments record the positional argument consume from the list of expected positional arguments consider the last n params handled where n is the number of defaults some positional arguments were not supplied load a library object from a template tag module
from collections.abc import Iterable
from functools import wraps
from importlib import import_module
from inspect import getfullargspec, unwrap

from django.utils.html import conditional_escape

from .base import Node, Template, token_kwargs
from .exceptions import TemplateSyntaxError


class InvalidTemplateLibrary(Exception):
    pass


class Library:
    

    def __init__(self):
        self.filters = {}
        self.tags = {}

    def tag(self, name=None, compile_function=None):
        if name is None and compile_function is None:
            
            return self.tag_function
        elif name is not None and compile_function is None:
            if callable(name):
                
                return self.tag_function(name)
            else:
                
                def dec(func):
                    return self.tag(name, func)

                return dec
        elif name is not None and compile_function is not None:
            
            self.tags[name] = compile_function
            return compile_function
        else:
            raise ValueError(
                "Unsupported arguments to Library.tag: (%r, %r)"
                % (name, compile_function),
            )

    def tag_function(self, func):
        self.tags[func.__name__] = func
        return func

    def filter(self, name=None, filter_func=None, **flags):
        
        if name is None and filter_func is None:
            
            def dec(func):
                return self.filter_function(func, **flags)

            return dec
        elif name is not None and filter_func is None:
            if callable(name):
                
                return self.filter_function(name, **flags)
            else:
                
                def dec(func):
                    return self.filter(name, func, **flags)

                return dec
        elif name is not None and filter_func is not None:
            
            self.filters[name] = filter_func
            for attr in ("expects_localtime", "is_safe", "needs_autoescape"):
                if attr in flags:
                    value = flags[attr]
                    
                    setattr(filter_func, attr, value)
                    
                    
                    setattr(unwrap(filter_func), attr, value)
            filter_func._filter_name = name
            return filter_func
        else:
            raise ValueError(
                "Unsupported arguments to Library.filter: (%r, %r)"
                % (name, filter_func),
            )

    def filter_function(self, func, **flags):
        return self.filter(func.__name__, func, **flags)

    def simple_tag(self, func=None, takes_context=None, name=None):
        

        def dec(func):
            (
                params,
                varargs,
                varkw,
                defaults,
                kwonly,
                kwonly_defaults,
                _,
            ) = getfullargspec(unwrap(func))
            function_name = name or func.__name__

            @wraps(func)
            def compile_func(parser, token):
                bits = token.split_contents()[1:]
                target_var = None
                if len(bits) >= 2 and bits[-2] == "as":
                    target_var = bits[-1]
                    bits = bits[:-2]
                args, kwargs = parse_bits(
                    parser,
                    bits,
                    params,
                    varargs,
                    varkw,
                    defaults,
                    kwonly,
                    kwonly_defaults,
                    takes_context,
                    function_name,
                )
                return SimpleNode(func, takes_context, args, kwargs, target_var)

            self.tag(function_name, compile_func)
            return func

        if func is None:
            
            return dec
        elif callable(func):
            
            return dec(func)
        else:
            raise ValueError("Invalid arguments provided to simple_tag")

    def inclusion_tag(self, filename, func=None, takes_context=None, name=None):
        

        def dec(func):
            (
                params,
                varargs,
                varkw,
                defaults,
                kwonly,
                kwonly_defaults,
                _,
            ) = getfullargspec(unwrap(func))
            function_name = name or func.__name__

            @wraps(func)
            def compile_func(parser, token):
                bits = token.split_contents()[1:]
                args, kwargs = parse_bits(
                    parser,
                    bits,
                    params,
                    varargs,
                    varkw,
                    defaults,
                    kwonly,
                    kwonly_defaults,
                    takes_context,
                    function_name,
                )
                return InclusionNode(
                    func,
                    takes_context,
                    args,
                    kwargs,
                    filename,
                )

            self.tag(function_name, compile_func)
            return func

        return dec


class TagHelperNode(Node):
    

    def __init__(self, func, takes_context, args, kwargs):
        self.func = func
        self.takes_context = takes_context
        self.args = args
        self.kwargs = kwargs

    def get_resolved_arguments(self, context):
        resolved_args = [var.resolve(context) for var in self.args]
        if self.takes_context:
            resolved_args = [context] + resolved_args
        resolved_kwargs = {k: v.resolve(context) for k, v in self.kwargs.items()}
        return resolved_args, resolved_kwargs


class SimpleNode(TagHelperNode):
    child_nodelists = ()

    def __init__(self, func, takes_context, args, kwargs, target_var):
        super().__init__(func, takes_context, args, kwargs)
        self.target_var = target_var

    def render(self, context):
        resolved_args, resolved_kwargs = self.get_resolved_arguments(context)
        output = self.func(*resolved_args, **resolved_kwargs)
        if self.target_var is not None:
            context[self.target_var] = output
            return ""
        if context.autoescape:
            output = conditional_escape(output)
        return output


class InclusionNode(TagHelperNode):
    def __init__(self, func, takes_context, args, kwargs, filename):
        super().__init__(func, takes_context, args, kwargs)
        self.filename = filename

    def render(self, context):
        
        resolved_args, resolved_kwargs = self.get_resolved_arguments(context)
        _dict = self.func(*resolved_args, **resolved_kwargs)

        t = context.render_context.get(self)
        if t is None:
            if isinstance(self.filename, Template):
                t = self.filename
            elif isinstance(getattr(self.filename, "template", None), Template):
                t = self.filename.template
            elif not isinstance(self.filename, str) and isinstance(
                self.filename, Iterable
            ):
                t = context.template.engine.select_template(self.filename)
            else:
                t = context.template.engine.get_template(self.filename)
            context.render_context[self] = t
        new_context = context.new(_dict)
        
        
        
        csrf_token = context.get("csrf_token")
        if csrf_token is not None:
            new_context["csrf_token"] = csrf_token
        return t.render(new_context)


def parse_bits(
    parser,
    bits,
    params,
    varargs,
    varkw,
    defaults,
    kwonly,
    kwonly_defaults,
    takes_context,
    name,
):
    
    if takes_context:
        if params and params[0] == "context":
            params = params[1:]
        else:
            raise TemplateSyntaxError(
                "'%s' is decorated with takes_context=True so it must "
                "have a first argument of 'context'" % name
            )
    args = []
    kwargs = {}
    unhandled_params = list(params)
    unhandled_kwargs = [
        kwarg for kwarg in kwonly if not kwonly_defaults or kwarg not in kwonly_defaults
    ]
    for bit in bits:
        
        kwarg = token_kwargs([bit], parser)
        if kwarg:
            
            param, value = kwarg.popitem()
            if param not in params and param not in kwonly and varkw is None:
                
                raise TemplateSyntaxError(
                    "'%s' received unexpected keyword argument '%s'" % (name, param)
                )
            elif param in kwargs:
                
                raise TemplateSyntaxError(
                    "'%s' received multiple values for keyword argument '%s'"
                    % (name, param)
                )
            else:
                
                kwargs[str(param)] = value
                if param in unhandled_params:
                    
                    
                    unhandled_params.remove(param)
                elif param in unhandled_kwargs:
                    
                    unhandled_kwargs.remove(param)
        else:
            if kwargs:
                raise TemplateSyntaxError(
                    "'%s' received some positional argument(s) after some "
                    "keyword argument(s)" % name
                )
            else:
                
                args.append(parser.compile_filter(bit))
                try:
                    
                    unhandled_params.pop(0)
                except IndexError:
                    if varargs is None:
                        raise TemplateSyntaxError(
                            "'%s' received too many positional arguments" % name
                        )
    if defaults is not None:
        
        
        unhandled_params = unhandled_params[: -len(defaults)]
    if unhandled_params or unhandled_kwargs:
        
        raise TemplateSyntaxError(
            "'%s' did not receive value(s) for the argument(s): %s"
            % (name, ", ".join("'%s'" % p for p in unhandled_params + unhandled_kwargs))
        )
    return args, kwargs


def import_library(name):
    
    try:
        module = import_module(name)
    except ImportError as e:
        raise InvalidTemplateLibrary(
            "Invalid template library specified. ImportError raised when "
            "trying to load '%s': %s" % (name, e)
        )
    try:
        return module.register
    except AttributeError:
        raise InvalidTemplateLibrary(
            "Module  %s does not have a variable named 'register'" % name,
        )