diff --git a/packages/@jsii/python-runtime/src/jsii/_reference_map.py b/packages/@jsii/python-runtime/src/jsii/_reference_map.py index ef9cce1795..72445c1a23 100644 --- a/packages/@jsii/python-runtime/src/jsii/_reference_map.py +++ b/packages/@jsii/python-runtime/src/jsii/_reference_map.py @@ -1,4 +1,5 @@ # This module exists to break an import cycle between jsii.runtime and jsii.kernel +import importlib import inspect from typing import Any, Iterable, List, Mapping, MutableMapping, Type @@ -10,6 +11,11 @@ _enums: MutableMapping[str, Any] = {} _interfaces: MutableMapping[str, Any] = {} +# Mapping from jsii assembly name to Python root module name, populated by +# JSIIAssembly.load() so that on-demand type resolution can trigger imports +# of lazily-loaded submodules when the kernel returns an unknown type. +_assembly_to_module: MutableMapping[str, str] = {} + def register_type(klass: Type): _types[klass.__jsii_type__] = klass @@ -27,6 +33,48 @@ def register_interface(iface: Any): _interfaces[iface.__jsii_type__] = iface +def _try_import_type_module(class_fqn: str) -> bool: + """Attempt to import the Python module containing a jsii type by FQN. + + With PEP 562 lazy loading, submodules are not imported until first attribute + access. If the jsii runtime needs to deserialize a type from a submodule + that hasn't been imported yet (e.g., a callback returns an object whose type + lives in an unloaded submodule), this function triggers the import so that + the type self-registers with the runtime. + + The FQN format is: ``assembly_name.submodule.path.TypeName`` + We strip the type name (last dot-separated component) and try to import + progressively shorter module paths until one succeeds. + + Returns True if an import was successfully triggered, False otherwise. + """ + # Split FQN into components: e.g. "jsii-calc.cdk16625.donotimport.MyType" + parts = class_fqn.split(".") + if len(parts) < 2: + return False + + # The first component is the assembly name + assembly_name = parts[0] + root_module = _assembly_to_module.get(assembly_name) + if root_module is None: + return False + + # Try importing submodule paths from most specific to least specific + # e.g. for "jsii-calc.cdk16625.donotimport.MyType": + # try: jsii_calc.cdk16625.donotimport + # try: jsii_calc.cdk16625 + submodule_parts = parts[1:] # Remove assembly name + for depth in range(len(submodule_parts), 0, -1): + module_path = f"{root_module}.{'.'.join(submodule_parts[:depth])}" + try: + importlib.import_module(module_path) + return True + except (ImportError, ModuleNotFoundError): + continue + + return False + + class _FakeReference: def __init__(self, ref: str) -> None: self.__jsii_ref__ = ref @@ -128,6 +176,17 @@ def resolve(self, kernel, ref): else: return InterfaceDynamicProxy(self.build_interface_proxies_for_ref(ref)) else: + # The type isn't registered yet. With lazy loading, it may live in + # a submodule that hasn't been imported. Try importing the module + # that should contain this type — importing triggers type + # registration as a side effect — then retry the lookup. + if _try_import_type_module(class_fqn): + if class_fqn in _types: + return self.resolve(kernel, ref) + elif class_fqn in _data_types: + return self.resolve(kernel, ref) + elif class_fqn in _enums: + return self.resolve(kernel, ref) raise ValueError(f"Unknown type: {class_fqn}") def resolve_id(self, id: str) -> Any: diff --git a/packages/@jsii/python-runtime/src/jsii/_runtime.py b/packages/@jsii/python-runtime/src/jsii/_runtime.py index 7d7cb1b469..1abf781a23 100644 --- a/packages/@jsii/python-runtime/src/jsii/_runtime.py +++ b/packages/@jsii/python-runtime/src/jsii/_runtime.py @@ -54,6 +54,10 @@ def load(cls, *args, _kernel=kernel, **kwargs) -> "JSIIAssembly": ) as assembly_path: _kernel.load(assembly.name, assembly.version, os.fspath(assembly_path)) + # Register the assembly-to-module mapping so the runtime can resolve + # types from lazily-loaded submodules by importing them on demand. + _reference_map._assembly_to_module[assembly.name] = assembly.module + # Give our record of the assembly back to the caller. return assembly diff --git a/packages/jsii-pacmak/lib/targets/python.ts b/packages/jsii-pacmak/lib/targets/python.ts index b2c1530ab3..a0a4a00195 100644 --- a/packages/jsii-pacmak/lib/targets/python.ts +++ b/packages/jsii-pacmak/lib/targets/python.ts @@ -1797,6 +1797,9 @@ class PythonModule implements PythonType { code.line('import builtins'); code.line('import datetime'); code.line('import enum'); + if (this.modules.length > 0) { + code.line('import importlib as _importlib'); + } code.line('import typing'); code.line(); code.line('import jsii'); @@ -1920,29 +1923,88 @@ class PythonModule implements PythonType { code.line('__all__: typing.List[typing.Any] = []'); } + // Emit TYPE_CHECKING block with explicit submodule imports so that + // static type checkers (pyright, mypy) can see the submodule names + // that are listed in __all__. At runtime TYPE_CHECKING is False, + // so these imports don't execute and lazy loading is preserved. + if (this.modules.length > 0) { + const submoduleNames = this.modules + .sort((l, r) => l.pythonName.localeCompare(r.pythonName)) + .map((module) => + module.pythonName.substring(this.pythonName.length + 1), + ); + + code.line(); + code.line( + '# Type-checking-only imports for static analyzers (pyright/mypy).', + ); + code.line( + '# At runtime TYPE_CHECKING is False, preserving lazy loading.', + ); + code.openBlock('if typing.TYPE_CHECKING'); + for (const name of submoduleNames) { + code.line(`from . import ${name} as ${name}`); + } + code.closeBlock(); + } + // Next up, we'll use publication to ensure that all of the non-public names // get hidden from dir(), tab-complete, etc. code.line(); code.line('publication.publish()'); - // Finally, we'll load all registered python modules + // Finally, we'll set up lazy loading for all registered python modules. + // We define __getattr__ and __dir__ and then install them on the public + // module (the one publication.publish() placed in sys.modules) so that + // lazy attribute access works through the publication barrier. if (this.modules.length > 0) { code.line(); - code.line( - '# Loading modules to ensure their types are registered with the jsii runtime library', - ); - for (const module of this.modules.sort((l, r) => - l.pythonName.localeCompare(r.pythonName), - )) { - // Rather than generating an absolute import like - // "import jsii_calc.submodule" this builds a relative import like - // "from . import submodule". This enables distributing python packages - // and using the generated modules in the same codebase. - const submodule = module.pythonName.substring( - this.pythonName.length + 1, + // Build sorted list of submodule short names + const submoduleNames = this.modules + .sort((l, r) => l.pythonName.localeCompare(r.pythonName)) + .map((module) => + module.pythonName.substring(this.pythonName.length + 1), ); - code.line(`from . import ${submodule}`); + + // Emit _SUBMODULES set + code.indent('_SUBMODULES = {'); + for (const name of submoduleNames) { + code.line(`"${name}",`); } + code.unindent('}'); + code.line(); + + // Emit __getattr__ function + code.openBlock('def __getattr__(name: str) -> object'); + code.openBlock('if name in _SUBMODULES'); + code.line('mod = _importlib.import_module(f".{name}", __name__)'); + code.line('globals()[name] = mod'); + code.line('return mod'); + code.closeBlock(); + code.line( + 'raise AttributeError(f"module {__name__!r} has no attribute {name!r}")', + ); + code.closeBlock(); + code.line(); + + // Emit __dir__ function — quoted return type because pyright flags + // bare `list[str]` as a runtime subscript error when pythonVersion < 3.9 + code.openBlock('def __dir__() -> "list[str]"'); + code.line('return [*__all__, *_SUBMODULES]'); + code.closeBlock(); + code.line(); + + // Install __getattr__ and __dir__ on the public module that + // publication.publish() placed in sys.modules. publication replaces + // the module object but doesn't copy __getattr__/__dir__, so without + // this, attribute access like `pkg.submodule` would raise AttributeError. + // + // We use setattr() instead of direct assignment because mypy treats + // __getattr__ and __dir__ as special methods on ModuleType and rejects + // direct assignment with "Cannot assign to a method [method-assign]". + code.line('import sys as _sys'); + code.line('setattr(_sys.modules[__name__], "__getattr__", __getattr__)'); + code.line('setattr(_sys.modules[__name__], "__dir__", __dir__)'); } context.typeCheckingHelper.flushStubs(code); 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 0e4d644604..6ae6edba03 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 @@ -1499,6 +1499,7 @@ import abc import builtins import datetime import enum +import importlib as _importlib import typing import jsii @@ -2118,11 +2119,32 @@ __all__ = [ "deprecation_removal", ] +# Type-checking-only imports for static analyzers (pyright/mypy). +# At runtime TYPE_CHECKING is False, preserving lazy loading. +if typing.TYPE_CHECKING: + from . import custom_submodule_name as custom_submodule_name + from . import deprecation_removal as deprecation_removal + publication.publish() -# Loading modules to ensure their types are registered with the jsii runtime library -from . import custom_submodule_name -from . import deprecation_removal +_SUBMODULES = { + "custom_submodule_name", + "deprecation_removal", +} + +def __getattr__(name: str) -> object: + if name in _SUBMODULES: + mod = _importlib.import_module(f".{name}", __name__) + globals()[name] = mod + return mod + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + +def __dir__() -> "list[str]": + return [*__all__, *_SUBMODULES] + +import sys as _sys +setattr(_sys.modules[__name__], "__getattr__", __getattr__) +setattr(_sys.modules[__name__], "__dir__", __dir__) for cls in [IDoublable, IFriendly, IThreeLevelsInterface]: typing.cast(typing.Any, cls).__protocol_attrs__ = typing.cast(typing.Any, cls).__protocol_attrs__ - set(['__jsii_proxy_class__', '__jsii_type__']) @@ -2602,7 +2624,7 @@ exports[`Generated code for "@scope/jsii-calc-lib": / 1 exports[`Generated code for "@scope/jsii-calc-lib": /python/src/scope/jsii_calc_lib/__init__.py.diff 1`] = ` --- python/src/scope/jsii_calc_lib/__init__.py --no-runtime-type-checking +++ python/src/scope/jsii_calc_lib/__init__.py --runtime-type-checking -@@ -55,19 +55,25 @@ +@@ -56,19 +56,25 @@ ''' :param very: - @@ -2628,7 +2650,7 @@ exports[`Generated code for "@scope/jsii-calc-lib": /py @jsii.data_type( jsii_type="@scope/jsii-calc-lib.DiamondLeft", -@@ -85,10 +91,14 @@ +@@ -86,10 +92,14 @@ :param hoisted_top: :param left: @@ -2643,7 +2665,7 @@ exports[`Generated code for "@scope/jsii-calc-lib": /py self._values["hoisted_top"] = hoisted_top if left is not None: self._values["left"] = left -@@ -137,10 +147,14 @@ +@@ -138,10 +148,14 @@ :param hoisted_top: :param right: @@ -2658,7 +2680,7 @@ exports[`Generated code for "@scope/jsii-calc-lib": /py self._values["hoisted_top"] = hoisted_top if right is not None: self._values["right"] = right -@@ -212,10 +226,13 @@ +@@ -213,10 +227,13 @@ ''' :param _: - @@ -2672,7 +2694,7 @@ exports[`Generated code for "@scope/jsii-calc-lib": /py @jsii.interface(jsii_type="@scope/jsii-calc-lib.IDoublable") class IDoublable(typing_extensions.Protocol): -@@ -363,10 +380,15 @@ +@@ -364,10 +381,15 @@ :param astring: (deprecated) A string value. :param first_optional: @@ -2688,7 +2710,7 @@ exports[`Generated code for "@scope/jsii-calc-lib": /py "astring": astring, } if first_optional is not None: -@@ -523,10 +545,15 @@ +@@ -524,10 +546,15 @@ :param optional2: :param optional3: @@ -2704,7 +2726,7 @@ exports[`Generated code for "@scope/jsii-calc-lib": /py self._values["optional1"] = optional1 if optional2 is not None: self._values["optional2"] = optional2 -@@ -586,10 +613,13 @@ +@@ -587,10 +614,13 @@ :param value: The number. @@ -2718,11 +2740,11 @@ exports[`Generated code for "@scope/jsii-calc-lib": /py @builtins.property @jsii.member(jsii_name="doubleValue") def double_value(self) -> jsii.Number: -@@ -631,7 +661,65 @@ +@@ -653,7 +683,65 @@ - # Loading modules to ensure their types are registered with the jsii runtime library - from . import custom_submodule_name - from . import deprecation_removal + import sys as _sys + setattr(_sys.modules[__name__], "__getattr__", __getattr__) + setattr(_sys.modules[__name__], "__dir__", __dir__) +def _typecheckingstub__46512218b53da06690990919b41a88d6c2379b11ef0a3243cf6d60add5197ae2( + very: _scope_jsii_calc_base_of_base_49fa37fe.Very, @@ -3398,6 +3420,7 @@ import abc import builtins import datetime import enum +import importlib as _importlib import typing import jsii @@ -12210,34 +12233,78 @@ __all__ = [ "union", ] +# Type-checking-only imports for static analyzers (pyright/mypy). +# At runtime TYPE_CHECKING is False, preserving lazy loading. +if typing.TYPE_CHECKING: + from . import anonymous as anonymous + from . import cdk16625 as cdk16625 + from . import cdk22369 as cdk22369 + from . import composition as composition + from . import covariant_overrides as covariant_overrides + from . import derived_class_has_no_properties as derived_class_has_no_properties + from . import homonymous_forward_references as homonymous_forward_references + from . import interface_in_namespace_includes_classes as interface_in_namespace_includes_classes + from . import interface_in_namespace_only_interface as interface_in_namespace_only_interface + from . import intersection as intersection + from . import jsii3656 as jsii3656 + from . import jsii4894 as jsii4894 + from . import module2530 as module2530 + from . import module2617 as module2617 + from . import module2647 as module2647 + from . import module2689 as module2689 + from . import module2692 as module2692 + from . import module2700 as module2700 + from . import module2702 as module2702 + from . import nodirect as nodirect + from . import onlystatic as onlystatic + from . import pascal_case_name as pascal_case_name + from . import python_self as python_self + from . import submodule as submodule + from . import union as union + publication.publish() -# Loading modules to ensure their types are registered with the jsii runtime library -from . import anonymous -from . import cdk16625 -from . import cdk22369 -from . import composition -from . import covariant_overrides -from . import derived_class_has_no_properties -from . import homonymous_forward_references -from . import interface_in_namespace_includes_classes -from . import interface_in_namespace_only_interface -from . import intersection -from . import jsii3656 -from . import jsii4894 -from . import module2530 -from . import module2617 -from . import module2647 -from . import module2689 -from . import module2692 -from . import module2700 -from . import module2702 -from . import nodirect -from . import onlystatic -from . import pascal_case_name -from . import python_self -from . import submodule -from . import union +_SUBMODULES = { + "anonymous", + "cdk16625", + "cdk22369", + "composition", + "covariant_overrides", + "derived_class_has_no_properties", + "homonymous_forward_references", + "interface_in_namespace_includes_classes", + "interface_in_namespace_only_interface", + "intersection", + "jsii3656", + "jsii4894", + "module2530", + "module2617", + "module2647", + "module2689", + "module2692", + "module2700", + "module2702", + "nodirect", + "onlystatic", + "pascal_case_name", + "python_self", + "submodule", + "union", +} + +def __getattr__(name: str) -> object: + if name in _SUBMODULES: + mod = _importlib.import_module(f".{name}", __name__) + globals()[name] = mod + return mod + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + +def __dir__() -> "list[str]": + return [*__all__, *_SUBMODULES] + +import sys as _sys +setattr(_sys.modules[__name__], "__getattr__", __getattr__) +setattr(_sys.modules[__name__], "__dir__", __dir__) for cls in [IAnonymousImplementationProvider, IAnonymouslyImplementMe, IAnotherPublicInterface, IBell, IBellRinger, IConcreteBellRinger, IDeprecatedInterface, IExperimentalInterface, IExtendsPrivateInterface, IExternalInterface, IFriendlier, IFriendlyRandomGenerator, IIndirectlyImplemented, IInterfaceImplementedByAbstractClass, IInterfaceThatShouldNotBeADataType, IInterfaceWithInternal, IInterfaceWithMethods, IInterfaceWithOptionalMethodArguments, IInterfaceWithProperties, IInterfaceWithPropertiesExtension, IJSII417Derived, IJSII417PublicBaseOfBase, IJavaReservedWordsInAnInterface, IJsii487External, IJsii487External2, IJsii496, IMutableObjectLiteral, INonInternalInterface, IObjectWithProperty, IOptionalMethod, IPrivatelyImplemented, IPublicInterface, IPublicInterface2, IRandomNumberGenerator, IReturnJsii976, IReturnsNumber, IStableInterface, IStringable, IStructReturningDelegate, IWallClock]: typing.cast(typing.Any, cls).__protocol_attrs__ = typing.cast(typing.Any, cls).__protocol_attrs__ - set(['__jsii_proxy_class__', '__jsii_type__']) @@ -12428,6 +12495,7 @@ import abc import builtins import datetime import enum +import importlib as _importlib import typing import jsii @@ -12496,10 +12564,30 @@ __all__ = [ "donotimport", ] +# Type-checking-only imports for static analyzers (pyright/mypy). +# At runtime TYPE_CHECKING is False, preserving lazy loading. +if typing.TYPE_CHECKING: + from . import donotimport as donotimport + publication.publish() -# Loading modules to ensure their types are registered with the jsii runtime library -from . import donotimport +_SUBMODULES = { + "donotimport", +} + +def __getattr__(name: str) -> object: + if name in _SUBMODULES: + mod = _importlib.import_module(f".{name}", __name__) + globals()[name] = mod + return mod + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + +def __dir__() -> "list[str]": + return [*__all__, *_SUBMODULES] + +import sys as _sys +setattr(_sys.modules[__name__], "__getattr__", __getattr__) +setattr(_sys.modules[__name__], "__dir__", __dir__) `; @@ -12804,6 +12892,7 @@ import abc import builtins import datetime import enum +import importlib as _importlib import typing import jsii @@ -12833,10 +12922,30 @@ __all__ = [ "class_overrides", ] +# Type-checking-only imports for static analyzers (pyright/mypy). +# At runtime TYPE_CHECKING is False, preserving lazy loading. +if typing.TYPE_CHECKING: + from . import class_overrides as class_overrides + publication.publish() -# Loading modules to ensure their types are registered with the jsii runtime library -from . import class_overrides +_SUBMODULES = { + "class_overrides", +} + +def __getattr__(name: str) -> object: + if name in _SUBMODULES: + mod = _importlib.import_module(f".{name}", __name__) + globals()[name] = mod + return mod + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + +def __dir__() -> "list[str]": + return [*__all__, *_SUBMODULES] + +import sys as _sys +setattr(_sys.modules[__name__], "__getattr__", __getattr__) +setattr(_sys.modules[__name__], "__dir__", __dir__) `; @@ -13149,6 +13258,7 @@ import abc import builtins import datetime import enum +import importlib as _importlib import typing import jsii @@ -13179,11 +13289,32 @@ __all__ = [ "foo", ] +# Type-checking-only imports for static analyzers (pyright/mypy). +# At runtime TYPE_CHECKING is False, preserving lazy loading. +if typing.TYPE_CHECKING: + from . import bar as bar + from . import foo as foo + publication.publish() -# Loading modules to ensure their types are registered with the jsii runtime library -from . import bar -from . import foo +_SUBMODULES = { + "bar", + "foo", +} + +def __getattr__(name: str) -> object: + if name in _SUBMODULES: + mod = _importlib.import_module(f".{name}", __name__) + globals()[name] = mod + return mod + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + +def __dir__() -> "list[str]": + return [*__all__, *_SUBMODULES] + +import sys as _sys +setattr(_sys.modules[__name__], "__getattr__", __getattr__) +setattr(_sys.modules[__name__], "__dir__", __dir__) `; @@ -14227,6 +14358,7 @@ import abc import builtins import datetime import enum +import importlib as _importlib import typing import jsii @@ -14259,13 +14391,36 @@ __all__ = [ "structs", ] +# Type-checking-only imports for static analyzers (pyright/mypy). +# At runtime TYPE_CHECKING is False, preserving lazy loading. +if typing.TYPE_CHECKING: + from . import methods as methods + from . import props as props + from . import retval as retval + from . import structs as structs + publication.publish() -# Loading modules to ensure their types are registered with the jsii runtime library -from . import methods -from . import props -from . import retval -from . import structs +_SUBMODULES = { + "methods", + "props", + "retval", + "structs", +} + +def __getattr__(name: str) -> object: + if name in _SUBMODULES: + mod = _importlib.import_module(f".{name}", __name__) + globals()[name] = mod + return mod + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + +def __dir__() -> "list[str]": + return [*__all__, *_SUBMODULES] + +import sys as _sys +setattr(_sys.modules[__name__], "__getattr__", __getattr__) +setattr(_sys.modules[__name__], "__dir__", __dir__) `; @@ -14564,6 +14719,7 @@ import abc import builtins import datetime import enum +import importlib as _importlib import typing import jsii @@ -14594,11 +14750,32 @@ __all__ = [ "submodule2", ] +# Type-checking-only imports for static analyzers (pyright/mypy). +# At runtime TYPE_CHECKING is False, preserving lazy loading. +if typing.TYPE_CHECKING: + from . import submodule1 as submodule1 + from . import submodule2 as submodule2 + publication.publish() -# Loading modules to ensure their types are registered with the jsii runtime library -from . import submodule1 -from . import submodule2 +_SUBMODULES = { + "submodule1", + "submodule2", +} + +def __getattr__(name: str) -> object: + if name in _SUBMODULES: + mod = _importlib.import_module(f".{name}", __name__) + globals()[name] = mod + return mod + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + +def __dir__() -> "list[str]": + return [*__all__, *_SUBMODULES] + +import sys as _sys +setattr(_sys.modules[__name__], "__getattr__", __getattr__) +setattr(_sys.modules[__name__], "__dir__", __dir__) `; @@ -15169,6 +15346,7 @@ import abc import builtins import datetime import enum +import importlib as _importlib import typing import jsii @@ -15199,11 +15377,32 @@ __all__ = [ "sub2", ] +# Type-checking-only imports for static analyzers (pyright/mypy). +# At runtime TYPE_CHECKING is False, preserving lazy loading. +if typing.TYPE_CHECKING: + from . import sub1 as sub1 + from . import sub2 as sub2 + publication.publish() -# Loading modules to ensure their types are registered with the jsii runtime library -from . import sub1 -from . import sub2 +_SUBMODULES = { + "sub1", + "sub2", +} + +def __getattr__(name: str) -> object: + if name in _SUBMODULES: + mod = _importlib.import_module(f".{name}", __name__) + globals()[name] = mod + return mod + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + +def __dir__() -> "list[str]": + return [*__all__, *_SUBMODULES] + +import sys as _sys +setattr(_sys.modules[__name__], "__getattr__", __getattr__) +setattr(_sys.modules[__name__], "__dir__", __dir__) `; @@ -15611,6 +15810,7 @@ import abc import builtins import datetime import enum +import importlib as _importlib import typing import jsii @@ -15742,15 +15942,40 @@ __all__ = [ "returnsparam", ] +# Type-checking-only imports for static analyzers (pyright/mypy). +# At runtime TYPE_CHECKING is False, preserving lazy loading. +if typing.TYPE_CHECKING: + from . import back_references as back_references + from . import child as child + from . import isolated as isolated + from . import nested_submodule as nested_submodule + from . import param as param + from . import returnsparam as returnsparam + publication.publish() -# Loading modules to ensure their types are registered with the jsii runtime library -from . import back_references -from . import child -from . import isolated -from . import nested_submodule -from . import param -from . import returnsparam +_SUBMODULES = { + "back_references", + "child", + "isolated", + "nested_submodule", + "param", + "returnsparam", +} + +def __getattr__(name: str) -> object: + if name in _SUBMODULES: + mod = _importlib.import_module(f".{name}", __name__) + globals()[name] = mod + return mod + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + +def __dir__() -> "list[str]": + return [*__all__, *_SUBMODULES] + +import sys as _sys +setattr(_sys.modules[__name__], "__getattr__", __getattr__) +setattr(_sys.modules[__name__], "__dir__", __dir__) `; @@ -16119,6 +16344,7 @@ import abc import builtins import datetime import enum +import importlib as _importlib import typing import jsii @@ -16180,10 +16406,30 @@ __all__ = [ "deeply_nested", ] +# Type-checking-only imports for static analyzers (pyright/mypy). +# At runtime TYPE_CHECKING is False, preserving lazy loading. +if typing.TYPE_CHECKING: + from . import deeply_nested as deeply_nested + publication.publish() -# Loading modules to ensure their types are registered with the jsii runtime library -from . import deeply_nested +_SUBMODULES = { + "deeply_nested", +} + +def __getattr__(name: str) -> object: + if name in _SUBMODULES: + mod = _importlib.import_module(f".{name}", __name__) + globals()[name] = mod + return mod + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + +def __dir__() -> "list[str]": + return [*__all__, *_SUBMODULES] + +import sys as _sys +setattr(_sys.modules[__name__], "__getattr__", __getattr__) +setattr(_sys.modules[__name__], "__dir__", __dir__) `; @@ -16538,7 +16784,7 @@ exports[`Generated code for "jsii-calc": / 1`] = ` exports[`Generated code for "jsii-calc": /python/src/jsii_calc/__init__.py.diff 1`] = ` --- python/src/jsii_calc/__init__.py --no-runtime-type-checking +++ python/src/jsii_calc/__init__.py --runtime-type-checking -@@ -129,10 +129,13 @@ +@@ -130,10 +130,13 @@ def work_it_all(self, seed: builtins.str) -> builtins.str: '''Sets \`\`seed\`\` to \`\`this.property\`\`, then calls \`\`someMethod\`\` with \`\`this.property\`\` and returns the result. @@ -16552,7 +16798,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @builtins.property @jsii.member(jsii_name="property") @abc.abstractmethod -@@ -149,19 +152,25 @@ +@@ -150,19 +153,25 @@ @jsii.member(jsii_name="someMethod") def _some_method(self, str: builtins.str) -> builtins.str: ''' @@ -16578,7 +16824,7 @@ exports[`Generated code for "jsii-calc": /python/src/js # Adding a "__jsii_proxy_class__(): typing.Type" function to the abstract class typing.cast(typing.Any, AbstractSuite).__jsii_proxy_class__ = lambda : _AbstractSuiteProxy -@@ -179,10 +188,13 @@ +@@ -180,10 +189,13 @@ @jsii.member(jsii_name="anyIn") def any_in(self, inp: typing.Any) -> None: ''' @@ -16592,7 +16838,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="anyOut") def any_out(self) -> typing.Any: return typing.cast(typing.Any, jsii.invoke(self, "anyOut", [])) -@@ -190,10 +202,13 @@ +@@ -191,10 +203,13 @@ @jsii.member(jsii_name="enumMethod") def enum_method(self, value: "StringEnum") -> "StringEnum": ''' @@ -16606,7 +16852,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @builtins.property @jsii.member(jsii_name="enumPropertyValue") def enum_property_value(self) -> jsii.Number: -@@ -204,73 +219,97 @@ +@@ -205,73 +220,97 @@ def any_array_property(self) -> typing.List[typing.Any]: return typing.cast(typing.List[typing.Any], jsii.get(self, "anyArrayProperty")) @@ -16704,7 +16950,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @builtins.property @jsii.member(jsii_name="mapProperty") def map_property( -@@ -281,28 +320,37 @@ +@@ -282,28 +321,37 @@ @map_property.setter def map_property( self, @@ -16742,7 +16988,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @builtins.property @jsii.member(jsii_name="unionArrayProperty") def union_array_property( -@@ -313,10 +361,13 @@ +@@ -314,10 +362,13 @@ @union_array_property.setter def union_array_property( self, @@ -16756,7 +17002,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @builtins.property @jsii.member(jsii_name="unionMapProperty") def union_map_property( -@@ -327,10 +378,13 @@ +@@ -328,10 +379,13 @@ @union_map_property.setter def union_map_property( self, @@ -16770,7 +17016,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @builtins.property @jsii.member(jsii_name="unionProperty") def union_property( -@@ -341,19 +395,25 @@ +@@ -342,19 +396,25 @@ @union_property.setter def union_property( self, @@ -16796,7 +17042,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @builtins.property @jsii.member(jsii_name="unknownMapProperty") def unknown_map_property(self) -> typing.Mapping[builtins.str, typing.Any]: -@@ -362,28 +422,37 @@ +@@ -363,28 +423,37 @@ @unknown_map_property.setter def unknown_map_property( self, @@ -16834,7 +17080,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.enum(jsii_type="jsii-calc.AllTypesEnum") class AllTypesEnum(enum.Enum): -@@ -403,36 +472,52 @@ +@@ -404,36 +473,52 @@ def get_bar(self, _p1: builtins.str, _p2: jsii.Number) -> None: ''' :param _p1: - @@ -16887,7 +17133,7 @@ exports[`Generated code for "jsii-calc": /python/src/js class AmbiguousParameters( metaclass=jsii.JSIIMeta, -@@ -448,10 +533,13 @@ +@@ -449,10 +534,13 @@ ''' :param scope_: - :param scope: @@ -16901,7 +17147,7 @@ exports[`Generated code for "jsii-calc": /python/src/js jsii.create(self.__class__, self, [scope_, props_]) @builtins.property -@@ -483,10 +571,16 @@ +@@ -484,10 +572,16 @@ :param obj: the receiver object. :param prop_a: the first property to read. :param prop_b: the second property to read. @@ -16918,7 +17164,7 @@ exports[`Generated code for "jsii-calc": /python/src/js class AsyncVirtualMethods( metaclass=jsii.JSIIMeta, -@@ -521,10 +615,13 @@ +@@ -522,10 +616,13 @@ @jsii.member(jsii_name="overrideMe") def override_me(self, mult: jsii.Number) -> jsii.Number: ''' @@ -16932,7 +17178,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="overrideMeToo") def override_me_too(self) -> jsii.Number: return typing.cast(jsii.Number, jsii.ainvoke(self, "overrideMeToo", [])) -@@ -585,10 +682,14 @@ +@@ -586,10 +683,14 @@ '''Creates a BinaryOperation. :param lhs: Left-hand side operand. @@ -16947,7 +17193,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="hello") def hello(self) -> builtins.str: '''Say hello!''' -@@ -649,10 +750,13 @@ +@@ -650,10 +751,13 @@ :param value: the value that should be returned. @@ -16961,7 +17207,7 @@ exports[`Generated code for "jsii-calc": /python/src/js # Adding a "__jsii_proxy_class__(): typing.Type" function to the abstract class typing.cast(typing.Any, BurriedAnonymousObject).__jsii_proxy_class__ = lambda : _BurriedAnonymousObjectProxy -@@ -700,18 +804,24 @@ +@@ -701,18 +805,24 @@ def add(self, value: jsii.Number) -> None: '''Adds a number to the current value. @@ -16986,7 +17232,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="neg") def neg(self) -> None: '''Negates the current value.''' -@@ -721,10 +831,13 @@ +@@ -722,10 +832,13 @@ def pow(self, value: jsii.Number) -> None: '''Raises the current value by a power. @@ -17000,7 +17246,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="readUnionValue") def read_union_value(self) -> jsii.Number: '''Returns teh value of the union property (if defined).''' -@@ -758,20 +871,26 @@ +@@ -759,20 +872,26 @@ '''The current value.''' return typing.cast("_scope_jsii_calc_lib_c61f082f.NumericValue", jsii.get(self, "curr")) @@ -17027,7 +17273,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @builtins.property @jsii.member(jsii_name="unionProperty") def union_property( -@@ -783,10 +902,13 @@ +@@ -784,10 +903,13 @@ @union_property.setter def union_property( self, @@ -17041,7 +17287,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.data_type( jsii_type="jsii-calc.CalculatorProps", -@@ -803,10 +925,14 @@ +@@ -804,10 +926,14 @@ '''Properties for Calculator. :param initial_value: The initial value of the calculator. NOTE: Any number works here, it's fine. Default: 0 @@ -17056,7 +17302,7 @@ exports[`Generated code for "jsii-calc": /python/src/js self._values["initial_value"] = initial_value if maximum_value is not None: self._values["maximum_value"] = maximum_value -@@ -852,10 +978,13 @@ +@@ -853,10 +979,13 @@ union_property: typing.Sequence[typing.Mapping[builtins.str, typing.Union[typing.Union["StructA", typing.Dict[builtins.str, typing.Any]], typing.Union["StructB", typing.Dict[builtins.str, typing.Any]]]]], ) -> None: ''' @@ -17070,7 +17316,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="staticMethodWithMapOfUnionsParam") @builtins.classmethod def static_method_with_map_of_unions_param( -@@ -863,20 +992,26 @@ +@@ -864,20 +993,26 @@ param: typing.Mapping[builtins.str, typing.Union[typing.Union["StructA", typing.Dict[builtins.str, typing.Any]], typing.Union["StructB", typing.Dict[builtins.str, typing.Any]]]], ) -> None: ''' @@ -17097,7 +17343,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @builtins.property @jsii.member(jsii_name="unionProperty") def union_property( -@@ -887,10 +1022,13 @@ +@@ -888,10 +1023,13 @@ @union_property.setter def union_property( self, @@ -17111,7 +17357,7 @@ exports[`Generated code for "jsii-calc": /python/src/js class ClassWithCollections( metaclass=jsii.JSIIMeta, -@@ -903,10 +1041,14 @@ +@@ -904,10 +1042,14 @@ ) -> None: ''' :param map: - @@ -17126,7 +17372,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="createAList") @builtins.classmethod def create_a_list(cls) -> typing.List[builtins.str]: -@@ -922,37 +1064,49 @@ +@@ -923,37 +1065,49 @@ def static_array(cls) -> typing.List[builtins.str]: # pyright: ignore [reportGeneralTypeIssues,reportRedeclaration] return typing.cast(typing.List[builtins.str], jsii.sget(cls, "staticArray")) @@ -17176,7 +17422,7 @@ exports[`Generated code for "jsii-calc": /python/src/js class ClassWithContainerTypes( metaclass=jsii.JSIIMeta, -@@ -974,10 +1128,15 @@ +@@ -975,10 +1129,15 @@ :param obj: - :param array_prop: :param obj_prop: @@ -17192,7 +17438,7 @@ exports[`Generated code for "jsii-calc": /python/src/js ) jsii.create(self.__class__, self, [array, record, obj, props]) -@@ -1027,17 +1186,23 @@ +@@ -1028,17 +1187,23 @@ ): def __init__(self, int: builtins.str) -> None: ''' @@ -17216,7 +17462,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @builtins.property @jsii.member(jsii_name="int") def int(self) -> builtins.str: -@@ -1056,10 +1221,13 @@ +@@ -1057,10 +1222,13 @@ def mutable_object(self) -> "IMutableObjectLiteral": return typing.cast("IMutableObjectLiteral", jsii.get(self, "mutableObject")) @@ -17230,7 +17476,7 @@ exports[`Generated code for "jsii-calc": /python/src/js class ClassWithNestedUnion( metaclass=jsii.JSIIMeta, -@@ -1070,10 +1238,13 @@ +@@ -1071,10 +1239,13 @@ union_property: typing.Sequence[typing.Union[typing.Mapping[builtins.str, typing.Union[typing.Union["StructA", typing.Dict[builtins.str, typing.Any]], typing.Union["StructB", typing.Dict[builtins.str, typing.Any]]]], typing.Sequence[typing.Union[typing.Union["StructA", typing.Dict[builtins.str, typing.Any]], typing.Union["StructB", typing.Dict[builtins.str, typing.Any]]]]]], ) -> None: ''' @@ -17244,7 +17490,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @builtins.property @jsii.member(jsii_name="unionProperty") def union_property( -@@ -1084,10 +1255,13 @@ +@@ -1085,10 +1256,13 @@ @union_property.setter def union_property( self, @@ -17258,7 +17504,7 @@ exports[`Generated code for "jsii-calc": /python/src/js class ConfusingToJackson( metaclass=jsii.JSIIMeta, -@@ -1118,10 +1292,13 @@ +@@ -1119,10 +1293,13 @@ @union_property.setter def union_property( self, @@ -17272,7 +17518,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.data_type( jsii_type="jsii-calc.ConfusingToJacksonStruct", -@@ -1135,10 +1312,13 @@ +@@ -1136,10 +1313,13 @@ union_property: typing.Optional[typing.Union["_scope_jsii_calc_lib_c61f082f.IFriendly", typing.Sequence[typing.Union["_scope_jsii_calc_lib_c61f082f.IFriendly", "AbstractClass"]]]] = None, ) -> None: ''' @@ -17286,7 +17532,7 @@ exports[`Generated code for "jsii-calc": /python/src/js self._values["union_property"] = union_property @builtins.property -@@ -1166,10 +1346,13 @@ +@@ -1167,10 +1347,13 @@ ): def __init__(self, consumer: "PartiallyInitializedThisConsumer") -> None: ''' @@ -17300,7 +17546,7 @@ exports[`Generated code for "jsii-calc": /python/src/js class Constructors(metaclass=jsii.JSIIMeta, jsii_type="jsii-calc.Constructors"): def __init__(self) -> None: -@@ -1217,10 +1400,13 @@ +@@ -1218,10 +1401,13 @@ ): def __init__(self, delegate: "IStructReturningDelegate") -> None: ''' @@ -17314,7 +17560,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="workItBaby") def work_it_baby(self) -> "StructB": return typing.cast("StructB", jsii.invoke(self, "workItBaby", [])) -@@ -1249,10 +1435,13 @@ +@@ -1250,10 +1436,13 @@ Returns whether the bell was rung. @@ -17328,7 +17574,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="staticImplementedByPrivateClass") @builtins.classmethod def static_implemented_by_private_class( -@@ -1263,10 +1452,13 @@ +@@ -1264,10 +1453,13 @@ Return whether the bell was rung. @@ -17342,7 +17588,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="staticImplementedByPublicClass") @builtins.classmethod def static_implemented_by_public_class(cls, ringer: "IBellRinger") -> builtins.bool: -@@ -1274,10 +1466,13 @@ +@@ -1275,10 +1467,13 @@ Return whether the bell was rung. @@ -17356,7 +17602,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="staticWhenTypedAsClass") @builtins.classmethod def static_when_typed_as_class(cls, ringer: "IConcreteBellRinger") -> builtins.bool: -@@ -1285,50 +1480,65 @@ +@@ -1286,50 +1481,65 @@ Return whether the bell was rung. @@ -17422,7 +17668,7 @@ exports[`Generated code for "jsii-calc": /python/src/js class ConsumersOfThisCrazyTypeSystem( metaclass=jsii.JSIIMeta, -@@ -1343,20 +1553,26 @@ +@@ -1344,20 +1554,26 @@ obj: "IAnotherPublicInterface", ) -> builtins.str: ''' @@ -17449,7 +17695,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.data_type( jsii_type="jsii-calc.ContainerProps", -@@ -1378,10 +1594,15 @@ +@@ -1379,10 +1595,15 @@ ''' :param array_prop: :param obj_prop: @@ -17465,7 +17711,7 @@ exports[`Generated code for "jsii-calc": /python/src/js "obj_prop": obj_prop, "record_prop": record_prop, } -@@ -1447,17 +1668,23 @@ +@@ -1448,17 +1669,23 @@ data: typing.Mapping[builtins.str, typing.Any], ) -> builtins.str: ''' @@ -17489,7 +17735,7 @@ exports[`Generated code for "jsii-calc": /python/src/js class Default(metaclass=jsii.JSIIMeta, jsii_type="jsii-calc.Default"): '''A class named "Default". -@@ -1486,10 +1713,15 @@ +@@ -1487,10 +1714,15 @@ ''' :param arg1: - :param arg2: - @@ -17505,7 +17751,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @builtins.property @jsii.member(jsii_name="arg1") def arg1(self) -> jsii.Number: -@@ -1547,10 +1779,14 @@ +@@ -1548,10 +1780,14 @@ :deprecated: this constructor is "just" okay @@ -17520,7 +17766,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="method") def method(self) -> None: ''' -@@ -1580,10 +1816,13 @@ +@@ -1581,10 +1817,13 @@ ''' return typing.cast(typing.Optional[jsii.Number], jsii.get(self, "mutableProperty")) @@ -17534,7 +17780,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.enum(jsii_type="jsii-calc.DeprecatedEnum") class DeprecatedEnum(enum.Enum): -@@ -1619,10 +1858,13 @@ +@@ -1620,10 +1859,13 @@ :deprecated: it just wraps a string @@ -17548,7 +17794,7 @@ exports[`Generated code for "jsii-calc": /python/src/js } @builtins.property -@@ -1687,10 +1929,21 @@ +@@ -1688,10 +1930,21 @@ :param non_primitive: An example of a non primitive property. :param another_optional: This is optional. :param optional_any: @@ -17570,7 +17816,7 @@ exports[`Generated code for "jsii-calc": /python/src/js "astring": astring, "another_required": another_required, "bool": bool, -@@ -1811,10 +2064,16 @@ +@@ -1812,10 +2065,16 @@ :param hoisted_top: :param left: :param right: @@ -17587,7 +17833,7 @@ exports[`Generated code for "jsii-calc": /python/src/js self._values["hoisted_top"] = hoisted_top if left is not None: self._values["left"] = left -@@ -1872,10 +2131,13 @@ +@@ -1873,10 +2132,13 @@ class DiamondInheritanceBaseLevelStruct: def __init__(self, *, base_level_property: builtins.str) -> None: ''' @@ -17601,7 +17847,7 @@ exports[`Generated code for "jsii-calc": /python/src/js } @builtins.property -@@ -1913,10 +2175,14 @@ +@@ -1914,10 +2176,14 @@ ) -> None: ''' :param base_level_property: @@ -17616,7 +17862,7 @@ exports[`Generated code for "jsii-calc": /python/src/js "first_mid_level_property": first_mid_level_property, } -@@ -1961,10 +2227,14 @@ +@@ -1962,10 +2228,14 @@ ) -> None: ''' :param base_level_property: @@ -17631,7 +17877,7 @@ exports[`Generated code for "jsii-calc": /python/src/js "second_mid_level_property": second_mid_level_property, } -@@ -2020,10 +2290,16 @@ +@@ -2021,10 +2291,16 @@ :param base_level_property: :param first_mid_level_property: :param second_mid_level_property: @@ -17648,7 +17894,7 @@ exports[`Generated code for "jsii-calc": /python/src/js "first_mid_level_property": first_mid_level_property, "second_mid_level_property": second_mid_level_property, "top_level_property": top_level_property, -@@ -2103,10 +2379,13 @@ +@@ -2104,10 +2380,13 @@ @jsii.member(jsii_name="changePrivatePropertyValue") def change_private_property_value(self, new_value: builtins.str) -> None: ''' @@ -17662,7 +17908,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="privateMethodValue") def private_method_value(self) -> builtins.str: return typing.cast(builtins.str, jsii.invoke(self, "privateMethodValue", [])) -@@ -2135,10 +2414,15 @@ +@@ -2136,10 +2415,15 @@ ''' :param _required_any: - :param _optional_any: - @@ -17678,7 +17924,7 @@ exports[`Generated code for "jsii-calc": /python/src/js class DocumentedClass(metaclass=jsii.JSIIMeta, jsii_type="jsii-calc.DocumentedClass"): '''Here's the first line of the TSDoc comment. -@@ -2210,10 +2494,14 @@ +@@ -2211,10 +2495,14 @@ ) -> builtins.str: ''' :param optional: - @@ -17693,7 +17939,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.data_type( jsii_type="jsii-calc.DontUseMe", -@@ -2226,10 +2514,13 @@ +@@ -2227,10 +2515,13 @@ Don't use this interface An interface that shouldn't be used, with the annotation in a weird place. @@ -17707,7 +17953,7 @@ exports[`Generated code for "jsii-calc": /python/src/js self._values["dont_set_me"] = dont_set_me @builtins.property -@@ -2263,10 +2554,13 @@ +@@ -2264,10 +2555,13 @@ class DummyObj: def __init__(self, *, example: builtins.str) -> None: ''' @@ -17721,7 +17967,7 @@ exports[`Generated code for "jsii-calc": /python/src/js } @builtins.property -@@ -2295,28 +2589,37 @@ +@@ -2296,28 +2590,37 @@ def __init__(self, value_store: builtins.str) -> None: ''' @@ -17759,7 +18005,7 @@ exports[`Generated code for "jsii-calc": /python/src/js class DynamicPropertyBearerChild( DynamicPropertyBearer, -@@ -2325,20 +2628,26 @@ +@@ -2326,20 +2629,26 @@ ): def __init__(self, original_value: builtins.str) -> None: ''' @@ -17786,7 +18032,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @builtins.property @jsii.member(jsii_name="originalValue") def original_value(self) -> builtins.str: -@@ -2351,10 +2660,13 @@ +@@ -2352,10 +2661,13 @@ def __init__(self, clock: "IWallClock") -> None: '''Creates a new instance of Entropy. @@ -17800,7 +18046,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="increase") def increase(self) -> builtins.str: '''Increases entropy by consuming time from the clock (yes, this is a long shot, please don't judge). -@@ -2382,10 +2694,13 @@ +@@ -2383,10 +2695,13 @@ :param word: the value to return. @@ -17814,7 +18060,7 @@ exports[`Generated code for "jsii-calc": /python/src/js # Adding a "__jsii_proxy_class__(): typing.Type" function to the abstract class typing.cast(typing.Any, Entropy).__jsii_proxy_class__ = lambda : _EntropyProxy -@@ -2422,10 +2737,14 @@ +@@ -2423,10 +2738,14 @@ are being erased when sending values from native code to JS. :param opts: - @@ -17829,7 +18075,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="prop1IsNull") @builtins.classmethod def prop1_is_null(cls) -> typing.Mapping[builtins.str, typing.Any]: -@@ -2453,10 +2772,14 @@ +@@ -2454,10 +2773,14 @@ ) -> None: ''' :param option1: @@ -17844,7 +18090,7 @@ exports[`Generated code for "jsii-calc": /python/src/js self._values["option1"] = option1 if option2 is not None: self._values["option2"] = option2 -@@ -2500,10 +2823,14 @@ +@@ -2501,10 +2824,14 @@ :param readonly_string: - :param mutable_number: - @@ -17859,7 +18105,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="method") def method(self) -> None: ''' -@@ -2527,10 +2854,13 @@ +@@ -2528,10 +2855,13 @@ ''' return typing.cast(typing.Optional[jsii.Number], jsii.get(self, "mutableProperty")) @@ -17873,7 +18119,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.enum(jsii_type="jsii-calc.ExperimentalEnum") class ExperimentalEnum(enum.Enum): -@@ -2558,10 +2888,13 @@ +@@ -2559,10 +2889,13 @@ ''' :param readonly_property: @@ -17887,7 +18133,7 @@ exports[`Generated code for "jsii-calc": /python/src/js } @builtins.property -@@ -2591,10 +2924,13 @@ +@@ -2592,10 +2925,13 @@ ): def __init__(self, success: builtins.bool) -> None: ''' @@ -17901,7 +18147,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @builtins.property @jsii.member(jsii_name="success") def success(self) -> builtins.bool: -@@ -2610,10 +2946,14 @@ +@@ -2611,10 +2947,14 @@ def __init__(self, *, boom: builtins.bool, prop: builtins.str) -> None: ''' :param boom: @@ -17916,7 +18162,7 @@ exports[`Generated code for "jsii-calc": /python/src/js "prop": prop, } -@@ -2655,10 +2995,14 @@ +@@ -2656,10 +2996,14 @@ :param readonly_string: - :param mutable_number: - @@ -17931,7 +18177,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="method") def method(self) -> None: ''' -@@ -2682,10 +3026,13 @@ +@@ -2683,10 +3027,13 @@ ''' return typing.cast(typing.Optional[jsii.Number], jsii.get(self, "mutableProperty")) @@ -17945,7 +18191,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.enum(jsii_type="jsii-calc.ExternalEnum") class ExternalEnum(enum.Enum): -@@ -2713,10 +3060,13 @@ +@@ -2714,10 +3061,13 @@ ''' :param readonly_property: @@ -17959,7 +18205,7 @@ exports[`Generated code for "jsii-calc": /python/src/js } @builtins.property -@@ -2859,10 +3209,13 @@ +@@ -2860,10 +3210,13 @@ def __init__(self, *, name: typing.Optional[builtins.str] = None) -> None: '''These are some arguments you can pass to a method. @@ -17973,7 +18219,7 @@ exports[`Generated code for "jsii-calc": /python/src/js self._values["name"] = name @builtins.property -@@ -2899,10 +3252,13 @@ +@@ -2900,10 +3253,13 @@ friendly: "_scope_jsii_calc_lib_c61f082f.IFriendly", ) -> builtins.str: ''' @@ -17987,7 +18233,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.interface(jsii_type="jsii-calc.IAnonymousImplementationProvider") class IAnonymousImplementationProvider(typing_extensions.Protocol): -@@ -2982,10 +3338,13 @@ +@@ -2983,10 +3339,13 @@ def a(self) -> builtins.str: return typing.cast(builtins.str, jsii.get(self, "a")) @@ -18001,7 +18247,7 @@ exports[`Generated code for "jsii-calc": /python/src/js # Adding a "__jsii_proxy_class__(): typing.Type" function to the interface typing.cast(typing.Any, IAnotherPublicInterface).__jsii_proxy_class__ = lambda : _IAnotherPublicInterfaceProxy -@@ -3028,10 +3387,13 @@ +@@ -3029,10 +3388,13 @@ @jsii.member(jsii_name="yourTurn") def your_turn(self, bell: "IBell") -> None: ''' @@ -18015,7 +18261,7 @@ exports[`Generated code for "jsii-calc": /python/src/js # Adding a "__jsii_proxy_class__(): typing.Type" function to the interface typing.cast(typing.Any, IBellRinger).__jsii_proxy_class__ = lambda : _IBellRingerProxy -@@ -3056,10 +3418,13 @@ +@@ -3057,10 +3419,13 @@ @jsii.member(jsii_name="yourTurn") def your_turn(self, bell: "Bell") -> None: ''' @@ -18029,7 +18275,7 @@ exports[`Generated code for "jsii-calc": /python/src/js # Adding a "__jsii_proxy_class__(): typing.Type" function to the interface typing.cast(typing.Any, IConcreteBellRinger).__jsii_proxy_class__ = lambda : _IConcreteBellRingerProxy -@@ -3115,10 +3480,13 @@ +@@ -3116,10 +3481,13 @@ ''' return typing.cast(typing.Optional[jsii.Number], jsii.get(self, "mutableProperty")) @@ -18043,7 +18289,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="method") def method(self) -> None: ''' -@@ -3173,10 +3541,13 @@ +@@ -3174,10 +3542,13 @@ ''' return typing.cast(typing.Optional[jsii.Number], jsii.get(self, "mutableProperty")) @@ -18057,7 +18303,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="method") def method(self) -> None: ''' -@@ -3218,10 +3589,13 @@ +@@ -3219,10 +3590,13 @@ def private(self) -> builtins.str: return typing.cast(builtins.str, jsii.get(self, "private")) @@ -18071,7 +18317,7 @@ exports[`Generated code for "jsii-calc": /python/src/js # Adding a "__jsii_proxy_class__(): typing.Type" function to the interface typing.cast(typing.Any, IExtendsPrivateInterface).__jsii_proxy_class__ = lambda : _IExtendsPrivateInterfaceProxy -@@ -3267,10 +3641,13 @@ +@@ -3268,10 +3642,13 @@ ''' return typing.cast(typing.Optional[jsii.Number], jsii.get(self, "mutableProperty")) @@ -18085,7 +18331,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="method") def method(self) -> None: ''' -@@ -3452,10 +3829,14 @@ +@@ -3453,10 +3830,14 @@ ) -> None: ''' :param arg1: - @@ -18100,7 +18346,7 @@ exports[`Generated code for "jsii-calc": /python/src/js # Adding a "__jsii_proxy_class__(): typing.Type" function to the interface typing.cast(typing.Any, IInterfaceWithOptionalMethodArguments).__jsii_proxy_class__ = lambda : _IInterfaceWithOptionalMethodArgumentsProxy -@@ -3490,10 +3871,13 @@ +@@ -3491,10 +3872,13 @@ def read_write_string(self) -> builtins.str: return typing.cast(builtins.str, jsii.get(self, "readWriteString")) @@ -18114,7 +18360,7 @@ exports[`Generated code for "jsii-calc": /python/src/js # Adding a "__jsii_proxy_class__(): typing.Type" function to the interface typing.cast(typing.Any, IInterfaceWithProperties).__jsii_proxy_class__ = lambda : _IInterfaceWithPropertiesProxy -@@ -3523,10 +3907,13 @@ +@@ -3524,10 +3908,13 @@ def foo(self) -> jsii.Number: return typing.cast(jsii.Number, jsii.get(self, "foo")) @@ -18128,7 +18374,7 @@ exports[`Generated code for "jsii-calc": /python/src/js # Adding a "__jsii_proxy_class__(): typing.Type" function to the interface typing.cast(typing.Any, IInterfaceWithPropertiesExtension).__jsii_proxy_class__ = lambda : _IInterfaceWithPropertiesExtensionProxy -@@ -4046,10 +4433,13 @@ +@@ -4047,10 +4434,13 @@ def value(self) -> builtins.str: return typing.cast(builtins.str, jsii.get(self, "value")) @@ -18142,7 +18388,7 @@ exports[`Generated code for "jsii-calc": /python/src/js # Adding a "__jsii_proxy_class__(): typing.Type" function to the interface typing.cast(typing.Any, IMutableObjectLiteral).__jsii_proxy_class__ = lambda : _IMutableObjectLiteralProxy -@@ -4085,19 +4475,25 @@ +@@ -4086,19 +4476,25 @@ def b(self) -> builtins.str: return typing.cast(builtins.str, jsii.get(self, "b")) @@ -18168,7 +18414,7 @@ exports[`Generated code for "jsii-calc": /python/src/js # Adding a "__jsii_proxy_class__(): typing.Type" function to the interface typing.cast(typing.Any, INonInternalInterface).__jsii_proxy_class__ = lambda : _INonInternalInterfaceProxy -@@ -4130,10 +4526,13 @@ +@@ -4131,10 +4527,13 @@ def property(self) -> builtins.str: return typing.cast(builtins.str, jsii.get(self, "property")) @@ -18182,7 +18428,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="wasSet") def was_set(self) -> builtins.bool: return typing.cast(builtins.bool, jsii.invoke(self, "wasSet", [])) -@@ -4326,10 +4725,13 @@ +@@ -4327,10 +4726,13 @@ def mutable_property(self) -> typing.Optional[jsii.Number]: return typing.cast(typing.Optional[jsii.Number], jsii.get(self, "mutableProperty")) @@ -18196,7 +18442,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="method") def method(self) -> None: return typing.cast(None, jsii.invoke(self, "method", [])) -@@ -4430,10 +4832,13 @@ +@@ -4431,10 +4833,13 @@ def prop(self) -> builtins.str: return typing.cast(builtins.str, jsii.get(self, "prop")) @@ -18210,7 +18456,7 @@ exports[`Generated code for "jsii-calc": /python/src/js class Implementation(metaclass=jsii.JSIIMeta, jsii_type="jsii-calc.Implementation"): def __init__(self) -> None: -@@ -4479,10 +4884,13 @@ +@@ -4480,10 +4885,13 @@ def private(self) -> builtins.str: return typing.cast(builtins.str, jsii.get(self, "private")) @@ -18224,7 +18470,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.data_type( jsii_type="jsii-calc.ImplictBaseOfBase", -@@ -4500,10 +4908,15 @@ +@@ -4501,10 +4909,15 @@ ''' :param foo: - :param bar: - @@ -18240,7 +18486,7 @@ exports[`Generated code for "jsii-calc": /python/src/js "bar": bar, "goo": goo, } -@@ -4578,10 +4991,13 @@ +@@ -4579,10 +4992,13 @@ count: jsii.Number, ) -> typing.List["_scope_jsii_calc_lib_c61f082f.IDoublable"]: ''' @@ -18254,7 +18500,7 @@ exports[`Generated code for "jsii-calc": /python/src/js class Isomorphism(metaclass=jsii.JSIIAbstractClass, jsii_type="jsii-calc.Isomorphism"): '''Checks the "same instance" isomorphism is preserved within the constructor. -@@ -4686,19 +5102,25 @@ +@@ -4687,19 +5103,25 @@ def prop_a(self) -> builtins.str: return typing.cast(builtins.str, jsii.get(self, "propA")) @@ -18280,7 +18526,7 @@ exports[`Generated code for "jsii-calc": /python/src/js class JavaReservedWords( metaclass=jsii.JSIIMeta, -@@ -4920,10 +5342,13 @@ +@@ -4921,10 +5343,13 @@ def while_(self) -> builtins.str: return typing.cast(builtins.str, jsii.get(self, "while")) @@ -18294,7 +18540,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.implements(IJsii487External2, IJsii487External) class Jsii487Derived(metaclass=jsii.JSIIMeta, jsii_type="jsii-calc.Jsii487Derived"): -@@ -5025,10 +5450,13 @@ +@@ -5026,10 +5451,13 @@ @builtins.classmethod def stringify(cls, value: typing.Any = None) -> typing.Optional[builtins.str]: ''' @@ -18308,7 +18554,7 @@ exports[`Generated code for "jsii-calc": /python/src/js class LevelOne(metaclass=jsii.JSIIMeta, jsii_type="jsii-calc.LevelOne"): '''Validates that nested classes get correct code generation for the occasional forward reference.''' -@@ -5058,10 +5486,13 @@ +@@ -5059,10 +5487,13 @@ class PropBooleanValue: def __init__(self, *, value: builtins.bool) -> None: ''' @@ -18322,7 +18568,7 @@ exports[`Generated code for "jsii-calc": /python/src/js } @builtins.property -@@ -5095,10 +5526,13 @@ +@@ -5096,10 +5527,13 @@ ''' :param prop: ''' @@ -18336,7 +18582,7 @@ exports[`Generated code for "jsii-calc": /python/src/js } @builtins.property -@@ -5133,10 +5567,13 @@ +@@ -5134,10 +5568,13 @@ ''' :param prop: ''' @@ -18350,7 +18596,7 @@ exports[`Generated code for "jsii-calc": /python/src/js } @builtins.property -@@ -5184,10 +5621,17 @@ +@@ -5185,10 +5622,17 @@ :param cpu: The number of cpu units used by the task. Valid values, which determines your range of valid values for the memory parameter: 256 (.25 vCPU) - Available memory values: 0.5GB, 1GB, 2GB 512 (.5 vCPU) - Available memory values: 1GB, 2GB, 3GB, 4GB 1024 (1 vCPU) - Available memory values: 2GB, 3GB, 4GB, 5GB, 6GB, 7GB, 8GB 2048 (2 vCPU) - Available memory values: Between 4GB and 16GB in 1GB increments 4096 (4 vCPU) - Available memory values: Between 8GB and 30GB in 1GB increments This default is set in the underlying FargateTaskDefinition construct. Default: 256 :param memory_mib: The amount (in MiB) of memory used by the task. This field is required and you must use one of the following values, which determines your range of valid values for the cpu parameter: 0.5GB, 1GB, 2GB - Available cpu values: 256 (.25 vCPU) 1GB, 2GB, 3GB, 4GB - Available cpu values: 512 (.5 vCPU) 2GB, 3GB, 4GB, 5GB, 6GB, 7GB, 8GB - Available cpu values: 1024 (1 vCPU) Between 4GB and 16GB in 1GB increments - Available cpu values: 2048 (2 vCPU) Between 8GB and 30GB in 1GB increments - Available cpu values: 4096 (4 vCPU) This default is set in the underlying FargateTaskDefinition construct. Default: 512 :param public_load_balancer: Determines whether the Application Load Balancer will be internet-facing. Default: true @@ -18368,7 +18614,7 @@ exports[`Generated code for "jsii-calc": /python/src/js self._values["container_port"] = container_port if cpu is not None: self._values["cpu"] = cpu -@@ -5314,10 +5758,14 @@ +@@ -5315,10 +5759,14 @@ '''Creates a BinaryOperation. :param lhs: Left-hand side operand. @@ -18383,7 +18629,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="farewell") def farewell(self) -> builtins.str: '''Say farewell.''' -@@ -5365,10 +5813,13 @@ +@@ -5366,10 +5814,13 @@ class NestedStruct: def __init__(self, *, number_prop: jsii.Number) -> None: ''' @@ -18397,7 +18643,7 @@ exports[`Generated code for "jsii-calc": /python/src/js } @builtins.property -@@ -5439,17 +5890,24 @@ +@@ -5440,17 +5891,24 @@ def __init__(self, _param1: builtins.str, optional: typing.Any = None) -> None: ''' :param _param1: - @@ -18422,7 +18668,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="giveMeUndefinedInsideAnObject") def give_me_undefined_inside_an_object( self, -@@ -5477,10 +5935,13 @@ +@@ -5478,10 +5936,13 @@ def change_me_to_undefined(self) -> typing.Optional[builtins.str]: return typing.cast(typing.Optional[builtins.str], jsii.get(self, "changeMeToUndefined")) @@ -18436,7 +18682,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.data_type( jsii_type="jsii-calc.NullShouldBeTreatedAsUndefinedData", -@@ -5499,10 +5960,14 @@ +@@ -5500,10 +5961,14 @@ ) -> None: ''' :param array_with_three_elements_and_undefined_as_second_argument: @@ -18451,7 +18697,7 @@ exports[`Generated code for "jsii-calc": /python/src/js } if this_should_be_undefined is not None: self._values["this_should_be_undefined"] = this_should_be_undefined -@@ -5537,17 +6002,23 @@ +@@ -5538,17 +6003,23 @@ def __init__(self, generator: "IRandomNumberGenerator") -> None: ''' @@ -18475,7 +18721,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="nextTimes100") def next_times100(self) -> jsii.Number: return typing.cast(jsii.Number, jsii.invoke(self, "nextTimes100", [])) -@@ -5557,10 +6028,13 @@ +@@ -5558,10 +6029,13 @@ def generator(self) -> "IRandomNumberGenerator": return typing.cast("IRandomNumberGenerator", jsii.get(self, "generator")) @@ -18489,7 +18735,7 @@ exports[`Generated code for "jsii-calc": /python/src/js class ObjectRefsInCollections( metaclass=jsii.JSIIMeta, -@@ -5578,10 +6052,13 @@ +@@ -5579,10 +6053,13 @@ ) -> jsii.Number: '''Returns the sum of all values. @@ -18503,7 +18749,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="sumFromMap") def sum_from_map( self, -@@ -5589,10 +6066,13 @@ +@@ -5590,10 +6067,13 @@ ) -> jsii.Number: '''Returns the sum of all values in a map. @@ -18517,7 +18763,7 @@ exports[`Generated code for "jsii-calc": /python/src/js class ObjectWithPropertyProvider( metaclass=jsii.JSIIMeta, -@@ -5633,10 +6113,13 @@ +@@ -5634,10 +6114,13 @@ ): def __init__(self, delegate: "IInterfaceWithOptionalMethodArguments") -> None: ''' @@ -18531,7 +18777,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="invokeWithOptional") def invoke_with_optional(self) -> None: return typing.cast(None, jsii.invoke(self, "invokeWithOptional", [])) -@@ -5659,10 +6142,15 @@ +@@ -5660,10 +6143,15 @@ ''' :param arg1: - :param arg2: - @@ -18547,7 +18793,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @builtins.property @jsii.member(jsii_name="arg1") def arg1(self) -> jsii.Number: -@@ -5687,10 +6175,13 @@ +@@ -5688,10 +6176,13 @@ class OptionalStruct: def __init__(self, *, field: typing.Optional[builtins.str] = None) -> None: ''' @@ -18561,7 +18807,7 @@ exports[`Generated code for "jsii-calc": /python/src/js self._values["field"] = field @builtins.property -@@ -5766,10 +6257,13 @@ +@@ -5767,10 +6258,13 @@ def _override_read_write(self) -> builtins.str: return typing.cast(builtins.str, jsii.get(self, "overrideReadWrite")) @@ -18575,7 +18821,7 @@ exports[`Generated code for "jsii-calc": /python/src/js class OverrideReturnsObject( metaclass=jsii.JSIIMeta, -@@ -5781,10 +6275,13 @@ +@@ -5782,10 +6276,13 @@ @jsii.member(jsii_name="test") def test(self, obj: "IReturnsNumber") -> jsii.Number: ''' @@ -18589,7 +18835,7 @@ exports[`Generated code for "jsii-calc": /python/src/js class ParamShadowsBuiltins( metaclass=jsii.JSIIMeta, -@@ -5806,10 +6303,14 @@ +@@ -5807,10 +6304,14 @@ :param str: should be set to something that is NOT a valid expression in Python (e.g: "\${NOPE}""). :param boolean_property: :param string_property: @@ -18604,7 +18850,7 @@ exports[`Generated code for "jsii-calc": /python/src/js string_property=string_property, struct_property=struct_property, ) -@@ -5839,10 +6340,15 @@ +@@ -5840,10 +6341,15 @@ :param string_property: :param struct_property: ''' @@ -18620,7 +18866,7 @@ exports[`Generated code for "jsii-calc": /python/src/js "string_property": string_property, "struct_property": struct_property, } -@@ -5895,10 +6401,13 @@ +@@ -5896,10 +6402,13 @@ scope: "_scope_jsii_calc_lib_c61f082f.Number", ) -> "_scope_jsii_calc_lib_c61f082f.Number": ''' @@ -18634,7 +18880,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.data_type( jsii_type="jsii-calc.ParentStruct982", -@@ -5909,10 +6418,13 @@ +@@ -5910,10 +6419,13 @@ def __init__(self, *, foo: builtins.str) -> None: '''https://github.com/aws/jsii/issues/982. @@ -18648,7 +18894,7 @@ exports[`Generated code for "jsii-calc": /python/src/js } @builtins.property -@@ -5967,10 +6479,15 @@ +@@ -5968,10 +6480,15 @@ ''' :param obj: - :param dt: - @@ -18664,7 +18910,7 @@ exports[`Generated code for "jsii-calc": /python/src/js # Adding a "__jsii_proxy_class__(): typing.Type" function to the abstract class typing.cast(typing.Any, PartiallyInitializedThisConsumer).__jsii_proxy_class__ = lambda : _PartiallyInitializedThisConsumerProxy -@@ -5985,10 +6502,13 @@ +@@ -5986,10 +6503,13 @@ friendly: "_scope_jsii_calc_lib_c61f082f.IFriendly", ) -> builtins.str: ''' @@ -18678,7 +18924,7 @@ exports[`Generated code for "jsii-calc": /python/src/js class Power( _CompositeOperation_1c4d123b, -@@ -6005,10 +6525,14 @@ +@@ -6006,10 +6526,14 @@ '''Creates a Power operation. :param base: The base of the power. @@ -18693,7 +18939,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @builtins.property @jsii.member(jsii_name="base") def base(self) -> "_scope_jsii_calc_lib_c61f082f.NumericValue": -@@ -6231,10 +6755,13 @@ +@@ -6232,10 +6756,13 @@ value: "_scope_jsii_calc_lib_c61f082f.EnumFromScopedModule", ) -> None: ''' @@ -18707,7 +18953,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @builtins.property @jsii.member(jsii_name="foo") def foo( -@@ -6245,10 +6772,13 @@ +@@ -6246,10 +6773,13 @@ @foo.setter def foo( self, @@ -18721,7 +18967,7 @@ exports[`Generated code for "jsii-calc": /python/src/js class ReturnsPrivateImplementationOfInterface( metaclass=jsii.JSIIMeta, -@@ -6290,10 +6820,14 @@ +@@ -6291,10 +6821,14 @@ :param string_prop: May not be empty. :param nested_struct: ''' @@ -18736,7 +18982,7 @@ exports[`Generated code for "jsii-calc": /python/src/js } if nested_struct is not None: self._values["nested_struct"] = nested_struct -@@ -6360,17 +6894,25 @@ +@@ -6361,17 +6895,25 @@ ''' :param arg1: - :param arg2: - @@ -18762,7 +19008,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="methodWithOptionalArguments") def method_with_optional_arguments( self, -@@ -6382,10 +6924,15 @@ +@@ -6383,10 +6925,15 @@ :param arg1: - :param arg2: - @@ -18778,7 +19024,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.data_type( jsii_type="jsii-calc.SecondLevelStruct", -@@ -6404,10 +6951,14 @@ +@@ -6405,10 +6952,14 @@ ) -> None: ''' :param deeper_required_prop: It's long and required. @@ -18793,7 +19039,7 @@ exports[`Generated code for "jsii-calc": /python/src/js } if deeper_optional_prop is not None: self._values["deeper_optional_prop"] = deeper_optional_prop -@@ -6469,10 +7020,13 @@ +@@ -6470,10 +7021,13 @@ @jsii.member(jsii_name="isSingletonInt") def is_singleton_int(self, value: jsii.Number) -> builtins.bool: ''' @@ -18807,7 +19053,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.enum(jsii_type="jsii-calc.SingletonIntEnum") class SingletonIntEnum(enum.Enum): -@@ -6491,10 +7045,13 @@ +@@ -6492,10 +7046,13 @@ @jsii.member(jsii_name="isSingletonString") def is_singleton_string(self, value: builtins.str) -> builtins.bool: ''' @@ -18821,7 +19067,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.enum(jsii_type="jsii-calc.SingletonStringEnum") class SingletonStringEnum(enum.Enum): -@@ -6518,10 +7075,14 @@ +@@ -6519,10 +7076,14 @@ ) -> None: ''' :param property: @@ -18836,7 +19082,7 @@ exports[`Generated code for "jsii-calc": /python/src/js "yet_anoter_one": yet_anoter_one, } -@@ -6575,10 +7136,13 @@ +@@ -6576,10 +7137,13 @@ class ParentStruct: def __init__(self, *, field1: builtins.str) -> None: ''' @@ -18850,7 +19096,7 @@ exports[`Generated code for "jsii-calc": /python/src/js } @builtins.property -@@ -6607,10 +7171,14 @@ +@@ -6608,10 +7172,14 @@ def __init__(self, *, field1: builtins.str, field2: builtins.str) -> None: ''' :param field1: @@ -18865,7 +19111,7 @@ exports[`Generated code for "jsii-calc": /python/src/js "field2": field2, } -@@ -6661,10 +7229,14 @@ +@@ -6662,10 +7230,14 @@ ) -> None: ''' :param readonly_string: - @@ -18880,7 +19126,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="method") def method(self) -> None: return typing.cast(None, jsii.invoke(self, "method", [])) -@@ -6679,10 +7251,13 @@ +@@ -6680,10 +7252,13 @@ def mutable_property(self) -> typing.Optional[jsii.Number]: return typing.cast(typing.Optional[jsii.Number], jsii.get(self, "mutableProperty")) @@ -18894,7 +19140,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.enum(jsii_type="jsii-calc.StableEnum") class StableEnum(enum.Enum): -@@ -6698,10 +7273,13 @@ +@@ -6699,10 +7274,13 @@ class StableStruct: def __init__(self, *, readonly_property: builtins.str) -> None: ''' @@ -18908,7 +19154,7 @@ exports[`Generated code for "jsii-calc": /python/src/js } @builtins.property -@@ -6738,10 +7316,13 @@ +@@ -6739,10 +7317,13 @@ def static_variable(cls) -> builtins.bool: # pyright: ignore [reportGeneralTypeIssues,reportRedeclaration] return typing.cast(builtins.bool, jsii.sget(cls, "staticVariable")) @@ -18922,7 +19168,7 @@ exports[`Generated code for "jsii-calc": /python/src/js class StaticHelloParent( metaclass=jsii.JSIIMeta, -@@ -6776,19 +7357,25 @@ +@@ -6777,19 +7358,25 @@ class Statics(metaclass=jsii.JSIIMeta, jsii_type="jsii-calc.Statics"): def __init__(self, value: builtins.str) -> None: ''' @@ -18948,7 +19194,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="justMethod") def just_method(self) -> builtins.str: return typing.cast(builtins.str, jsii.invoke(self, "justMethod", [])) -@@ -6825,19 +7412,25 @@ +@@ -6826,19 +7413,25 @@ ''' return typing.cast("Statics", jsii.sget(cls, "instance")) @@ -18974,7 +19220,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @builtins.property @jsii.member(jsii_name="value") def value(self) -> builtins.str: -@@ -6893,10 +7486,13 @@ +@@ -6894,10 +7487,13 @@ def you_see_me(self) -> builtins.str: return typing.cast(builtins.str, jsii.get(self, "youSeeMe")) @@ -18988,7 +19234,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.data_type( jsii_type="jsii-calc.StructA", -@@ -6919,10 +7515,15 @@ +@@ -6920,10 +7516,15 @@ :param required_string: :param optional_number: @@ -19004,7 +19250,7 @@ exports[`Generated code for "jsii-calc": /python/src/js } if optional_number is not None: self._values["optional_number"] = optional_number -@@ -6980,10 +7581,15 @@ +@@ -6981,10 +7582,15 @@ :param optional_boolean: :param optional_struct_a: ''' @@ -19020,7 +19266,7 @@ exports[`Generated code for "jsii-calc": /python/src/js } if optional_boolean is not None: self._values["optional_boolean"] = optional_boolean -@@ -7035,10 +7641,14 @@ +@@ -7036,10 +7642,14 @@ See: https://github.com/aws/aws-cdk/issues/4302 :param scope: @@ -19035,7 +19281,7 @@ exports[`Generated code for "jsii-calc": /python/src/js } if props is not None: self._values["props"] = props -@@ -7081,10 +7691,14 @@ +@@ -7082,10 +7692,14 @@ ) -> jsii.Number: ''' :param _positional: - @@ -19050,7 +19296,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="roundTrip") @builtins.classmethod def round_trip( -@@ -7099,10 +7713,13 @@ +@@ -7100,10 +7714,13 @@ :param _positional: - :param required: This is a required field. :param second_level: A union to really stress test our serialization. @@ -19064,7 +19310,7 @@ exports[`Generated code for "jsii-calc": /python/src/js ) return typing.cast("TopLevelStruct", jsii.sinvoke(cls, "roundTrip", [_positional, input])) -@@ -7119,10 +7736,13 @@ +@@ -7120,10 +7737,13 @@ struct: typing.Union[typing.Union["StructA", typing.Dict[builtins.str, typing.Any]], typing.Union["StructB", typing.Dict[builtins.str, typing.Any]]], ) -> builtins.bool: ''' @@ -19078,7 +19324,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="isStructB") @builtins.classmethod def is_struct_b( -@@ -7130,18 +7750,24 @@ +@@ -7131,18 +7751,24 @@ struct: typing.Union[typing.Union["StructA", typing.Dict[builtins.str, typing.Any]], typing.Union["StructB", typing.Dict[builtins.str, typing.Any]]], ) -> builtins.bool: ''' @@ -19103,7 +19349,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.data_type( jsii_type="jsii-calc.StructWithCollectionOfUnionts", -@@ -7155,10 +7781,13 @@ +@@ -7156,10 +7782,13 @@ union_property: typing.Sequence[typing.Mapping[builtins.str, typing.Union[typing.Union["StructA", typing.Dict[builtins.str, typing.Any]], typing.Union["StructB", typing.Dict[builtins.str, typing.Any]]]]], ) -> None: ''' @@ -19117,7 +19363,7 @@ exports[`Generated code for "jsii-calc": /python/src/js } @builtins.property -@@ -7195,10 +7824,14 @@ +@@ -7196,10 +7825,14 @@ ) -> None: ''' :param foo: An enum value. @@ -19132,7 +19378,7 @@ exports[`Generated code for "jsii-calc": /python/src/js } if bar is not None: self._values["bar"] = bar -@@ -7254,10 +7887,16 @@ +@@ -7255,10 +7888,16 @@ :param default: :param assert_: :param result: @@ -19149,7 +19395,7 @@ exports[`Generated code for "jsii-calc": /python/src/js } if assert_ is not None: self._values["assert_"] = assert_ -@@ -7327,10 +7966,13 @@ +@@ -7328,10 +7967,13 @@ @parts.setter def parts( self, @@ -19163,7 +19409,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.data_type( jsii_type="jsii-calc.SupportsNiceJavaBuilderProps", -@@ -7346,10 +7988,14 @@ +@@ -7347,10 +7989,14 @@ ) -> None: ''' :param bar: Some number, like 42. @@ -19178,7 +19424,7 @@ exports[`Generated code for "jsii-calc": /python/src/js } if id is not None: self._values["id"] = id -@@ -7398,10 +8044,13 @@ +@@ -7399,10 +8045,13 @@ ''' :param id_: some identifier of your choice. :param bar: Some number, like 42. @@ -19192,7 +19438,7 @@ exports[`Generated code for "jsii-calc": /python/src/js jsii.create(self.__class__, self, [id_, props]) @builtins.property -@@ -7479,17 +8128,23 @@ +@@ -7480,17 +8129,23 @@ @jsii.member(jsii_name="modifyOtherProperty") def modify_other_property(self, value: builtins.str) -> None: ''' @@ -19216,7 +19462,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="readA") def read_a(self) -> jsii.Number: return typing.cast(jsii.Number, jsii.invoke(self, "readA", [])) -@@ -7509,17 +8164,23 @@ +@@ -7510,17 +8165,23 @@ @jsii.member(jsii_name="virtualMethod") def virtual_method(self, n: jsii.Number) -> jsii.Number: ''' @@ -19240,7 +19486,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @builtins.property @jsii.member(jsii_name="readonlyProperty") def readonly_property(self) -> builtins.str: -@@ -7530,46 +8191,61 @@ +@@ -7531,46 +8192,61 @@ def a(self) -> jsii.Number: return typing.cast(jsii.Number, jsii.get(self, "a")) @@ -19302,7 +19548,7 @@ exports[`Generated code for "jsii-calc": /python/src/js class TestStructWithEnum( metaclass=jsii.JSIIMeta, -@@ -7652,10 +8328,15 @@ +@@ -7653,10 +8329,15 @@ ''' :param required: This is a required field. :param second_level: A union to really stress test our serialization. @@ -19318,7 +19564,7 @@ exports[`Generated code for "jsii-calc": /python/src/js "second_level": second_level, } if optional is not None: -@@ -7746,10 +8427,13 @@ +@@ -7747,10 +8428,13 @@ def __init__(self, operand: "_scope_jsii_calc_lib_c61f082f.NumericValue") -> None: ''' @@ -19332,7 +19578,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @builtins.property @jsii.member(jsii_name="operand") def operand(self) -> "_scope_jsii_calc_lib_c61f082f.NumericValue": -@@ -7780,10 +8464,14 @@ +@@ -7781,10 +8465,14 @@ ) -> None: ''' :param bar: @@ -19347,7 +19593,7 @@ exports[`Generated code for "jsii-calc": /python/src/js } if foo is not None: self._values["foo"] = foo -@@ -7820,10 +8508,13 @@ +@@ -7821,10 +8509,13 @@ def __init__(self, delegate: typing.Mapping[builtins.str, typing.Any]) -> None: ''' @@ -19361,7 +19607,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.python.classproperty @jsii.member(jsii_name="reflector") def REFLECTOR( -@@ -7868,10 +8559,13 @@ +@@ -7869,10 +8560,13 @@ ): def __init__(self, obj: "IInterfaceWithProperties") -> None: ''' @@ -19375,7 +19621,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="justRead") def just_read(self) -> builtins.str: return typing.cast(builtins.str, jsii.invoke(self, "justRead", [])) -@@ -7882,17 +8576,23 @@ +@@ -7883,17 +8577,23 @@ ext: "IInterfaceWithPropertiesExtension", ) -> builtins.str: ''' @@ -19399,7 +19645,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @builtins.property @jsii.member(jsii_name="obj") def obj(self) -> "IInterfaceWithProperties": -@@ -7902,25 +8602,34 @@ +@@ -7903,25 +8603,34 @@ class VariadicInvoker(metaclass=jsii.JSIIMeta, jsii_type="jsii-calc.VariadicInvoker"): def __init__(self, method: "VariadicMethod") -> None: ''' @@ -19434,7 +19680,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="asArray") def as_array( self, -@@ -7929,10 +8638,14 @@ +@@ -7930,10 +8639,14 @@ ) -> typing.List[jsii.Number]: ''' :param first: the first element of the array to be returned (after the \`\`prefix\`\` provided at construction time). @@ -19449,7 +19695,7 @@ exports[`Generated code for "jsii-calc": /python/src/js class VariadicTypeUnion( metaclass=jsii.JSIIMeta, -@@ -7940,19 +8653,25 @@ +@@ -7941,19 +8654,25 @@ ): def __init__(self, *union: typing.Union["StructA", "StructB"]) -> None: ''' @@ -19475,7 +19721,7 @@ exports[`Generated code for "jsii-calc": /python/src/js class VirtualMethodPlayground( metaclass=jsii.JSIIMeta, -@@ -7964,38 +8683,53 @@ +@@ -7965,38 +8684,53 @@ @jsii.member(jsii_name="overrideMeAsync") def override_me_async(self, index: jsii.Number) -> jsii.Number: ''' @@ -19529,7 +19775,7 @@ exports[`Generated code for "jsii-calc": /python/src/js class VoidCallback( metaclass=jsii.JSIIAbstractClass, -@@ -8057,10 +8791,13 @@ +@@ -8058,10 +8792,13 @@ ''' return typing.cast(typing.Optional[builtins.str], jsii.get(self, "dontReadMe")) @@ -19543,7 +19789,7 @@ exports[`Generated code for "jsii-calc": /python/src/js class WithPrivatePropertyInConstructor( metaclass=jsii.JSIIMeta, -@@ -8070,10 +8807,13 @@ +@@ -8071,10 +8808,13 @@ def __init__(self, private_field: typing.Optional[builtins.str] = None) -> None: ''' @@ -19557,7 +19803,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @builtins.property @jsii.member(jsii_name="success") def success(self) -> builtins.bool: -@@ -8114,10 +8854,13 @@ +@@ -8115,10 +8855,13 @@ @jsii.member(jsii_name="abstractMethod") def abstract_method(self, name: builtins.str) -> builtins.str: ''' @@ -19571,7 +19817,7 @@ exports[`Generated code for "jsii-calc": /python/src/js # Adding a "__jsii_proxy_class__(): typing.Type" function to the abstract class typing.cast(typing.Any, AbstractClass).__jsii_proxy_class__ = lambda : _AbstractClassProxy -@@ -8133,10 +8876,14 @@ +@@ -8134,10 +8877,14 @@ '''Creates a BinaryOperation. :param lhs: Left-hand side operand. @@ -19586,7 +19832,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="toString") def to_string(self) -> builtins.str: '''String representation of the value.''' -@@ -8180,10 +8927,13 @@ +@@ -8181,10 +8928,13 @@ def rung(self) -> builtins.bool: return typing.cast(builtins.bool, jsii.get(self, "rung")) @@ -19600,7 +19846,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.data_type( jsii_type="jsii-calc.ChildStruct982", -@@ -8194,10 +8944,14 @@ +@@ -8195,10 +8945,14 @@ def __init__(self, *, foo: builtins.str, bar: jsii.Number) -> None: ''' :param foo: @@ -19615,7 +19861,7 @@ exports[`Generated code for "jsii-calc": /python/src/js "bar": bar, } -@@ -8238,37 +8992,49 @@ +@@ -8239,37 +8993,49 @@ def a(self) -> builtins.str: return typing.cast(builtins.str, jsii.get(self, "a")) @@ -19665,7 +19911,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.implements(INonInternalInterface) class ClassThatImplementsThePrivateInterface( -@@ -8283,37 +9049,49 @@ +@@ -8284,37 +9050,49 @@ def a(self) -> builtins.str: return typing.cast(builtins.str, jsii.get(self, "a")) @@ -19715,7 +19961,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.implements(IInterfaceWithProperties) class ClassWithPrivateConstructorAndAutomaticProperties( -@@ -8331,10 +9109,14 @@ +@@ -8332,10 +9110,14 @@ ) -> "ClassWithPrivateConstructorAndAutomaticProperties": ''' :param read_only_string: - @@ -19730,7 +19976,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @builtins.property @jsii.member(jsii_name="readOnlyString") def read_only_string(self) -> builtins.str: -@@ -8345,10 +9127,13 @@ +@@ -8346,10 +9128,13 @@ def read_write_string(self) -> builtins.str: return typing.cast(builtins.str, jsii.get(self, "readWriteString")) @@ -19744,7 +19990,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.implements(IIndirectlyImplemented) class FullCombo(BaseClass, metaclass=jsii.JSIIMeta, jsii_type="jsii-calc.FullCombo"): -@@ -8463,10 +9248,13 @@ +@@ -8464,10 +9249,13 @@ ): def __init__(self, property: builtins.str) -> None: ''' @@ -19758,7 +20004,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="bar") def bar(self) -> None: return typing.cast(None, jsii.invoke(self, "bar", [])) -@@ -8487,10 +9275,13 @@ +@@ -8488,10 +9276,13 @@ def __init__(self, operand: "_scope_jsii_calc_lib_c61f082f.NumericValue") -> None: ''' @@ -19772,7 +20018,7 @@ exports[`Generated code for "jsii-calc": /python/src/js @jsii.member(jsii_name="farewell") def farewell(self) -> builtins.str: '''Say farewell.''' -@@ -8555,10 +9346,16 @@ +@@ -8556,10 +9347,16 @@ :param id: some identifier. :param default_bar: the default value of \`\`bar\`\`. :param props: some props once can provide. @@ -19789,11 +20035,11 @@ exports[`Generated code for "jsii-calc": /python/src/js @builtins.property @jsii.member(jsii_name="id") def id(self) -> jsii.Number: -@@ -8870,7 +9667,1573 @@ - from . import pascal_case_name - from . import python_self - from . import submodule - from . import union +@@ -8915,7 +9712,1573 @@ + + import sys as _sys + setattr(_sys.modules[__name__], "__getattr__", __getattr__) + setattr(_sys.modules[__name__], "__dir__", __dir__) +def _typecheckingstub__8348af6419fc01178f78ba59cea59d0c7437626169866d772f4e957d09e6e13a( + seed: builtins.str, @@ -21435,7 +21681,7 @@ exports[`Generated code for "jsii-calc": /python/src/js exports[`Generated code for "jsii-calc": /python/src/jsii_calc/cdk16625/__init__.py.diff 1`] = ` --- python/src/jsii_calc/cdk16625/__init__.py --no-runtime-type-checking +++ python/src/jsii_calc/cdk16625/__init__.py --runtime-type-checking -@@ -60,10 +60,13 @@ +@@ -61,10 +61,13 @@ def _unwrap(self, gen: "_IRandomNumberGenerator_9643a8b9") -> jsii.Number: '''Implement this functin to return \`\`gen.next()\`\`. It is extremely important that the \`\`donotimport\`\` submodule is NEVER explicitly loaded in the testing application (otherwise this test is void). @@ -21449,12 +21695,12 @@ exports[`Generated code for "jsii-calc": /python/src/js # Adding a "__jsii_proxy_class__(): typing.Type" function to the abstract class typing.cast(typing.Any, Cdk16625).__jsii_proxy_class__ = lambda : _Cdk16625Proxy -@@ -75,5 +78,11 @@ +@@ -96,5 +99,11 @@ + return [*__all__, *_SUBMODULES] - publication.publish() - - # Loading modules to ensure their types are registered with the jsii runtime library - from . import donotimport + import sys as _sys + setattr(_sys.modules[__name__], "__getattr__", __getattr__) + setattr(_sys.modules[__name__], "__dir__", __dir__) + +def _typecheckingstub__92f621cedc18f68d281c38191023e89bba6348836db623bc5d780630324b992b( + gen: _IRandomNumberGenerator_9643a8b9, @@ -22450,7 +22696,7 @@ exports[`Generated code for "jsii-calc": /python/src/js exports[`Generated code for "jsii-calc": /python/src/jsii_calc/submodule/__init__.py.diff 1`] = ` --- python/src/jsii_calc/submodule/__init__.py --no-runtime-type-checking +++ python/src/jsii_calc/submodule/__init__.py --runtime-type-checking -@@ -57,10 +57,13 @@ +@@ -58,10 +58,13 @@ :param foo: @@ -22464,7 +22710,7 @@ exports[`Generated code for "jsii-calc": /python/src/js } @builtins.property -@@ -125,10 +128,13 @@ +@@ -126,10 +129,13 @@ def all_types(self) -> typing.Optional["_AllTypes_b08307c5"]: return typing.cast(typing.Optional["_AllTypes_b08307c5"], jsii.get(self, "allTypes")) @@ -22478,12 +22724,12 @@ exports[`Generated code for "jsii-calc": /python/src/js __all__ = [ "Default", -@@ -148,5 +154,18 @@ - from . import child - from . import isolated - from . import nested_submodule - from . import param - from . import returnsparam +@@ -174,5 +180,18 @@ + return [*__all__, *_SUBMODULES] + + import sys as _sys + setattr(_sys.modules[__name__], "__getattr__", __getattr__) + setattr(_sys.modules[__name__], "__dir__", __dir__) + +def _typecheckingstub__a44c39bd2002b354344d30dcb1ae0e2dc7ef8f604c2d31c61616cbbfc6a6fc40( + *, diff --git a/packages/jsii-pacmak/test/targets/python/lazy-imports.test.ts b/packages/jsii-pacmak/test/targets/python/lazy-imports.test.ts new file mode 100644 index 0000000000..cd1fab652d --- /dev/null +++ b/packages/jsii-pacmak/test/targets/python/lazy-imports.test.ts @@ -0,0 +1,193 @@ +import * as fs from 'fs-extra'; +import * as os from 'os'; +import * as path from 'path'; + +import { pacmak, TargetName } from '../../../lib'; + +/** + * Tests for the Python lazy imports feature. + * + * These tests verify that the code generator correctly emits PEP 562 + * lazy loading code (`_SUBMODULES`, `__getattr__`, `__dir__`) instead of + * eager `from . import ` statements. + */ + +let outDir: string; + +beforeEach(() => { + outDir = fs.mkdtempSync(path.join(os.tmpdir(), 'lazy-imports-test-')); +}); + +afterEach(() => { + fs.removeSync(outDir); +}); + +/** + * Helper to generate Python code for a given fixture package and return + * the content of a specific __init__.py file. + */ +async function generateAndRead(pkg: string, initPath: string): Promise { + const pkgRoot = path.resolve(__dirname, '..', '..', '..', '..', pkg); + await pacmak({ + codeOnly: true, + fingerprint: false, + inputDirectories: [pkgRoot], + outputDirectory: outDir, + runtimeTypeChecking: false, + targets: [TargetName.PYTHON], + }); + + const fullPath = path.join(outDir, 'python', 'src', initPath); + return fs.readFileSync(fullPath, 'utf-8'); +} + +describe('Python lazy imports code generation', () => { + // Use jsii-calc-lib which has submodules (custom_submodule_name, deprecation_removal) + const LIB_PKG = '@scope/jsii-calc-lib'; + const LIB_INIT = 'scope/jsii_calc_lib/__init__.py'; + + // Use jsii-calc which has many submodules + const CALC_PKG = 'jsii-calc'; + const CALC_INIT = 'jsii_calc/__init__.py'; + + // Use jsii-calc-base-of-base which has NO submodules + const BASE_OF_BASE_PKG = '@scope/jsii-calc-base-of-base'; + const BASE_OF_BASE_INIT = 'scope/jsii_calc_base_of_base/__init__.py'; + + test('module with submodules generates import importlib as _importlib', async () => { + const content = await generateAndRead(LIB_PKG, LIB_INIT); + expect(content).toContain('import importlib as _importlib'); + }); + + test('module with submodules generates _SUBMODULES set', async () => { + const content = await generateAndRead(LIB_PKG, LIB_INIT); + expect(content).toContain('_SUBMODULES = {'); + expect(content).toContain('"custom_submodule_name",'); + expect(content).toContain('"deprecation_removal",'); + }); + + test('module with submodules generates __getattr__ with importlib.import_module', async () => { + const content = await generateAndRead(LIB_PKG, LIB_INIT); + expect(content).toContain('def __getattr__(name: str) -> object:'); + expect(content).toContain('if name in _SUBMODULES:'); + expect(content).toContain( + 'mod = _importlib.import_module(f".{name}", __name__)', + ); + expect(content).toContain('globals()[name] = mod'); + expect(content).toContain('return mod'); + }); + + test('module with submodules generates __getattr__ that raises AttributeError', async () => { + const content = await generateAndRead(LIB_PKG, LIB_INIT); + expect(content).toContain( + 'raise AttributeError(f"module {__name__!r} has no attribute {name!r}")', + ); + }); + + test('module with submodules generates __dir__ function', async () => { + const content = await generateAndRead(LIB_PKG, LIB_INIT); + expect(content).toContain('def __dir__() -> "list[str]":'); + expect(content).toContain('return [*__all__, *_SUBMODULES]'); + }); + + test('module with submodules does NOT generate eager from . import statements outside TYPE_CHECKING', async () => { + const content = await generateAndRead(LIB_PKG, LIB_INIT); + // Should not have eager imports outside of TYPE_CHECKING block + // The only "from . import" should be inside "if typing.TYPE_CHECKING:" + const lines = content.split('\n'); + let inTypeChecking = false; + for (const line of lines) { + if (line.includes('if typing.TYPE_CHECKING:')) { + inTypeChecking = true; + continue; + } + // TYPE_CHECKING block ends at next non-indented line + if (inTypeChecking && !line.startsWith(' ') && line.trim() !== '') { + inTypeChecking = false; + } + if (!inTypeChecking) { + expect(line).not.toMatch( + /^\s*from \. import (custom_submodule_name|deprecation_removal)/, + ); + } + } + expect(content).not.toContain( + '# Loading modules to ensure their types are registered', + ); + }); + + test('module with submodules generates TYPE_CHECKING imports for pyright compatibility', async () => { + const content = await generateAndRead(LIB_PKG, LIB_INIT); + expect(content).toContain('if typing.TYPE_CHECKING:'); + expect(content).toContain( + 'from . import custom_submodule_name as custom_submodule_name', + ); + expect(content).toContain( + 'from . import deprecation_removal as deprecation_removal', + ); + }); + + test('module with zero submodules does NOT generate lazy loading code', async () => { + const content = await generateAndRead(BASE_OF_BASE_PKG, BASE_OF_BASE_INIT); + expect(content).not.toContain('import importlib as _importlib'); + expect(content).not.toContain('_SUBMODULES'); + expect(content).not.toContain('def __getattr__'); + expect(content).not.toContain('def __dir__'); + }); + + test('publication.publish() appears before the lazy loading block', async () => { + const content = await generateAndRead(LIB_PKG, LIB_INIT); + const publishIdx = content.indexOf('publication.publish()'); + const submodulesIdx = content.indexOf('_SUBMODULES = {'); + expect(publishIdx).toBeGreaterThan(-1); + expect(submodulesIdx).toBeGreaterThan(-1); + expect(publishIdx).toBeLessThan(submodulesIdx); + }); + + test('submodule names are included in __all__', async () => { + const content = await generateAndRead(LIB_PKG, LIB_INIT); + // __all__ should contain the submodule names + const allMatch = content.match(/__all__ = \[([\s\S]*?)\]/); + expect(allMatch).not.toBeNull(); + const allContent = allMatch![1]; + expect(allContent).toContain('"custom_submodule_name"'); + expect(allContent).toContain('"deprecation_removal"'); + }); + + test('_SUBMODULES set entries are sorted', async () => { + const content = await generateAndRead(CALC_PKG, CALC_INIT); + const submodulesMatch = content.match(/_SUBMODULES = \{([\s\S]*?)\}/); + expect(submodulesMatch).not.toBeNull(); + const entries = submodulesMatch![1] + .split('\n') + .map((l) => l.trim()) + .filter((l) => l.startsWith('"')) + .map((l) => l.replace(/[",]/g, '')); + // Verify sorted order + const sorted = [...entries].sort(); + expect(entries).toEqual(sorted); + }); + + test('non-assembly module preserves from ._jsii import * statement', async () => { + const content = await generateAndRead(LIB_PKG, LIB_INIT); + expect(content).toMatch(/from \.+_jsii import \*/); + }); + + test('jsii-calc root module with many submodules generates correct lazy loading', async () => { + const content = await generateAndRead(CALC_PKG, CALC_INIT); + // Should have lazy loading + expect(content).toContain('import importlib as _importlib'); + expect(content).toContain('_SUBMODULES = {'); + expect(content).toContain('def __getattr__(name: str) -> object:'); + expect(content).toContain('def __dir__() -> "list[str]":'); + // Should have TYPE_CHECKING imports + expect(content).toContain('if typing.TYPE_CHECKING:'); + // Should NOT have eager imports outside TYPE_CHECKING + // (the only "from . import X" lines should be inside the TYPE_CHECKING block) + const outsideTypeChecking = content.split('if typing.TYPE_CHECKING:')[0]; + expect(outsideTypeChecking).not.toMatch(/^from \. import \w+/m); + // Verify some known submodules are in _SUBMODULES + expect(content).toContain('"composition"'); + expect(content).toContain('"submodule"'); + }); +});