text: import sys
  
  import django
  from django.core.exceptions import ImproperlyConfigured
  from django.template import Context, Node, TemplateSyntaxError
  from django.template.base import Parser, Template
  from django.template.loader import get_template, select_template
  from django.utils.itercompat import is_iterable
  
  from tag_parser.parser import parse_as_var, parse_token_kwargs
  
  string_types = (str,) if sys.version_info[0] >= 3 else (str, unicode)
  
  
  __all__ = ("BaseNode", "BaseInclusionNode")
  
  
  class BaseNode(Node):
      """
      Base class for template tag nodes.
  
      Plain style:
  
      .. code-block:: python
  
          class MyTag(BaseNode):
              def render_tag(self, context, *args, **kwargs):
                  return "Tag output"
  
          register.tag('my_tag', MyTag.parse)
  
      or classical style:
  
      .. code-block:: python
  
          @register.tag
          def my_tag(parser, token):
              return MyTag.parse(parser, token)
  
      or using the decorator:
  
      .. code-block:: python
  
          @template_tag(register, 'my_tag')
          class MyTag(BaseNode):
              def render_tag(self, context, *args, **kwargs):
                  return "Tag output"
      """
  
      #: The names of the allowed keyword arguments in the template tag.
      #: Using ``None`` to disable this check, thus allowing all keyword arguments.
      allowed_kwargs = ()
  
      #: If you want that it this will also parse the text until the end tag,
      #: and store that in ``self.nodelist``.
      end_tag_name = None
  
      #: The minimum number of required positional arguments. Use ``None`` to disable the check.
      min_args = 0
  
      #: The maximum number of allowed positional arguments. Use ``None`` for infinite.
      max_args = 0
  
      #: Whether the parser should treat the arguments as filter expressions.
      compile_args = True
  
      #: Whether the parser should treat the keyword arguments as filter expressions.
      compile_kwargs = True
  
      @staticmethod
      def __new__(cls, *args, **kwargs):
          if not kwargs and len(args) == 2 and isinstance(args[0], Parser):
              # Reasons for this code:
              # * When @register.tag() is used as class decorator,
              #   this class itself is called as compile_function(parser, token).
              # * By intercepting cls() calls, we can call parse() instead.
              # * We want to keep __init__() clean for unit testing and inheritance reuse.
              # * We must inherit from cls so template tag detection works as expected.
              #
              # Python note: When __new__ returns an instance of cls,
              #              the __init__ method will be called on that object afterwards.
              class CompileWrapper(cls):
                  __real_class = cls
  
                  def __init__(self, parser, token):
                      self.__real_node = self.__real_class.parse(parser, token)
  
                  def __getattribute__(self, item):
                      # Proxy all accessed attributes to the real object type.
                      # Note this even intercepts __class__, etc.. type(self) gives the real type.
                      if item.startswith("_CompileWrapper_"):
                          return super(CompileWrapper, self).__getattribute__(item)
  
                      return getattr(self.__real_node, item)
  
              return object.__new__(CompileWrapper)
              # return _CompileWrapper(cls, *args)
          else:
              # Standard object construction (typically done from parse())
              # The created object instance will be receiving the __init__(*args, **kwargs) call on return.
              return object.__new__(cls)
  
      def __init__(self, tag_name, *args, **kwargs):
          """
          The constructor receives the parsed arguments.
          The values are stored in :attr:`tagname`, :attr:`args`, :attr:`kwargs`.
          """
          if isinstance(tag_name, Parser):
              raise TypeError(
                  "Unexpected call of {0}.__init__(parser, token)!".format(
                      self.__class__.__name__
                  )
              )
  
          if self.end_tag_name and "nodelist" in kwargs:
              self.nodelist = kwargs.pop("nodelist")
          self.tag_name = tag_name  # May differ from cls.tag_name, and doesn't affect the 'cls' attribute at all.
          self.args = args
          self.kwargs = kwargs
  
      def __repr__(self):
          show_as_is = lambda text: text
          show_as_token = lambda expr: expr.token
          format_arg = show_as_token if self.compile_args else show_as_is
          format_kwarg = show_as_token if self.compile_kwargs else show_as_is
  
          return u"<{0}: {{% {1}{2}{3} %}}>".format(
              self.__class__.__name__,
              self.tag_name,
              u"".join(u" {0}".format(format_arg(a)) for a in self.args),
              u"".join(
                  u" {0}={1}".format(k, format_kwarg(v)) for k, v in self.kwargs.items()
              ),
          )
  
      @classmethod
      def parse(cls, parser, token):
          """
          Parse the tag, instantiate the class.
  
          :type parser: django.template.base.Parser
          :type token: django.template.base.Token
          """
          tag_name, args, kwargs = parse_token_kwargs(
              parser,
              token,
              allowed_kwargs=cls.allowed_kwargs,
              compile_args=cls.compile_args,
              compile_kwargs=cls.compile_kwargs,
          )
          cls.validate_args(tag_name, *args, **kwargs)
          if cls.end_tag_name:
              kwargs["nodelist"] = parser.parse((cls.end_tag_name,))
              parser.delete_first_token()
  
          return cls(tag_name, *args, **kwargs)
  
      def render(self, context):
          """
          The default Django render() method for the tag.
  
          This method resolves the filter expressions, and calls :func:`render_tag`.
          """
          # Resolve token kwargs
          tag_args = (
              [expr.resolve(context) for expr in self.args]
              if self.compile_args
              else self.args
          )
          tag_kwargs = (
              dict([(name, expr.resolve(context)) for name, expr in self.kwargs.items()])
              if self.compile_kwargs
              else self.kwargs
          )
  
          return self.render_tag(context, *tag_args, **tag_kwargs)
  
      def render_tag(self, context, *tag_args, **tag_kwargs):
          """
          Render the tag, with all arguments resolved to their actual values.
          """
          raise NotImplementedError(
              "{0}.render_tag() is not implemented!".format(self.__class__.__name__)
          )
  
      @classmethod
      def validate_args(cls, tag_name, *args, **kwargs):
          """
          Validate the syntax of the template tag.
          """
          if cls.min_args is not None and len(args) < cls.min_args:
              if cls.min_args == 1:
                  raise TemplateSyntaxError(
                      "'{0}' tag requires at least {1} argument".format(
                          tag_name, cls.min_args
                      )
                  )
              else:
                  raise TemplateSyntaxError(
                      "'{0}' tag requires at least {1} arguments".format(
                          tag_name, cls.min_args
                      )
                  )
  
          if cls.max_args is not None and len(args) > cls.max_args:
              if cls.max_args == 0:
                  if cls.allowed_kwargs:
                      raise TemplateSyntaxError(
                          "'{0}' tag only allows keywords arguments, for example {1}=\"...\".".format(
                              tag_name, cls.allowed_kwargs[0]
                          )
                      )
                  else:
                      raise TemplateSyntaxError(
                          "'{0}' tag doesn't support any arguments".format(tag_name)
                      )
              elif cls.max_args == 1:
                  raise TemplateSyntaxError(
                      "'{0}' tag only allows {1} argument.".format(tag_name, cls.max_args)
                  )
              else:
                  raise TemplateSyntaxError(
                      "'{0}' tag only allows {1} arguments.".format(
                          tag_name, cls.max_args
                      )
                  )
  
      def get_request(self, context):
          """
          Fetch the request from the context.
  
          This enforces the use of a RequestProcessor, e.g.
          ::
  
              render_to_response("page.html", context, context_instance=RequestContext(request))
          """
          if "request" not in context:
              # This error message is issued to help newcomers find solutions faster!
              raise ImproperlyConfigured(
                  "The '{0}' tag requires a 'request' variable in the template context.\n"
                  "Make sure 'RequestContext' is used and 'TEMPLATE_CONTEXT_PROCESSORS' includes 'django.core.context_processors.request'.".format(
                      self.tag_name
                  )
              )
  
          return context["request"]
  
  
  class BaseInclusionNode(BaseNode):
      """
      Base class to render a template tag with a template.
  
      This class allows more flexibility then Django's default :func:`django.template.Library.inclusion_tag` decorator.
  
      It allows specify the template name via:
  
      * a static ``template_name`` property.
      * a ``get_template_name()`` method
      * a 'template' kwarg in the HTML.
  
      The :func:`get_context_data` function should be overwritten to provide the required context.
      """
  
      template_name = None
      allowed_kwargs = ("template",)
  
      def render_tag(self, context, *tag_args, **tag_kwargs):
          data = self.get_context_data(context, *tag_args, **tag_kwargs)
          new_context = self.get_context(context, data)
  
          if django.VERSION >= (1, 8):
              t = context.render_context.get(self)
              if t is None:
                  file_name = self.get_template_name(*tag_args, **tag_kwargs)
  
                  if isinstance(file_name, Template):
                      t = file_name
                  elif isinstance(getattr(file_name, "template", None), Template):
                      t = file_name.template
                  elif not isinstance(file_name, string_types) and is_iterable(file_name):
                      t = context.template.engine.select_template(file_name)
                  else:
                      t = context.template.engine.get_template(file_name)
                  context.render_context[self] = t
  
              return t.render(new_context)
          else:
              # Get template nodes, and cache it.
              # Note that self.nodelist has a special meaning in the Node base class.
              if not getattr(self, "nodelist", None):
                  file_name = self.get_template_name(*tag_args, **tag_kwargs)
  
                  if not isinstance(file_name, string_types) and is_iterable(file_name):
                      tpl = select_template(file_name)
                  else:
                      tpl = get_template(file_name)
  
                  self.nodelist = tpl.nodelist
  
              # Render the node
              return self.nodelist.render(new_context)
  
      def get_template_name(self, *tag_args, **tag_kwargs):
          """
          Get the template name, by default using the :attr:`template_name` attribute.
          """
          return tag_kwargs.get("template", self.template_name)
  
      def get_context_data(self, parent_context, *tag_args, **tag_kwargs):
          """
          Return the context data for the included template.
          """
          raise NotImplementedError(
              "{0}.get_context_data() is not implemented.".format(self.__class__.__name__)
          )
  
      def get_context(self, parent_context, data):
          """
          Wrap the context data in a :class:`~django.template.Context` object.
  
          :param parent_context: The context of the parent template.
          :type parent_context: :class:`~django.template.Context`
          :param data: The result from :func:`get_context_data`
          :type data: dict
          :return: Context data.
          :rtype: :class:`~django.template.Context`
          """
          if django.VERSION >= (1, 8):
              new_context = parent_context.new(data)
          else:
              settings = {
                  "autoescape": parent_context.autoescape,
                  "current_app": parent_context.current_app,
                  "use_l10n": parent_context.use_l10n,
                  "use_tz": parent_context.use_tz,
              }
              new_context = Context(data, **settings)
  
          # Pass CSRF token for same reasons as @register.inclusion_tag does.
          csrf_token = parent_context.get("csrf_token", None)
          if csrf_token is not None:
              new_context["csrf_token"] = csrf_token
  
          return new_context
  
  
  class BaseAssignmentNode(BaseNode):
      """
      Base assignment node for an ``as var`` syntax.
      """
  
      def __init__(self, tag_name, as_var, *args, **kwargs):
          super(BaseAssignmentNode, self).__init__(tag_name, *args, **kwargs)
          self.as_var = as_var
  
      @classmethod
      def parse(cls, parser, token):
          """
          Parse the "as var" syntax.
          """
          bits, as_var = parse_as_var(parser, token)
          tag_name, args, kwargs = parse_token_kwargs(
              parser,
              bits,
              cls.allowed_kwargs,
              compile_args=cls.compile_args,
              compile_kwargs=cls.compile_kwargs,
          )
  
          # Pass through standard chain
          cls.validate_args(tag_name, *args)
          return cls(tag_name, as_var, *args, **kwargs)
  
      def render_tag(self, context, *tag_args, **tag_kwargs):
          """
          Rendering of the tag. It either assigns the value as variable, or renders it.
          """
          if self.as_var:
              # Assign the value in the parent context
              context[self.as_var] = self.get_value(context, *tag_args, **tag_kwargs)
  
          return u""
  
      def get_value(self, context, *tag_args, **tag_kwargs):
          """
          Return the value for the tag.
  
          :param tag_args:
          :param tag_kwargs:
          """
          raise NotImplementedError(
              "{0}.get_value() is not implemented'.".format(self.__class__.__name__)
          )
  
  
  class BaseAssignmentOrOutputNode(BaseAssignmentNode):
      """
      Base class to assign a value, or output it.
      This works like ``{% trans .. %}`` and ``{% url .. %}`` which both optionally
      have an ``as var`` syntax; ``{% trans .. as .. %}`` and ``{% url .. as .. %}``.
      """
  
      def render_tag(self, context, *tag_args, **tag_kwargs):
          if self.as_var:
              return super(BaseAssignmentOrOutputNode, self).render_tag(
                  context, *tag_args, **tag_kwargs
              )
          else:
              return self.get_value(context, *tag_args, **tag_kwargs)
  
  
  class BaseAssignmentOrInclusionNode(BaseInclusionNode, BaseAssignmentNode):
      """
      Base class to either assign a tag, or render it using a template.
      """
  
      context_value_name = "value"
  
      @classmethod
      def parse(cls, parser, token):
          """
          Parse the "as var" syntax.
          """
          bits, as_var = parse_as_var(parser, token)
          tag_name, args, kwargs = parse_token_kwargs(
              parser,
              bits,
              ("template",) + cls.allowed_kwargs,
              compile_args=cls.compile_args,
              compile_kwargs=cls.compile_kwargs,
          )
  
          # Pass through standard chain
          cls.validate_args(tag_name, *args)
          return cls(tag_name, as_var, *args, **kwargs)
  
      def render_tag(self, context, *tag_args, **tag_kwargs):
          """
          Rendering of the tag. It either assigns the value as variable, or renders it.
          """
          # Be very explicit about which base functionality is used:
          # Using super() for mixin support will not work nicely anyway here.
          if self.as_var:
              # Assign the value in the parent context
              return BaseAssignmentNode.render_tag(self, context, *tag_args, **tag_kwargs)
          else:
              # Render the output using the BaseInclusionNode features
              return BaseInclusionNode.render_tag(self, context, *tag_args, **tag_kwargs)
  
      def get_context_data(self, parent_context, *tag_args, **tag_kwargs):
          """
          Return the context data for the inclusion tag.
  
          Returns ``{'value': self.get_value(parent_context, *tag_args, **tag_kwargs)}`` by default.
          """
          if "template" not in self.allowed_kwargs:
              # The overwritten get_value() doesn't have to take care of our customly inserted tag parameters,
              # It can safely assume passing **tag_kwargs to another function.
              tag_kwargs.pop("template", None)
  
          return {
              self.context_value_name: self.get_value(
                  parent_context, *tag_args, **tag_kwargs
              )
          }
  