api: fix python code generation (#7764)

* api: fix python code generation

* fix python types

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
This commit is contained in:
EvgenyandEvgeny @ SimpleX Chat authored and GitHub committed 2026-10-19 08:32:09 +01:00
1 parent a766d0af0f
commit 924f2b9644
2 files changed
+26 -26

No files matched your search

+22 -23
View File
@@ -60,10 +60,7 @@ commandsCodeText =
<> respAliasLine
where
constrName = T.pack $ fstToUpper tag
classDef =
("class " <> constrName <> "(TypedDict):\n")
<> bodyOrPass (fieldsCodePy " " "T." params)
<> "\n"
classDef = typedDictCode constrName (fieldsCodePy "T." params) <> "\n"
cmdStringFunc =
("\ndef " <> constrName <> "_cmd_string(self: " <> constrName <> ") -> str:\n")
<> " return " <> pySelfSyntaxText "T." (fstToUpper tag, params) syntax <> "\n"
@@ -143,9 +140,7 @@ typesCodeText =
"FileProtocol" -> map toUpper m
_ -> m
typeDefCode = case typeDef of
ATDRecord fields ->
("\nclass " <> name' <> "(TypedDict):\n")
<> bodyOrPass (fieldsCodePy " " "" fields)
ATDRecord fields -> "\n" <> typedDictCode name' (fieldsCodePy "" fields)
ATDEnum cs ->
"\n" <> name' <> " = Literal["
<> T.intercalate ", " (map (\m -> "\"" <> T.pack (enumValue m) <> "\"") $ L.toList cs)
@@ -231,9 +226,7 @@ unionTypeCodePy memberName typesNamespace name cs =
constrTypeRef (ATUnionMember tag _) = memberName name tag
tagLiterals = T.intercalate ", " $ map (\(ATUnionMember tag _) -> "\"" <> T.pack tag <> "\"") $ L.toList cs
memberClass (ATUnionMember tag fields) =
("\nclass " <> memberName name tag <> "(TypedDict):\n")
<> (" type: Literal[\"" <> T.pack tag <> "\"]\n")
<> fieldsCodePy " " typesNamespace fields
"\n" <> typedDictCode (memberName name tag) (("type", "Literal[\"" <> T.pack tag <> "\"]", "") : fieldsCodePy typesNamespace fields)
-- | Render the right-hand side of a union alias: either inline (one line) or
-- multi-line wrapped in parentheses with `|` separators between alternatives.
@@ -248,21 +241,27 @@ unionAliasRhs lhs constr cs
(h : t) -> h : map ("| " <>) t
cs' = map constr cs
-- | Emit a body of `pass` if there are no fields, otherwise the rendered
-- fields as-is.
bodyOrPass :: Text -> Text
bodyOrPass body
| T.null body = " pass\n"
| otherwise = body
typedDictCode :: Text -> [(Text, Text, Text)] -> Text
typedDictCode name fields
| any (\(field, _, _) -> field `elem` pyKeywords) fields =
(name <> " = TypedDict(\"" <> name <> "\", {\n")
<> foldMap (\(field, t, comment) -> " \"" <> field <> "\": " <> t <> "," <> comment <> "\n") fields
<> "})\n"
| null fields = "class " <> name <> "(TypedDict):\n pass\n"
| otherwise =
("class " <> name <> "(TypedDict):\n")
<> foldMap (\(field, t, comment) -> " " <> field <> ": " <> t <> comment <> "\n") fields
-- | Render record fields for a TypedDict body. Each field becomes
-- `<indent><name>: <type>[ # <comment>]`. Optional fields wrap the type in
-- `NotRequired[...]`.
fieldsCodePy :: Text -> Text -> [APIRecordField] -> Text
fieldsCodePy indent namespace = foldMap render
pyKeywords :: [Text]
pyKeywords =
["False", "None", "True", "and", "as", "assert", "async", "await", "break", "class", "continue", "def", "del", "elif", "else", "except", "finally", "for", "from", "global", "if", "import", "in", "is", "lambda", "nonlocal", "not", "or", "pass", "raise", "return", "try", "while", "with", "yield"]
-- | Render record fields for a TypedDict as name, type and comment.
-- Optional fields wrap the type in `NotRequired[...]`.
fieldsCodePy :: Text -> [APIRecordField] -> [(Text, Text, Text)]
fieldsCodePy namespace = map render
where
render (APIRecordField name t) =
indent <> T.pack name <> ": " <> wrapOptional t (typeText t) <> typeComment t <> "\n"
render (APIRecordField name t) = (T.pack name, wrapOptional t (typeText t), typeComment t)
wrapOptional t inner = case t of
ATOptional _ -> "NotRequired[" <> inner <> "]"
_ -> inner