File indexing completed on 2025-01-05 04:00:27
0001 import json 0002 import os 0003 import sys 0004 import argparse 0005 0006 0007 def ucfirst(x): 0008 return x[0].upper() + x[1:] 0009 0010 0011 def schema2py(out, filename, data, limit): 0012 if limit and class_name(filename) not in limit: 0013 return 0014 if data["type"] == "number": 0015 return schema2enum(out, filename, data) 0016 if data["type"] == "object": 0017 return schema2class(out, filename, data) 0018 0019 0020 def schema2enum(out, filename, data): 0021 if filename.endswith("/boolean.json"): 0022 return 0023 0024 out.write("\n\n## \ingroup Lottie\nclass ") 0025 out.write(class_name(filename)) 0026 out.write("(TgsEnum):\n") 0027 vals = {} 0028 for c in data["oneOf"]: 0029 out.write(" "*4) 0030 name = ucfirst(c["standsFor"]).replace(" ", "") 0031 value = c["const"] 0032 out.write("%s = %s\n" % (name, value)) 0033 vals[value] = name 0034 0035 if "default" in data: 0036 out.write("\n") 0037 out.write(" "*4) 0038 out.write("@classmethod\n") 0039 out.write(" "*4) 0040 out.write("def default(cls):\n") 0041 out.write(" "*8) 0042 out.write("return cls.%s\n" % vals[data["default"]]) 0043 0044 0045 class ClassProp: 0046 def __init__(self, out, data): 0047 self.out = out 0048 self.name = self._prop_name(data) 0049 self.value = None 0050 self.raw = data 0051 self.value_comment = None 0052 self.list = False 0053 self.type = "float" 0054 if "const" in data: 0055 self.value = repr(data["const"]) 0056 else: 0057 self._get_value(data) 0058 if self.value_comment == "MultiDimensional, MultiDimensionalKeyframed": 0059 self.value_comment = None 0060 elif self.value_comment == "Value, ValueKeyframed": 0061 self.value_comment = None 0062 0063 def ensure_unique_name(self, properties): 0064 if self._check_unique_name(properties): 0065 return 0066 if "description" in self.raw: 0067 self.name = self._format_name(self.raw["description"].replace(".", "")) 0068 if self._check_unique_name(properties): 0069 return 0070 self.name = self.out 0071 if not self._check_unique_name(properties): 0072 raise Exception("Cannot find unique property name") 0073 0074 def _check_unique_name(self, properties): 0075 for p in properties: 0076 if p.name == self.name: 0077 return False 0078 return True 0079 0080 def _prop_name(self, data): 0081 if "extended_name" in data: 0082 title = data["extended_name"] 0083 elif "title" in data: 0084 title = data["title"] 0085 else: 0086 raise Exception("No name") 0087 if title[0] == "3": 0088 return "threedimensional" 0089 title = self._format_name(title) 0090 if title == "class": 0091 return "css_class" 0092 return title 0093 0094 def _format_name(self, n): 0095 return n.lower().replace(" ", "_").replace("-", "_") 0096 0097 def _get_value(self, data): 0098 if data["type"] == "number": 0099 if "oneOf" in data: 0100 if data["oneOf"][0]["$ref"] == "#/helpers/boolean": 0101 self.type = bool.__name__ 0102 self.value = "False" 0103 else: 0104 self._get_value_object(data) 0105 elif "enum" in data and data["enum"] == [0, 1]: 0106 self.type = bool.__name__ 0107 self.value = "False" 0108 else: 0109 self.value = "0" 0110 elif data["type"] == "object": 0111 if "properties" in data and "oneOf" not in data: 0112 self.value = 'None' 0113 self._get_value_object(data) 0114 elif data["type"] == "array": 0115 self.list = True 0116 self.value = "[]" 0117 if "items" in data: 0118 intype = data["items"].get("type", "object") 0119 if intype == "object": 0120 self.type = "todo_func" 0121 if len(data["items"]["oneOf"]) == 1: 0122 self.type = class_name(data["items"]["oneOf"][0]["$ref"]) 0123 self.value_comment = ", ". join( 0124 class_name(oo["$ref"]) 0125 for oo in data["items"]["oneOf"] 0126 ) 0127 else: 0128 self.value_comment = intype 0129 elif data["type"] == "string": 0130 self.type = "str" 0131 self.value = '""' 0132 else: 0133 self.value = 'None' 0134 0135 def _get_value_object(self, data): 0136 if "oneOf" not in data: 0137 self.value = 'None' 0138 else: 0139 desc = data["oneOf"][0] 0140 if "value" in desc: 0141 self.value = desc["value"] 0142 self.value_comment = ", ". join("%(value)s: %(standsFor)s" % oo for oo in data["oneOf"]) 0143 else: 0144 clsname = class_name(desc["$ref"]) 0145 if data["type"] == "number": 0146 self.value = "%s.default()" % clsname 0147 else: 0148 self.type = clsname 0149 self.value = "%s()" % clsname 0150 0151 if len(data["oneOf"]) > 1: 0152 self.value_comment = ", ".join( 0153 class_name(oo["$ref"]) 0154 for oo in data["oneOf"] 0155 ) 0156 if self.value_comment == "MultiDimensional, MultiDimensionalKeyframed": 0157 self.type = "MultiDimensional" 0158 elif self.value_comment == "Value, ValueKeyframed": 0159 self.type = "Value" 0160 else: 0161 self.type = "todo_func" 0162 0163 def write_init(self, out, indent): 0164 out.write(indent) 0165 out.write("## %s\n" % self.raw["description"]) 0166 out.write(indent) 0167 out.write("self.%s = %s" % (self.name, self.value)) 0168 if self.value_comment: 0169 out.write(" # %s" % self.value_comment) 0170 out.write("\n") 0171 0172 0173 def schema2class(out, filename, data, ei=""): 0174 if "properties" not in data: 0175 return 0176 0177 out.write("\n\n## \ingroup Lottie\nclass ") 0178 out.write(class_name(filename)) 0179 out.write("(TgsObject): # TODO check\n") 0180 properties = [] 0181 for n, p in data["properties"].items(): 0182 prop = ClassProp(n, p) 0183 prop.ensure_unique_name(properties) 0184 properties.append(prop) 0185 0186 out.write(" "*4+ei) 0187 out.write("_props = [\n") 0188 for p in properties: 0189 out.write(" "*8+ei) 0190 out.write("TgsProp(\"%s\", \"%s\", %s, %s),\n" % (p.name, p.out, p.type, p.list)) 0191 out.write(" "*4+ei) 0192 out.write("]\n\n") 0193 0194 out.write(" "*4+ei) 0195 out.write("def __init__(self):\n") 0196 for p in properties: 0197 p.write_init(out, " "*8+ei) 0198 0199 0200 def class_name(filename): 0201 bits = os.path.splitext(filename)[0].rsplit("/", 2) 0202 name = ucfirst(bits.pop()) 0203 if bits[-1] == "layers": 0204 name += "Layer" 0205 elif bits[-1] == "effects": 0206 name += "Effect" 0207 elif name == "Transform" and bits[-1] == "shapes": 0208 name += "Shape" 0209 elif name == "Shape" and bits[-1] == "properties": 0210 name += "Property" 0211 elif name == "ShapeKeyframed": 0212 name = "ShapePropertyKeyframed" 0213 return name 0214 0215 0216 p = argparse.ArgumentParser( 0217 description="Generates Python classes from the lottie-web JSON schema" 0218 ) 0219 p.add_argument("limit", nargs="*") 0220 schema_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "docs", "json") 0221 p.add_argument("--load", "-l", default=schema_path) 0222 0223 0224 if __name__ == "__main__": 0225 ns = p.parse_args() 0226 0227 outfile = sys.stdout 0228 limit = ns.limit 0229 0230 for root, _, files in os.walk(ns.load): 0231 for file in files: 0232 filepath = os.path.join(root, file) 0233 with open(filepath, "r") as fp: 0234 data = json.load(fp) 0235 if not data: 0236 continue 0237 schema2py(outfile, filepath, data, limit)