Open automartin5000 opened 4 months ago
So based on my testing, what seems to be happening is JSII is generating classes in alphabetical order (is that correct?). But if Class A depends on Class B, then Class B is not yet defined and it throws an error. So I was able to work around this problem by renaming Class B to something like Class AppB so it went before the original Class A.
Does this seem like expected behavior? If so, I would expect that some dependency resolution is needed.
I just ran into this again today. I can't possibly be the only person experiencing this?
Hi, @automartin5000. I can't reproduce the bug based on the information you provided. Is there a complete example you can share?
JSII is generating classes in alphabetical order
jsii generates classes in topological order, where class A
is a predecessor of class B
if A
depends on B
in some way (being a subclass is one example). This is done precisely to avoid this issue. The root cause must be something else.
This issue has not received a response in a while. If you want to keep this issue open, please leave a comment below and auto-close will be canceled.
Thanks for the response @otaviomacedo. I'll try to recreate it this week or next
Sorry for the delay, here's a clean example:
constructs.ts
import { Construct } from "constructs";
import { AProps } from "./interfaces";
export class TestConstruct extends Construct {
constructor(scope: Construct, id: string, props: AProps) {
console.log(`Initialized prop: ${props.testProp}`);
super(scope, id);
}
}
interfaces.ts
export interface AProps extends BProps {}
export interface BProps {
readonly testProp: string;
}
Python app code:
from <construct_library>.test_construct import AProps
from aws_cdk import App
class TestConstruct:
def __init__(self, scope, id, props: AProps):
super().__init__(scope, id, props)
test = TestConstruct(App(), 'TestConstruct', AProps(test_prop='test'))
Error:
_init__.py", line 20, in <module>
jsii_struct_bases=[BProps],
^^^^^^
NameError: name 'BProps' is not defined
Full JSII generated code (anonymized):
import abc
import builtins
import datetime
import enum
import typing
import jsii
import publication
import typing_extensions
from typeguard import check_type
from .._jsii import *
import constructs as _constructs_77d1e7e8
@jsii.data_type(
jsii_type="<construct_library>.testConstruct.AProps",
jsii_struct_bases=[BProps],
name_mapping={"test_prop": "testProp"},
)
class AProps(BProps):
def __init__(self, *, test_prop: builtins.str) -> None:
'''
:param test_prop:
'''
if __debug__:
type_hints = typing.get_type_hints(_typecheckingstub__6c50750a3eed61a5e3bf6110860acdf626b4cbe1cbe7b2cee25f235971070407)
check_type(argname="argument test_prop", value=test_prop, expected_type=type_hints["test_prop"])
self._values: typing.Dict[builtins.str, typing.Any] = {
"test_prop": test_prop,
}
@builtins.property
def test_prop(self) -> builtins.str:
result = self._values.get("test_prop")
assert result is not None, "Required property 'test_prop' is missing"
return typing.cast(builtins.str, result)
def __eq__(self, rhs: typing.Any) -> builtins.bool:
return isinstance(rhs, self.__class__) and rhs._values == self._values
def __ne__(self, rhs: typing.Any) -> builtins.bool:
return not (rhs == self)
def __repr__(self) -> str:
return "AProps(%s)" % ", ".join(
k + "=" + repr(v) for k, v in self._values.items()
)
@jsii.data_type(
jsii_type="<construct_library>.testConstruct.BProps",
jsii_struct_bases=[],
name_mapping={"test_prop": "testProp"},
)
class BProps:
def __init__(self, *, test_prop: builtins.str) -> None:
'''
:param test_prop:
'''
if __debug__:
type_hints = typing.get_type_hints(_typecheckingstub__a410f745cf17b952ca7c740989abf5daa91608073357db663df598089ac271c0)
check_type(argname="argument test_prop", value=test_prop, expected_type=type_hints["test_prop"])
self._values: typing.Dict[builtins.str, typing.Any] = {
"test_prop": test_prop,
}
@builtins.property
def test_prop(self) -> builtins.str:
result = self._values.get("test_prop")
assert result is not None, "Required property 'test_prop' is missing"
return typing.cast(builtins.str, result)
def __eq__(self, rhs: typing.Any) -> builtins.bool:
return isinstance(rhs, self.__class__) and rhs._values == self._values
def __ne__(self, rhs: typing.Any) -> builtins.bool:
return not (rhs == self)
def __repr__(self) -> str:
return "BProps(%s)" % ", ".join(
k + "=" + repr(v) for k, v in self._values.items()
)
class TestConstruct(
_constructs_77d1e7e8.Construct,
metaclass=jsii.JSIIMeta,
jsii_type="<construct_library>.testConstruct.TestConstruct",
):
def __init__(
self,
scope: _constructs_77d1e7e8.Construct,
id: builtins.str,
*,
test_prop: builtins.str,
) -> None:
'''
:param scope: -
:param id: -
:param test_prop:
'''
if __debug__:
type_hints = typing.get_type_hints(_typecheckingstub__6b4fdcd0c8c3aad835d0884ffd49ae763492010eba1e1acc2290e07ff8485088)
check_type(argname="argument scope", value=scope, expected_type=type_hints["scope"])
check_type(argname="argument id", value=id, expected_type=type_hints["id"])
props = AProps(test_prop=test_prop)
jsii.create(self.__class__, self, [scope, id, props])
__all__ = [
"AProps",
"BProps",
"TestConstruct",
]
publication.publish()
def _typecheckingstub__6c50750a3eed61a5e3bf6110860acdf626b4cbe1cbe7b2cee25f235971070407(
*,
test_prop: builtins.str,
) -> None:
"""Type checking stubs"""
pass
def _typecheckingstub__a410f745cf17b952ca7c740989abf5daa91608073357db663df598089ac271c0(
*,
test_prop: builtins.str,
) -> None:
"""Type checking stubs"""
pass
def _typecheckingstub__6b4fdcd0c8c3aad835d0884ffd49ae763492010eba1e1acc2290e07ff8485088(
scope: _constructs_77d1e7e8.Construct,
id: builtins.str,
*,
test_prop: builtins.str,
) -> None:
"""Type checking stubs"""
pass
Describe the bug
I have a CDK construct library which has been working fine. But suddenly, I think possibly with the addition of named exports, I'm getting an error when attempting to import a construct when it attempts to reference one of the interfaces in a Python CDK app. Looking at the generated Python code, I noticed that the reference occurs before the class is defined.
Here's an example of how the interfaces are defined. Note: These are not really empty.
And here's the generated code:
Stack trace:
Expected Behavior
CDK Synth correctly
Current Behavior
Synth error (See above)
Reproduction Steps
See above code
Possible Solution
No response
Additional Information/Context
No response
SDK version used
5.3.12
Environment details (OS name and version, etc.)
Mac OS Sonoma