static IEnumerable <object> GetMembers(PyClass pyClass) { var setters = pyClass.Methods.Where(a => a.Attributes.Any(AttributeKind.Setter)).ToDictionary(a => a.Name, a => a, StringComparer.Ordinal); foreach (var method in pyClass.Methods) { if (method.Attributes.Any(AttributeKind.Setter)) { continue; } if (method.Attributes.Any(AttributeKind.Getter)) { setters.TryGetValue(method.Name, out var setterMethod); setters.Remove(method.Name); yield return(new PyProperty(method, setterMethod)); } else { yield return(method); } } if (setters.Count != 0) { throw new InvalidOperationException($"{pyClass.Name}: Setter without a getter: {setters.First().Value.Name}"); } }
void AddPyClass(PyClass pyClass) { if (pyClasses.ContainsKey(pyClass.Name)) { throw GetException($"Duplicate struct {pyClass.Name}"); } pyClasses.Add(pyClass.Name, pyClass); }
static string GetReturnType(PyClass pyClass, string methodName, string rustType, string sphinxType) { var typeStr = GetType(pyClass, methodName, rustType, sphinxType); if (methodName == "__iter__") { string returnType = pyClass.Name switch { "Decoder" => "Instruction", _ => throw new InvalidOperationException($"Unexpected iterator class {pyClass.Name}"), }; return($"Iterator[{returnType}]"); } return(typeStr); }
static string GetType(PyClass pyClass, string methodName, string rustType, string sphinxType) { // The type in the docs (sphinx type) is more accurate than the type in the source code // since `u32` is used in the source code if it's an enum value. if (sphinxType != string.Empty) { var sphinxTypes = ParseUtils.SplitSphinxTypes(sphinxType).ToList(); var convertedTypes = new List <string>(); foreach (var stype in sphinxTypes) { if (!ParseUtils.TryConvertSphinxTypeToTypeName(stype, out var typeName)) { typeName = stype; } convertedTypes.Add(typeName); } int index = convertedTypes.Count == 1 ? -1 : convertedTypes.IndexOf("None"); if (index >= 0) { convertedTypes.RemoveAt(index); } string typeStr; if (convertedTypes.Count > 1) { typeStr = "Union[" + string.Join(", ", convertedTypes.ToArray()) + "]"; } else { typeStr = convertedTypes[0]; } if (index >= 0) { return("Optional[" + typeStr + "]"); } return(typeStr); } if (ParseUtils.TryRemovePrefixSuffix(rustType, "PyResult<", ">", out var extractedType)) { rustType = extractedType; } switch (rustType) { case "i8" or "i16" or "i32" or "i64" or "isize" or "u8" or "u16" or "u32" or "u64" or "usize": return("int"); case "bool": return("bool"); case "&str" or "String": return("str"); case "PyRef<Self>" or "PyRefMut<Self>" or "Self": return(pyClass.Name); case "&PyAny": return("Any"); default: if (ParseUtils.TryRemovePrefixSuffix(rustType, "IterNextOutput<", ", ()>", out extractedType)) { return(extractedType); } break; } throw new InvalidOperationException($"Method {pyClass.Name}.{methodName}(): Couldn't convert Rust/sphinx type to Python type: Rust=`{rustType}`, sphinx=`{sphinxType}`"); }
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("..."); } } }