From e573aab4d6cdf6af148dde024129a386151403fe Mon Sep 17 00:00:00 2001 From: Kaizen Conroy <36202692+kaizencc@users.noreply.github.com> Date: Fri, 11 Oct 2024 14:42:22 -0400 Subject: [PATCH] fix(rosetta): python identifiers mishandle identifier translations with capital letters (#4644) Fixes https://github.com/aws/aws-cdk/issues/29138 Our python translation currently translates `sizeInMBs` to `size_in_mbs` for the python examples. However, the actual property is named `size_in_m_bs`. This is because we are currently handling identifiers with the regex `[^A-Z][A-Z]`, which misses the `MB` pair. I cannot see a use case where we would want to handle _identifiers_ this way (as opposed to interface names ala `IInterface`), so I've changed the logic. --- By submitting this pull request, I confirm that my contribution is made under the terms of the [Apache 2.0 license]. [Apache 2.0 license]: https://www.apache.org/licenses/LICENSE-2.0 --- packages/jsii-rosetta/lib/languages/python.ts | 4 +-- packages/jsii-rosetta/test/translate.test.ts | 30 +++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/packages/jsii-rosetta/lib/languages/python.ts b/packages/jsii-rosetta/lib/languages/python.ts index a9b16a2269..ecafd72754 100644 --- a/packages/jsii-rosetta/lib/languages/python.ts +++ b/packages/jsii-rosetta/lib/languages/python.ts @@ -111,7 +111,7 @@ export class PythonVisitor extends DefaultVisitor { * Bump this when you change something in the implementation to invalidate * existing cached translations. */ - public static readonly VERSION = '2'; + public static readonly VERSION = '3'; public readonly language = TargetLanguage.PYTHON; public readonly defaultContext = {}; @@ -818,7 +818,7 @@ function mangleIdentifier(originalIdentifier: string) { return originalIdentifier; } // Turn into snake-case - const cased = originalIdentifier.replace(/[^A-Z][A-Z]/g, (m) => `${m[0].slice(0, 1)}_${m.slice(1).toLowerCase()}`); + const cased = originalIdentifier.replace(/[A-Z]/g, (m) => `_${m.toLowerCase()}`); return IDENTIFIER_KEYWORDS.includes(cased) ? `${cased}_` : cased; } diff --git a/packages/jsii-rosetta/test/translate.test.ts b/packages/jsii-rosetta/test/translate.test.ts index 2eaf1b762f..d25f00b9e8 100644 --- a/packages/jsii-rosetta/test/translate.test.ts +++ b/packages/jsii-rosetta/test/translate.test.ts @@ -218,3 +218,33 @@ test('declarations are translated correctly in all jsii languages', () => { assembly.cleanup(); } }); + +test('handling capital letters in identifiers', () => { + // Create an assembly in a temp directory + const assembly = TestJsiiModule.fromSource( + { + 'index.ts': ` + export interface InterfaceA { + readonly sizeInMBs: Number; + } + `, + }, + { + name: 'my_assembly', + jsii: DUMMY_JSII_CONFIG, + }, + ); + try { + const ts = assembly.translateHere( + ["import * as masm from 'my_assembly';", 'declare let intA: masm.InterfaceA;', 'intA = { sizeInMBs: 3 };'].join('\n'), + ); + + expect(ts.get(TargetLanguage.PYTHON)?.source).toEqual( + ['import example_test_demo as masm', '# int_a: masm.InterfaceA', 'int_a = masm.InterfaceA(size_in_m_bs=3)'].join('\n'), + ); + expect(ts.get(TargetLanguage.JAVA)?.source).toEqual(['import example.test.demo.*;', 'InterfaceA intA;', 'intA = InterfaceA.builder().sizeInMBs(3).build();'].join('\n')); + expect(ts.get(TargetLanguage.CSHARP)?.source).toEqual(['using Example.Test.Demo;', 'InterfaceA intA;', 'intA = new InterfaceA { SizeInMBs = 3 };'].join('\n')); + } finally { + assembly.cleanup(); + } +}); \ No newline at end of file