import re
import cgi
import inspect

def call(view, x, template=None):
    if callable(x):
        (args, _, _, _) = inspect.getargspec(x)
        if len(args) is 0:
            x = x()
        elif len(args) is 1 and args[0] == 'self':
            x = x(view)
        elif len(args) is 1:
            x = x(template)
        else:
            x = x(view, template)
    return unicode(x)

def sectionTag(name, parsed, template, delims):
    def func(self):
        data = self.get(name)
        ast = parsed
        if not data:
            return ''
        elif callable(data):
            tmpl = Template(call(self, data, template))
            tmpl.otag, tmpl.ctag = delims
            tmpl._compile_regexps()
            ast = tmpl._parse()
            data = [ data ]
        elif type(data) not in [list, tuple]:
            data = [ data ]

        parts = []
        for element in data:
            self.context_list.insert(0, element)
            parts.append(''.join(map(call, [self] * len(ast), ast)))
            del self.context_list[0]

        return ''.join(parts)
    return func

def inverseTag(name, parsed, template, delims):
    def func(self):
        data = self.get(name)
        if data:
            return ''
        return ''.join(map(call, [self] * len(parsed), parsed))
    return func

def escapedTag(name):
    fetch = unescapedTag(name)
    def func(self):
        return cgi.escape(fetch(self), True)
    return func

def unescapedTag(name):
    def func(self):
        return unicode(call(self, self.get(name)))
    return func

class EndOfSection(Exception):
    def __init__(self, buffer, template, position):
        self.buffer   = buffer
        self.template = template
        self.position = position

class Template(object):
    tag_re = None
    otag, ctag = '{{', '}}'

    def __init__(self, template=None, context={}, **kwargs):
        from view import View

        self.template = template

        if kwargs:
            context.update(kwargs)

        self.view = context if isinstance(context, View) else View(context=context)
        self._compile_regexps()

    def _compile_regexps(self):
        tags = {'otag': re.escape(self.otag), 'ctag': re.escape(self.ctag)}
        tag = r"""
            (?P<content>[\s\S]*?)
            (?P<whitespace>[\ \t]*)
            %(otag)s \s*
            (?:
              (?P<change>=) \s* (?P<delims>.+?)   \s* = |
              (?P<raw>{)    \s* (?P<raw_name>.+?) \s* } |
              (?P<tag>\W?)  \s* (?P<name>[\s\S]+?)
            )
            \s* %(ctag)s
        """
        self.tag_re = re.compile(tag % tags, re.M | re.X)

    def _parse(self, template=None, section=None, index=0):
        """Parse a template into a syntax tree."""

        template = template != None and template or self.template
        buffer = []
        pos = index

        while True:
            match = self.tag_re.search(template, pos)

            if match is None:
                break

            pos = self._handle_match(template, match, buffer, index)

        # Save the rest of the template.
        buffer.append(template[pos:])

        return buffer

    def _handle_match(self, template, match, buffer, index):
        # Normalize the captures dictionary.
        captures = match.groupdict()
        if captures['change'] is not None:
            captures.update(tag='=', name=captures['delims'])
        elif captures['raw'] is not None:
            captures.update(tag='{', name=captures['raw_name'])

        # Save the literal text content.
        buffer.append(captures['content'])
        pos = match.end()
        tagPos = match.end('content')

        # Standalone (non-interpolation) tags consume the entire line,
        # both leading whitespace and trailing newline.
        tagBeganLine = not tagPos or template[tagPos - 1] in ['\r', '\n']
        tagEndedLine = (pos == len(template) or template[pos] in ['\r', '\n'])
        interpolationTag = captures['tag'] in ['', '&', '{']

        if (tagBeganLine and tagEndedLine and not interpolationTag):
            if pos < len(template):
                pos += template[pos] == '\r' and 1 or 0
            if pos < len(template):
                pos += template[pos] == '\n' and 1 or 0
        elif captures['whitespace']:
            buffer.append(captures['whitespace'])
            tagPos += len(captures['whitespace'])
            captures['whitespace'] = ''

        name = captures['name']
        if captures['tag'] == '!':
            pass
        elif captures['tag'] == '=':
            self.otag, self.ctag = name.split()
            self._compile_regexps()
        elif captures['tag'] == '>':
            tmpl = Template(self.view.partial(name))
            tmpl.view = self.view
            buffer += tmpl._parse()
        elif captures['tag'] in ['#', '^']:
            try:
                self._parse(template, name, pos)
            except EndOfSection as e:
                bufr = e.buffer
                tmpl = e.template
                pos  = e.position

            tag = { '#': sectionTag, '^': inverseTag }[captures['tag']]
            buffer.append(tag(name, bufr, tmpl, (self.otag, self.ctag)))
        elif captures['tag'] == '/':
            raise EndOfSection(buffer, template[index:tagPos], pos)
        elif captures['tag'] in ['{', '&']:
            buffer.append(unescapedTag(name))
        elif captures['tag'] == '':
            buffer.append(escapedTag(name))
        else:
            raise Exception("'%s' is an unrecognized type!" % captures['tag'])

        return pos

    def render(self, encoding=None):
        parsed = self._parse()
        result = ''.join(map(call, [self.view] * len(parsed), parsed))
        if encoding is not None:
            result = result.encode(encoding)

        return result

@@ -382,16 +382,239 @@
 ode(x)%0A%0A
+def partialTag(name):%0A    def func(self):%0A        tmpl = Template(self.partial(name))%0A        tmpl.view = self%0A        parsed = tmpl._parse()%0A        return ''.join(map(call, %5Bself%5D * len(parsed), parsed))%0A    return func%0A%0A
 def sect
@@ -4901,49 +4901,40 @@
     
-tmpl = Template(self.view.
+buffer.append(
 partial
+Tag
 (name))%0A
     
@@ -4933,78 +4933,8 @@
 e))%0A
-            tmpl.view = self.view%0A            buffer += tmpl._parse()%0A
     

Fix the partial recursion error.