#!/usr/bin/env python # -*- coding: ascii -*- """ Commandline-tool for pyratemp. - check template for syntax-errors - render templates with data and print the result in utf-8. Errors and help-messages are written to stderr, resulting data to stdout. Data can be read as key-value-pairs from the command-line and/or from JSON- and YAML-files. Additionally, ``date`` and ``mtime_CCMMYYDD`` are set to the current date as "%Y-%m-%d". By default, HTML-escaping is used for "*.htm" and "*.html" and LaTeX-escaping for "*.tex". Exit-codes: - 0: ok - 1: some Python-modules are missing (import error) - 2: --help / usage printed - 3: invalid command-line options - 10: template syntax-error / parse error - 20: datafile error / cannot load data - 30: render error :Version: 0.3.2 :Usage: see USAGE or "pyratemp_tool.py --help" :Requires: Python >= 2.6 / 3.x, pyratemp, (optional: yaml) :Author: Roland Koebler (rk at simple-is-better dot org) :Copyright: Roland Koebler :License: MIT/X11-like, see __license__ :RCS: $Id: pyratemp_tool.py,v 1.16 2013/09/17 07:45:04 rk Exp $ """ from __future__ import unicode_literals from __future__ import print_function __version__ = "0.3.2" __author__ = "Roland Koebler " __license__ = """Copyright (c) 2007-2013 by Roland Koebler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.""" #----------------------------------------- USAGE = """pyratemp_tool.py [-s] <-d NAME=VALUE> <-f DATAFILE [-N NAME] [-n NR_OF_ENTRY]> [--xml] TEMPLATEFILES -s syntax-check only (don't render the template) -d define variables (these also override the values from files) -f use variables from a JSON/YAML file -n use nth entry of the JSON/YAML-file (JSON: n-th element of the root-array, YAML: n-th entry) -N namespace for variables from the JSON/YAML file --xml encode output as ASCII+xmlcharrefreplace (instead of utf-8) """ #----------------------------------------- import os, sys, getopt, time try: import pyratemp except ImportError: print("ERROR: Python-module 'pyratemp' not found.", file=sys.stderr) sys.exit(1) #----------------------------------------- def parse(template_name): """Parse template + set encoding according to filename-extension. :Returns: the parsed template """ ext = os.path.splitext(template_name)[1] if ext == ".htm" or ext == ".html": t = pyratemp.Template(filename=template_name, escape=pyratemp.HTML) elif ext == ".tex": t = pyratemp.Template(filename=template_name, escape=pyratemp.LATEX) else: t = pyratemp.Template(filename=template_name) return t #---------------------- def load_data(datafiles): """Load data from data-files using either 'json' or 'yaml'. :Parameters: - datafiles: [ [filename, nr_of_entry, namespace], ...] :Returns: read data (dict) :Raises: ImportError, ValueError """ imported_json = False imported_yaml = False mydata = {} for filename, n, namespace in datafiles: if filename[-5:].lower() == ".json": if not imported_json: try: import simplejson as json except ImportError: import json imported_json = True try: myjson = json.load(open(filename, 'r')) if n != -1: myjson = myjson[n] if namespace is None: mydata.update(myjson) else: mydata.update({namespace: myjson}) except ValueError as err: raise ValueError("Invalid JSON in file '%s'. (%s)" % (filename, str(err))) elif filename[-5:].lower() == ".yaml": if not imported_yaml: import yaml imported_yaml = True if n == -1: n = 0 myyaml = yaml.load_all(open(filename, 'r')) if namespace is not None: mydata.update({namespace: list(myyaml)[n]}) else: mydata.update(list(myyaml)[n]) else: raise ValueError("Invalid data-file '%s', must be .json or .yaml" % filename) return mydata #----------------------------------------- if __name__ == "__main__": # parse parameters try: opt_list, files = getopt.getopt(sys.argv[1:], "sd:f:n:N:h", ("help", "xml")) except getopt.GetoptError as err: print("ERROR: Invalid option. (%s)" % err, file=sys.stderr) sys.exit(3) render = True template_name = "" namevals = {} datafiles = [] #[ [filename, nr_of_entry], ...] output_xml = False for key, value in opt_list: if "-h" == key or "--help" == key: print(USAGE, file=sys.stderr) sys.exit(2) elif "-s" == key: render = False elif "-d" == key: (name, value) = value.split("=", 1) namevals[name] = value elif "-f" == key: datafiles.append([value, -1, None]) elif "-n" == key: if not datafiles: print("ERROR: -n only allowed after -f.", file=sys.stderr) sys.exit(3) datafiles[-1][1] = int(value) elif "-N" == key: if not datafiles: print("ERROR: -N only allowed after -f.", file=sys.stderr) sys.exit(3) datafiles[-1][2] = value elif "--xml" in key: output_xml = True if not files: print(USAGE, file=sys.stderr) sys.exit(2) for f in files: if f == "--": break elif f[0] == "-": print("ERROR: Invalid order of parameters. (%s)" % f, file=sys.stderr) sys.exit(3) # template for template_name in files: # parse + syntax-check try: t = parse(template_name) except pyratemp.TemplateSyntaxError as err: print("file '%s':" % template_name, file=sys.stderr) print(" TemplateSyntaxError:", str(err), file=sys.stderr) sys.exit(10) if render: # load data try: filedata = load_data(datafiles) except ImportError as err: print("ImportError/missing Python-module:", str(err), file=sys.stderr) sys.exit(1) except ValueError as err: print("Datafile error:", str(err), file=sys.stderr) sys.exit(20) localtime = time.localtime() data = { 'mtime_CCYYMMDD':time.strftime("%Y-%m-%d",localtime), 'date' :time.strftime("%Y-%m-%d",localtime), } data.update(filedata) data.update(namevals) data = pyratemp.dictkeyclean(data) # render try: if output_xml: result = t(**data).encode("ascii", "xmlcharrefreplace") else: result = t(**data).encode("utf-8") os.write(sys.stdout.fileno(), result) except pyratemp.TemplateRenderError as err: print("file '%s':\n" % template_name, file=sys.stderr) print(" TemplateRenderError:", str(err), file=sys.stderr) sys.exit(30) #-----------------------------------------