diff --git a/packages/@jsii/python-runtime/tests/test_runtime_type_checking.py b/packages/@jsii/python-runtime/tests/test_runtime_type_checking.py index 700ea73208..b31ae8bf92 100644 --- a/packages/@jsii/python-runtime/tests/test_runtime_type_checking.py +++ b/packages/@jsii/python-runtime/tests/test_runtime_type_checking.py @@ -1,6 +1,7 @@ import pytest import re +from scope.jsii_calc_lib.custom_submodule_name import NestingClass import jsii_calc @@ -114,3 +115,7 @@ def test_setter_to_union(self): ), ): subject.union_property = jsii_calc.StringEnum.B # type:ignore + + def test_nested_struct(self): + # None of these should throw... + NestingClass.NestedStruct(name="Queen B") diff --git a/packages/jsii-pacmak/lib/targets/python.ts b/packages/jsii-pacmak/lib/targets/python.ts index 3f085f25b1..b25b5f4bb9 100644 --- a/packages/jsii-pacmak/lib/targets/python.ts +++ b/packages/jsii-pacmak/lib/targets/python.ts @@ -24,6 +24,7 @@ import { PythonImports, mergePythonImports, toPackageName, + toPythonFullName, } from './python/type-name'; import { die, toPythonIdentifier } from './python/util'; import { toPythonVersionRange, toReleaseVersion } from './version-utils'; @@ -466,7 +467,6 @@ abstract class BaseMethod implements PythonBase { private readonly returns: spec.OptionalValue | undefined, public readonly docs: spec.Docs | undefined, public readonly isStatic: boolean, - private readonly pythonParent: PythonType, opts: BaseMethodOpts, ) { this.abstract = !!opts.abstract; @@ -674,7 +674,9 @@ abstract class BaseMethod implements PythonBase { emitParameterTypeChecks( code, pythonParams.slice(1), - `${this.pythonParent.pythonName}.${this.pythonName}`, + `${toPythonFullName(this.parent.fqn, context.assembly)}.${ + this.pythonName + }`, ); } this.emitBody( @@ -867,7 +869,6 @@ abstract class BaseProperty implements PythonBase { private readonly jsName: string, private readonly type: spec.OptionalValue, public readonly docs: spec.Docs | undefined, - private readonly pythonParent: PythonType, opts: BasePropertyOpts, ) { const { abstract = false, immutable = false, isStatic = false } = opts; @@ -952,9 +953,10 @@ abstract class BaseProperty implements PythonBase { // In order to get a property accessor, we must resort to getting the // attribute on the type, instead of the value (where the getter would // be implicitly invoked for us...) - `getattr(${this.pythonParent.pythonName}, ${JSON.stringify( - this.pythonName, - )}).fset`, + `getattr(${toPythonFullName( + this.parent.fqn, + context.assembly, + )}, ${JSON.stringify(this.pythonName)}).fset`, ); code.line( `jsii.${this.jsiiSetMethod}(${this.implicitParameter}, "${this.jsName}", value)`, @@ -1141,7 +1143,11 @@ class Struct extends BasePythonClassType { code.line(`${member.pythonName} = ${typeName}(**${member.pythonName})`); code.closeBlock(); } - emitParameterTypeChecks(code, kwargs, `${this.pythonName}.__init__`); + emitParameterTypeChecks( + code, + kwargs, + `${toPythonFullName(this.spec.fqn, context.assembly)}.__init__`, + ); // Required properties, those will always be put into the dict assignDictionary( @@ -2605,7 +2611,6 @@ class PythonGenerator extends Generator { undefined, cls.initializer.docs, false, // Never static - klass, { liftedProp: this.getliftedProp(cls.initializer), parent: cls }, ), ); @@ -2628,7 +2633,6 @@ class PythonGenerator extends Generator { method.returns, method.docs, true, // Always static - klass, { abstract: method.abstract, liftedProp: this.getliftedProp(method), @@ -2647,7 +2651,6 @@ class PythonGenerator extends Generator { prop.name, prop, prop.docs, - klass, { abstract: prop.abstract, immutable: prop.immutable, @@ -2673,7 +2676,6 @@ class PythonGenerator extends Generator { method.returns, method.docs, !!method.static, - klass, { abstract: method.abstract, liftedProp: this.getliftedProp(method), @@ -2691,7 +2693,6 @@ class PythonGenerator extends Generator { method.returns, method.docs, !!method.static, - klass, { abstract: method.abstract, liftedProp: this.getliftedProp(method), @@ -2711,7 +2712,6 @@ class PythonGenerator extends Generator { prop.name, prop, prop.docs, - klass, { abstract: prop.abstract, immutable: prop.immutable, @@ -2773,7 +2773,6 @@ class PythonGenerator extends Generator { method.returns, method.docs, !!method.static, - klass, { liftedProp: this.getliftedProp(method), parent: ifc }, ), ); @@ -2793,7 +2792,6 @@ class PythonGenerator extends Generator { prop.name, prop, prop.docs, - klass, { immutable: prop.immutable, isStatic: prop.static, parent: ifc }, ); } diff --git a/packages/jsii-pacmak/lib/targets/python/type-name.ts b/packages/jsii-pacmak/lib/targets/python/type-name.ts index f26100d818..40574e3c45 100644 --- a/packages/jsii-pacmak/lib/targets/python/type-name.ts +++ b/packages/jsii-pacmak/lib/targets/python/type-name.ts @@ -403,6 +403,20 @@ export function toPythonFqn(fqn: string, rootAssm: Assembly) { return { assemblyName, packageName, pythonFqn: fqnParts.join('.') }; } +/** + * Computes the nesting-qualified name of a type. + * + * @param fqn the fully qualified jsii name of the type. + * @param rootAssm the root assembly for the project. + * + * @returns the nesting-qualified python type name (the name of the class, + * qualified with all nesting parent classes). + */ +export function toPythonFullName(fqn: string, rootAssm: Assembly): string { + const { packageName, pythonFqn } = toPythonFqn(fqn, rootAssm); + return pythonFqn.slice(packageName.length + 1); +} + /** * Computes the python relative import path from `fromModule` to `toModule`. * diff --git a/packages/jsii-pacmak/test/generated-code/__snapshots__/examples.test.js.snap b/packages/jsii-pacmak/test/generated-code/__snapshots__/examples.test.js.snap index 07b210535e..d4a153b75e 100644 --- a/packages/jsii-pacmak/test/generated-code/__snapshots__/examples.test.js.snap +++ b/packages/jsii-pacmak/test/generated-code/__snapshots__/examples.test.js.snap @@ -2570,7 +2570,7 @@ class Namespace1(metaclass=jsii.JSIIMeta, jsii_type="testpkg.Namespace1"): :param bar: - ''' if __debug__: - type_hints = typing.get_type_hints(Foo.__init__) + type_hints = typing.get_type_hints(Namespace1.Foo.__init__) check_type(argname="argument bar", value=bar, expected_type=type_hints["bar"]) self._values: typing.Dict[str, typing.Any] = { "bar": bar, diff --git a/packages/jsii-pacmak/test/generated-code/__snapshots__/target-python.test.js.snap b/packages/jsii-pacmak/test/generated-code/__snapshots__/target-python.test.js.snap index f210e1dcbf..fe9ffd165f 100644 --- a/packages/jsii-pacmak/test/generated-code/__snapshots__/target-python.test.js.snap +++ b/packages/jsii-pacmak/test/generated-code/__snapshots__/target-python.test.js.snap @@ -2021,7 +2021,7 @@ class NestingClass( :stability: deprecated ''' if __debug__: - type_hints = typing.get_type_hints(NestedStruct.__init__) + type_hints = typing.get_type_hints(NestingClass.NestedStruct.__init__) check_type(argname="argument name", value=name, expected_type=type_hints["name"]) self._values: typing.Dict[str, typing.Any] = { "name": name, @@ -7840,7 +7840,7 @@ class LevelOne(metaclass=jsii.JSIIMeta, jsii_type="jsii-calc.LevelOne"): :param value: ''' if __debug__: - type_hints = typing.get_type_hints(PropBooleanValue.__init__) + type_hints = typing.get_type_hints(LevelOne.PropBooleanValue.__init__) check_type(argname="argument value", value=value, expected_type=type_hints["value"]) self._values: typing.Dict[str, typing.Any] = { "value": value, @@ -7880,7 +7880,7 @@ class LevelOne(metaclass=jsii.JSIIMeta, jsii_type="jsii-calc.LevelOne"): if isinstance(prop, dict): prop = PropBooleanValue(**prop) if __debug__: - type_hints = typing.get_type_hints(PropProperty.__init__) + type_hints = typing.get_type_hints(LevelOne.PropProperty.__init__) check_type(argname="argument prop", value=prop, expected_type=type_hints["prop"]) self._values: typing.Dict[str, typing.Any] = { "prop": prop, diff --git a/packages/jsii-rosetta/jest.config.mjs b/packages/jsii-rosetta/jest.config.mjs index 11cd59aafc..3e14763c3e 100644 --- a/packages/jsii-rosetta/jest.config.mjs +++ b/packages/jsii-rosetta/jest.config.mjs @@ -1,6 +1,7 @@ import { createRequire } from 'node:module'; -import { overriddenConfig } from '../../jest.config.mjs'; +import { default as defaultConfig, overriddenConfig } from '../../jest.config.mjs'; export default overriddenConfig({ setupFiles: [createRequire(import.meta.url).resolve('./jestsetup.js')], + testTimeout: process.env.CI ? 30_000 : defaultConfig.testTimeout, });