[python] write back the fully qualified apiPackage/modelPackage
Fixes #3285.
With --api-package my_api and packageName=my_pkg, the generated python client cannot be imported: my_pkg/__init__.py contains
from my_api.pet_api import PetApi as PetApi
instead of
from my_pkg.my_api.pet_api import PetApi as PetApi
The api modules themselves are written to the correct my_pkg/my_api/ folder, and model imports are fine, only the api imports lose the package prefix, so importing the generated package raises ModuleNotFoundError.
Cause :
PythonClientCodegen.processOpts() ends with
modelPackage = this.packageName + "." + modelPackage;
apiPackage = this.packageName + "." + apiPackage;
but leaves the template-facing copies in additionalProperties untouched. When DefaultGenerator builds the api template bundle it puts the derived values first and then applies additionalProperties on top of them:
operation.put("apiPackage", config.apiPackage());
operation.put("modelPackage", config.modelPackage());
operation.putAll(config.additionalProperties());
so the raw user-supplied apiPackage shadows the prefixed one in any template that resolves {{apiPackage}} while iterating {{#apiInfo}}{{#apis}}, which is what exports_api.mustache and exports_package.mustache do. The supporting-file bundle is unaffected because it applies additionalProperties before setting these keys.
Fix
Write the fully qualified names back to additionalProperties, which is what ~25 other generators already do (AbstractKotlinCodegen, ScalaAkkaClientCodegen, AspNetServerCodegen, ClojureClientCodegen, …).
The underlying cause is the ordering in DefaultGenerator; reordering it there removes the shadowing for every generator, and I verified that doing so also leaves all 798 sample configs byte-identical. I kept this PR scoped to the generator that actually produces broken output, since making additionalProperties lose to the config values is a wider behavioural decision. Happy to switch to the generic fix if you prefer it.
Tests
Added
PythonClientCodegenTest#testInitFileImportsExportsWithCustomApiPackage
, which fails on master with:
File '.../my_pkg/init.py' does not contain line
[from my_pkg.my_api.pet_api import PetApi as PetApi] expected [true] but found [false]
PR checklist
- [ X ] Read the contribution guidelines.
- [ X ] Run the following to build the project and update samples:
(For Windows users, please run the script in WSL)./mvnw clean package || exit ./bin/generate-samples.sh ./bin/configs/*.yaml || exit ./bin/utils/export_docs_generators.sh || exit
Commit all changed files.
This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
These must match the expectations made by your contribution.
You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example./bin/generate-samples.sh bin/configs/java*.
IMPORTANT: Do NOT purge/delete any folders/files (e.g. tests) when regenerating the samples as manually written tests may be removed. - [ X ] If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request.
@cbornet (2017/09) @tomplus (2018/10) @krjakbrjak (2023/02) @fa0311 (2023/10)
Summary by cubic
Fixes missing package prefixes in generated Python __init__.py imports when using packageName with a custom apiPackage. Old behavior: from my_api.pet_api import PetApi raised ModuleNotFoundError; new behavior: from my_pkg.my_api.pet_api import PetApi.
- Writes fully qualified
apiPackageandmodelPackageback intoadditionalPropertiesinPythonClientCodegen.processOptsso templates don’t shadow prefixed values. - Scoped to the Python generator; no change to
DefaultGeneratorordering. - Adds
PythonClientCodegenTest#testInitFileImportsExportsWithCustomApiPackage.