Skip to content

Commit 20639f2

Browse files
authored
fix: python generation to follow more PEP8 (#2280)
1 parent 6c78922 commit 20639f2

File tree

19 files changed

+134
-46
lines changed

19 files changed

+134
-46
lines changed

examples/generate-python-complete-models/__snapshots__/index.spec.ts.snap

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,13 @@ exports[`Should be able to render python models and should generate \`implicit\`
6262
Array [
6363
"from __future__ import annotations
6464
from typing import Any, Dict
65-
from . import ObjProperty
65+
from modelina.obj_property import ObjProperty
6666
class Root:
6767
def __init__(self, input: Dict):
6868
if 'email' in input:
6969
self._email: str = input['email']
7070
if 'obj_property' in input:
71-
self._obj_property: ObjProperty.ObjProperty = ObjProperty.ObjProperty(input['obj_property'])
71+
self._obj_property: ObjProperty = ObjProperty(input['obj_property'])
7272
7373
@property
7474
def email(self) -> str:
@@ -78,10 +78,10 @@ class Root:
7878
self._email = email
7979
8080
@property
81-
def obj_property(self) -> ObjProperty.ObjProperty:
81+
def obj_property(self) -> ObjProperty:
8282
return self._obj_property
8383
@obj_property.setter
84-
def obj_property(self, obj_property: ObjProperty.ObjProperty):
84+
def obj_property(self, obj_property: ObjProperty):
8585
self._obj_property = obj_property
8686
",
8787
]
@@ -91,13 +91,13 @@ exports[`Should be able to render python models and should generate \`implicit\`
9191
Array [
9292
"from __future__ import annotations
9393
from typing import Any, Dict
94-
from . import ObjProperty
94+
from modelina.obj_property import ObjProperty
9595
class Root:
9696
def __init__(self, input: Dict):
9797
if 'email' in input:
9898
self._email: str = input['email']
9999
if 'obj_property' in input:
100-
self._obj_property: ObjProperty.ObjProperty = ObjProperty.ObjProperty(input['obj_property'])
100+
self._obj_property: ObjProperty = ObjProperty(input['obj_property'])
101101
102102
@property
103103
def email(self) -> str:
@@ -107,10 +107,10 @@ class Root:
107107
self._email = email
108108
109109
@property
110-
def obj_property(self) -> ObjProperty.ObjProperty:
110+
def obj_property(self) -> ObjProperty:
111111
return self._obj_property
112112
@obj_property.setter
113-
def obj_property(self, obj_property: ObjProperty.ObjProperty):
113+
def obj_property(self, obj_property: ObjProperty):
114114
self._obj_property = obj_property
115115
",
116116
]

examples/generate-python-complete-models/index.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,17 @@ export async function generate(): Promise<void> {
2424
const generator = new PythonGenerator();
2525

2626
let models = await generator.generateCompleteModels(jsonSchemaDraft7, {
27-
importsStyle: 'implicit'
27+
importsStyle: 'implicit',
28+
packageName: 'modelina'
2829
});
2930

3031
for (const model of models) {
3132
console.log(model.result);
3233
}
3334

3435
models = await generator.generateCompleteModels(jsonSchemaDraft7, {
35-
importsStyle: 'explicit'
36+
importsStyle: 'explicit',
37+
packageName: 'modelina'
3638
});
3739

3840
for (const model of models) {

examples/generate-python-pydantic-models/__snapshots__/index.spec.ts.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Array [
66
optional_field: Optional[str] = Field(description='''this field is optional''', default=None)
77
required_field: str = Field(description='''this field is required''')
88
no_description: Optional[str] = Field(default=None)
9-
options: Optional[Options.Options] = Field(default=None)
9+
options: Optional[Options] = Field(default=None)
1010
content_type: Optional[str] = Field(default=None)
1111
",
1212
]

modelina-cli/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ FLAGS
434434
--javaJackson Java specific, generate the models with Jackson serialization support
435435
--namespace=<value> C#, C++ and PHP specific, define the namespace to use for the generated models. This
436436
is required when language is `csharp`,`c++` or `php`.
437-
--packageName=<value> Go, Java and Kotlin specific, define the package to use for the generated models. This
437+
--packageName=<value> Go, Java, Python and Kotlin specific, define the package to use for the generated models. This
438438
is required when language is `go`, `java` or `kotlin`.
439439
--pyDantic Python specific, generate the Pydantic models.
440440
--tsEnumType=<option> [default: enum] TypeScript specific, define which type of enums needs to be generated.

modelina-cli/src/helpers/python.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,13 @@ export const PythonOclifFlags = {
1818
* @returns
1919
*/
2020
export function buildPythonGenerator(flags: any): BuilderReturnType {
21-
const {pyDantic} = flags;
22-
const presets = [];
21+
const {packageName, pyDantic} = flags;
22+
23+
if (packageName === undefined) {
24+
throw new Error('In order to generate models to Python, we need to know which package they are under. Add `--packageName=PACKAGENAME` to set the desired package name.');
25+
}
26+
27+
const presets = [];
2328
if (pyDantic) {
2429
presets.push(PYTHON_PYDANTIC_PRESET);
2530
}
@@ -30,4 +35,4 @@ export function buildPythonGenerator(flags: any): BuilderReturnType {
3035
fileOptions,
3136
fileGenerator
3237
};
33-
}
38+
}

modelina-cli/test/integration/generate.test.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ describe('models', () => {
120120
test
121121
.stderr()
122122
.stdout()
123-
.command([...generalOptions, 'python', ASYNCAPI_V2_DOCUMENT, `-o=${ path.resolve(outputDir, './python')}`])
123+
.command([...generalOptions, 'python', ASYNCAPI_V2_DOCUMENT, `-o=${ path.resolve(outputDir, './python')}`, '--packageName', 'test'])
124124
.it('works when file path is passed', (ctx, done) => {
125125
expect(ctx.stdout).to.contain(
126126
'Successfully generated the following models: '
@@ -130,13 +130,21 @@ describe('models', () => {
130130
test
131131
.stderr()
132132
.stdout()
133-
.command([...generalOptions, 'python', ASYNCAPI_V2_DOCUMENT, '--pyDantic'])
133+
.command([...generalOptions, 'python', ASYNCAPI_V2_DOCUMENT, '--pyDantic', '--packageName', 'test'])
134134
.it('works when --pyDantic is set', (ctx, done) => {
135135
expect(ctx.stdout).to.contain(
136136
'Successfully generated the following models: '
137137
);
138138
done();
139139
});
140+
test
141+
.stderr()
142+
.stdout()
143+
.command([...generalOptions, 'python', ASYNCAPI_V2_DOCUMENT, `-o=${ path.resolve(outputDir, './python')}`])
144+
.it('fails when no package defined', (ctx, done) => {
145+
expect(ctx.stderr).to.contain('Error: In order to generate models to Python, we need to know which package they are under. Add `--packageName=PACKAGENAME` to set the desired package name.\n');
146+
done();
147+
});
140148
});
141149

142150
describe('for Rust', () => {
Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,65 @@
1-
import React from 'react';
1+
import { debounce } from 'lodash';
2+
import React, { useCallback, useContext, useEffect, useState } from 'react';
3+
4+
import { PlaygroundPythonConfigContext } from '@/components/contexts/PlaygroundConfigContext';
5+
import InfoModal from '@/components/InfoModal';
26

37
interface PythonGeneratorOptionsProps {
48
setNewConfig?: (queryKey: string, queryValue: any) => void;
59
}
610

7-
interface PythonGeneratorState {}
11+
interface PythonGeneratorState {
12+
packageName?: string;
13+
}
814

915
export const defaultState: PythonGeneratorState = {};
1016

11-
const PythonGeneratorOptions: React.FC<PythonGeneratorOptionsProps> = () => (
17+
const PythonGeneratorOptions: React.FC<PythonGeneratorOptionsProps> = ({ setNewConfig }) => {
18+
const context = useContext(PlaygroundPythonConfigContext);
19+
const [state, setState] = useState<PythonGeneratorState>(defaultState);
20+
21+
const debouncedSetNewConfig = debounce(
22+
(queryKey: string, queryValue: any) => setNewConfig?.(queryKey, queryValue),
23+
500
24+
);
25+
26+
useEffect(() => {
27+
setState({ ...state, packageName: context?.pythonPackageName });
28+
}, [context?.pythonPackageName]);
29+
30+
const onChangePackageName = useCallback(
31+
(event: React.ChangeEvent<HTMLInputElement>) => {
32+
const packageName = event.target.value;
33+
34+
setState({ ...state, packageName });
35+
setNewConfig && debouncedSetNewConfig('pythonPackageName', packageName);
36+
},
37+
[setNewConfig, debouncedSetNewConfig, state]
38+
);
39+
40+
return (
1241
<ul className='flex flex-col'>
1342
<h3 className='w-full border-b border-gray-700 py-2 text-left'>Python Specific options</h3>
14-
<span className='mt-1 max-w-2xl text-sm text-gray-500'>Currently no options are available</span>
43+
<li className='flex items-center gap-1'>
44+
<InfoModal text='Package Name :'>
45+
<p>
46+
In Python, a package name is used to organize code into logical groups or containers. It serves as a namespace
47+
for the code elements within it and helps in avoiding naming conflicts.
48+
</p>
49+
</InfoModal>
50+
<label className='flex grow cursor-pointer items-center justify-between gap-1 py-2'>
51+
<span className='mt-1 max-w-2xl text-sm text-gray-500'>Package Name</span>
52+
<input
53+
type='text'
54+
className='text-md form-input w-[90%] cursor-pointer rounded-md border-gray-300 font-regular text-gray-700 focus-within:text-gray-900 hover:bg-gray-50'
55+
name='pythonPackageName'
56+
value={state?.packageName}
57+
onChange={onChangePackageName}
58+
/>
59+
</label>
60+
</li>
1561
</ul>
1662
);
63+
};
1764

1865
export default PythonGeneratorOptions;

modelina-website/src/pages/api/functions/PythonGenerator.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ export async function getPythonModels(input: any, generatorOptions: ModelinaPyth
2727

2828
try {
2929
const generator = new PythonGenerator(options);
30-
const generatedModels = await generator.generateCompleteModels(input, {});
30+
const generatedModels = await generator.generateCompleteModels(input, {
31+
packageName: generatorOptions.pythonPackageName || 'asyncapimodels'
32+
});
3133

3234
return convertModelsToProps(generatedModels);
3335
} catch (e: any) {

modelina-website/src/types/index.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,9 @@ export interface ModelinaScalaOptions extends ModelinaGeneralOptions {
9595
scalaCollectionType: 'List' | 'Array' | undefined;
9696
scalaPackageName?: string;
9797
}
98-
export interface ModelinaPythonOptions extends ModelinaGeneralOptions {}
98+
export interface ModelinaPythonOptions extends ModelinaGeneralOptions {
99+
pythonPackageName?: string;
100+
}
99101
export interface ModelinaDartOptions extends ModelinaGeneralOptions {}
100102

101103
export interface ModelinaGeneralQueryOptions {
@@ -140,7 +142,9 @@ export interface ModelinaScalaQueryOptions {
140142
scalaCollectionType?: string;
141143
scalaPackageName?: string;
142144
}
143-
export interface ModelinaPythonQueryOptions {}
145+
export interface ModelinaPythonQueryOptions {
146+
pythonPackageName?: string;
147+
}
144148
export interface ModelinaCplusplusQueryOptions {
145149
cplusplusNamespace?: string;
146150
}

src/generators/python/PythonConstrainer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export const PythonDefaultTypeMapping: PythonTypeMapping = {
1414
return constrainedModel.name;
1515
},
1616
Reference({ constrainedModel }): string {
17-
return `${constrainedModel.name}.${constrainedModel.name}`;
17+
return `${constrainedModel.name}`;
1818
},
1919
Any({ dependencyManager }): string {
2020
dependencyManager.addDependency('from typing import Any');

0 commit comments

Comments
 (0)