Skip to content

Instantly share code, notes, and snippets.

@yampelo
Created March 4, 2020 05:15
Show Gist options
  • Save yampelo/d3df4324759d7d7b84a30c58fdb6eae8 to your computer and use it in GitHub Desktop.
Save yampelo/d3df4324759d7d7b84a30c58fdb6eae8 to your computer and use it in GitHub Desktop.
import inspect
import sys
from datetime import datetime
from enum import EnumMeta
from typing import Any, Dict, List, Tuple, Union, _GenericAlias, get_type_hints
from pydantic import BaseModel
# Import your pydnatic models here
models = inspect.getmembers(
sys.modules[__name__],
lambda i: inspect.isclass(i) and issubclass(i, BaseModel) and i != BaseModel,
)
TYPE_MAP: Dict[str, str] = {
bool: "boolean",
str: "string",
int: "number",
float: "number",
datetime: "Date",
Any: "any",
List: "Array<any>",
Tuple: "[any]",
Union: "any",
}
SUBSCRIPT_FORMAT_MAP: Dict[str, str] = {
"List": "Array<{}>",
"Optional": "{} | null",
"Tuple": "[{}]",
"Union": "{}",
}
InterfaceAttributes = Dict[str, str]
ParsedInterfaces = Dict[str, InterfaceAttributes]
interfaces: ParsedInterfaces = {}
enums: Dict[str, Dict[str, str]] = {}
def parse_type_hint(type_hint):
if not isinstance(type_hint, _GenericAlias):
if type_hint not in TYPE_MAP:
parse_class(type_hint)
return TYPE_MAP.get(type_hint, str(type_hint.__name__))
if type_hint.__origin__ == Union:
return parse_union(type_hint)
if type_hint.__origin__ == list:
return f"Array<{parse_type_hint(type_hint.__args__[0])}>"
def parse_union(union: Union):
arg1, arg2 = union.__args__
if arg2 == type(None):
return f"{parse_type_hint(arg1)} | null"
else:
return f"{parse_type_hint(arg1)} | {parse_type_hint(arg2)}"
def parse_class(model: BaseModel):
attrs: InterfaceAttributes = {}
for annotation, value in get_type_hints(model).items():
if value in TYPE_MAP:
attrs[annotation] = TYPE_MAP[value]
elif value.__class__ == EnumMeta and value.__name__ not in enums:
enums[value.__name__] = {k.name: k.value for k in value}
attrs[annotation] = f"{value.__name__}"
elif isinstance(value, _GenericAlias):
attrs[annotation] = parse_type_hint(value)
else:
attrs[annotation] = str(value.__name__)
interfaces[model.__name__] = attrs
for m in models:
parse_class(m[1])
for enum in enums:
s = f"export enum {enum} {{\n"
for attribute_name, attribute_type in enums[enum].items():
s += f' {attribute_name} = "{attribute_type}",\n'
s += "}"
print(s)
for interface in interfaces:
s = f"export interface {interface.replace('Base', '')} {{\n"
for attribute_name, attribute_type in interfaces[interface].items():
s += f" {attribute_name}: {attribute_type.replace('Base', '')};\n"
s += "}"
print(s)
@yampelo
Copy link
Author

yampelo commented Mar 4, 2020

This will covert pydnatic models into typescript interfaces and enums. For example:

class TaskBase(OpexBase):
    assignee: IndividualRead
    creator: IndividualRead
    status: Optional[str]
    description: Optional[str]

class TaskRead(TaskBase):
    id: int
    created_at: Optional[datetime]
    resolved_at: Optional[datetime]
    updated_at: Optional[datetime]
    ticket: TicketNested

becomes

export interface TaskRead {
    assignee: IndividualRead;
    creator: IndividualRead;
    status: string | null;
    description: string | null;
    id: number;
    created_at: Date | null;
    resolved_at: Date | null;
    updated_at: Date | null;
    ticket: TicketNested;
}

export interface IndividualRead {
  name: string
  email: string
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment