The two are now linked together in build.py by specifying the input module. Updated gendoc.py to specify the right module.
173 lines
5.5 KiB
Python
Executable file
173 lines
5.5 KiB
Python
Executable file
#!/usr/bin/env python
|
|
"""
|
|
Builds the Matrix Specification as restructed text (RST).
|
|
|
|
Architecture
|
|
============
|
|
+-------+ +----------+
|
|
| units |-+ | sections |-+
|
|
+-------+ |-+ === used to create ==> +----------- | === used to create ==> SPEC
|
|
+-------+ | +----------+
|
|
+--------+
|
|
RAW DATA (e.g. json) Blobs of RST
|
|
|
|
Units
|
|
=====
|
|
Units are random bits of unprocessed data, e.g. schema JSON files. Anything can
|
|
be done to them, from processing it with Jinja to arbitrary python processing.
|
|
They are dicts.
|
|
|
|
Sections
|
|
========
|
|
Sections are short segments of RST. They will be in the final spec, but they
|
|
are unordered. They typically use a combination of templates + units to
|
|
construct bits of RST.
|
|
|
|
Skeleton
|
|
========
|
|
The skeleton is a single RST file which is passed through a templating system to
|
|
replace variable names with sections.
|
|
|
|
Processing
|
|
==========
|
|
- Execute all unit functions to load units into memory and process them.
|
|
- Execute all section functions (which can now be done because the units exist)
|
|
- Execute the skeleton function to bring it into a single file.
|
|
|
|
Checks
|
|
======
|
|
- Any units made which were not used at least once will produce a warning.
|
|
- Any sections made but not used in the skeleton will produce a warning.
|
|
"""
|
|
from batesian import AccessKeyStore
|
|
|
|
from jinja2 import Environment, FileSystemLoader, StrictUndefined, Template
|
|
from argparse import ArgumentParser, FileType
|
|
import importlib
|
|
import json
|
|
import os
|
|
import sys
|
|
from textwrap import TextWrapper
|
|
|
|
def create_from_template(template, sections):
|
|
return template.render(sections)
|
|
|
|
def check_unaccessed(name, store):
|
|
unaccessed_keys = store.get_unaccessed_set()
|
|
if len(unaccessed_keys) > 0:
|
|
print "Found %s unused %s keys." % (len(unaccessed_keys), name)
|
|
print unaccessed_keys
|
|
|
|
def main(input_module, file_stream=None, out_dir=None, verbose=False):
|
|
if out_dir and not os.path.exists(out_dir):
|
|
os.makedirs(out_dir)
|
|
|
|
in_mod = importlib.import_module(input_module)
|
|
|
|
# add a template filter to produce pretty pretty JSON
|
|
def jsonify(input, indent=None, pre_whitespace=0):
|
|
code = json.dumps(input, indent=indent, sort_keys=True)
|
|
if pre_whitespace:
|
|
code = code.replace("\n", ("\n" +" "*pre_whitespace))
|
|
|
|
return code
|
|
|
|
def indent_block(input, indent):
|
|
return input.replace("\n", ("\n" + " "*indent))
|
|
|
|
def indent(input, indent):
|
|
return " "*indent + input
|
|
|
|
def wrap(input, wrap=80, initial_indent=""):
|
|
if len(input) == 0:
|
|
return initial_indent
|
|
wrapper = TextWrapper(initial_indent=initial_indent, width=wrap)
|
|
return wrapper.fill(input)
|
|
|
|
# make Jinja aware of the templates and filters
|
|
env = Environment(
|
|
loader=FileSystemLoader(in_mod.exports["templates"]),
|
|
undefined=StrictUndefined
|
|
)
|
|
env.filters["jsonify"] = jsonify
|
|
env.filters["indent"] = indent
|
|
env.filters["indent_block"] = indent_block
|
|
env.filters["wrap"] = wrap
|
|
|
|
# load up and parse the lowest single units possible: we don't know or care
|
|
# which spec section will use it, we just need it there in memory for when
|
|
# they want it.
|
|
units = AccessKeyStore(
|
|
existing_data=in_mod.exports["units"](debug=verbose).get_units()
|
|
)
|
|
|
|
# use the units to create RST sections
|
|
sections = in_mod.exports["sections"](env, units, debug=verbose).get_sections()
|
|
|
|
# print out valid section keys if no file supplied
|
|
if not file_stream:
|
|
print "\nValid template variables:"
|
|
for key in sections.keys():
|
|
print " %s" % key
|
|
return
|
|
|
|
# check the input files and substitute in sections where required
|
|
print "Parsing input template: %s" % file_stream.name
|
|
temp = Template(file_stream.read())
|
|
print "Creating output for: %s" % file_stream.name
|
|
output = create_from_template(temp, sections)
|
|
with open(
|
|
os.path.join(out_dir, os.path.basename(file_stream.name)), "w"
|
|
) as f:
|
|
f.write(output)
|
|
print "Output file for: %s" % file_stream.name
|
|
|
|
check_unaccessed("units", units)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
parser = ArgumentParser(
|
|
"Process a file (typically .rst) and replace templated areas with "+
|
|
"section information from the provided input module. For a list of "+
|
|
"possible template variables, add --show-template-vars."
|
|
)
|
|
parser.add_argument(
|
|
"file", nargs="?", type=FileType('r'),
|
|
help="The input file to process."
|
|
)
|
|
parser.add_argument(
|
|
"--input", "-i",
|
|
help="The python module which contains the sections/units classes."
|
|
)
|
|
parser.add_argument(
|
|
"--out-directory", "-o", help="The directory to output the file to."+
|
|
" Default: /out",
|
|
default="out"
|
|
)
|
|
parser.add_argument(
|
|
"--show-template-vars", "-s", action="store_true",
|
|
help="Show a list of all possible variables you can use in the"+
|
|
" input file."
|
|
)
|
|
parser.add_argument(
|
|
"--verbose", "-v", action="store_true",
|
|
help="Turn on verbose mode."
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
if not args.input:
|
|
raise Exception("Missing input module")
|
|
|
|
if (args.show_template_vars):
|
|
main(args.input, verbose=args.verbose)
|
|
sys.exit(0)
|
|
|
|
if not args.file:
|
|
print "No file supplied."
|
|
parser.print_help()
|
|
sys.exit(1)
|
|
|
|
main(
|
|
args.input, file_stream=args.file, out_dir=args.out_directory,
|
|
verbose=args.verbose
|
|
)
|