-
Notifications
You must be signed in to change notification settings - Fork 210
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Task/python imports refactor (#2379)
* Add python relative import manager * Add a codeusingwriter for python * Add option to include parent namespace when adding discriminator mapping usings * Call method to add discriminator mapping usings to parent classes * Add type checking import * Remove lazy import using * Update class declaration writer * Update property writer to add local imports for request builders with no parameters * Add logic to write discriminator method body * Write local imports in indexers and request builders with parameters * Add deferred imports for request executors and deserializers * Use snake case to match variable name passed to request builder * Update python writer with missing parameters * Add incluparentnamespace parameter when crawling tree * Add code class declaration writer tests * Add property writer tests * Add codemethodwriter tests * Fix formatting issues * Update method writer tests * Add codeusing writer tests * Fix formatting * Add changelog entry * Add type annotation for discriminator fields variable * Update test * Fix fields type hint * Bump test coverage in classdeclarationwriter * Add python code element order comparer * Add code renderer for python * Write methods before properties * Add python element comparer tests * Fix formatting * Update CHANGELOG * Remove suppressions for passing it tests * Apply suggestions from code review --------- Co-authored-by: Vincent Biret <[email protected]>
- Loading branch information
1 parent
0119d15
commit a9a0dd1
Showing
20 changed files
with
792 additions
and
106 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
using Kiota.Builder.CodeDOM; | ||
|
||
namespace Kiota.Builder; | ||
public class CodeElementOrderComparerPython : CodeElementOrderComparer | ||
{ | ||
protected override int GetTypeFactor(CodeElement element) | ||
{ | ||
return element switch | ||
{ | ||
CodeUsing => 1, | ||
ClassDeclaration => 2, | ||
InterfaceDeclaration => 3, | ||
CodeMethod => 4, | ||
CodeIndexer => 5, | ||
CodeProperty => 6, | ||
CodeClass => 7, | ||
BlockEnd => 8, | ||
_ => 0, | ||
}; | ||
} | ||
protected override int methodKindWeight { get; } = 200; | ||
protected override int GetMethodKindFactor(CodeElement element) | ||
{ | ||
if (element is CodeMethod method) | ||
return method.Kind switch | ||
{ | ||
CodeMethodKind.ClientConstructor => 1, | ||
CodeMethodKind.Constructor => 0, | ||
CodeMethodKind.RawUrlConstructor => 3, | ||
_ => 2, | ||
}; | ||
return 0; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
using System.Linq; | ||
using Kiota.Builder.CodeDOM; | ||
using Kiota.Builder.Configuration; | ||
|
||
namespace Kiota.Builder.CodeRenderers; | ||
public class PythonCodeRenderer : CodeRenderer | ||
{ | ||
public PythonCodeRenderer(GenerationConfiguration configuration) : base(configuration, new CodeElementOrderComparerPython()) { } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
87 changes: 21 additions & 66 deletions
87
src/Kiota.Builder/Writers/Python/CodeClassDeclarationWriter.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,95 +1,50 @@ | ||
using System; | ||
using System.Linq; | ||
|
||
using Kiota.Builder.CodeDOM; | ||
using Kiota.Builder.Extensions; | ||
|
||
namespace Kiota.Builder.Writers.Python; | ||
public class CodeClassDeclarationWriter : BaseElementWriter<ClassDeclaration, PythonConventionService> | ||
{ | ||
|
||
public CodeClassDeclarationWriter(PythonConventionService conventionService) : base(conventionService) | ||
private readonly CodeUsingWriter _codeUsingWriter; | ||
public CodeClassDeclarationWriter(PythonConventionService conventionService, string clientNamespaceName) : base(conventionService) | ||
{ | ||
_codeUsingWriter = new(clientNamespaceName); | ||
} | ||
public override void WriteCodeElement(ClassDeclaration codeElement, LanguageWriter writer) | ||
{ | ||
ArgumentNullException.ThrowIfNull(codeElement); | ||
ArgumentNullException.ThrowIfNull(writer); | ||
WriteExternalImports(codeElement, writer); // external imports before internal imports | ||
WriteInternalImports(codeElement, writer); | ||
var parentNamespace = codeElement.GetImmediateParentOfType<CodeNamespace>(); | ||
_codeUsingWriter.WriteExternalImports(codeElement, writer); // external imports before internal imports | ||
_codeUsingWriter.WriteConditionalInternalImports(codeElement, writer, parentNamespace); | ||
|
||
if (codeElement.Parent is CodeClass parentClass) | ||
{ | ||
if (codeElement.Inherits != null) | ||
_codeUsingWriter.WriteDeferredImport(parentClass, codeElement.Inherits.Name, writer); | ||
foreach (var implement in codeElement.Implements) | ||
_codeUsingWriter.WriteDeferredImport(parentClass, implement.Name, writer); | ||
|
||
} | ||
|
||
|
||
var abcClass = !codeElement.Implements.Any() ? string.Empty : $"{codeElement.Implements.Select(static x => x.Name.ToFirstCharacterUpperCase()).Aggregate((x, y) => x + ", " + y)}"; | ||
var derivation = codeElement.Inherits is CodeType inheritType && | ||
conventions.GetTypeString(inheritType, codeElement) is string inheritSymbol && | ||
!string.IsNullOrEmpty(inheritSymbol) ? | ||
inheritSymbol : | ||
abcClass; | ||
|
||
|
||
|
||
if (codeElement.Parent?.Parent is CodeClass) | ||
{ | ||
writer.WriteLine("@dataclass"); | ||
} | ||
writer.WriteLine($"class {codeElement.Name.ToFirstCharacterUpperCase()}({derivation}):"); | ||
writer.IncreaseIndent(); | ||
if (codeElement.Parent is CodeClass parentClass) | ||
conventions.WriteShortDescription(parentClass.Documentation.Description, writer); | ||
} | ||
|
||
private static void WriteExternalImports(ClassDeclaration codeElement, LanguageWriter writer) | ||
{ | ||
var externalImportSymbolsAndPaths = codeElement.Usings | ||
.Where(static x => x.IsExternal) | ||
.Select(x => (x.Name, string.Empty, x.Declaration?.Name)) | ||
.GroupBy(x => x.Item3) | ||
.OrderBy(x => x.Key); | ||
if (externalImportSymbolsAndPaths.Any()) | ||
{ | ||
foreach (var codeUsing in externalImportSymbolsAndPaths) | ||
if (!string.IsNullOrWhiteSpace(codeUsing.Key)) | ||
{ | ||
if (codeUsing.Key == "-") | ||
writer.WriteLine($"import {codeUsing.Select(x => GetAliasedSymbol(x.Item1, x.Item2)).Distinct().OrderBy(x => x).Aggregate((x, y) => x + ", " + y)}"); | ||
else | ||
writer.WriteLine($"from {codeUsing.Key.ToSnakeCase()} import {codeUsing.Select(x => GetAliasedSymbol(x.Item1, x.Item2)).Distinct().OrderBy(x => x).Aggregate((x, y) => x + ", " + y)}"); | ||
} | ||
writer.WriteLine(); | ||
} | ||
} | ||
|
||
private static void WriteInternalImports(ClassDeclaration codeElement, LanguageWriter writer) | ||
{ | ||
var internalImportSymbolsAndPaths = codeElement.Usings | ||
.Where(x => !x.IsExternal) | ||
.Select(static x => GetImportPathForUsing(x)) | ||
.GroupBy(x => x.Item3) | ||
.Where(x => !string.IsNullOrEmpty(x.Key)) | ||
.OrderBy(x => x.Key); | ||
if (internalImportSymbolsAndPaths.Any()) | ||
{ | ||
foreach (var codeUsing in internalImportSymbolsAndPaths) | ||
foreach (var symbol in codeUsing.Select(static x => GetAliasedSymbol(x.Item1, x.Item2)).Distinct().OrderBy(static x => x)) | ||
writer.WriteLine($"{symbol} = lazy_import('{codeUsing.Key.ToSnakeCase().Replace("._", ".")}.{symbol}')"); | ||
writer.WriteLine(); | ||
} | ||
} | ||
private static string GetAliasedSymbol(string symbol, string alias) | ||
{ | ||
return string.IsNullOrEmpty(alias) ? symbol : $"{symbol} as {alias}"; | ||
} | ||
|
||
/// <summary> | ||
/// Returns the import path for the given using and import context namespace. | ||
/// </summary> | ||
/// <param name="codeUsing">The using to import into the current namespace context</param> | ||
/// <returns>The import symbol, it's alias if any and the import path</returns> | ||
private static (string, string, string) GetImportPathForUsing(CodeUsing codeUsing) | ||
{ | ||
var typeDef = codeUsing.Declaration?.TypeDefinition; | ||
if (typeDef == null) | ||
return (codeUsing.Name, codeUsing.Alias, ""); // it's relative to the folder, with no declaration or type definition (default failsafe) | ||
|
||
var importSymbol = typeDef.Name.ToSnakeCase(); | ||
|
||
var importPath = typeDef.GetImmediateParentOfType<CodeNamespace>().Name; | ||
return (importSymbol, codeUsing.Alias, importPath); | ||
if (codeElement.Parent is CodeClass parent) | ||
conventions.WriteShortDescription(parent.Documentation.Description, writer); | ||
} | ||
} |
Oops, something went wrong.