diff --git a/CHANGELOG.md b/CHANGELOG.md index 2aa703b0e5..97bfb1fc19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added support for external documentation links within descriptions in Python. [2041](https://github.com/microsoft/kiota/issues/2041) + ### Changed diff --git a/src/Kiota.Builder/Writers/Python/CodeClassDeclarationWriter.cs b/src/Kiota.Builder/Writers/Python/CodeClassDeclarationWriter.cs index 65fdf6ec37..1c745b1fe5 100644 --- a/src/Kiota.Builder/Writers/Python/CodeClassDeclarationWriter.cs +++ b/src/Kiota.Builder/Writers/Python/CodeClassDeclarationWriter.cs @@ -50,7 +50,7 @@ public override void WriteCodeElement(ClassDeclaration codeElement, LanguageWrit _codeUsingWriter.WriteExternalImports(codeElement, writer); _codeUsingWriter.WriteConditionalInternalImports(codeElement, writer, parentNamespace); } - conventions.WriteShortDescription(parent.Documentation.Description, writer); + conventions.WriteLongDescription(parent.Documentation, writer); } } } diff --git a/src/Kiota.Builder/Writers/Python/CodeMethodWriter.cs b/src/Kiota.Builder/Writers/Python/CodeMethodWriter.cs index d6879b28bf..75bd5cd3e1 100644 --- a/src/Kiota.Builder/Writers/Python/CodeMethodWriter.cs +++ b/src/Kiota.Builder/Writers/Python/CodeMethodWriter.cs @@ -674,32 +674,18 @@ private void WriteSerializerBodyForIntersectionModel(CodeClass parentClass, Lang } } } - private void WriteMethodDocumentation(CodeMethod code, LanguageWriter writer, string returnType, bool isVoid) { - var isDescriptionPresent = !string.IsNullOrEmpty(code.Documentation.Description); - var parametersWithDescription = code.Parameters - .Where(static x => !string.IsNullOrEmpty(x.Documentation.Description)) - .ToArray(); var nullablePrefix = code.ReturnType.IsNullable && !isVoid ? "Optional[" : string.Empty; var nullableSuffix = code.ReturnType.IsNullable && !isVoid ? "]" : string.Empty; - if (isDescriptionPresent || parametersWithDescription.Any()) - { - writer.WriteLine(conventions.DocCommentStart); - if (isDescriptionPresent) - writer.WriteLine($"{conventions.DocCommentPrefix}{PythonConventionService.RemoveInvalidDescriptionCharacters(code.Documentation.Description)}"); - if (parametersWithDescription.Any()) - { - writer.StartBlock("Args:"); - - foreach (var paramWithDescription in parametersWithDescription.OrderBy(x => x.Name, StringComparer.OrdinalIgnoreCase)) - writer.WriteLine($"{conventions.DocCommentPrefix}{paramWithDescription.Name.ToSnakeCase()}: {PythonConventionService.RemoveInvalidDescriptionCharacters(paramWithDescription.Documentation.Description)}"); - writer.DecreaseIndent(); - } - if (!isVoid) - writer.WriteLine($"{conventions.DocCommentPrefix}Returns: {nullablePrefix}{returnType}{nullableSuffix}"); - writer.WriteLine(conventions.DocCommentEnd); - } + var returnRemark = isVoid ? "Returns: None" : $"Returns: {nullablePrefix}{returnType}{nullableSuffix}"; + conventions.WriteLongDescription(code.Documentation, + writer, + code.Parameters + .Where(static x => x.Documentation.DescriptionAvailable) + .OrderBy(static x => x.Name, StringComparer.OrdinalIgnoreCase) + .Select(x => $"param {x.Name.ToSnakeCase()}: {PythonConventionService.RemoveInvalidDescriptionCharacters(x.Documentation.Description)}") + .Union(new[] { returnRemark })); } private static readonly PythonCodeParameterOrderComparer parameterOrderComparer = new(); private void WriteMethodPrototype(CodeMethod code, LanguageWriter writer, string returnType, bool isVoid) diff --git a/src/Kiota.Builder/Writers/Python/CodePropertyWriter.cs b/src/Kiota.Builder/Writers/Python/CodePropertyWriter.cs index c3ee176c5e..9bf239520d 100644 --- a/src/Kiota.Builder/Writers/Python/CodePropertyWriter.cs +++ b/src/Kiota.Builder/Writers/Python/CodePropertyWriter.cs @@ -27,7 +27,7 @@ public override void WriteCodeElement(CodeProperty codeElement, LanguageWriter w writer.WriteLine("@property"); writer.WriteLine($"def {codeElement.Name.ToSnakeCase()}(self) -> {returnType}:"); writer.IncreaseIndent(); - conventions.WriteShortDescription(codeElement.Documentation.Description, writer); + conventions.WriteLongDescription(codeElement.Documentation, writer); _codeUsingWriter.WriteDeferredImport(parentClass, codeElement.Type.Name, writer); conventions.AddRequestBuilderBody(parentClass, returnType, writer); writer.CloseBlock(string.Empty); diff --git a/src/Kiota.Builder/Writers/Python/PythonConventionService.cs b/src/Kiota.Builder/Writers/Python/PythonConventionService.cs index 80ab1c0365..9b42876492 100644 --- a/src/Kiota.Builder/Writers/Python/PythonConventionService.cs +++ b/src/Kiota.Builder/Writers/Python/PythonConventionService.cs @@ -162,6 +162,25 @@ public override void WriteShortDescription(string description, LanguageWriter wr writer.WriteLine(DocCommentEnd); } } + public void WriteLongDescription(CodeDocumentation documentation, LanguageWriter writer, IEnumerable? additionalRemarks = default) + { + ArgumentNullException.ThrowIfNull(writer); + if (documentation is null) return; + if (additionalRemarks == default) + additionalRemarks = Enumerable.Empty(); + var additionalRemarksArray = additionalRemarks.ToArray(); + if (documentation.DescriptionAvailable || documentation.ExternalDocumentationAvailable || additionalRemarksArray.Any()) + { + writer.WriteLine(DocCommentStart); + if (documentation.DescriptionAvailable) + writer.WriteLine($"{RemoveInvalidDescriptionCharacters(documentation.Description)}"); + foreach (var additionalRemark in additionalRemarksArray.Where(static x => !string.IsNullOrEmpty(x))) + writer.WriteLine($"{additionalRemark}"); + if (documentation.ExternalDocumentationAvailable) + writer.WriteLine($"{documentation.DocumentationLabel}: {documentation.DocumentationLink}"); + writer.WriteLine(DocCommentEnd); + } + } public void WriteInLineDescription(string description, LanguageWriter writer) { diff --git a/tests/Kiota.Builder.Tests/Writers/Python/CodeMethodWriterTests.cs b/tests/Kiota.Builder.Tests/Writers/Python/CodeMethodWriterTests.cs index 00f5e1e86c..63ba33b467 100644 --- a/tests/Kiota.Builder.Tests/Writers/Python/CodeMethodWriterTests.cs +++ b/tests/Kiota.Builder.Tests/Writers/Python/CodeMethodWriterTests.cs @@ -909,6 +909,8 @@ public void WritesMethodAsyncDescription() { setup(); method.Documentation.Description = MethodDescription; + method.Documentation.DocumentationLabel = "see more"; + method.Documentation.DocumentationLink = new("https://example.org/docs"); var parameter = new CodeParameter { Documentation = new() @@ -926,10 +928,11 @@ public void WritesMethodAsyncDescription() var result = tw.ToString(); Assert.Contains("\"\"\"", result); Assert.Contains(MethodDescription, result); - Assert.Contains("Args:", result); - Assert.Contains("param_name", result); + Assert.Contains("param param_name:", result); Assert.Contains(ParamDescription, result); - Assert.Contains("Returns:", result); + Assert.Contains("see more:", result); + Assert.Contains("https://example.org/docs", result); + Assert.Contains("Returns: Optional[Somecustomtype]", result); Assert.Contains("await", result); } [Fact] @@ -937,6 +940,8 @@ public void WritesMethodSyncDescription() { setup(); method.Documentation.Description = MethodDescription; + method.Documentation.DocumentationLabel = "see more"; + method.Documentation.DocumentationLink = new("https://example.org/docs"); method.IsAsync = false; var parameter = new CodeParameter { @@ -955,9 +960,11 @@ public void WritesMethodSyncDescription() var result = tw.ToString(); Assert.Contains("\"\"\"", result); Assert.Contains(MethodDescription, result); - Assert.Contains("Args:", result); - Assert.Contains("param_name", result); + Assert.Contains("param param_name:", result); Assert.Contains(ParamDescription, result); + Assert.Contains("see more:", result); + Assert.Contains("https://example.org/docs", result); + Assert.Contains("Returns: Optional[Somecustomtype]", result); Assert.DoesNotContain("await", result); } [Fact]