Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore/tidy server codegen #119

Merged
merged 3 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 17 additions & 95 deletions replit_river/codegen/client.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import json
import re
from dataclasses import dataclass
from pathlib import Path
from textwrap import dedent, indent
from textwrap import dedent
from typing import (
Any,
Dict,
List,
Literal,
NewType,
Optional,
OrderedDict,
Sequence,
Expand All @@ -21,92 +19,24 @@
import black
from pydantic import BaseModel, Field, RootModel

TypeName = NewType("TypeName", str)
ModuleName = NewType("ModuleName", str)
ClassName = NewType("ClassName", str)
FileContents = NewType("FileContents", str)
HandshakeType = NewType("HandshakeType", str)

RenderedPath = NewType("RenderedPath", str)


@dataclass
class DictTypeExpr:
nested: "TypeExpression"


@dataclass
class ListTypeExpr:
nested: "TypeExpression"


@dataclass
class LiteralTypeExpr:
nested: int | str


@dataclass
class UnionTypeExpr:
nested: list["TypeExpression"]


TypeExpression = (
TypeName | DictTypeExpr | ListTypeExpr | LiteralTypeExpr | UnionTypeExpr
from replit_river.codegen.format import reindent
from replit_river.codegen.typing import (
ClassName,
DictTypeExpr,
FileContents,
HandshakeType,
ListTypeExpr,
LiteralTypeExpr,
ModuleName,
RenderedPath,
TypeExpression,
TypeName,
UnionTypeExpr,
ensure_literal_type,
extract_inner_type,
render_type_expr,
)


def render_type_expr(value: TypeExpression) -> str:
match value:
case DictTypeExpr(nested):
return f"dict[str, {render_type_expr(nested)}]"
case ListTypeExpr(nested):
return f"list[{render_type_expr(nested)}]"
case LiteralTypeExpr(inner):
return f"Literal[{repr(inner)}]"
case UnionTypeExpr(inner):
return " | ".join(render_type_expr(x) for x in inner)
case other:
return other


def extract_inner_type(value: TypeExpression) -> TypeName:
match value:
case DictTypeExpr(nested):
return extract_inner_type(nested)
case ListTypeExpr(nested):
return extract_inner_type(nested)
case LiteralTypeExpr(_):
raise ValueError(f"Unexpected literal type: {value}")
case UnionTypeExpr(_):
raise ValueError(
f"Attempting to extract from a union, currently not possible: {value}"
)
case other:
return other


def ensure_literal_type(value: TypeExpression) -> TypeName:
match value:
case DictTypeExpr(_):
raise ValueError(
f"Unexpected expression when expecting a type name: {value}"
)
case ListTypeExpr(_):
raise ValueError(
f"Unexpected expression when expecting a type name: {value}"
)
case LiteralTypeExpr(_):
raise ValueError(
f"Unexpected expression when expecting a type name: {value}"
)
case UnionTypeExpr(_):
raise ValueError(
f"Unexpected expression when expecting a type name: {value}"
)
case other:
return other


_NON_ALNUM_RE = re.compile(r"[^a-zA-Z0-9_]+")

# Literal is here because HandshakeType can be Literal[None]
Expand Down Expand Up @@ -214,14 +144,6 @@ class RiverSchema(BaseModel):
RiverSchemaFile = RootModel[RiverSchema]


def reindent(prefix: str, code: str) -> str:
"""
Take an arbitrarily indented code block, dedent to the lowest common
indent level and then reindent based on the supplied prefix
"""
return indent(dedent(code.rstrip()), prefix)


def is_literal(tpe: RiverType) -> bool:
if isinstance(tpe, RiverUnionType):
return all(is_literal(t) for t in tpe.anyOf)
Expand Down
9 changes: 9 additions & 0 deletions replit_river/codegen/format.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from textwrap import dedent, indent


def reindent(prefix: str, code: str) -> str:
"""
Take an arbitrarily indented code block, dedent to the lowest common
indent level and then reindent based on the supplied prefix
"""
return indent(dedent(code.rstrip()), prefix)
Loading
Loading