static void Write(FileWriter writer, PyiDocGen docGen, IdentifierConverter idConverter, PyClass pyClass, PyMethod method, DocComments docComments, Dictionary <string, EnumType> toEnumType) { if (method.Attributes.Any(AttributeKind.ClassMethod)) { writer.WriteLine("@classmethod"); } if (method.Attributes.Any(AttributeKind.StaticMethod)) { writer.WriteLine("@staticmethod"); } bool isGetter = method.Attributes.Any(AttributeKind.Getter); bool isSetter = method.Attributes.Any(AttributeKind.Setter); if (isGetter) { writer.WriteLine("@property"); } if (isSetter) { writer.WriteLine($"@{method.Name}.setter"); } string sphinxReturnType = string.Empty; if (isGetter || isSetter) { if (docComments.Sections.FirstOrDefault() is not TextDocCommentSection textDocs || textDocs.Lines.Length == 0) { throw new InvalidOperationException(); } if (!ParseUtils.TryParseTypeAndDocs(textDocs.Lines[0], out _, out var typeInfo)) { throw new InvalidOperationException(); } sphinxReturnType = typeInfo.SphinxType; } else { var returns = docComments.Sections.OfType <ReturnsDocCommentSection>().FirstOrDefault(); if (returns is not null) { sphinxReturnType = returns.Returns.SphinxType; } } bool isCtor = method.Attributes.Any(AttributeKind.New); writer.Write("def "); writer.Write(isCtor ? "__init__" : method.Name); writer.Write("("); int argCount = 0; if (isCtor) { writer.Write("self"); argCount++; } var argsDocs = docComments.Sections.OfType <ArgsDocCommentSection>().FirstOrDefault(); int hasThis = method.Arguments.Count != 0 && method.Arguments[0].IsSelf ? 1 : 0; Dictionary <string, string> toDefaultValue; var argsAttr = method.Attributes.Attributes.FirstOrDefault(a => a.Kind == AttributeKind.Args); if (argsAttr is null) { toDefaultValue = new Dictionary <string, string>(StringComparer.Ordinal); } else { toDefaultValue = ParseUtils.GetArgsNameValues(argsAttr.Text).ToDictionary(a => a.name, a => a.value, StringComparer.Ordinal); } for (int i = 0; i < method.Arguments.Count; i++) { if (argsDocs is not null && argsDocs.Args.Length != method.Arguments.Count - hasThis) { throw new InvalidOperationException(); } var methodArg = method.Arguments[i]; if (argCount > 0) { writer.Write(", "); } argCount++; if (methodArg.IsSelf) { writer.Write("self"); } else { writer.Write(methodArg.Name); string docsSphinxType; if (argsDocs is not null) { var docsArg = argsDocs.Args[i - hasThis]; if (docsArg.Name != methodArg.Name) { throw new InvalidOperationException(); } docsSphinxType = docsArg.SphinxType; } else { docsSphinxType = string.Empty; } if (i == 1 && isSetter) { docsSphinxType = sphinxReturnType; } writer.Write(": "); var type = GetType(pyClass, method.Name, methodArg.RustType, docsSphinxType); writer.Write(type); if (toDefaultValue.TryGetValue(methodArg.Name, out var defaultValueStr)) { writer.Write(" = "); if (!TryGetValueStr(idConverter, type, defaultValueStr, toEnumType, out var valueStr)) { throw new InvalidOperationException($"method {pyClass.Name}.{method.Name}(): Couldn't convert default value `{defaultValueStr}` to a Python value"); } writer.Write(valueStr); } } } writer.Write(") -> "); if (method.HasReturnType && !isCtor) { writer.Write(GetReturnType(pyClass, method.Name, method.RustReturnType, sphinxReturnType)); } else { writer.Write("None"); } if (method.DocComments.Sections.Count == 0) { writer.WriteLine(": ..."); } else { writer.WriteLine(":"); using (writer.Indent()) { WriteDocs(writer, docGen.Convert(method.DocComments)); writer.WriteLine("..."); } } }
void WritePyi(List <PyClass> classes) { var reqEnumFields = GetRequiredEnumFields(classes); var filename = genTypes.Dirs.GetPythonPyFilename("_iced_x86_py.pyi"); using (var writer = new FileWriter(TargetLanguage.Python, FileUtils.OpenWrite(filename))) { writer.WriteFileHeader(); writer.WriteLine("from collections.abc import Iterator"); writer.WriteLine("from enum import IntEnum, IntFlag"); writer.WriteLine("from typing import Any, List, Optional, Union"); writer.WriteLine(); var idConverter = PythonIdentifierConverter.Create(); var allEnumTypes = exportedPythonTypes.Enums.Select(a => (enumType: a, pythonName: a.Name(idConverter))); var toEnumType = allEnumTypes.ToDictionary(a => a.pythonName, a => a.enumType, StringComparer.Ordinal); foreach (var(enumType, pythonName) in allEnumTypes.OrderBy(a => a.pythonName, StringComparer.Ordinal)) { var baseClass = enumType.IsFlags ? "IntFlag" : "IntEnum"; if (reqEnumFields.TryGetValue(enumType, out var fields)) { writer.WriteLine($"class {pythonName}({baseClass}):"); using (writer.Indent()) { bool uppercaseRawName = PythonUtils.UppercaseEnum(enumType.TypeId.Id1); foreach (var value in enumType.Values) { if (fields.Contains(value)) { fields.Remove(value); var(valueName, numStr) = PythonUtils.GetEnumNameValue(idConverter, value, uppercaseRawName); writer.WriteLine($"{valueName} = {numStr}"); } if (fields.Count == 0) { break; } } if (fields.Count != 0) { throw new InvalidOperationException(); } writer.WriteLine("..."); } } else { writer.WriteLine($"class {pythonName}({baseClass}): ..."); } } var docGen = new PyiDocGen(); foreach (var pyClass in classes.OrderBy(a => a.Name, StringComparer.Ordinal)) { writer.WriteLine(); writer.WriteLine($"class {idConverter.Type(pyClass.Name)}:"); using (writer.Indent()) { WriteDocs(writer, docGen.Convert(pyClass.DocComments)); int defCount = 0; foreach (var member in GetMembers(pyClass)) { switch (member) { case PyMethod method: var docComments = method.Attributes.Any(AttributeKind.New) ? pyClass.DocComments : method.DocComments; Write(writer, docGen, idConverter, pyClass, method, docComments, toEnumType); defCount++; break; case PyProperty property: Write(writer, docGen, idConverter, pyClass, property.Getter, property.Getter.DocComments, toEnumType); defCount++; if (property.Setter is not null) { Write(writer, docGen, idConverter, pyClass, property.Setter, property.Getter.DocComments, toEnumType); defCount++; } break; default: throw new InvalidOperationException(); } } if (defCount == 0) { throw new InvalidOperationException($"class {pyClass.Name}: No class members"); } } } } }