mirror of
				https://github.com/RGBCube/GitHubWrapper
				synced 2025-10-31 14:02:46 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			193 lines
		
	
	
	
		
			6.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			193 lines
		
	
	
	
		
			6.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| from __future__ import annotations
 | |
| 
 | |
| from typing import Tuple  # , Any
 | |
| 
 | |
| types = {
 | |
|     "string": "str",
 | |
|     "number": "float",
 | |
|     "integer": "int",
 | |
|     "boolean": "bool",
 | |
|     "object": "dict",
 | |
|     "array": "list",
 | |
|     "null": "None",
 | |
| }
 | |
| 
 | |
| 
 | |
| # # Used for debugging
 | |
| # class AppendPrint(list):
 | |
| #     def append(self, obj: Any) -> None:
 | |
| #         if obj:
 | |
| #             print(obj)
 | |
| #         super().append(obj)
 | |
| 
 | |
| 
 | |
| def generate(
 | |
|     obj: dict, /, *, title: str = "GeneratedObject", no_comments: bool = False
 | |
| ) -> Tuple[str, str]:  # sourcery skip: low-code-quality
 | |
|     """Makes TypedDict from a JSON Schema object.
 | |
| 
 | |
|     Arguments:
 | |
|         obj: JSON Schema dict.
 | |
|         title: The title of the result.
 | |
|         no_comments: If True, no comments will be added.
 | |
| 
 | |
|     Returns:
 | |
|         The generated TypedDicts, the result values name and the comments.
 | |
|     """
 | |
|     # The other TypedDicts
 | |
|     result = []
 | |
| 
 | |
|     # The real annotation
 | |
|     annotation: str
 | |
| 
 | |
|     obj_type = obj.get("type")
 | |
| 
 | |
|     # allOf, anyOf, oneOf, (not)?
 | |
|     if not obj_type:
 | |
|         # Treating oneOf as allOf, since is kinda the
 | |
|         # same and there isn't a way to type it in Python
 | |
|         if objs := obj.get("anyOf") or obj.get("oneOf"):
 | |
|             union_items = []
 | |
|             optional = False
 | |
|             for obj_schema in objs:
 | |
|                 if obj_schema["type"] == "null":
 | |
|                     optional = True
 | |
|                 else:
 | |
|                     extras, target = generate(obj_schema, title=title, no_comments=no_comments)
 | |
|                     result.append(extras)
 | |
|                     union_items.append(target)
 | |
| 
 | |
|             annotation = f"{'Optional' if optional else 'Union'}[{', '.join(union_items)}]"
 | |
|         else:
 | |
|             annotation = "Any"
 | |
| 
 | |
|     # Union for parameters
 | |
|     elif isinstance(obj_type, list):
 | |
|         union_items = []
 | |
|         is_optional = False
 | |
| 
 | |
|         for obj_type_item in obj_type:
 | |
|             if obj_type_item == "null":
 | |
|                 is_optional = True
 | |
|             else:
 | |
|                 union_items.append(types[obj_type_item])
 | |
| 
 | |
|         annotation = f"{'Optional' if is_optional else 'Optional'}[{', '.join(union_items)}]"
 | |
| 
 | |
|     elif obj_type == "boolean":
 | |
|         annotation = "bool"
 | |
| 
 | |
|     elif obj_type == "null":
 | |
|         annotation = "None"
 | |
| 
 | |
|     elif obj_type == "string":
 | |
|         if not no_comments:
 | |
|             if obj_min_len := obj.get("minLength"):
 | |
|                 result.append(f"    # Minimum length: {obj_min_len}")
 | |
|             if obj_max_len := obj.get("maxLength"):
 | |
|                 result.append(f"    # Maximum length: {obj_max_len}")
 | |
|             if obj_pattern := obj.get("pattern"):
 | |
|                 result.append(f"    # Pattern: {obj_pattern!r}")
 | |
| 
 | |
|         annotation = "str"
 | |
| 
 | |
|     elif obj_type in {"integer", "number"}:
 | |
|         if not no_comments:
 | |
|             if obj_multiple := obj.get("multipleOf"):
 | |
|                 result.append(f"    # Multiple of: {obj_multiple}")
 | |
|             if obj_minimum := obj.get("minimum"):
 | |
|                 result.append(f"    # Minimum (x >= N): {obj_minimum}")
 | |
|             if obj_exclusive_minimum := obj.get("exclusiveMinimum"):
 | |
|                 result.append(f"    # Exclusive minimum (x > N): {obj_exclusive_minimum}")
 | |
|             if obj_maximum := obj.get("maximum"):
 | |
|                 result.append(f"    # Maximum (x <= N): {obj_maximum}")
 | |
|             if obj_exclusive_maximum := obj.get("exclusiveMaximum"):
 | |
|                 result.append(f"    # Exclusive maximum (x < N): {obj_exclusive_maximum}")
 | |
|             if any([obj_minimum, obj_exclusive_minimum, obj_maximum, obj_exclusive_maximum]):
 | |
|                 result.append("     # x = the variable, N = the min/max")
 | |
| 
 | |
|         annotation = "int" if obj_type == "integer" else "float"
 | |
| 
 | |
|     elif obj_type == "object":
 | |
|         # TODO: add support for patternProperties, unevaluatedProperties,
 | |
|         # propertyNames, minProperties, maxProperties
 | |
| 
 | |
|         if obj_properties := obj.get("properties"):
 | |
|             # TODO: make it so the extra properties are typed instead of Any, somehow
 | |
|             total = ", total=False" if obj.get("additionalProperties") else ""
 | |
| 
 | |
|             annotation = obj_title = obj.get("title", title).replace(" ", "")
 | |
|             typed_dict = [f"class {obj_title}(TypedDict{total}):"]
 | |
| 
 | |
|             for key, value in obj_properties.items():
 | |
|                 extras, param_annotation = generate(
 | |
|                     value, title=key.capitalize(), no_comments=no_comments
 | |
|                 )
 | |
|                 result.append(extras)
 | |
| 
 | |
|                 if key not in obj.get("required", []):
 | |
|                     param_annotation = f"NotRequired[{param_annotation}]"
 | |
| 
 | |
|                 if not no_comments:
 | |
|                     if examples := value.get("examples"):
 | |
|                         s = "" if len(examples) == 1 else "s"
 | |
| 
 | |
|                         examples = ", ".join([str(example) for example in examples]).replace(
 | |
|                             "\n", "\\n"
 | |
|                         )
 | |
| 
 | |
|                         if (examples_short := examples[:70]) != examples:
 | |
|                             examples = f"{examples_short}[...]"
 | |
| 
 | |
|                         examples = f"    # Example{s}: {examples}" if examples else ""
 | |
| 
 | |
|                     typed_dict.extend(
 | |
|                         [f"    # Format: {fmt}" if (fmt := value.get("format")) else "", examples]
 | |
|                     )
 | |
| 
 | |
|                 typed_dict.append(f"    {key}: {param_annotation}")
 | |
| 
 | |
|             result.extend(typed_dict)
 | |
|         else:
 | |
|             annotation = "dict"
 | |
| 
 | |
|     elif obj_type == "array":
 | |
|         # TODO: add support for contains, minContains,
 | |
|         # maxContains, minLength, maxLength, uniqueItems
 | |
| 
 | |
|         if obj_items := obj.get("items"):
 | |
|             extras, target = generate(obj_items, no_comments=no_comments)
 | |
|             result.append(extras)
 | |
|             annotation = f"List[{target}]"
 | |
| 
 | |
|         elif obj_prefix_items := obj.get("prefixItems"):
 | |
|             tuple_annotation = []
 | |
|             for item in obj_prefix_items:
 | |
|                 (
 | |
|                     extras,
 | |
|                     target,
 | |
|                 ) = generate(item, no_comments=no_comments)
 | |
|                 result.append(extras)
 | |
|                 tuple_annotation.append(target)
 | |
| 
 | |
|             if extra_item_type := obj.get("items"):
 | |
|                 if extra_item_type is not True:
 | |
|                     extras, extra_type = generate(extra_item_type, no_comments=no_comments)
 | |
|                     result.append(extras)
 | |
|                     if not no_comments:
 | |
|                         result.append(
 | |
|                             f"    # The extra items for the tuple are typed as: {extra_type}"
 | |
|                         )
 | |
| 
 | |
|                 tuple_annotation.append("...")
 | |
| 
 | |
|             annotation = f"Tuple[{', '.join(tuple_annotation)}]"
 | |
|         else:
 | |
|             annotation = "list"
 | |
| 
 | |
|     else:
 | |
|         annotation = "Any"
 | |
| 
 | |
|     result = [i for i in result if i]
 | |
| 
 | |
|     return "\n".join(result), annotation
 | 
