From 86ac4c4ab841c50222f339c3b293ecb810276a05 Mon Sep 17 00:00:00 2001 From: Joey de l'Arago Date: Fri, 25 Nov 2022 14:09:12 +0100 Subject: [PATCH] Fix regex for list/dict match last item in list without comma at end. Add some custom ordering to the json. This is so that important values like "version" don't get pushed to the end of the json. --- .../formatters/def_json_formatter.py | 73 ++++++++++++++++--- 1 file changed, 64 insertions(+), 9 deletions(-) diff --git a/printer-linter/src/printerlinter/formatters/def_json_formatter.py b/printer-linter/src/printerlinter/formatters/def_json_formatter.py index ddeccacffd..c74f077249 100644 --- a/printer-linter/src/printerlinter/formatters/def_json_formatter.py +++ b/printer-linter/src/printerlinter/formatters/def_json_formatter.py @@ -1,29 +1,84 @@ import json import re +from collections import OrderedDict from pathlib import Path +from typing import Dict from .formatter import FileFormatter + +# Dictionary items with matching keys will be sorted as if they were the value +# Example: "version" will be sorted as if it was "0" +TOP_LEVEL_SORT_PRIORITY = { + "version": "0", + "name": "1", + "inherits": "3" +} + +METADATA_SORT_PRIORITY = { + "visible": "0", + "author": "1", + "manufacturer": "2", + "file_formats": "3", + "platform" : "4" +} + + class DefJsonFormatter(FileFormatter): def format(self, file: Path): - """ Format .def.json files according to the rules in settings """ - definition = json.loads(file.read_text()) - content = json.dumps(definition, indent=self._settings["format"].get("format-definition-indent", 4), - sort_keys=self._settings["format"].get("format-definition-sort-keys", True)) + """ Format .def.json files according to the rules in settings. + + You can assume that you will be running regex on standard formatted json files, because we load the json first and then + dump it to a string. This means you only have to write regex that works on the output of json.dump() + """ + + definition = json.loads(file.read_text(), object_pairs_hook=OrderedDict) + + if self._settings["format"].get("format-definition-sort-keys", True): + definition = self.order_keys(definition) + + content = json.dumps(definition, indent=self._settings["format"].get("format-definition-indent", 4)) if self._settings["format"].get("format-definition-bracket-newline", True): newline = re.compile(r"(\B\s+)(\"[\w\"]+)(\:\s\{)") content = newline.sub(r"\1\2:\1{", content) if self._settings["format"].get("format-definition-single-value-single-line", True): - single_value_dict = re.compile(r"(:)(\s*\n?.*\{\s+)(\".*)(\d*\s*\})(\s*)(,?)") - content = single_value_dict.sub(r"\1 { \3 }\6", content) + single_value_dict = re.compile(r"(:)(\s*\n?.*\{\s+)(\".*)(\d*\s*\})(.*\n,?)") + content = single_value_dict.sub(r"\1 { \3 }\5", content) - single_value_list = re.compile(r"(:)(\s*\n?.*\[\s+)(\".*)(\d*\s*\])(\s*)(,?)") - content = single_value_list.sub(r"\1 [ \3 ]\6", content) + single_value_list = re.compile(r"(:)(\s*\n?.*\[\s+)(\".*)(\d*\s*\])(.*\n,?)") + content = single_value_list.sub(r"\1 [ \3 ]\5", content) if self._settings["format"].get("format-definition-paired-coordinate-array", True): paired_coordinates = re.compile(r"(\[)\s+(-?\d*),\s*(-?\d*)\s*(\])") content = paired_coordinates.sub(r"\1 \2, \3 \4", content) - file.write_text(content) \ No newline at end of file + file.write_text(content) + + def order_keys(self, json_content: OrderedDict) -> OrderedDict: + """ Orders json keys lexicographically """ + # First order all keys (Recursive) lexicographically + json_content_text = json.dumps(json_content, sort_keys=True) + json_content = json.loads(json_content_text, object_pairs_hook=OrderedDict) + + # Do a custom ordered sort on the top level items in the json. This is so that keys like "version" appear at the top. + json_content = self.custom_sort_keys(json_content, TOP_LEVEL_SORT_PRIORITY) + + # Do a custom ordered sort on collections that are one level deep into the json + if "metadata" in json_content.keys(): + json_content["metadata"] = self.custom_sort_keys(json_content["metadata"], METADATA_SORT_PRIORITY) + + return json_content + + + def custom_sort_keys(self, ordered_dictionary: OrderedDict, sort_priority: Dict[str, str]) -> OrderedDict: + """ Orders keys in dictionary lexicographically, except for keys with matching strings in sort_priority. + + Keys in ordered_dictionary that match keys in sort_priority will sort based on the value in sort_priority. + + @param ordered_dictionary: A dictionary that will have it's top level keys sorted + @param sort_priority: A mapping from string keys to alternative strings to be used instead when sorting. + @return: A dictionary sorted by it's top level keys + """ + return OrderedDict(sorted(ordered_dictionary.items(), key=lambda x: sort_priority[x[0]] if str(x[0]) in sort_priority.keys() else str(x[0])))