Source code for py1.template_reader

# 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)