예제 #1
0
        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}");
            }
        }
예제 #2
0
 void AddPyClass(PyClass pyClass)
 {
     if (pyClasses.ContainsKey(pyClass.Name))
     {
         throw GetException($"Duplicate struct {pyClass.Name}");
     }
     pyClasses.Add(pyClass.Name, pyClass);
 }
예제 #3
0
        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);
        }
예제 #4
0
        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}`");
        }
예제 #5
0
        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("...");
                }
            }
        }