| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120 |
- #!/usr/bin/env python
- import json
- import re
- import sys
- from pathlib import Path
- enum_types = []
- enum_types_names = []
- def create_attribute(name):
- """
- Gets an attribute name from the enum and convert it to snake_case
- """
- # Removes any hyphens or dots from the name, substituting by an underscore
- name_normalized = re.sub("[.]", "", name)
- name_normalized = re.sub("[-]", "_", name_normalized)
- # The following substitution will add an underscore between
- # any character and a word starting with a capital letter, followed by
- # a lowercase letters. For example:
- # 1) "EVConnected" -> EV_Connected
- # 2) "SuspendedEVSE" -> SuspendedEVSE
- # 3) "CSMSRootCertificate" -> CSMS_RootCertificate
- # For names with numbers within, this conversion does not work so well
- # 4) "Other1PhMax16A" -> Other1_PhMax16A
- name_normalized = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", name_normalized)
- # This one will add an underscore between a word ending with non-capital
- # letter and one starting with a capital. It will also lowercase it.
- # For example:
- # 1) "EV_Connected" -> ev_connected
- # 2) "SuspendedEVSE" -> suspended_evse
- # 3) "CSMS_RootCertificate" -> csms_root_certificate
- # 4) "Other1_PhMax16A" -> other1_ph_max16a (ideally should be
- # other_1ph_max_16a or similar.
- name_converted = re.sub("([a-z])([A-Z])", r"\1_\2", name_normalized).lower()
- return Attribute(name, name_converted)
- class NormalClass:
- def __init__(self, name):
- self.name = name
- self.attrs = []
- def add_attr(self, attr):
- self.attrs.append(attr)
- def __str__(self):
- output = f"class {self.name}(str, Enum):\n"
- if len(self.attrs) == 0:
- return output + " pass\n"
- for attr in self.attrs:
- output += str(attr)
- return output
- class Attribute:
- def __init__(self, name, name_converted):
- self.name = name
- self.name_converted = name_converted
- def __str__(self):
- name = self.name
- if not re.match("^[a-zA-Z_]", self.name):
- name = "_" + self.name
- # The 4 spaces after the start of the string is to guarantee the proper
- # indentation
- return f' {self.name_converted} = "{name}"\n'
- def __repr__(self):
- return f"<{self.name}, {self.name_converted}> "
- def parse_schema(schema):
- with open(schema, "r") as f:
- schema = json.loads(f.read())
- try:
- definitions = schema["definitions"]
- except KeyError:
- print("Error: No definitions field found on the schema")
- return
- for enum_type, value in definitions.items():
- if "Enum" in enum_type:
- type_name = enum_type.replace("Enum", "")
- if type_name not in enum_types_names:
- nc = NormalClass(type_name)
- for enum_attr in value["enum"]:
- attr = create_attribute(enum_attr)
- nc.add_attr(attr)
- enum_types.append(nc)
- enum_types_names.append(nc.name)
- if __name__ == "__main__":
- if len(sys.argv) != 2:
- print("Pass path to folder with schemas")
- sys.exit(-1)
- # The second argument of argv is the path for the schemas
- p = Path(sys.argv[1])
- schemas = list(p.glob("*.json"))
- # This loop will update the lists enum_types and enum_types_names:
- # The former contains the enum objects that are later parsed to the
- # enum.py, using the str representation. And the latter, is used to keep
- # track of the already processed Enums, avoiding data duplication
- for schema in schemas:
- parse_schema(schema)
- with open("enums.py", "wb+") as f:
- f.write(b"from enum import Enum\n")
- for enum_type_ in sorted(enum_types, key=lambda enum_type: enum_type.name):
- f.write(b"\n\n")
- f.write(str(enum_type_).encode("utf-8"))
|