Skip to content

Commit 32edd13

Browse files
authored
Merge pull request #3510 from hey-api/fix/py-dsl-decorator
fix: correctly render decorators
2 parents 02e5991 + 9fb365c commit 32edd13

4 files changed

Lines changed: 39 additions & 41 deletions

File tree

packages/openapi-python/src/plugins/@hey-api/sdk/v1/node.ts

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -68,19 +68,17 @@ function childToNode(
6868
plugin.config.operations.methodName.casing ?? 'camelCase',
6969
);
7070
const memberName = plugin.symbol(memberNameStr);
71+
const cachedProp = plugin.external('functools.cached_property');
72+
7173
return [
72-
$.func(
73-
memberName,
74-
(f) => f.returns('None'),
75-
// f.returns(refChild).do(
76-
// $('this')
77-
// .attr(privateName)
78-
// .nullishAssign(
79-
// $.new(refChild).args($.object().prop('client', $('this').attr('client'))),
80-
// )
81-
// .return(),
82-
// ),
83-
),
74+
$.func(memberName)
75+
.decorator(cachedProp)
76+
.returns(refChild)
77+
.do(
78+
$(refChild)
79+
.call($.kwarg('client', $('self').attr('client')))
80+
.return(),
81+
),
8482
];
8583
}
8684

packages/openapi-python/src/plugins/@hey-api/sdk/v1/plugin.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ export const handlerV1: HeyApiSdkPlugin['Handler'] = ({ plugin }) => {
2020
},
2121
});
2222

23+
plugin.symbol('cached_property', {
24+
external: 'functools',
25+
meta: {
26+
category: 'external',
27+
resource: 'functools.cached_property',
28+
},
29+
});
30+
2331
const structure = new StructureModel();
2432
const shell = createShell(plugin);
2533
const strategy = resolveStrategy(plugin);

packages/openapi-python/src/py-dsl/decl/func.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export class FuncPyDsl extends Mixed {
1919
override readonly nameSanitizer = safeRuntimeName;
2020

2121
protected _parameters: Array<py.FunctionParameter> = [];
22-
protected _returnType?: py.Expression;
22+
protected _returnType?: NodeName | py.Expression;
2323

2424
constructor(name: NodeName, fn?: (f: FuncPyDsl) => void) {
2525
super();
@@ -56,7 +56,7 @@ export class FuncPyDsl extends Mixed {
5656
return this;
5757
}
5858

59-
returns(returnType: string | py.Expression): this {
59+
returns(returnType: NodeName | py.Expression): this {
6060
this._returnType =
6161
typeof returnType === 'string' ? py.factory.createIdentifier(returnType) : returnType;
6262
return this;

packages/openapi-python/src/py-dsl/mixins/decorator.ts

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,51 +4,43 @@ import { py } from '../../ts-python';
44
import type { MaybePyDsl } from '../base';
55
import type { BaseCtor, MixinCtor } from './types';
66

7+
type DecoratorInput = {
8+
args: ReadonlyArray<MaybePyDsl<py.Expression>>;
9+
name: NodeName | MaybePyDsl<py.Expression>;
10+
};
11+
712
export interface DecoratorMethods extends Node {
813
$decorators(): ReadonlyArray<py.Expression>;
9-
decorator(
10-
name: NodeName | MaybePyDsl<py.Expression>,
11-
...args: ReadonlyArray<MaybePyDsl<py.Expression>>
12-
): this;
14+
decorator(name: DecoratorInput['name'], ...args: DecoratorInput['args']): this;
1315
}
1416

1517
export function DecoratorMixin<T extends py.Node, TBase extends BaseCtor<T>>(Base: TBase) {
1618
abstract class Decorator extends Base {
17-
protected decorators: Array<py.Expression> = [];
19+
protected _decorators: Array<DecoratorInput> = [];
1820

1921
override analyze(ctx: AnalysisContext): void {
2022
super.analyze(ctx);
21-
for (const decorator of this.decorators) {
22-
ctx.analyze(decorator);
23+
for (const decorator of this._decorators) {
24+
ctx.analyze(decorator.name);
25+
for (const arg of decorator.args) {
26+
ctx.analyze(arg);
27+
}
2328
}
2429
}
2530

26-
protected decorator(
27-
name: NodeName | MaybePyDsl<py.Expression>,
28-
...args: ReadonlyArray<MaybePyDsl<py.Expression>>
29-
): this {
30-
const nameNode =
31-
typeof name === 'string' || isPyNode(name)
32-
? name
33-
: py.factory.createIdentifier(String(name));
34-
const decoratorExpr = args.length
35-
? py.factory.createCallExpression(
36-
nameNode as py.Expression,
37-
args.map((a) => this.$node(a)),
38-
)
39-
: (nameNode as py.Expression);
40-
this.decorators.push(decoratorExpr);
31+
protected decorator(name: DecoratorInput['name'], ...args: DecoratorInput['args']): this {
32+
this._decorators.push({ args, name });
4133
return this;
4234
}
4335

4436
protected $decorators(): ReadonlyArray<py.Expression> {
45-
return this.decorators;
37+
return this._decorators.map((decorator) =>
38+
decorator.args.length > 0
39+
? py.factory.createCallExpression(this.$node(decorator.name), this.$node(decorator.args))
40+
: this.$node(decorator.name),
41+
);
4642
}
4743
}
4844

4945
return Decorator as unknown as MixinCtor<TBase, DecoratorMethods>;
5046
}
51-
52-
function isPyNode(value: unknown): value is { toAst(): unknown } {
53-
return typeof value === 'object' && value !== null && 'toAst' in value;
54-
}

0 commit comments

Comments
 (0)