From c66a83c9ff47a5a13af7115e4d4b1c2026420131 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Thu, 13 Oct 2016 22:10:14 +0100 Subject: [PATCH] Order props in the spec the same as the API Use an OrderedDict when reading the api docs so that properties defined in the API are rendered in the same order in the spec. --- templating/matrix_templates/units.py | 31 ++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/templating/matrix_templates/units.py b/templating/matrix_templates/units.py index 7fbaee45c..42beccc5b 100644 --- a/templating/matrix_templates/units.py +++ b/templating/matrix_templates/units.py @@ -21,6 +21,7 @@ For the actual conversion of data -> RST (including templates), see the sections file instead. """ from batesian.units import Units +from collections import OrderedDict import logging import inspect import json @@ -48,6 +49,20 @@ STATE_EVENT = "core-event-schema/state_event.yaml" logger = logging.getLogger(__name__) +# a yaml Loader which loads mappings into OrderedDicts instead of regular +# dicts, so that we preserve the ordering of properties from the api files. +# +# with thanks to http://stackoverflow.com/a/21912744/637864 +class OrderedLoader(yaml.Loader): + pass +def construct_mapping(loader, node): + loader.flatten_mapping(node) + pairs = loader.construct_pairs(node) + return OrderedDict(pairs) +OrderedLoader.add_constructor( + yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, + construct_mapping) + def resolve_references(path, schema): if isinstance(schema, dict): # do $ref first @@ -55,11 +70,11 @@ def resolve_references(path, schema): value = schema['$ref'] path = os.path.join(os.path.dirname(path), value) with open(path) as f: - ref = yaml.load(f) + ref = yaml.load(f, OrderedLoader) result = resolve_references(path, ref) del schema['$ref'] else: - result = {} + result = OrderedDict() for key, value in schema.items(): result[key] = resolve_references(path, value) @@ -194,12 +209,9 @@ def process_prop(key_name, prop, required): value_type = None desc = prop.get("description", "") - prop_type = prop.get('type') + prop_type = prop['type'] tables = [] - if prop_type is None: - raise KeyError("Property '%s' of object '%s' missing 'type' field" - % (key_name, obj)) logger.debug("%s is a %s", key_name, prop_type) if prop_type == "object": @@ -381,6 +393,7 @@ def get_example_for_param(param): return json.dumps(get_example_for_schema(param['schema']), indent=2) + class MatrixUnits(Units): def _load_swagger_meta(self, api, group_name): endpoints = [] @@ -563,7 +576,7 @@ class MatrixUnits(Units): # strip .yaml group_name = filename[:-5].replace("-", "_") group_name = "%s_%s" % (group_name, suffix) - api = yaml.load(f.read()) + api = yaml.load(f.read(), OrderedLoader) api = resolve_references(filepath, api) api["__meta"] = self._load_swagger_meta( api, group_name @@ -584,7 +597,7 @@ class MatrixUnits(Units): filepath = os.path.join(root, filename) with open(filepath) as f: try: - event_info = yaml.load(f) + event_info = yaml.load(f, OrderedLoader) except Exception as e: raise ValueError( "Error reading file %r" % (filepath,), e @@ -677,7 +690,7 @@ class MatrixUnits(Units): logger.info("Reading %s" % filepath) with open(filepath, "r") as f: - json_schema = yaml.load(f) + json_schema = yaml.load(f, OrderedLoader) schema = { "typeof": "",