diff --git a/templating/batesian/units.py b/templating/batesian/units.py index 072508e6..8f748f6d 100644 --- a/templating/batesian/units.py +++ b/templating/batesian/units.py @@ -12,11 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. """Parent class for writing units.""" -from . import AccessKeyStore import inspect -import json -import os -import subprocess + class Units(object): @@ -57,9 +54,6 @@ class Units(object): unit_dict[unit_key] = func(self.substitutions) else: unit_dict[unit_key] = func() - self.log("Generated unit '%s' : %s" % ( - unit_key, json.dumps(unit_dict[unit_key])[:50].replace( - "\n","" - ) - )) + self.log("Generated unit '%s'" % unit_key) + return unit_dict diff --git a/templating/build.py b/templating/build.py index 307a26e7..d18569b6 100755 --- a/templating/build.py +++ b/templating/build.py @@ -59,10 +59,12 @@ import importlib import json import logging import os -import re import sys from textwrap import TextWrapper +from matrix_templates.units import TypeTableRow + + def create_from_template(template, sections): return template.render(sections) @@ -117,15 +119,23 @@ def main(input_module, files=None, out_dir=None, verbose=False, substitutions={} Given a list of rows, returns a list giving the maximum length of the values in each column. - :param list[dict[str, str]] input: a list of rows. Each row should be a - dict with the keys given in ``keys``. + :param list[TypeTableRow|dict[str,str]] input: + a list of rows :param list[str] keys: the keys corresponding to the table columns :param list[int] defaults: for each column, the default column width. :param int default_width: if ``defaults`` is shorter than ``keys``, this will be used as a fallback """ + def getrowattribute(row, k): + # the row may be a dict (particularly the title row, which is + # generated by the template + if not isinstance(row, TypeTableRow): + return row[k] + return getattr(row, k) + def colwidth(key, default): - return reduce(max, (len(row[key]) for row in input), + rowwidths = (len(getrowattribute(row, key)) for row in input) + return reduce(max, rowwidths, default if default is not None else default_width) results = map(colwidth, keys, defaults) diff --git a/templating/matrix_templates/templates/tables.tmpl b/templating/matrix_templates/templates/tables.tmpl index aba6c0c4..bd25a156 100644 --- a/templating/matrix_templates/templates/tables.tmpl +++ b/templating/matrix_templates/templates/tables.tmpl @@ -6,8 +6,7 @@ {# # write a table for a list of parameters. # - # 'rows' is the list of parameters. Each row should have the keys - # 'key', 'type', and 'desc'. + # 'rows' is the list of parameters. Each row should be a TypeTableRow. #} {% macro paramtable(rows, titles=["Parameter", "Type", "Description"]) -%} {{ split_paramtable({None: rows}, titles) }} diff --git a/templating/matrix_templates/units.py b/templating/matrix_templates/units.py index e1926713..6d554737 100644 --- a/templating/matrix_templates/units.py +++ b/templating/matrix_templates/units.py @@ -62,6 +62,50 @@ OrderedLoader.add_constructor( yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, construct_mapping) + +class TypeTable(object): + """Describes a table documenting an object type + + Attributes: + title(str|None): Title of the table - normally the object type + desc(str|None): description of the object + rows(list[TypeTableRow]): the rows in the table + """ + def __init__(self, title=None, desc=None, rows=[]): + self.title=title + self.desc=desc + self._rows = [] + for row in rows: + self.add_row(row) + + def add_row(self, row): + if not isinstance(row, TypeTableRow): + raise ValueError("Can only add TypeTableRows to TypeTable") + + self._rows.append(row) + + def __getattr__(self, item): + if item == 'rows': + return list(self._rows) + return super(TypeTable, self).__getattr__(item) + + def __repr__(self): + return "TypeTable[%s, rows=%s]" % (self.title, self._rows) + + +class TypeTableRow(object): + """Describes an object field defined in the json schema + """ + def __init__(self, key, typ, desc, required=False): + self.key = key + self.type = typ + self.desc = desc + self.required = required + + def __repr__(self): + return "TypeTableRow[%s: %s]" % (self.key, self.desc) + + def resolve_references(path, schema): if isinstance(schema, dict): # do $ref first @@ -171,12 +215,12 @@ def get_json_schema_object_fields(obj, enforce_title=False): required = key_name in required_keys res = process_data_type(props[key_name], required) - first_table_rows.append({ - "key": key_name, - "type": res["type"], - "required": required, - "desc": res["desc"], - }) + first_table_rows.append(TypeTableRow( + key=key_name, + typ=res["type"], + required=required, + desc=res["desc"], + )) tables.extend(res["tables"]) logger.debug("Done property %s" % key_name) @@ -187,10 +231,10 @@ def get_json_schema_object_fields(obj, enforce_title=False): # we don't lose information about where the error occurred. raise e2, None, sys.exc_info()[2] - tables.insert(0, { - "title": obj_title, - "rows": first_table_rows, - }) + tables.insert(0, TypeTable(title=obj_title, rows=first_table_rows)) + + for table in tables: + assert isinstance(table, TypeTable) return { "type": obj_title, @@ -241,10 +285,12 @@ def process_data_type(prop, required=False, enforce_title=True): if isinstance(prop_type, list): prop_type = " or ".join(prop_type) - rq = "**Required.**" if required else None desc = " ".join(x for x in [rq, prop.get("description"), enum_desc] if x) + for table in tables: + assert isinstance(table, TypeTable) + return { "type": prop_type, "desc": desc, @@ -263,13 +309,10 @@ def deduplicate_tables(tables): titles = set() filtered = [] for table in reversed(tables): - if table.get("no-table"): + if table.title in titles: continue - if table.get("title") in titles: - continue - - titles.add(table.get("title")) + titles.add(table.title) filtered.append(table) filtered.reverse() @@ -286,14 +329,10 @@ def get_tables_for_response(schema): # make up the first table, with just the 'body' row in, unless the response # is an object, in which case there's little point in having one. if not pv["is_object"]: - tables = [{ - "title": None, - "rows": [{ - "key": "
", - "type": pv["type"], - "desc": pv["desc"], - }] - }] + tables + first_table_row = TypeTableRow( + key="", typ=pv["type"], desc=pv["desc"], + ) + tables.insert(0, TypeTable(None, rows=[first_table_row])) logger.debug("response: %r" % tables) @@ -440,11 +479,9 @@ class MatrixUnits(Units): " One of: %s" % json.dumps(param.get("enum")) ) - endpoint["req_param_by_loc"].setdefault(param_loc, []).append({ - "key": param_name, - "type": val_type, - "desc": desc - }) + endpoint["req_param_by_loc"].setdefault(param_loc, []).append( + TypeTableRow(key=param_name, typ=val_type, desc=desc), + ) example = get_example_for_param(param) if example is None: @@ -487,11 +524,10 @@ class MatrixUnits(Units): headers = [] for (header_name, header) in good_response[ "headers"].iteritems(): - headers.append({ - "key": header_name, - "type": header["type"], - "desc": header["description"], - }) + headers.append( + TypeTableRow(key=header_name, typ=header["type"], + desc=header["description"]), + ) endpoint["res_headers"] = headers query_string = "" if len( example_query_params) == 0 else "?" + urllib.urlencode( @@ -531,7 +567,7 @@ class MatrixUnits(Units): # put the top-level parameters into 'req_param_by_loc', and the others # into 'req_body_tables' body_params = endpoint_data['req_param_by_loc'].setdefault("JSON body",[]) - body_params.extend(req_body_tables[0]["rows"]) + body_params.extend(req_body_tables[0].rows) body_tables = req_body_tables[1:] endpoint_data['req_body_tables'].extend(body_tables) @@ -586,19 +622,18 @@ class MatrixUnits(Units): if "event" not in event_type: continue # filter ImageInfo and co - table = { - "title": event_info["title"], - "desc": event_info["description"], - "rows": [] - } + table = TypeTable( + title=event_info["title"], + desc=event_info["description"], + ) for prop in sorted(event_info["properties"]): - row = { - "key": prop, - "type": event_info["properties"][prop]["type"], - "desc": event_info["properties"][prop].get("description","") - } - table["rows"].append(row) + prop_info = event_info["properties"][prop] + table.add_row(TypeTableRow( + key=prop, + typ=prop_info["type"], + desc=prop_info.get("description", ""), + )) event_types[event_type] = table return event_types @@ -606,29 +641,31 @@ class MatrixUnits(Units): def load_apis(self, substitutions): cs_ver = substitutions.get("%CLIENT_RELEASE_LABEL%", "unstable") fed_ver = substitutions.get("%SERVER_RELEASE_LABEL%", "unstable") - return { - "rows": [{ - "key": "`Client-Server API