Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/Kiota.Builder/Refiners/PythonRefiner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ public override Task RefineAsync(CodeNamespace generatedCode, CancellationToken
CorrectCoreType(generatedCode, CorrectMethodType, CorrectPropertyType, CorrectImplements);
cancellationToken.ThrowIfCancellationRequested();
CorrectCoreTypesForBackingStore(generatedCode, "field(default_factory=BackingStoreFactorySingleton(backing_store_factory=None).backing_store_factory.create_backing_store, repr=False)");
SnakeCaseNamespaceNames(generatedCode);
ShortenOversizedNamespaceSegments(generatedCode);
AddPropertiesAndMethodTypesImports(generatedCode, true, true, true, codeTypeFilter);
AddParsableImplementsForModelClasses(generatedCode, "Parsable");
Expand Down Expand Up @@ -158,6 +159,26 @@ public override Task RefineAsync(CodeNamespace generatedCode, CancellationToken
}, cancellationToken);
}

private static void SnakeCaseNamespaceNames(CodeElement currentElement)
{
if (currentElement is CodeNamespace codeNamespace &&
!string.IsNullOrEmpty(codeNamespace.Name))
{
var normalizedName = string.Join('.',
codeNamespace.Name
.Split('.', StringSplitOptions.RemoveEmptyEntries)
.Select(static x => x.ToSnakeCase()));
if (!codeNamespace.Name.Equals(normalizedName, StringComparison.Ordinal))
{
if (codeNamespace.Parent is CodeNamespace parentNamespace)
parentNamespace.RenameChildElement(codeNamespace.Name, normalizedName);
else
codeNamespace.Name = normalizedName;
}
}
CrawlTree(currentElement, SnakeCaseNamespaceNames);
}

private const string MultipartBodyClassName = "MultipartBody";
private const string AbstractionsPackageName = "kiota_abstractions";
private const string SerializationModuleName = $"{AbstractionsPackageName}.serialization";
Expand Down
18 changes: 13 additions & 5 deletions src/Kiota.Builder/Writers/Python/PythonWriter.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
using Kiota.Builder.PathSegmenters;
using System;
using System.Linq;
using Kiota.Builder.Extensions;
using Kiota.Builder.PathSegmenters;

namespace Kiota.Builder.Writers.Python;

public class PythonWriter : LanguageWriter
{
public PythonWriter(string rootPath, string clientNamespaceName, bool usesBackingStore = false)
{
PathSegmenter = new PythonPathSegmenter(rootPath, clientNamespaceName);
ArgumentNullException.ThrowIfNull(clientNamespaceName);
var normalizedClientNamespaceName = string.Join('.',
clientNamespaceName
.Split('.', StringSplitOptions.RemoveEmptyEntries)
.Select(static x => x.ToSnakeCase()));
PathSegmenter = new PythonPathSegmenter(rootPath, normalizedClientNamespaceName);
var conventionService = new PythonConventionService();
AddOrReplaceCodeElementWriter(new CodeClassDeclarationWriter(conventionService, clientNamespaceName));
AddOrReplaceCodeElementWriter(new CodeClassDeclarationWriter(conventionService, normalizedClientNamespaceName));
AddOrReplaceCodeElementWriter(new CodeBlockEndWriter());
AddOrReplaceCodeElementWriter(new CodeEnumWriter(conventionService));
AddOrReplaceCodeElementWriter(new CodeMethodWriter(conventionService, clientNamespaceName, usesBackingStore));
AddOrReplaceCodeElementWriter(new CodePropertyWriter(conventionService, clientNamespaceName));
AddOrReplaceCodeElementWriter(new CodeMethodWriter(conventionService, normalizedClientNamespaceName, usesBackingStore));
AddOrReplaceCodeElementWriter(new CodePropertyWriter(conventionService, normalizedClientNamespaceName));
AddOrReplaceCodeElementWriter(new CodeTypeWriter(conventionService));
AddOrReplaceCodeElementWriter(new CodeNameSpaceWriter(conventionService));
}
Expand Down
20 changes: 10 additions & 10 deletions tests/Kiota.Builder.Tests/Export/PublicAPIExportServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -180,16 +180,16 @@ private static void ValidateExportGo(HashSet<string> exportContents)
private static void ValidateExportPython(HashSet<string> exportContents)
{
Assert.NotEmpty(exportContents);
Assert.Contains("exportNamespace.Graph-->BaseRequestBuilder", exportContents); // captures class inheritance
Assert.Contains("exportNamespace.models.microsoft.graph.User~~>AdditionalDataHolder; Parsable", exportContents);// captures implemented interfaces
Assert.Contains("exportNamespace.models.microsoft.graph.User::|public|id():str", exportContents);// captures property getter location,type and access inheritance
Assert.Contains("exportNamespace.models.microsoft.graph.User::|public|id(value:str):None", exportContents);// captures property setter location,type and access inheritance
Assert.Contains("exportNamespace.me.MeRequestBuilder::|public|constructor(path_parameters:Union[str, dict[str, Any]]; request_adapter:RequestAdapter):None", exportContents); // captures constructors, their parameters(name and types), return and access
Assert.Contains("exportNamespace.me.get.GetRequestBuilder::|public|to_get_request_information(request_configuration?:RequestConfiguration[QueryParameters]):RequestInformation", exportContents);// captures methods, their parameters(name and types), return and access
Assert.Contains("exportNamespace.models.microsoft.graph.User::|static|public|create_from_discriminator_value(parse_node:ParseNode):User", exportContents);// captures static methods too :)
Assert.Contains("exportNamespace.models.microsoft.graph.Importance::0000-Low", exportContents);// captures enum members
Assert.Contains("exportNamespace.models.microsoft.graph.User::|public|other_names():list[str]", exportContents);// captures collection info in language specific format
Assert.Contains("exportNamespace.models.microsoft.graph.User::|public|other_names(value:list[str]):None", exportContents);// captures collection info in language specific format
Assert.Contains("export_namespace.Graph-->BaseRequestBuilder", exportContents); // captures class inheritance
Assert.Contains("export_namespace.models.microsoft.graph.User~~>AdditionalDataHolder; Parsable", exportContents);// captures implemented interfaces
Assert.Contains("export_namespace.models.microsoft.graph.User::|public|id():str", exportContents);// captures property getter location,type and access inheritance
Assert.Contains("export_namespace.models.microsoft.graph.User::|public|id(value:str):None", exportContents);// captures property setter location,type and access inheritance
Assert.Contains("export_namespace.me.MeRequestBuilder::|public|constructor(path_parameters:Union[str, dict[str, Any]]; request_adapter:RequestAdapter):None", exportContents); // captures constructors, their parameters(name and types), return and access
Assert.Contains("export_namespace.me.get.GetRequestBuilder::|public|to_get_request_information(request_configuration?:RequestConfiguration[QueryParameters]):RequestInformation", exportContents);// captures methods, their parameters(name and types), return and access
Assert.Contains("export_namespace.models.microsoft.graph.User::|static|public|create_from_discriminator_value(parse_node:ParseNode):User", exportContents);// captures static methods too :)
Assert.Contains("export_namespace.models.microsoft.graph.Importance::0000-Low", exportContents);// captures enum members
Assert.Contains("export_namespace.models.microsoft.graph.User::|public|other_names():list[str]", exportContents);// captures collection info in language specific format
Assert.Contains("export_namespace.models.microsoft.graph.User::|public|other_names(value:list[str]):None", exportContents);// captures collection info in language specific format
}

private static void ValidateExportTypeScript(HashSet<string> exportContents)
Expand Down
3 changes: 2 additions & 1 deletion tests/Kiota.Builder.Tests/KiotaBuilderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -848,7 +848,8 @@ public async Task DoesNotAddSuperflousFieldsToModelsAsync(GenerationLanguage lan
var node = builder.CreateUriSpace(document);
var codeModel = builder.CreateSourceModel(node);
await builder.ApplyLanguageRefinementAsync(generationConfiguration, codeModel, TestContext.Current.CancellationToken);
var requestBuilderNamespace = codeModel.FindNamespaceByName("ApiSdk.api.all");
var requestBuilderNamespace = codeModel.FindNamespaceByName(
language == GenerationLanguage.Python ? "api_sdk.api.all" : "ApiSdk.api.all");
Assert.NotNull(requestBuilderNamespace);
if (language == GenerationLanguage.TypeScript || language == GenerationLanguage.Go)
{// these languages use CodeFiles
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,27 @@ public async Task AddsDefaultImportsAsync()
await ILanguageRefiner.RefineAsync(new GenerationConfiguration { Language = GenerationLanguage.Python }, graphNS, cancellationToken: TestContext.Current.CancellationToken);
Assert.Contains("annotations", declaration.Usings.Select(x => x.Name));
}

[Fact]
public async Task ConvertsNamespaceSegmentsToSnakeCaseAsync()
{
var clientNamespace = root.AddNamespace("graphSdk");
var childNamespace = root.AddNamespace("graphSdk.someNamespace");

await ILanguageRefiner.RefineAsync(
new GenerationConfiguration
{
Language = GenerationLanguage.Python,
ClientNamespaceName = "graphSdk",
},
root,
cancellationToken: TestContext.Current.CancellationToken);

Assert.Equal("graph_sdk", clientNamespace.Name);
Assert.Equal("graph_sdk.some_namespace", childNamespace.Name);
Assert.Same(childNamespace, root.FindNamespaceByName("graph_sdk.some_namespace"));
}

[Fact]
public async Task AddsQueryParameterMapperMethodAsync()
{
Expand Down
28 changes: 28 additions & 0 deletions tests/Kiota.Builder.Tests/Writers/Python/PythonWriterTests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using System;
using System.IO;
using System.Linq;

using Kiota.Builder.CodeDOM;
using Kiota.Builder.Writers.Python;

using Xunit;
Expand All @@ -17,4 +20,29 @@ public void Instantiates()
Assert.Throws<ArgumentNullException>(() => new PythonWriter(null, "graph"));
Assert.Throws<ArgumentNullException>(() => new PythonWriter("./", null));
}

[Fact]
public void NormalizesClientNamespacePrefix()
{
var outputPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
try
{
var writer = new PythonWriter(outputPath, "graphSdk");
var root = CodeNamespace.InitRootNamespace();
var childNamespace = root.AddNamespace("graph_sdk.some_namespace");
var model = childNamespace.AddClass(new CodeClass { Name = "TestModel" });

var path = writer.PathSegmenter!.GetPath(childNamespace, model.First(), false);

Assert.EndsWith(
Path.Combine("some_namespace", "test_model.py"),
path,
StringComparison.Ordinal);
}
finally
{
if (Directory.Exists(outputPath))
Directory.Delete(outputPath, true);
}
}
}