OpenAPITools / OpenAPITools/openapi-generator
[BUG] [Python] Cyclic Dependencies in Python Generator
Open
Nobody has claimed this yet.
Issue: Bug
Server: Python
- Dominant language
- Java
- Stars
- 26.8k
- Forks
- 7.7k
- PR merge metrics
- PR metrics pending
Description
Bug Report Checklist
- Have you provided a full/minimal spec to reproduce the issue?
- Have you validated the input using an OpenAPI validator (example)?
- What's the version of OpenAPI Generator used?
- Have you search for related issues/PRs?
- What's the actual output vs expected output?
Description
The generated models do not work in python because the import statement is build the wrong way.
To fully fix the issue you also need #2978.
openapi-generator version
4.0.0 stable
OpenAPI declaration file content or url
---
components:
schemas:
Group:
type: object
description: "A simple description this is a group"
properties:
groupId:
format: int32
type: integer
groupName:
type: string
groupDescription:
type: string
scope:
format: int32
type: integer
members:
type: array
items:
$ref: "#/components/schemas/User"
User:
type: object
description: "Simple Description this is a User"
properties:
userId:
format: int32
type: integer
userName:
type: string
firstName:
type: string
lastName:
type: string
email:
type: string
groups:
type: array
items:
$ref: "#/components/schemas/Group"
Command line used for generation
java -DsupportingFiles=util.py,__init__.py,base_model_.py,typing_utils.py,openapi.yaml,schemas.py -Dmodels -DskipFormModel=true -jar /usr/share/java/openapi-generator-cli-4.0.0.jar generate -i ../openapi-v3.yaml -g python-aiohttp -o ./out/python --skip-validate-spec -c ../config.json --model-package dataobjects
Steps to reproduce
- First build the objects.
- Create a Package from them.
- After trying to import either Group or Userm, it will fail because of the way python handles importing classes with the following syntax:
# change
from model_package.dataobjects.group import Group
# to
import model_package.dataobjects.group
# no more cyclic dependencies
Suggest a fix
@Override
public Map<String, Object> postProcessAllModels(Map<String, Object> objs) {
Map<String, Object> result = super.postProcessAllModels(objs);
for (Map.Entry<String, Object> entry : result.entrySet()) {
Map<String, Object> inner = (Map<String, Object>) entry.getValue();
List<Map<String, Object>> models = (List<Map<String, Object>>) inner.get("models");
for (Map<String, Object> mo : models) {
CodegenModel cm = (CodegenModel) mo.get("model");
// Add additional filename information for imports
mo.put("pyImports", toPyImports(cm, cm.imports));
}
}
return result;
}
@Override
public String toModelImport(String name) {
String modelImport;
if (StringUtils.startsWithAny(name, "import", "from")) {
modelImport = name;
} else {
modelImport = "import ";
if (!"".equals(modelPackage())) {
modelImport += modelPackage() + ".";
}
modelImport += toModelFilename(name);
}
return modelImport;
}
private List<Map<String, String>> toPyImports(CodegenModel cm, Set<String> imports) {
List<Map<String, String>> pyImports = new ArrayList<>();
for (String im : imports) {
if (!im.equals(cm.classname)) {
HashMap<String, String> pyImport = new HashMap<>();
pyImport.put("import", toModelImport(im));
pyImports.add(pyImport);
}
}
return pyImports;
}
@Override
public String getTypeDeclaration(Schema p) {
if (ModelUtils.isArraySchema(p)) {
ArraySchema ap = (ArraySchema) p;
Schema inner = ap.getItems();
return getSchemaType(p) + "[" + getTypeDeclaration(inner) + "]";
} else if (ModelUtils.isMapSchema(p)) {
Schema inner = ModelUtils.getAdditionalProperties(p);
return getSchemaType(p) + "[str, " + getTypeDeclaration(inner) + "]";
} else if (ModelUtils.isComposedSchema(p)) {
if (ModelUtils.getInterfaces((ComposedSchema)p) != null) {
// quickfix for oneOf in python
return "object";
}
}
String type = super.getTypeDeclaration(p);
if (languageSpecificPrimitives.contains(type)) {
return type;
}
return "locate(\'" + this.toModelFilename(type)+ "." + type + "\')";
}
# coding: utf-8
from datetime import date, datetime
from typing import List, Dict, Type
from {{modelPackage}}.base_model_ import Model
{{#models}}
{{#model}}
{{#pyImports}}
{{import}}
{{/pyImports}}
{{/model}}
{{/models}}
from {{packageName}} import util
from pydoc import locate
....
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the Python generator entry points postProcessAllModels, toModelImport, and getTypeDeclaration, then inspect the model-generation Mustache template shown in the issue. Reproduce the Group/User schema cycle with the provided command and compare the generated imports. Done means the generated models can be imported without the cyclic dependency failure, while also accounting for related issue #2978.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, openapi, python
- Domain
- backend-api-design, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100