# Copyright (c) 2013-2015, Brice Arnould <unbrice@vleu.net>
# All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following condition are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
"""The template with the conveniency variables."""
import pkgutil
import optparse
import os
import sys
import warnings
from py1 import curly
from py1 import runner
_TOP_LEVEL_INDENT = ' ' * 4
[docs]class Error(Exception):
"""Base class for errors from this module."""
[docs]class InvalidTemplateError(Error):
"""Raised by build_code when the template is invalid."""
def _get_template():
"""Returns the content of user_template_reader.py as a utf-8 string."""
# pkgutil.get_data triggers a ResourceWarnin'g on Python 3.2
if sys.version_info[:2] == (3, 2):
warnings.simplefilter('ignore', ResourceWarning)
bytes = pkgutil.get_data(__name__, 'user_code_template.py')
return bytes.decode('utf-8')
def _read_template_until(iterator, searched):
"""Reads iterator until we find `searched`.
Args:
iterator: Will be consumed until we find `searched`.
searched (str): The string we are looking for.
Returns:
list(str): Consumed elements from `iterator`.
Raises:
InvalidTemplateError: Could not find `searched`.
"""
result = []
for s in iterator:
if s == searched:
return result
result.append(s)
raise InvalidTemplateError('Could not find "%s"' % searched)
def _sum_up_defs(iterator):
"""Yields the first lines of top level blocks in the code it iterates on.
Also coalesces consecutive line feeds.
Assumes an indent of _TOP_LEVEL_INDENT.
Args:
iterator: Iterator over lines of python code as string.
"""
show_pydoc = False
for line in iterator:
stripped = line.strip()
hidden_def = stripped.startswith('def _')
if not stripped:
continue
elif line.startswith(_TOP_LEVEL_INDENT):
if show_pydoc and stripped.startswith('"""'):
if stripped.endswith('"""'):
yield line
else:
yield line + '[...]"""'
yield _TOP_LEVEL_INDENT + '# [Pass --code=full for full code]'
show_pydoc = False
elif not hidden_def:
yield line
show_pydoc = stripped.startswith('def ') and not hidden_def
[docs]def build_code(begin, each_line, end, concise):
"""Instantiates the templates running the specified code.
Args:
begin: List of strings, will be run before the main loop.
each_line: List of strings, will be run in the main loop.
end: List of strings, will be run just after the main loop.
concise: boolean, if true the code won't actually be runnable but will
be shorter.
"""
result = []
# An iterator over the template's line. We advance it while consuming.
reader = iter(_get_template().splitlines())
# Start discarding everything above the code.
_read_template_until(
reader, '# __________ REMOVE EVERYTHING ABOVE __________')
prelude = _read_template_until(
reader, '# __________ INSERT USER CODE FOR --begin HERE __________')
if concise:
prelude = _sum_up_defs(prelude)
result.extend(prelude)
if begin:
result.append('## --begin')
result += begin
result.append('')
for_loop = _read_template_until(
reader, _TOP_LEVEL_INDENT +
'# __________ INSERT USER CODE FOR --each-line HERE __________')
if each_line:
result.extend(for_loop)
for s in each_line:
result.append(_TOP_LEVEL_INDENT + '## --each-line')
result.append(_TOP_LEVEL_INDENT
+ s.replace('\n', '\n' + _TOP_LEVEL_INDENT))
result.append('')
result.extend(_read_template_until(
reader, '# __________ INSERT USER CODE FOR --end HERE __________'))
if end:
result.append('## --end')
result += end
result.append('')
result.extend(reader)
return '\n'.join(result)