Skip to content

Commit

Permalink
Add the beginnings of a postmortem debugger
Browse files Browse the repository at this point in the history
The debugger will only activate when
* debugging is set to True
* debugger.py exists
* The template raised a SyntaxError

Otherwise it will act like normal
  • Loading branch information
noahmorrison committed Dec 23, 2014
1 parent dbccec5 commit dd121b3
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 74 deletions.
10 changes: 6 additions & 4 deletions chevron/renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def _get_partial(name, partials_dict, partials_path, partials_ext):

def render(template='', data={}, partials_path='.', partials_ext='mustache',
partials_dict={}, padding=0, def_ldel='{{', def_rdel='}}',
scopes=None):
scopes=None, debugging=True):
"""Render a mustache template.
Renders a mustache template with a data scope and partial capability.
Expand Down Expand Up @@ -147,7 +147,7 @@ def render(template='', data={}, partials_path='.', partials_ext='mustache',
tokens = template
else:
# Otherwise make a generator
tokens = tokenize(template, def_ldel, def_rdel)
tokens = tokenize(template, def_ldel, def_rdel, debugging)

output = unicode('', 'utf-8')

Expand Down Expand Up @@ -220,7 +220,8 @@ def render(template='', data={}, partials_path='.', partials_ext='mustache',
partials_path=partials_path,
partials_ext=partials_ext,
partials_dict=partials_dict,
def_ldel=def_ldel, def_rdel=def_rdel)
def_ldel=def_ldel, def_rdel=def_rdel,
debugging=debugging)

else:
# Otherwise we're just a scope section
Expand Down Expand Up @@ -249,7 +250,8 @@ def render(template='', data={}, partials_path='.', partials_ext='mustache',
partials_ext=partials_ext,
partials_dict=partials_dict,
def_ldel=def_ldel, def_rdel=def_rdel,
padding=part_padding, scopes=scopes)
padding=part_padding, scopes=scopes,
debugging=debugging)

# If the partial was indented
if left.isspace():
Expand Down
158 changes: 90 additions & 68 deletions chevron/tokenizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ def parse_tag(template, l_del, r_del):
# The main tokenizing function
#

def tokenize(template, def_ldel='{{', def_rdel='}}'):
def tokenize(template, def_ldel='{{', def_rdel='}}', debugging=True):
"""Tokenize a mustache template
Tokenizes a mustache template in a generator fashion,
Expand Down Expand Up @@ -146,77 +146,99 @@ def tokenize(template, def_ldel='{{', def_rdel='}}'):

# If the template is a file-like object then read it
try:
template = template.read()
org_template = template.read()
template = org_template
except AttributeError:
pass
org_template = template

is_standalone = True
open_sections = []
l_del = def_ldel
r_del = def_rdel
error = None

while template:
literal, template = grab_literal(template, l_del)

# If the template is completed
if not template:
# Then yield the literal and leave
yield ('literal', literal)
break

# Do the first check to see if we could be a standalone
is_standalone = l_sa_check(template, literal, is_standalone)

# Parse the tag
tag, template = parse_tag(template, l_del, r_del)
tag_type, tag_key = tag

# Special tag logic

# If we are a set delimiter tag
if tag_type == 'set delimiter':
# Then get and set the delimiters
dels = tag_key.strip().split(' ')
l_del, r_del = dels[0], dels[-1]

# If we are a section tag
elif tag_type in ['section', 'inverted section']:
# Then open a new section
open_sections.append(tag_key)

# If we are an end tag
elif tag_type == 'end':
# Then check to see if the last opened section
# is the same as us
last_section = open_sections.pop()
if tag_key != last_section:
# Otherwise we need to complain
raise SyntaxError('End tag does not match '
'the currently opened section')

# Do the second check to see if we're a standalone
is_standalone = r_sa_check(template, tag_type, is_standalone)

# Which if we are
if is_standalone:
# Remove the stuff before the newline
template = template.split('\n', 1)[-1]

# Partials need to keep the spaces on their left
if tag_type != 'partial':
# But other tags don't
literal = literal.rstrip(' ')

# Start yielding
# Ignore literals that are empty
if literal != '':
yield ('literal', literal)

# Ignore comments and set delimiters
if tag_type not in ['comment', 'set delimiter?']:
yield (tag_type, tag_key)

# If there are any open sections when we're done
if open_sections:
# Then we need to complain
raise SyntaxError("End of file while a section was open")
try:
while template:
literal, template = grab_literal(template, l_del)

# If the template is completed
if not template:
# Then yield the literal and leave
yield ('literal', literal)
break

# Do the first check to see if we could be a standalone
is_standalone = l_sa_check(template, literal, is_standalone)

# Parse the tag
tag, template = parse_tag(template, l_del, r_del)
tag_type, tag_key = tag

# Special tag logic

# If we are a set delimiter tag
if tag_type == 'set delimiter':
# Then get and set the delimiters
dels = tag_key.strip().split(' ')
l_del, r_del = dels[0], dels[-1]

# If we are a section tag
elif tag_type in ['section', 'inverted section']:
# Then open a new section
open_sections.append(tag_key)

# If we are an end tag
elif tag_type == 'end':
# Then check to see if the last opened section
# is the same as us
try:
last_section = open_sections.pop()
except IndexError:
# No open sections, we should complain
raise SyntaxError('End tag without any currently '
'open tags')

if tag_key != last_section:
# Otherwise we need to complain
raise SyntaxError('End tag does not match '
'the currently opened section')

# Do the second check to see if we're a standalone
is_standalone = r_sa_check(template, tag_type, is_standalone)

# Which if we are
if is_standalone:
# Remove the stuff before the newline
template = template.split('\n', 1)[-1]

# Partials need to keep the spaces on their left
if tag_type != 'partial':
# But other tags don't
literal = literal.rstrip(' ')

# Start yielding
# Ignore literals that are empty
if literal != '':
yield ('literal', literal)

# Ignore comments and set delimiters
if tag_type not in ['comment', 'set delimiter?']:
yield (tag_type, tag_key)

# If there are any open sections when we're done
if open_sections:
# Then we need to complain
raise SyntaxError("End of file while a section was open")

except SyntaxError as e:
if debugging:
try:
from debugger import debug
debug(org_template)
except ImportError:
error = e
else:
error = e

if error:
raise SyntaxError(error)
6 changes: 4 additions & 2 deletions test_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,13 @@ class ExpandedCoverage(unittest.TestCase):

def test_unclosed_sections(self):
test1 = {
'template': '{{# section }} oops {{/ wrong_section }}'
'template': '{{# section }} oops {{/ wrong_section }}',
'debugging': False
}

test2 = {
'template': '{{# section }} end of file'
'template': '{{# section }} end of file',
'debugging': False
}

self.assertRaises(SyntaxError, chevron.render, **test1)
Expand Down

0 comments on commit dd121b3

Please sign in to comment.