Esempio n. 1
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}`");
        }
Esempio n. 2
0
        // Gets all required enum fields that must be part of the pyi file because they're
        // default values in some methods.
        Dictionary <EnumType, HashSet <EnumValue> > GetRequiredEnumFields(List <PyClass> classes)
        {
            var reqEnumFields = new Dictionary <EnumType, HashSet <EnumValue> >();
            var argToEnumType = new Dictionary <string, EnumType>(StringComparer.Ordinal);

            foreach (var pyClass in classes)
            {
                foreach (var method in pyClass.Methods)
                {
                    DocComments docComments;
                    if (method.Attributes.Any(AttributeKind.New))
                    {
                        docComments = pyClass.DocComments;
                    }
                    else
                    {
                        docComments = method.DocComments;
                    }
                    var docs = docComments.Sections.OfType <ArgsDocCommentSection>().FirstOrDefault();
                    if (docs is null)
                    {
                        continue;
                    }
                    int hasThis = method.Arguments.Count != 0 && method.Arguments[0].IsSelf ? 1 : 0;
                    if (docs.Args.Length != (method.Arguments.Count - hasThis))
                    {
                        throw new InvalidOperationException();
                    }
                    argToEnumType.Clear();
                    for (int i = 0; i < docs.Args.Length; i++)
                    {
                        var docArg = docs.Args[i];
                        if (docArg.Name != method.Arguments[hasThis + i].Name)
                        {
                            throw new InvalidOperationException();
                        }
                        if (!ParseUtils.TryConvertSphinxTypeToTypeName(docArg.SphinxType, out var typeName))
                        {
                            continue;
                        }
                        if (!exportedPythonTypes.TryFindByName(typeName, out var enumType))
                        {
                            continue;
                        }
                        argToEnumType.Add(docArg.Name, enumType);
                    }

                    var argsAttr = method.Attributes.Attributes.FirstOrDefault(a => a.Kind == AttributeKind.Args);
                    if (argsAttr is null)
                    {
                        continue;
                    }
                    foreach (var(name, value) in ParseUtils.GetArgsNameValues(argsAttr.Text))
                    {
                        if (!argToEnumType.TryGetValue(name, out var enumType))
                        {
                            continue;
                        }
                        if (!uint.TryParse(value, out var rawValue))
                        {
                            throw new InvalidOperationException($"Couldn't parse {value} as an integer");
                        }
                        var enumValue = enumType.Values.FirstOrDefault(a => a.Value == rawValue);
                        if (enumValue is null)
                        {
                            throw new InvalidOperationException($"Couldn't find an enum value in {enumType.RawName} with a value equal to {value}");
                        }
                        if (!reqEnumFields.TryGetValue(enumType, out var hash))
                        {
                            reqEnumFields.Add(enumType, hash = new HashSet <EnumValue>());
                        }
                        hash.Add(enumValue);
                    }
                }
            }
            return(reqEnumFields);
        }
Esempio n. 3
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("...");
                }
            }
        }