in packages/jsii-pacmak/lib/targets/python.ts [1693:1872]
public emit(code: CodeMaker, context: EmitContext) {
this.emitModuleDocumentation(code);
const resolver = this.fqn
? context.resolver.bind(this.fqn, this.pythonName)
: context.resolver;
context = {
...context,
submodule: this.fqn ?? context.submodule,
resolver,
};
// Before we write anything else, we need to write out our module headers, this
// is where we handle stuff like imports, any required initialization, etc.
// If multiple packages use the same namespace (in Python, a directory) it
// depends on how they are laid out on disk if deep imports of multiple packages
// will succeed. `pip` merges all packages into the same directory, and deep
// imports work automatically. `bazel` puts packages into different directories,
// and `import aws_cdk.subpackage` will fail if `aws_cdk/__init__.py` and
// `aws_cdk/subpackage/__init__.py` are not in the same directory.
//
// We can get around this by using `pkgutil` to extend the search path for the
// current module (`__path__`) with all packages found on `sys.path`.
code.line('from pkgutil import extend_path');
code.line('__path__ = extend_path(__path__, __name__)');
code.line();
code.line('import abc');
code.line('import builtins');
code.line('import datetime');
code.line('import enum');
code.line('import typing');
code.line();
code.line('import jsii');
code.line('import publication');
code.line('import typing_extensions');
code.line();
code.line('import typeguard');
code.line(
'from importlib.metadata import version as _metadata_package_version',
);
code.line(
"TYPEGUARD_MAJOR_VERSION = int(_metadata_package_version('typeguard').split('.')[0])",
);
code.line();
code.openBlock(
'def check_type(argname: str, value: object, expected_type: typing.Any) -> typing.Any',
);
code.openBlock('if TYPEGUARD_MAJOR_VERSION <= 2');
code.line(
'return typeguard.check_type(argname=argname, value=value, expected_type=expected_type) # type:ignore',
);
code.closeBlock();
code.openBlock('else');
code.line(
'if isinstance(value, jsii._reference_map.InterfaceDynamicProxy): # pyright: ignore [reportAttributeAccessIssue]',
);
code.line(' pass');
code.openBlock('else');
code.openBlock('if TYPEGUARD_MAJOR_VERSION == 3');
code.line(
'typeguard.config.collection_check_strategy = typeguard.CollectionCheckStrategy.ALL_ITEMS # type:ignore',
);
code.line(
'typeguard.check_type(value=value, expected_type=expected_type) # type:ignore',
);
code.closeBlock();
code.openBlock('else');
code.line(
'typeguard.check_type(value=value, expected_type=expected_type, collection_check_strategy=typeguard.CollectionCheckStrategy.ALL_ITEMS) # type:ignore',
);
code.closeBlock();
code.closeBlock();
code.closeBlock();
code.closeBlock();
// Determine if we need to write out the kernel load line.
if (this.loadAssembly) {
this.emitDependencyImports(code);
code.line();
emitList(
code,
'__jsii_assembly__ = jsii.JSIIAssembly.load(',
[
JSON.stringify(this.assembly.name),
JSON.stringify(this.assembly.version),
'__name__[0:-6]',
`${JSON.stringify(this.assemblyFilename)}`,
],
')',
);
} else {
// Then we must import the ._jsii subpackage.
code.line();
let distanceFromRoot = 0;
for (
let curr = this.fqn!;
curr !== this.assembly.name;
curr = curr.substring(0, curr.lastIndexOf('.'))
) {
distanceFromRoot++;
}
code.line(`from ${'.'.repeat(distanceFromRoot + 1)}_jsii import *`);
this.emitRequiredImports(code, context);
}
// Emit all of our members.
for (const member of prepareMembers(this.members, resolver)) {
code.line();
code.line();
member.emit(code, context);
}
// Whatever names we've exported, we'll write out our __all__ that lists them.
//
// __all__ is normally used for when users write `from library import *`, but we also
// use it with the `publication` module to hide everything that's NOT in the list.
//
// Normally adding submodules to `__all__` has the (negative?) side-effect
// that all submodules get loaded when the user does `import *`, but we
// already load submodules anyway so it doesn't make a difference, and in combination
// with the `publication` module NOT having them in this list hides any submodules
// we import as part of typechecking.
const exportedMembers = [
...this.members.map((m) => `"${m.pythonName}"`),
...this.modules
.filter((m) => this.isDirectChild(m))
.map((m) => `"${lastComponent(m.pythonName)}"`),
];
if (this.loadAssembly) {
exportedMembers.push('"__jsii_assembly__"');
}
// Declare the list of "public" members this module exports
if (this.members.length > 0) {
code.line();
}
code.line();
if (exportedMembers.length > 0) {
code.indent('__all__ = [');
for (const member of exportedMembers.sort()) {
// Writing one by line might be _a lot_ of lines, but it'll make reviewing changes to the list easier. Trust me.
code.line(`${member},`);
}
code.unindent(']');
} else {
code.line('__all__: typing.List[typing.Any] = []');
}
// 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
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,
);
code.line(`from . import ${submodule}`);
}
}
}