PK\H \eztemplate/version.py__version__ = '0.2.6' PK+WHLg66eztemplate/__main__.py#!/usr/bin/env python """Provide a simple templating system for text files.""" from __future__ import absolute_import from __future__ import print_function import argparse import errno import os import os.path import re import sys from . import engines from . import __version__ def is_filelike(ob): """Check for filelikeness of an object. Needed to distinguish it from file names. Returns true if it has a read or a write method. """ if hasattr(ob, 'read') and callable(ob.read): return True if hasattr(ob, 'write') and callable(ob.write): return True return False class _PyArg(str): """Wrap a command line python argument. Makes it distinguishable from a plain text argument. """ pass def parse_args(args=None): """Parse command line arguments.""" # The argparse module provides a nice abstraction for argument parsing. # It automatically builds up the help text, too. parser = argparse.ArgumentParser( prog=__package__, description='Make substitutions in text files.', ) parser.add_argument('-V', '--version', action='version', version="%%(prog)s %s" % (__version__,), ) group = parser.add_argument_group("Engine") group.add_argument('-e', '--engine', dest='engine', default='string.Template', help="templating engine", metavar="ENGINE", ) group.add_argument('-t', '--tolerant', action='store_true', dest='tolerant', help="don't fail on missing names", ) group = parser.add_argument_group("Output") group.add_argument('-s', '--stdout', action='append_const', dest='outfiles', const=sys.stdout, help="use standard output", ) group.add_argument('-o', '--outfile', action='append', dest='outfiles', help="output file", metavar="FILE", ) group.add_argument('--vary', action='store_true', dest='vary', help="vary output file name according to template", ) group.add_argument('-r', '--read-old', action='store_true', dest='read_old', help="read preexisting output files and" "hand the respective content to the template", ) group.add_argument('-d', '--delete-empty', action='store_true', dest='delete_empty', help="delete file if output is empty", ) group = parser.add_argument_group("Input") group.add_argument('--stdin', action='append_const', dest='infiles', const=sys.stdin, help="use standard input", ) group.add_argument('-i', '--infile', action='append', dest='infiles', help="any number of input files", metavar="FILE", ) group.add_argument('-c', '--concatenate', action='store_true', dest='concatenate', help="concatenate multiple input files into one output", ) group = parser.add_argument_group("Name-value pairs") group.add_argument('-a', '--arg', action='append', dest='args', help="any number of name-value pairs", metavar="NAME=VALUE", ) group.add_argument('-p', '--pyarg', action='append', dest='args', type=_PyArg, help="evaluate a python expression", metavar="NAME=EXPRESSION", ) group.add_argument('-n', '--next', action='append_const', dest='args', const='--', help="begin next argument group", ) parser.add_argument( dest='remainder', nargs=argparse.REMAINDER, help="possible input files and name-value pair groups " "if not already specified through options", ) args = parser.parse_args(args) if args.engine == 'help': dump_engines() parser.exit(0) if args.engine not in engines.engines: parser.error("Engine '%s' is not available." % (args.engine,)) if args.vary: if len(args.outfiles) != 1: parser.error("need exactly one output file template") if is_filelike(args.outfiles[0]): parser.error("vary requires an output file template") elif not args.outfiles: args.outfiles = [sys.stdout] if not args.infiles: if args.args: infiles = args.remainder args.remainder = [] try: infiles.remove('--') except ValueError: pass else: first = 1 if args.remainder and args.remainder[0] == '--' else 0 last = (len(args.remainder) if args.vary or args.concatenate else first + 1) for split, infile in enumerate(args.remainder[first:last], first): if infile == '--' or '=' in infile: break else: split = last infiles = args.remainder[first:split] args.remainder = args.remainder[split:] args.infiles = [path if path != '-' else sys.stdin for path in infiles] if infiles else [sys.stdin] if args.args: flat_args = args.args else: flat_args = args.remainder args.remainder = [] if flat_args and flat_args[0] == '--': flat_args = flat_args[1:] args.args = [] mapping = {} for arg in flat_args: if isinstance(arg, _PyArg): name_value = arg.split('=', 1) mapping[name_value[0]] = eval(name_value[1], {}, mapping) elif arg == '--': args.args.append(mapping) mapping = {} else: name_value = arg.split('=', 1) mapping[name_value[0]] = (name_value[1] if len(name_value) > 1 else None) args.args.append(mapping) if args.remainder: parser.error("extraneous arguments left over") else: del args.remainder return args def dump_engines(target=sys.stderr): """Print successfully imported templating engines.""" print("Available templating engines:", file=target) width = max(len(engine) for engine in engines.engines) for handle, engine in sorted(engines.engines.items()): description = engine.__doc__.split('\n', 0)[0] print(" %-*s - %s" % (width, handle, description), file=target) def check_engine(handle): """Check availability of requested template engine.""" if handle == 'help': dump_engines() sys.exit(0) if handle not in engines.engines: print('Engine "%s" is not available.' % (handle,), file=sys.stderr) sys.exit(1) def make_mapping(args): """Make a mapping from the name=value pairs.""" mapping = {} if args: for arg in args: name_value = arg.split('=', 1) mapping[name_value[0]] = (name_value[1] if len(name_value) > 1 else None) return mapping def make_path_properties(file_or_path, prefix=''): """Build useful properties from a file path.""" is_std = file_or_path in (sys.stdin, sys.stdout, sys.stderr) if is_std: path = '-' elif is_filelike(file_or_path): try: path = str(file_or_path.name) except AttributeError: path = None else: path = str(file_or_path) if is_std or not path: abspath = dirname = basename = stem = ext = None realpath = realdrive = realdir = realbase = realstem = realext = None numbers = num = None else: abspath = os.path.abspath(path) dirname, basename = os.path.split(path) stem, ext = os.path.splitext(basename) if not dirname: dirname = os.curdir realpath = os.path.realpath(path) realdrive, tail = os.path.splitdrive(realpath) realdir, realbase = os.path.split(tail) realstem, realext = os.path.splitext(realbase) numbers = [int(s) for s in re.findall(r'\d+', basename)] num = numbers[-1] if numbers else None return { prefix + 'path': path, prefix + 'abspath': abspath, prefix + 'dirname': dirname, prefix + 'basename': basename, prefix + 'stem': stem, prefix + 'ext': ext, prefix + 'realpath': realpath, prefix + 'realdrive': realdrive, prefix + 'realdir': realdir, prefix + 'realbase': realbase, prefix + 'realstem': realstem, prefix + 'realext': realext, prefix + 'numbers': numbers, prefix + 'num': num, } def constant_outfile_iterator(outfiles, infiles, arggroups): """Iterate over all output files.""" assert len(infiles) == 1 assert len(arggroups) == 1 return ((outfile, infiles[0], arggroups[0]) for outfile in outfiles) def variable_outfile_iterator(outfiles, infiles, arggroups, engine): """Iterate over variable output file name template.""" assert len(outfiles) == 1 template = engine(outfiles[0], tolerant=False) for infile in infiles: properties = make_path_properties(infile, prefix='') for arggroup in arggroups: outfile = template.apply(dict(arggroup, **properties)) yield (outfile, infile, arggroup) class CachedTemplateReader(object): """Read templates and cache them.""" def __init__(self, engine, tolerant=False): """Initialize reader.""" self._engine = engine self._tolerant = tolerant self._cached_templates = {} def read(self, file_or_path): """Read template from cache or file.""" if file_or_path in self._cached_templates: return self._cached_templates[file_or_path] if is_filelike(file_or_path): template = file_or_path.read() dirname = None else: with open(file_or_path, 'r') as f: template = f.read() dirname = os.path.dirname(file_or_path) template = self._engine(template, dirname=dirname, tolerant=self._tolerant) self._cached_templates[file_or_path] = template return template def process_combinations(combinations, engine, tolerant=False, read_old=False, delete_empty=False, ): """Process outfile-infile-arggroup combinations.""" outfiles = set() templatereader = CachedTemplateReader(engine, tolerant=tolerant) for outfile, infile, arggroup in combinations: template = templatereader.read(infile) properties = make_path_properties(outfile, prefix='ez_') if read_old: if is_filelike(outfile): raise Exception("cannot read already open output streams") try: with open(outfile, 'r') as f: properties['ez_content'] = f.read() except IOError: properties['ez_content'] = None result = template.apply(dict(arggroup, **properties)) if is_filelike(outfile): if result: outfile.write(result) elif result or not delete_empty: if outfile in outfiles: raise IOError("trying to write twice to the same file") outfiles.add(outfile) with open(outfile, 'w') as f: f.write(result) else: try: os.remove(outfile) except OSError as e: if e.errno != errno.ENOENT: raise def perform_templating(args): """Perform templating according to the given arguments.""" engine = engines.engines[args.engine] if args.vary: it = variable_outfile_iterator(args.outfiles, args.infiles, args.args, engine) else: it = constant_outfile_iterator(args.outfiles, args.infiles, args.args) process_combinations(it, engine, tolerant=args.tolerant, read_old=args.read_old, delete_empty=args.delete_empty, ) def main_command(): """Parse command line arguments and perform main action.""" args = parse_args() perform_templating(args) if __name__ == '__main__': sys.exit(main_command()) PKSVH6eztemplate/__init__.py#!/usr/bin/env python from __future__ import absolute_import from __future__ import print_function from .version import __version__ PKVHvв!eztemplate/engines/mako_engine.py#!/usr/bin/env python """Provide the mako templating engine.""" from __future__ import absolute_import from __future__ import print_function from mako.template import Template from mako.lookup import TemplateLookup from . import Engine class MakoEngine(Engine): """Mako templating engine.""" handle = 'mako' def __init__(self, template, dirname=None, tolerant=False, **kwargs): """Initialize mako template.""" super(MakoEngine, self).__init__(**kwargs) directories = [dirname] if dirname is not None else ['.'] lookup = TemplateLookup(directories=directories) default_filters = ['filter_undefined'] if tolerant else None encoding_errors = 'replace' if tolerant else 'strict' imports = ['def filter_undefined(value):\n' ' if value is UNDEFINED:\n' ' return \'\'\n' ' return value\n'] self.template = Template(template, default_filters=default_filters, encoding_errors=encoding_errors, imports=imports, lookup=lookup, strict_undefined=not tolerant, ) def apply(self, mapping): """Apply a mapping of name-value-pairs to a template.""" return self.template.render(**mapping) PKVHM4,eztemplate/engines/string_template_engine.py#!/usr/bin/env python """Provide the standard Python string.Template engine.""" from __future__ import absolute_import from __future__ import print_function from string import Template from . import Engine class StringTemplate(Engine): """String.Template engine.""" handle = 'string.Template' def __init__(self, template, tolerant=False, **kwargs): """Initialize string.Template.""" super(StringTemplate, self).__init__(**kwargs) self.template = Template(template) self.tolerant = tolerant def apply(self, mapping): """Apply a mapping of name-value-pairs to a template.""" mapping = {name: self.str(value, tolerant=self.tolerant) for name, value in mapping.items() if value is not None or self.tolerant} if self.tolerant: return self.template.safe_substitute(mapping) return self.template.substitute(mapping) PK\H'vo!eztemplate/engines/empy_engine.py#!/usr/bin/env python """Provide the empy templating engine.""" from __future__ import absolute_import from __future__ import print_function import os.path import em try: from StringIO import StringIO except ImportError: from io import StringIO from . import Engine class SubsystemWrapper(em.Subsystem): """Wrap EmPy's Subsystem class. Allows to open files relative to a base directory. """ def __init__(self, basedir=None, **kwargs): """Initialize Subsystem plus a possible base directory.""" em.Subsystem.__init__(self, **kwargs) self.basedir = basedir def open(self, name, *args, **kwargs): """Open file, possibly relative to a base directory.""" if self.basedir is not None: name = os.path.join(self.basedir, name) return em.Subsystem.open(self, name, *args, **kwargs) class EmpyEngine(Engine): """Empy templating engine.""" handle = 'empy' def __init__(self, template, dirname=None, **kwargs): """Initialize empy template.""" super(EmpyEngine, self).__init__(**kwargs) if dirname is not None: # FIXME: This is a really bad idea, as it works like a global. # Blame EmPy. em.theSubsystem = SubsystemWrapper(basedir=dirname) self.output = StringIO() self.interpreter = em.Interpreter(output=self.output) self.template = template def apply(self, mapping): """Apply a mapping of name-value-pairs to a template.""" self.output.seek(0) self.output.truncate(0) self.interpreter.string(self.template, locals=mapping) return self.output.getvalue() PKVHqQeztemplate/engines/__init__.py#!/usr/bin/env python """Templating engine package.""" from __future__ import absolute_import from __future__ import print_function import collections import itertools import numbers import sys try: basestring except NameError: basestring = str class Engine(object): """Abstract class representing a templating engine.""" handle = None def __init__(self, dirname=None, tolerant=False, **kwargs): """Initialize template, potentially "compiling" it.""" assert self.__class__ is not Engine, ( "must only instantiate subclasses of Engine") super(Engine, self).__init__(**kwargs) if tolerant: print("WARNING: This engine doesn't support tolerant mode", file=sys.stderr) def str(self, value, tolerant=False, limit=1000, seen=frozenset()): """Transform value into a representation suitable for substitution.""" if value is None: if tolerant: return "" raise ValueError("value is None") if isinstance(value, (bool, numbers.Number, basestring)): return str(value) if not isinstance(value, collections.Iterable): if not tolerant: raise ValueError("unknown value type") try: name = value.name except AttributeError: try: name = value.__name__ except AttributeError: try: name = value.__class__.__name__ except AttributeError: return "" return "<%s>" % (name,) is_mapping = isinstance(value, collections.Mapping) if not seen: wrap = "%s" elif is_mapping: wrap = "{%s}" else: wrap = "[%s]" id_ = id(value) if id_ in seen: if tolerant: return wrap % ("...",) raise ValueError("recursive representation") seen = seen.union((id_,)) if is_mapping: items = [(self.str(n, tolerant=tolerant, limit=limit, seen=seen), self.str(v, tolerant=tolerant, limit=limit, seen=seen)) for n, v in value.items()] items.sort() items = ("%s=%s" for n, v in items) else: it = iter(value) items = [self.str(item, tolerant=tolerant, limit=limit, seen=seen) for item in itertools.islice( it, len(value) if isinstance(value, collections.Sized) else limit)] items.sort() try: next(it) except StopIteration: pass else: if not tolerant: raise ValueError("iterable too long") items.append("...") return wrap % (", ".join(items),) def apply(self, mapping): """Apply a mapping of name-value-pairs to a template.""" raise NotImplementedError engines = {} def _init(): """Dynamically import engines that initialize successfully.""" import importlib import os import re filenames = os.listdir(os.path.dirname(__file__)) module_names = set() for filename in filenames: match = re.match(r'^(?P[A-Z_a-z]\w*)\.py[co]?$', filename) if match: module_names.add(match.group('name')) for module_name in module_names: try: module = importlib.import_module('.' + module_name, __name__) except ImportError: continue for name, member in module.__dict__.items(): if not isinstance(member, type): # skip non-new-style classes continue if not issubclass(member, Engine): # skip non-subclasses of Engine continue if member is Engine: # skip "abstract" class Engine continue try: handle = member.handle except AttributeError: continue engines[handle] = member _init() PK\Hל''*eztemplate-0.2.6.dist-info/DESCRIPTION.rsteztemplate ========== Simple templating program to generate plain text (like config files) from name-value pairs. Lets you create text files from templates in a versatile way. It's designed with easy operation from the command line or scripts, Makefiles, etc. in mind. You can make use of several third party templating engines like **mako** or **empy** as well as simple built-in ones. Installation ------------ from PyPI into a virtualenv (recommended) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ With **virtualenv** you create a separate python environment without affecting the rest of your system, therefore this approach is recommended for playing around. Install virtualenv ^^^^^^^^^^^^^^^^^^ on Debian-based distributions (such as Ubuntu) '''''''''''''''''''''''''''''''''''''''''''''' .. code:: sh $ sudo apt-get install virtualenv on Fedora ''''''''' .. code:: sh $ sudo yum install python-virtualenv Install eztemplate ^^^^^^^^^^^^^^^^^^ .. code:: sh $ virtualenv myvenv # create a new environment in a subdirectory $ . myvenv/bin/activate # switch to virtualenv (important) $ pip install eztemplate # install eztemplate from PyPI $ eztemplate --version # check if the correct version was installed Upgrade eztemplate ^^^^^^^^^^^^^^^^^^ .. code:: sh $ . myvenv/bin/activate # switch to virtualenv (if not there already) $ pip install --upgrade eztemplate # upgrade eztemplate from PyPI $ eztemplate --version # check if the corrent version was installed from a git repository into a virtualenv ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This is a good approach if you work on the repository and want to test the changes. Install eztemplate ^^^^^^^^^^^^^^^^^^ .. code:: sh $ git clone https://github.com/blubberdiblub/eztemplate.git $ cd eztemplate # change into the cloned repository $ virtualenv venv # create a new environment in a subdirectory $ . venv/bin/activate # switch to virtualenv (important) $ pip install . # just specify the directory to install from $ eztemplate --version # check if the correct version was installed Upgrade eztemplate ^^^^^^^^^^^^^^^^^^ .. code:: sh $ git pull # pull latest commits from remote repository $ . venv/bin/activate # switch to virtualenv (if not there already) $ pip install --upgrade --force-reinstall . # force upgrade eztemplate $ eztemplate --version # check if the correct version was installed from PyPI as system command ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Install eztemplate ^^^^^^^^^^^^^^^^^^ .. code:: sh $ pip install eztemplate # install eztemplate from PyPI $ eztemplate --version # check if the correct version was installed Upgrade eztemplate ^^^^^^^^^^^^^^^^^^ .. code:: sh $ pip install --upgrade eztemplate # upgrade eztemplate from PyPI $ eztemplate --version # check if the corrent version was installed Usage ----- Getting quick help ~~~~~~~~~~~~~~~~~~ Use the help option: .. code:: sh $ eztemplate --help You can also call the package explictly with Python (and thereby choose which Python installation to use): .. code:: sh $ python -m eztemplate --help Running without arguments ~~~~~~~~~~~~~~~~~~~~~~~~~ When you run ``eztemplate`` without arguments, it will expect a template on standard input, possibly waiting forever: .. code:: sh $ eztemplate Hello, world! Hello, world! $ On \_\_\*ix\_\_ terminals you can manually cause an end of file by pressing ``Ctrl-D``. Quick demonstration ~~~~~~~~~~~~~~~~~~~ You can check that substitution is working by piping a template into the program and specifying a name-value pair (make sure to protect the string with single quotes, otherwise the shell believes you want to substitute a shell variable, replacing it by an empty string): .. code:: sh $ echo 'Hello, $entity.' | eztemplate entity=world Hello, world. $ When you're calling ``eztemplate`` from a script or similar - i. e. non-interactively - you should specify everything as explicitly as possible (in particular all input files or *stdin* as well as name-value pairs) and refrain from using positional arguments. Everything can be specified using options, which avoids ambiguities: .. code:: sh $ echo 'Hello, $entity.' | eztemplate --stdin --arg entity=world Hello, world. $ Templating engines ------------------ **eztemplate** supports several templating engines. You select the one you want to use with the ``-e`` or ``--engine`` option. Specifying ``help`` instead of a name will list all currently available engines: .. code:: sh $ eztemplate -e help Available templating engines: empy - Empy templating engine. mako - Mako templating engine. string.Template - String.Template engine. $ Engines missing the required packages, modules or libraries will not be displayed. For instance to be able to use the ``mako`` or the ``empy`` engine, you need to have the respective python packages installed and working. However, **eztemplate** comes with simple built-in engines which are available at all times. The ``string.Template`` engine is the default when you don't explicitly specify one. string.Template engine ~~~~~~~~~~~~~~~~~~~~~~ This engine is named after the `string.Template class `__ in the Python standard library. It substitutes identifiers beginning with a dollar sign. To resolve ambiguities, you can also enclose the identifier in curly braces. It's similar to shell variable subsitution minus the more sophisticated features. It suffices for simple cases where you just need to insert some values into a text: .. code:: bash $ eztemplate --stdin \ > --arg user="$( getent passwd "$USER" | cut -d: -f5 | cut -d, -f1 )" \ > --arg food=cake --arg vendor=cafeteria --arg price="$RANDOM" \ > <<\EOF > Hello, $user. > > If you're hungry, get some ${food}s from the $vendor. > They're only $$$price per piece. > EOF Hello, Niels Boehm. If you're hungry, get some cakes from the cafeteria. They're only $29993 per piece. $ PK\HsAA+eztemplate-0.2.6.dist-info/entry_points.txt[console_scripts] eztemplate = eztemplate.__main__:main_command PK\H=SS(eztemplate-0.2.6.dist-info/metadata.json{"classifiers": ["Development Status :: 4 - Beta", "Environment :: Console", "Intended Audience :: End Users/Desktop", "Intended Audience :: System Administrators", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Topic :: System :: Systems Administration", "Topic :: Text Processing :: General", "Topic :: Utilities"], "extensions": {"python.commands": {"wrap_console": {"eztemplate": "eztemplate.__main__:main_command"}}, "python.details": {"contacts": [{"email": "blubberdiblub@gmail.com", "name": "Niels Boehm", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/blubberdiblub/eztemplate/"}}, "python.exports": {"console_scripts": {"eztemplate": "eztemplate.__main__:main_command"}}}, "extras": ["empy", "mako"], "generator": "bdist_wheel (0.26.0)", "keywords": ["templating", "text"], "license": "MIT", "metadata_version": "2.0", "name": "eztemplate", "run_requires": [{"requires": ["argparse"]}, {"extra": "empy", "requires": ["empy"]}, {"extra": "mako", "requires": ["mako"]}], "summary": "Simple templating program to generate plain text (like config files) from name-value pairs.", "version": "0.2.6"}PK\H> (eztemplate-0.2.6.dist-info/top_level.txteztemplate PK\H''\\ eztemplate-0.2.6.dist-info/WHEELWheel-Version: 1.0 Generator: bdist_wheel (0.26.0) Root-Is-Purelib: true Tag: py2-none-any PK\HVV#eztemplate-0.2.6.dist-info/METADATAMetadata-Version: 2.0 Name: eztemplate Version: 0.2.6 Summary: Simple templating program to generate plain text (like config files) from name-value pairs. Home-page: https://github.com/blubberdiblub/eztemplate/ Author: Niels Boehm Author-email: blubberdiblub@gmail.com License: MIT Keywords: templating,text Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Environment :: Console Classifier: Intended Audience :: End Users/Desktop Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Topic :: System :: Systems Administration Classifier: Topic :: Text Processing :: General Classifier: Topic :: Utilities Requires-Dist: argparse Provides-Extra: empy Requires-Dist: empy; extra == 'empy' Provides-Extra: mako Requires-Dist: mako; extra == 'mako' eztemplate ========== Simple templating program to generate plain text (like config files) from name-value pairs. Lets you create text files from templates in a versatile way. It's designed with easy operation from the command line or scripts, Makefiles, etc. in mind. You can make use of several third party templating engines like **mako** or **empy** as well as simple built-in ones. Installation ------------ from PyPI into a virtualenv (recommended) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ With **virtualenv** you create a separate python environment without affecting the rest of your system, therefore this approach is recommended for playing around. Install virtualenv ^^^^^^^^^^^^^^^^^^ on Debian-based distributions (such as Ubuntu) '''''''''''''''''''''''''''''''''''''''''''''' .. code:: sh $ sudo apt-get install virtualenv on Fedora ''''''''' .. code:: sh $ sudo yum install python-virtualenv Install eztemplate ^^^^^^^^^^^^^^^^^^ .. code:: sh $ virtualenv myvenv # create a new environment in a subdirectory $ . myvenv/bin/activate # switch to virtualenv (important) $ pip install eztemplate # install eztemplate from PyPI $ eztemplate --version # check if the correct version was installed Upgrade eztemplate ^^^^^^^^^^^^^^^^^^ .. code:: sh $ . myvenv/bin/activate # switch to virtualenv (if not there already) $ pip install --upgrade eztemplate # upgrade eztemplate from PyPI $ eztemplate --version # check if the corrent version was installed from a git repository into a virtualenv ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This is a good approach if you work on the repository and want to test the changes. Install eztemplate ^^^^^^^^^^^^^^^^^^ .. code:: sh $ git clone https://github.com/blubberdiblub/eztemplate.git $ cd eztemplate # change into the cloned repository $ virtualenv venv # create a new environment in a subdirectory $ . venv/bin/activate # switch to virtualenv (important) $ pip install . # just specify the directory to install from $ eztemplate --version # check if the correct version was installed Upgrade eztemplate ^^^^^^^^^^^^^^^^^^ .. code:: sh $ git pull # pull latest commits from remote repository $ . venv/bin/activate # switch to virtualenv (if not there already) $ pip install --upgrade --force-reinstall . # force upgrade eztemplate $ eztemplate --version # check if the correct version was installed from PyPI as system command ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Install eztemplate ^^^^^^^^^^^^^^^^^^ .. code:: sh $ pip install eztemplate # install eztemplate from PyPI $ eztemplate --version # check if the correct version was installed Upgrade eztemplate ^^^^^^^^^^^^^^^^^^ .. code:: sh $ pip install --upgrade eztemplate # upgrade eztemplate from PyPI $ eztemplate --version # check if the corrent version was installed Usage ----- Getting quick help ~~~~~~~~~~~~~~~~~~ Use the help option: .. code:: sh $ eztemplate --help You can also call the package explictly with Python (and thereby choose which Python installation to use): .. code:: sh $ python -m eztemplate --help Running without arguments ~~~~~~~~~~~~~~~~~~~~~~~~~ When you run ``eztemplate`` without arguments, it will expect a template on standard input, possibly waiting forever: .. code:: sh $ eztemplate Hello, world! Hello, world! $ On \_\_\*ix\_\_ terminals you can manually cause an end of file by pressing ``Ctrl-D``. Quick demonstration ~~~~~~~~~~~~~~~~~~~ You can check that substitution is working by piping a template into the program and specifying a name-value pair (make sure to protect the string with single quotes, otherwise the shell believes you want to substitute a shell variable, replacing it by an empty string): .. code:: sh $ echo 'Hello, $entity.' | eztemplate entity=world Hello, world. $ When you're calling ``eztemplate`` from a script or similar - i. e. non-interactively - you should specify everything as explicitly as possible (in particular all input files or *stdin* as well as name-value pairs) and refrain from using positional arguments. Everything can be specified using options, which avoids ambiguities: .. code:: sh $ echo 'Hello, $entity.' | eztemplate --stdin --arg entity=world Hello, world. $ Templating engines ------------------ **eztemplate** supports several templating engines. You select the one you want to use with the ``-e`` or ``--engine`` option. Specifying ``help`` instead of a name will list all currently available engines: .. code:: sh $ eztemplate -e help Available templating engines: empy - Empy templating engine. mako - Mako templating engine. string.Template - String.Template engine. $ Engines missing the required packages, modules or libraries will not be displayed. For instance to be able to use the ``mako`` or the ``empy`` engine, you need to have the respective python packages installed and working. However, **eztemplate** comes with simple built-in engines which are available at all times. The ``string.Template`` engine is the default when you don't explicitly specify one. string.Template engine ~~~~~~~~~~~~~~~~~~~~~~ This engine is named after the `string.Template class `__ in the Python standard library. It substitutes identifiers beginning with a dollar sign. To resolve ambiguities, you can also enclose the identifier in curly braces. It's similar to shell variable subsitution minus the more sophisticated features. It suffices for simple cases where you just need to insert some values into a text: .. code:: bash $ eztemplate --stdin \ > --arg user="$( getent passwd "$USER" | cut -d: -f5 | cut -d, -f1 )" \ > --arg food=cake --arg vendor=cafeteria --arg price="$RANDOM" \ > <<\EOF > Hello, $user. > > If you're hungry, get some ${food}s from the $vendor. > They're only $$$price per piece. > EOF Hello, Niels Boehm. If you're hungry, get some cakes from the cafeteria. They're only $29993 per piece. $ PK\H_!eztemplate-0.2.6.dist-info/RECORDeztemplate/__init__.py,sha256=dkwNz5XIdgx3vG4UTUIB9qa-9BGrFkhneISTRjWqVdM,134 eztemplate/__main__.py,sha256=l3xb8eZn3dYuXESv7f0A_t3SRZvxwUUxOS6ODgB-fh8,13980 eztemplate/version.py,sha256=T150U4daRZ7ULOwxGUzzoBDAqm3bW9UydvCf0KJii9I,22 eztemplate/engines/__init__.py,sha256=sQr4ZetyLMKa5Ub417114bGPCsqZanxrbsWPPXN1IjU,4290 eztemplate/engines/empy_engine.py,sha256=8KD_Cpw1u2DpSbnZWahhS41NCS8scfg9-igc3UjAlgg,1692 eztemplate/engines/mako_engine.py,sha256=89IYeVqXvRm7ewvm6iYdmWK-zbcpOJ0QVFe8FLqzBoo,1458 eztemplate/engines/string_template_engine.py,sha256=-902OVTL1mfKfR1b_LcR_a6H9eEMU1DYYZ5NB98dBCY,951 eztemplate-0.2.6.dist-info/DESCRIPTION.rst,sha256=rTtwli9IJNQ0Z4H0Udibffd7zBPYHdEVSSBKh9uPnyM,6439 eztemplate-0.2.6.dist-info/METADATA,sha256=kwWgVreX-cJIR0KmNtJntXfn3ysF2Y0SI-vOaBMw6nA,7510 eztemplate-0.2.6.dist-info/RECORD,, eztemplate-0.2.6.dist-info/WHEEL,sha256=JTb7YztR8fkPg6aSjc571Q4eiVHCwmUDlX8PhuuqIIE,92 eztemplate-0.2.6.dist-info/entry_points.txt,sha256=YIFhLxy1ZOo-x6f6B4iyK9gybrntgxJsZfRraUf0FwE,65 eztemplate-0.2.6.dist-info/metadata.json,sha256=ffAlnQFyaNQ9rbMK8hCsk8UIAlaSBd1mMxD7Li_PQM8,1363 eztemplate-0.2.6.dist-info/top_level.txt,sha256=rpkNxtoXdEZNDrXk75p3xeUMGdQAHRevburMWqmHtYI,11 PK\H \eztemplate/version.pyPK+WHLg66Ieztemplate/__main__.pyPKSVH67eztemplate/__init__.pyPKVHvв!7eztemplate/engines/mako_engine.pyPKVHM4,=eztemplate/engines/string_template_engine.pyPK\H'vo!Aeztemplate/engines/empy_engine.pyPKVHqQHeztemplate/engines/__init__.pyPK\Hל''*Yeztemplate-0.2.6.dist-info/DESCRIPTION.rstPK\HsAA+ seztemplate-0.2.6.dist-info/entry_points.txtPK\H=SS(seztemplate-0.2.6.dist-info/metadata.jsonPK\H> (0yeztemplate-0.2.6.dist-info/top_level.txtPK\H''\\ yeztemplate-0.2.6.dist-info/WHEELPK\HVV#zeztemplate-0.2.6.dist-info/METADATAPK\H_!eztemplate-0.2.6.dist-info/RECORDPKZ