@zzzen/pyright-internal 1.2.0-dev.20260705 → 1.2.0-dev.20260719
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analyzer/checker.d.ts +2 -0
- package/dist/analyzer/checker.js +89 -4
- package/dist/analyzer/checker.js.map +1 -1
- package/dist/analyzer/functionTransform.js +188 -0
- package/dist/analyzer/functionTransform.js.map +1 -1
- package/dist/analyzer/packageTypeVerifier.js +11 -1
- package/dist/analyzer/packageTypeVerifier.js.map +1 -1
- package/dist/analyzer/properties.js +73 -8
- package/dist/analyzer/properties.js.map +1 -1
- package/dist/analyzer/typeEvaluator.js +7 -1
- package/dist/analyzer/typeEvaluator.js.map +1 -1
- package/dist/analyzer/typeGuards.js +1 -1
- package/dist/analyzer/typeGuards.js.map +1 -1
- package/dist/analyzer/typeUtils.js.map +1 -1
- package/dist/analyzer/types.d.ts +1 -1
- package/dist/analyzer/types.js.map +1 -1
- package/dist/tests/typeEvaluator3.test.js +6 -0
- package/dist/tests/typeEvaluator3.test.js.map +1 -1
- package/dist/tests/typeEvaluator8.test.js +38 -0
- package/dist/tests/typeEvaluator8.test.js.map +1 -1
- package/dist/tests/typeServer/inProcTypeServerTestUtils.d.ts +39 -0
- package/dist/tests/typeServer/inProcTypeServerTestUtils.js +366 -0
- package/dist/tests/typeServer/inProcTypeServerTestUtils.js.map +1 -0
- package/dist/tests/typeServer/notebook.typeServer.test.d.ts +1 -0
- package/dist/tests/typeServer/notebook.typeServer.test.js +315 -0
- package/dist/tests/typeServer/notebook.typeServer.test.js.map +1 -0
- package/dist/tests/typeServer/typeServer.inProc.test.d.ts +1 -0
- package/dist/tests/typeServer/typeServer.inProc.test.js +234 -0
- package/dist/tests/typeServer/typeServer.inProc.test.js.map +1 -0
- package/dist/tests/typeServer/typeServer.virtualFileRedirect.test.d.ts +1 -0
- package/dist/tests/typeServer/typeServer.virtualFileRedirect.test.js +298 -0
- package/dist/tests/typeServer/typeServer.virtualFileRedirect.test.js.map +1 -0
- package/dist/typeServer/cancellation.d.ts +5 -0
- package/dist/typeServer/cancellation.js +14 -0
- package/dist/typeServer/cancellation.js.map +1 -0
- package/dist/typeServer/diagnosticUtils.d.ts +4 -0
- package/dist/typeServer/diagnosticUtils.js +71 -0
- package/dist/typeServer/diagnosticUtils.js.map +1 -0
- package/dist/typeServer/enums.d.ts +14 -0
- package/dist/typeServer/enums.js +524 -0
- package/dist/typeServer/enums.js.map +1 -0
- package/dist/typeServer/eventEmitter.d.ts +12 -0
- package/dist/typeServer/eventEmitter.js +32 -0
- package/dist/typeServer/eventEmitter.js.map +1 -0
- package/dist/typeServer/nodeMain.d.ts +1 -0
- package/dist/typeServer/nodeMain.js +65 -0
- package/dist/typeServer/nodeMain.js.map +1 -0
- package/dist/typeServer/notebookCellChain.d.ts +34 -0
- package/dist/typeServer/notebookCellChain.js +182 -0
- package/dist/typeServer/notebookCellChain.js.map +1 -0
- package/dist/typeServer/notebookDocumentHandler.d.ts +23 -0
- package/dist/typeServer/notebookDocumentHandler.js +158 -0
- package/dist/typeServer/notebookDocumentHandler.js.map +1 -0
- package/dist/typeServer/notebookUriMapper.d.ts +39 -0
- package/dist/typeServer/notebookUriMapper.js +164 -0
- package/dist/typeServer/notebookUriMapper.js.map +1 -0
- package/dist/typeServer/profilingStub.d.ts +10 -0
- package/dist/typeServer/profilingStub.js +12 -0
- package/dist/typeServer/profilingStub.js.map +1 -0
- package/dist/typeServer/programTypes.d.ts +136 -0
- package/dist/typeServer/programTypes.js +3 -0
- package/dist/typeServer/programTypes.js.map +1 -0
- package/dist/typeServer/programWrapper.d.ts +134 -0
- package/dist/typeServer/programWrapper.js +731 -0
- package/dist/typeServer/programWrapper.js.map +1 -0
- package/dist/typeServer/protocol/tspSupplemental.d.ts +43 -0
- package/dist/typeServer/protocol/tspSupplemental.js +48 -0
- package/dist/typeServer/protocol/tspSupplemental.js.map +1 -0
- package/dist/typeServer/protocol/typeServerProtocol.d.ts +1102 -0
- package/dist/typeServer/protocol/typeServerProtocol.js +290 -0
- package/dist/typeServer/protocol/typeServerProtocol.js.map +1 -0
- package/dist/typeServer/server.d.ts +54 -0
- package/dist/typeServer/server.js +620 -0
- package/dist/typeServer/server.js.map +1 -0
- package/dist/typeServer/serverUtils.d.ts +4 -0
- package/dist/typeServer/serverUtils.js +21 -0
- package/dist/typeServer/serverUtils.js.map +1 -0
- package/dist/typeServer/stubGenerator.d.ts +42 -0
- package/dist/typeServer/stubGenerator.js +895 -0
- package/dist/typeServer/stubGenerator.js.map +1 -0
- package/dist/typeServer/typeCache.d.ts +31 -0
- package/dist/typeServer/typeCache.js +54 -0
- package/dist/typeServer/typeCache.js.map +1 -0
- package/dist/typeServer/typeEvalUtils.d.ts +13 -0
- package/dist/typeServer/typeEvalUtils.js +83 -0
- package/dist/typeServer/typeEvalUtils.js.map +1 -0
- package/dist/typeServer/typeGuards.d.ts +3 -0
- package/dist/typeServer/typeGuards.js +55 -0
- package/dist/typeServer/typeGuards.js.map +1 -0
- package/dist/typeServer/typeServerConversionTypes.d.ts +39 -0
- package/dist/typeServer/typeServerConversionTypes.js +448 -0
- package/dist/typeServer/typeServerConversionTypes.js.map +1 -0
- package/dist/typeServer/typeServerConversionUtils.d.ts +35 -0
- package/dist/typeServer/typeServerConversionUtils.js +524 -0
- package/dist/typeServer/typeServerConversionUtils.js.map +1 -0
- package/dist/typeServer/typeServerEvaluator.d.ts +16 -0
- package/dist/typeServer/typeServerEvaluator.js +36 -0
- package/dist/typeServer/typeServerEvaluator.js.map +1 -0
- package/dist/typeServer/typeServerFileSystem.d.ts +47 -0
- package/dist/typeServer/typeServerFileSystem.js +129 -0
- package/dist/typeServer/typeServerFileSystem.js.map +1 -0
- package/dist/typeServer/typeServerProtocolUtils.d.ts +2 -0
- package/dist/typeServer/typeServerProtocolUtils.js +7 -0
- package/dist/typeServer/typeServerProtocolUtils.js.map +1 -0
- package/dist/typeServer/typeServerServiceKeys.d.ts +7 -0
- package/dist/typeServer/typeServerServiceKeys.js +20 -0
- package/dist/typeServer/typeServerServiceKeys.js.map +1 -0
- package/dist/typeServer/typeUtils.d.ts +4 -0
- package/dist/typeServer/typeUtils.js +31 -0
- package/dist/typeServer/typeUtils.js.map +1 -0
- package/dist/typeServer/virtualFileOverlayFileSystem.d.ts +87 -0
- package/dist/typeServer/virtualFileOverlayFileSystem.js +181 -0
- package/dist/typeServer/virtualFileOverlayFileSystem.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,895 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Copyright (c) Microsoft Corporation.
|
|
4
|
+
* Licensed under the MIT license.
|
|
5
|
+
*
|
|
6
|
+
* stubGenerator.ts
|
|
7
|
+
*
|
|
8
|
+
* Generates Python stub (.pyi) file content from Pyright function type information.
|
|
9
|
+
*/
|
|
10
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
13
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
14
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
15
|
+
}
|
|
16
|
+
Object.defineProperty(o, k2, desc);
|
|
17
|
+
}) : (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
o[k2] = m[k];
|
|
20
|
+
}));
|
|
21
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
22
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
23
|
+
}) : function(o, v) {
|
|
24
|
+
o["default"] = v;
|
|
25
|
+
});
|
|
26
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
27
|
+
var ownKeys = function(o) {
|
|
28
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
29
|
+
var ar = [];
|
|
30
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
31
|
+
return ar;
|
|
32
|
+
};
|
|
33
|
+
return ownKeys(o);
|
|
34
|
+
};
|
|
35
|
+
return function (mod) {
|
|
36
|
+
if (mod && mod.__esModule) return mod;
|
|
37
|
+
var result = {};
|
|
38
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
39
|
+
__setModuleDefault(result, mod);
|
|
40
|
+
return result;
|
|
41
|
+
};
|
|
42
|
+
})();
|
|
43
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
|
+
exports.generateStubFromTypeVar = generateStubFromTypeVar;
|
|
45
|
+
exports.generateStubFromFunctionType = generateStubFromFunctionType;
|
|
46
|
+
exports.generateStubFromClassType = generateStubFromClassType;
|
|
47
|
+
const typePrinter_1 = require("../analyzer/typePrinter");
|
|
48
|
+
const PyrightTypes = __importStar(require("../analyzer/types"));
|
|
49
|
+
const pythonVersion_1 = require("../common/pythonVersion");
|
|
50
|
+
function generateStubFromTypeVar(typeVar, options) {
|
|
51
|
+
const context = {
|
|
52
|
+
imports: { imports: new Map() },
|
|
53
|
+
pythonVersion: options.pythonVersion,
|
|
54
|
+
moduleImports: new Map(),
|
|
55
|
+
};
|
|
56
|
+
const typeVarName = typeVar.shared.name || 'T';
|
|
57
|
+
let initializer = '';
|
|
58
|
+
if (PyrightTypes.isParamSpec(typeVar)) {
|
|
59
|
+
addImport(context, getTypingModule(context.pythonVersion, 'ParamSpec'), 'ParamSpec');
|
|
60
|
+
initializer = `ParamSpec('${typeVarName}')`;
|
|
61
|
+
}
|
|
62
|
+
else if (PyrightTypes.isTypeVarTuple(typeVar)) {
|
|
63
|
+
addImport(context, getTypingModule(context.pythonVersion, 'TypeVarTuple'), 'TypeVarTuple');
|
|
64
|
+
initializer = `TypeVarTuple('${typeVarName}')`;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
addImport(context, 'typing', 'TypeVar');
|
|
68
|
+
const args = [`'${typeVarName}'`];
|
|
69
|
+
if (typeVar.shared.constraints?.length) {
|
|
70
|
+
const constraintStrings = typeVar.shared.constraints.map((constraint) => pyrightTypeToString(constraint, context));
|
|
71
|
+
args.push(...constraintStrings);
|
|
72
|
+
}
|
|
73
|
+
if (typeVar.shared.boundType) {
|
|
74
|
+
const boundString = pyrightTypeToString(typeVar.shared.boundType, context);
|
|
75
|
+
args.push(`bound=${boundString}`);
|
|
76
|
+
}
|
|
77
|
+
if (typeVar.shared.defaultType && typeVar.shared.isDefaultExplicit) {
|
|
78
|
+
const defaultString = pyrightTypeToString(typeVar.shared.defaultType, context);
|
|
79
|
+
args.push(`default=${defaultString}`);
|
|
80
|
+
}
|
|
81
|
+
initializer = `TypeVar(${args.join(', ')})`;
|
|
82
|
+
}
|
|
83
|
+
const declarationLine = `${typeVarName} = ${initializer}`;
|
|
84
|
+
const importLines = generateImportStatements(context);
|
|
85
|
+
const lines = ['# This stub file was generated from Pyright type information', ''];
|
|
86
|
+
if (importLines.length > 0) {
|
|
87
|
+
lines.push(...importLines);
|
|
88
|
+
lines.push('');
|
|
89
|
+
}
|
|
90
|
+
lines.push(declarationLine);
|
|
91
|
+
lines.push('');
|
|
92
|
+
const stubContent = lines.join('\n');
|
|
93
|
+
const primaryDefinitionOffset = Math.max(0, stubContent.indexOf(declarationLine));
|
|
94
|
+
return {
|
|
95
|
+
stubContent,
|
|
96
|
+
primaryDefinitionOffset,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Generates a Python stub file (.pyi) from a Pyright FunctionType.
|
|
101
|
+
*
|
|
102
|
+
* @param type The Pyright function type
|
|
103
|
+
* @param options Generation options including Python version
|
|
104
|
+
* @returns Stub generation result with content and metadata
|
|
105
|
+
*/
|
|
106
|
+
function generateStubFromFunctionType(evaluator, type, options) {
|
|
107
|
+
// Check if this function is a method (has a methodClass)
|
|
108
|
+
// If so, generate the entire class stub with marker on this specific method
|
|
109
|
+
const methodClass = type.shared.methodClass;
|
|
110
|
+
if (methodClass && PyrightTypes.isClass(methodClass)) {
|
|
111
|
+
// Generate class stub with marker on the specific method
|
|
112
|
+
return generateStubFromClassType(evaluator, methodClass, options, type.shared.name);
|
|
113
|
+
}
|
|
114
|
+
const context = {
|
|
115
|
+
imports: { imports: new Map() },
|
|
116
|
+
pythonVersion: options.pythonVersion,
|
|
117
|
+
moduleImports: new Map(),
|
|
118
|
+
methodClass: type.shared.methodClass,
|
|
119
|
+
};
|
|
120
|
+
const lines = [];
|
|
121
|
+
// Generate header comment
|
|
122
|
+
lines.push('# This stub file was generated from Pyright type information');
|
|
123
|
+
lines.push('');
|
|
124
|
+
// Collect TypeVars/ParamSpecs used in the function signature
|
|
125
|
+
// Use typeParams if available, otherwise collect from signature
|
|
126
|
+
let typeVarsToGenerate = [];
|
|
127
|
+
if (type.shared.typeParams && type.shared.typeParams.length > 0) {
|
|
128
|
+
typeVarsToGenerate = type.shared.typeParams;
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
// Collect TypeVars/ParamSpecs from parameter types and return type
|
|
132
|
+
typeVarsToGenerate = collectTypeVarsFromFunctionSignature(type);
|
|
133
|
+
}
|
|
134
|
+
// Check if we have a Self TypeVar - if so, we need to replace it with a regular TypeVar
|
|
135
|
+
// since there's no class context for a standalone function
|
|
136
|
+
const selfTypeVar = typeVarsToGenerate.find((tv) => PyrightTypes.TypeVarType.isSelf(tv));
|
|
137
|
+
if (selfTypeVar) {
|
|
138
|
+
// Filter out Self and track that we need to replace it
|
|
139
|
+
typeVarsToGenerate = typeVarsToGenerate.filter((tv) => !PyrightTypes.TypeVarType.isSelf(tv));
|
|
140
|
+
context.selfTypeVarName = '__type_of_self__';
|
|
141
|
+
// Create a TypeVar declaration for __type_of_self__ with the bound to the class (if any)
|
|
142
|
+
const bound = selfTypeVar.shared.boundType;
|
|
143
|
+
addImport(context, 'typing', 'TypeVar');
|
|
144
|
+
if (bound && PyrightTypes.isClass(bound)) {
|
|
145
|
+
// Make sure this class is imported
|
|
146
|
+
addImport(context, bound.shared.moduleName || '', bound.shared.name);
|
|
147
|
+
lines.push(`__type_of_self__ = TypeVar('__type_of_self__', bound='${bound.shared.name}')`);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
lines.push(`__type_of_self__ = TypeVar('__type_of_self__')`);
|
|
151
|
+
}
|
|
152
|
+
lines.push('');
|
|
153
|
+
}
|
|
154
|
+
// Generate TypeVar/ParamSpec declarations
|
|
155
|
+
if (typeVarsToGenerate.length > 0) {
|
|
156
|
+
const typeVarDecls = generateTypeVarDeclarations(typeVarsToGenerate, context);
|
|
157
|
+
if (typeVarDecls.length > 0) {
|
|
158
|
+
lines.push(...typeVarDecls);
|
|
159
|
+
lines.push('');
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// Generate the function stub with docstring and deprecated decorator
|
|
163
|
+
const functionStub = generateFunctionStub(type, context, type.shared.docString, type.shared.deprecatedMessage);
|
|
164
|
+
lines.push(functionStub);
|
|
165
|
+
// Generate imports (will be inserted at top after we know what's needed)
|
|
166
|
+
const importLines = generateImportStatements(context);
|
|
167
|
+
// Combine: imports + blank line + content
|
|
168
|
+
const result = [];
|
|
169
|
+
if (importLines.length > 0) {
|
|
170
|
+
result.push(...importLines);
|
|
171
|
+
result.push('');
|
|
172
|
+
}
|
|
173
|
+
result.push(...lines);
|
|
174
|
+
// Calculate offset to the function definition
|
|
175
|
+
// The function stub is always at the end of the result content
|
|
176
|
+
const stubContent = result.join('\n') + '\n';
|
|
177
|
+
const functionStubIndex = stubContent.lastIndexOf(functionStub);
|
|
178
|
+
if (functionStubIndex < 0) {
|
|
179
|
+
throw new Error(`Failed to find function stub in generated content`);
|
|
180
|
+
}
|
|
181
|
+
return {
|
|
182
|
+
stubContent,
|
|
183
|
+
primaryDefinitionOffset: functionStubIndex,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Generates a function stub signature from a Pyright function type.
|
|
188
|
+
*
|
|
189
|
+
* @param type The Pyright function type
|
|
190
|
+
* @param context Generation context
|
|
191
|
+
* @param docString Optional docstring to include
|
|
192
|
+
* @param deprecatedMessage Optional deprecation message
|
|
193
|
+
* @returns Function stub string (e.g., "def foo(x: int, y: str) -> bool: ...")
|
|
194
|
+
*/
|
|
195
|
+
function generateFunctionStub(type, context, docString, deprecatedMessage) {
|
|
196
|
+
const lines = [];
|
|
197
|
+
// Add @deprecated decorator if deprecated
|
|
198
|
+
if (deprecatedMessage) {
|
|
199
|
+
context.imports.imports.set('warnings', new Set(['deprecated']));
|
|
200
|
+
// Escape the message for Python string literal
|
|
201
|
+
const escapedMessage = deprecatedMessage.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
202
|
+
lines.push(`@deprecated("${escapedMessage}")`);
|
|
203
|
+
}
|
|
204
|
+
// Function name - use 'generated_function' as a placeholder
|
|
205
|
+
const functionName = type.shared.name || 'generated_function';
|
|
206
|
+
const parts = [];
|
|
207
|
+
// Start building the function signature
|
|
208
|
+
parts.push('def ');
|
|
209
|
+
parts.push(functionName);
|
|
210
|
+
parts.push('(');
|
|
211
|
+
// Generate parameters
|
|
212
|
+
const paramStrings = [];
|
|
213
|
+
for (let i = 0; i < type.shared.parameters.length; i++) {
|
|
214
|
+
const param = type.shared.parameters[i];
|
|
215
|
+
const paramParts = [];
|
|
216
|
+
// Handle special parameter categories
|
|
217
|
+
if (param.category === 1 /* ParamCategory.ArgsList */) {
|
|
218
|
+
paramParts.push('*');
|
|
219
|
+
}
|
|
220
|
+
else if (param.category === 2 /* ParamCategory.KwargsDict */) {
|
|
221
|
+
paramParts.push('**');
|
|
222
|
+
}
|
|
223
|
+
// Parameter name
|
|
224
|
+
if (param.name) {
|
|
225
|
+
paramParts.push(param.name);
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
paramParts.push('_');
|
|
229
|
+
}
|
|
230
|
+
// Parameter type annotation
|
|
231
|
+
if (param._type) {
|
|
232
|
+
paramParts.push(': ');
|
|
233
|
+
const isSynthesizedSelfParam = param._type && PyrightTypes.isTypeVar(param._type) && param._type.shared.isSynthesizedSelf;
|
|
234
|
+
let paramTypeString = pyrightTypeToString(param._type, isSynthesizedSelfParam ? undefined : context);
|
|
235
|
+
// Replace 'Self' with our TypeVar if we're in a standalone function context
|
|
236
|
+
if (context.selfTypeVarName && paramTypeString === 'Self' && isSynthesizedSelfParam) {
|
|
237
|
+
paramTypeString = context.selfTypeVarName;
|
|
238
|
+
}
|
|
239
|
+
paramParts.push(paramTypeString);
|
|
240
|
+
}
|
|
241
|
+
// Default value (if present, just show "...")
|
|
242
|
+
const defaultType = PyrightTypes.FunctionType.getParamDefaultType(type, i);
|
|
243
|
+
if (defaultType || param.defaultExpr) {
|
|
244
|
+
paramParts.push(' = ...');
|
|
245
|
+
}
|
|
246
|
+
paramStrings.push(paramParts.join(''));
|
|
247
|
+
}
|
|
248
|
+
parts.push(paramStrings.join(', '));
|
|
249
|
+
parts.push(')');
|
|
250
|
+
// Return type annotation
|
|
251
|
+
const returnType = getReturnTypeStringFromFunction(type, context);
|
|
252
|
+
parts.push(' -> ');
|
|
253
|
+
parts.push(returnType);
|
|
254
|
+
parts.push(':');
|
|
255
|
+
lines.push(parts.join(''));
|
|
256
|
+
// Add docstring if present
|
|
257
|
+
if (docString) {
|
|
258
|
+
// Indent the docstring content
|
|
259
|
+
const docLines = docString.split('\n');
|
|
260
|
+
lines.push(' """');
|
|
261
|
+
for (const docLine of docLines) {
|
|
262
|
+
lines.push(` ${docLine}`);
|
|
263
|
+
}
|
|
264
|
+
lines.push(' """');
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
// Just add ellipsis on same line if no docstring
|
|
268
|
+
lines[lines.length - 1] += ' ...';
|
|
269
|
+
}
|
|
270
|
+
return lines.join('\n');
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Gets the return type string for a function.
|
|
274
|
+
*/
|
|
275
|
+
function getReturnTypeStringFromFunction(type, context) {
|
|
276
|
+
const returnType = PyrightTypes.FunctionType.getEffectiveReturnType(type, true);
|
|
277
|
+
let returnTypeString = returnType ? pyrightTypeToString(returnType, context) : 'None';
|
|
278
|
+
// Replace 'Self' with our TypeVar if we're in a standalone function context
|
|
279
|
+
if (context.selfTypeVarName && returnTypeString === 'Self') {
|
|
280
|
+
returnTypeString = context.selfTypeVarName;
|
|
281
|
+
}
|
|
282
|
+
return returnTypeString;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Converts a Pyright type to a Python type string using printType.
|
|
286
|
+
*/
|
|
287
|
+
function pyrightTypeToString(type, context) {
|
|
288
|
+
const flags = 256 /* PrintTypeFlags.PythonSyntax */ | 4096 /* PrintTypeFlags.UseFullyQualifiedNames */;
|
|
289
|
+
// Simple callback for getting return types - just use declared or inferred return type
|
|
290
|
+
const returnTypeCallback = (funcType) => {
|
|
291
|
+
return (funcType.shared.declaredReturnType ??
|
|
292
|
+
funcType.shared.inferredReturnType?.type ??
|
|
293
|
+
PyrightTypes.UnknownType.create());
|
|
294
|
+
};
|
|
295
|
+
const typeString = (0, typePrinter_1.printType)(type, flags, returnTypeCallback);
|
|
296
|
+
// Remove 'builtins.' prefix from builtin types
|
|
297
|
+
const cleanedTypeString = typeString.replace(/builtins\./g, '');
|
|
298
|
+
// Extract and track module imports if context is provided.
|
|
299
|
+
if (context) {
|
|
300
|
+
extractModuleImportsFromTypeString(cleanedTypeString, type, context);
|
|
301
|
+
}
|
|
302
|
+
return cleanedTypeString;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Extracts module-qualified names from a type string and tracks them as imports.
|
|
306
|
+
* For example, "test.Box[int]" would extract module "test".
|
|
307
|
+
* Also detects typing module types like "Any", "Callable", "Optional", etc.
|
|
308
|
+
*/
|
|
309
|
+
function extractModuleImportsFromTypeString(typeString, type, context) {
|
|
310
|
+
// Match module-qualified names like "test.Box" or "foo.bar.Baz"
|
|
311
|
+
const moduleQualifiedPattern = /([a-zA-Z_][a-zA-Z0-9_]*(?:\.[a-zA-Z_][a-zA-Z0-9_]*)*)\./g;
|
|
312
|
+
const matches = typeString.matchAll(moduleQualifiedPattern);
|
|
313
|
+
for (const match of matches) {
|
|
314
|
+
const fullPath = match[1]; // e.g., "test" or "foo.bar"
|
|
315
|
+
const moduleName = fullPath;
|
|
316
|
+
// Skip ParamSpec's special attributes (.args and .kwargs)
|
|
317
|
+
// These are not module imports - e.g., "P.args" where P is a ParamSpec
|
|
318
|
+
const matchStart = match.index;
|
|
319
|
+
const afterDot = typeString.substring(matchStart + match[0].length);
|
|
320
|
+
if (afterDot.startsWith('args') || afterDot.startsWith('kwargs')) {
|
|
321
|
+
// Check if this looks like a ParamSpec attribute by checking if the name
|
|
322
|
+
// doesn't contain dots (ParamSpec names are typically single identifiers)
|
|
323
|
+
if (!moduleName.includes('.')) {
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
// Track this module import (no specific names needed since we use "import module")
|
|
328
|
+
if (!context.moduleImports.has(moduleName)) {
|
|
329
|
+
context.moduleImports.set(moduleName, new Set());
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
// Extract typing module imports (types that come from typing or typing_extensions)
|
|
333
|
+
extractTypingImports(typeString, context);
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Extracts typing module types from a type string and adds appropriate imports.
|
|
337
|
+
* Handles types like Any, Callable, Optional, etc., and determines whether to use
|
|
338
|
+
* typing or typing_extensions based on Python version.
|
|
339
|
+
*/
|
|
340
|
+
function extractTypingImports(typeString, context) {
|
|
341
|
+
// Common typing module types
|
|
342
|
+
const typingTypes = [
|
|
343
|
+
'Any',
|
|
344
|
+
'Callable',
|
|
345
|
+
'Optional',
|
|
346
|
+
'Union',
|
|
347
|
+
'List',
|
|
348
|
+
'Dict',
|
|
349
|
+
'Set',
|
|
350
|
+
'Tuple',
|
|
351
|
+
'Type',
|
|
352
|
+
'Literal',
|
|
353
|
+
'Protocol',
|
|
354
|
+
'TypedDict',
|
|
355
|
+
'NamedTuple',
|
|
356
|
+
'Generic',
|
|
357
|
+
'ClassVar',
|
|
358
|
+
'Final',
|
|
359
|
+
'Annotated',
|
|
360
|
+
'TypeGuard',
|
|
361
|
+
'TypeAlias',
|
|
362
|
+
'ParamSpec',
|
|
363
|
+
'Concatenate',
|
|
364
|
+
'TypeVarTuple',
|
|
365
|
+
'Unpack',
|
|
366
|
+
'Never',
|
|
367
|
+
'NoReturn',
|
|
368
|
+
'Self',
|
|
369
|
+
];
|
|
370
|
+
// Check which typing types appear in the type string
|
|
371
|
+
for (const typeName of typingTypes) {
|
|
372
|
+
// Use word boundary to match complete words only (e.g., "Any" but not "AnyThing")
|
|
373
|
+
const pattern = new RegExp(`\\b${typeName}\\b`);
|
|
374
|
+
if (pattern.test(typeString)) {
|
|
375
|
+
// Determine which module to import from
|
|
376
|
+
const moduleName = getTypingModule(context.pythonVersion, typeName);
|
|
377
|
+
addImport(context, moduleName, typeName);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Adds an import to the collection.
|
|
383
|
+
*/
|
|
384
|
+
function addImport(context, moduleName, importName) {
|
|
385
|
+
if (!context.imports.imports.has(moduleName)) {
|
|
386
|
+
context.imports.imports.set(moduleName, new Set());
|
|
387
|
+
}
|
|
388
|
+
context.imports.imports.get(moduleName).add(importName);
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Determines which typing module to use based on Python version and feature.
|
|
392
|
+
*/
|
|
393
|
+
function getTypingModule(pythonVersion, feature) {
|
|
394
|
+
// Features that require typing_extensions in older Python versions
|
|
395
|
+
const typingExtensionsFeatures = {
|
|
396
|
+
ParamSpec: pythonVersion_1.pythonVersion3_10, // Python 3.10+
|
|
397
|
+
TypeVarTuple: pythonVersion_1.pythonVersion3_11, // Python 3.11+
|
|
398
|
+
Never: pythonVersion_1.pythonVersion3_11, // Python 3.11+
|
|
399
|
+
TypeAlias: pythonVersion_1.pythonVersion3_10, // Python 3.10+
|
|
400
|
+
};
|
|
401
|
+
const requiredVersion = typingExtensionsFeatures[feature];
|
|
402
|
+
if (requiredVersion && pythonVersion_1.PythonVersion.isLessThan(pythonVersion, requiredVersion)) {
|
|
403
|
+
return 'typing_extensions';
|
|
404
|
+
}
|
|
405
|
+
return 'typing';
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Generates import statements from collected imports.
|
|
409
|
+
*/
|
|
410
|
+
function generateImportStatements(context) {
|
|
411
|
+
const lines = [];
|
|
412
|
+
// First, add typing imports (if any)
|
|
413
|
+
const sortedModules = Array.from(context.imports.imports.keys()).sort();
|
|
414
|
+
for (const moduleName of sortedModules) {
|
|
415
|
+
const imports = Array.from(context.imports.imports.get(moduleName)).sort();
|
|
416
|
+
lines.push(`from ${moduleName} import ${imports.join(', ')}`);
|
|
417
|
+
}
|
|
418
|
+
// Then, add module imports (without comments - metadata contains the paths)
|
|
419
|
+
const sortedModuleImports = Array.from(context.moduleImports.keys()).sort();
|
|
420
|
+
for (const moduleName of sortedModuleImports) {
|
|
421
|
+
lines.push(`import ${moduleName}`);
|
|
422
|
+
}
|
|
423
|
+
return lines;
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Generates a Python stub file (.pyi) from a Pyright ClassType.
|
|
427
|
+
*
|
|
428
|
+
* @param type The Pyright class type
|
|
429
|
+
* @param options Generation options including Python version
|
|
430
|
+
* @param targetMethodName If provided, marks this specific method as the primary definition
|
|
431
|
+
* @returns Stub generation result with content and metadata
|
|
432
|
+
*/
|
|
433
|
+
function generateStubFromClassType(evaluator, type, options, targetMethodName) {
|
|
434
|
+
const context = {
|
|
435
|
+
imports: { imports: new Map() },
|
|
436
|
+
pythonVersion: options.pythonVersion,
|
|
437
|
+
moduleImports: new Map(),
|
|
438
|
+
targetMethodName,
|
|
439
|
+
};
|
|
440
|
+
const lines = [];
|
|
441
|
+
// Generate header comment
|
|
442
|
+
lines.push('# This stub file was generated from Pyright type information');
|
|
443
|
+
lines.push('');
|
|
444
|
+
// Special handling for NewType classes - generate as NewType call instead of class definition
|
|
445
|
+
if (PyrightTypes.ClassType.isNewTypeClass(type)) {
|
|
446
|
+
return generateNewTypeStub(type, context, options);
|
|
447
|
+
}
|
|
448
|
+
// Generate the class stub (will include marker for main class)
|
|
449
|
+
const classStub = generateClassStub(evaluator, type, context, true);
|
|
450
|
+
lines.push(classStub);
|
|
451
|
+
// Generate imports (will be inserted at top after we know what's needed)
|
|
452
|
+
const importLines = generateImportStatements(context);
|
|
453
|
+
// Combine: imports + blank line + content
|
|
454
|
+
const result = [];
|
|
455
|
+
if (importLines.length > 0) {
|
|
456
|
+
result.push(...importLines);
|
|
457
|
+
result.push('');
|
|
458
|
+
}
|
|
459
|
+
result.push(...lines);
|
|
460
|
+
// Calculate offset to the primary definition
|
|
461
|
+
const stubContent = result.join('\n') + '\n';
|
|
462
|
+
let primaryDefinitionOffset;
|
|
463
|
+
if (targetMethodName) {
|
|
464
|
+
// Find the target method within the class stub
|
|
465
|
+
// Look for "def <methodName>(" pattern
|
|
466
|
+
const methodPattern = new RegExp(`\\bdef ${targetMethodName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\(`, 'm');
|
|
467
|
+
const match = stubContent.match(methodPattern);
|
|
468
|
+
if (!match || match.index === undefined) {
|
|
469
|
+
throw new Error(`Failed to find method '${targetMethodName}' in generated class stub`);
|
|
470
|
+
}
|
|
471
|
+
primaryDefinitionOffset = match.index;
|
|
472
|
+
}
|
|
473
|
+
else {
|
|
474
|
+
// Find the class definition - look for "class <ClassName>"
|
|
475
|
+
const classPattern = new RegExp(`\\bclass ${type.shared.name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`, 'm');
|
|
476
|
+
const match = stubContent.match(classPattern);
|
|
477
|
+
if (!match || match.index === undefined) {
|
|
478
|
+
throw new Error(`Failed to find class '${type.shared.name}' in generated stub`);
|
|
479
|
+
}
|
|
480
|
+
primaryDefinitionOffset = match.index;
|
|
481
|
+
}
|
|
482
|
+
return {
|
|
483
|
+
stubContent,
|
|
484
|
+
primaryDefinitionOffset,
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Generates a stub for a NewType class using NewType call syntax.
|
|
489
|
+
*/
|
|
490
|
+
function generateNewTypeStub(type, context, options) {
|
|
491
|
+
const lines = [];
|
|
492
|
+
// Add typing import for NewType
|
|
493
|
+
addImport(context, 'typing', 'NewType');
|
|
494
|
+
// Get the base type (first base class, excluding object)
|
|
495
|
+
const baseType = type.shared.baseClasses[0];
|
|
496
|
+
let baseTypeStr = baseType ? pyrightTypeToString(baseType, context) : 'object';
|
|
497
|
+
// Remove type[] wrapper if present (NewType base should be the instance type, not type[])
|
|
498
|
+
const typeWrapperMatch = baseTypeStr.match(/^type\[(.+)\]$/);
|
|
499
|
+
if (typeWrapperMatch) {
|
|
500
|
+
baseTypeStr = typeWrapperMatch[1];
|
|
501
|
+
}
|
|
502
|
+
// Generate: TypeName = NewType('TypeName', BaseType)
|
|
503
|
+
lines.push(`${type.shared.name} = NewType('${type.shared.name}', ${baseTypeStr})`);
|
|
504
|
+
// Generate imports
|
|
505
|
+
const importLines = generateImportStatements(context);
|
|
506
|
+
const result = [];
|
|
507
|
+
result.push('# This stub file was generated from Pyright type information');
|
|
508
|
+
result.push('');
|
|
509
|
+
if (importLines.length > 0) {
|
|
510
|
+
result.push(...importLines);
|
|
511
|
+
result.push('');
|
|
512
|
+
}
|
|
513
|
+
result.push(...lines);
|
|
514
|
+
// Calculate offset to the NewType assignment
|
|
515
|
+
const stubContent = result.join('\n') + '\n';
|
|
516
|
+
const assignmentPattern = new RegExp(`\\b${type.shared.name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*=`, 'm');
|
|
517
|
+
const match = stubContent.match(assignmentPattern);
|
|
518
|
+
if (!match || match.index === undefined) {
|
|
519
|
+
throw new Error(`Failed to find NewType assignment for '${type.shared.name}' in generated stub`);
|
|
520
|
+
}
|
|
521
|
+
return {
|
|
522
|
+
stubContent,
|
|
523
|
+
primaryDefinitionOffset: match.index,
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* Generates a class stub from a Pyright class type.
|
|
528
|
+
* @param type - The class type to generate a stub for
|
|
529
|
+
* @param context - The stub generation context
|
|
530
|
+
* @param isTopLevel - Whether this is the main class (vs a synthesized base class)
|
|
531
|
+
*/
|
|
532
|
+
function generateClassStub(evaluator, type, context, isTopLevel = false) {
|
|
533
|
+
const lines = [];
|
|
534
|
+
// Check for special TypedDict or NamedTuple
|
|
535
|
+
if (type.shared.typedDictEntries) {
|
|
536
|
+
return generateTypedDictStub(type, context);
|
|
537
|
+
}
|
|
538
|
+
if (type.shared.namedTupleEntries) {
|
|
539
|
+
return generateNamedTupleStub(evaluator, type, context);
|
|
540
|
+
}
|
|
541
|
+
// Add @deprecated decorator if deprecated
|
|
542
|
+
if (type.shared.deprecatedMessage) {
|
|
543
|
+
context.imports.imports.set('warnings', new Set(['deprecated']));
|
|
544
|
+
// Escape the message for Python string literal
|
|
545
|
+
const escapedMessage = type.shared.deprecatedMessage.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
546
|
+
lines.push(`@deprecated("${escapedMessage}")`);
|
|
547
|
+
}
|
|
548
|
+
// Start class definition
|
|
549
|
+
const parts = [];
|
|
550
|
+
parts.push('class ');
|
|
551
|
+
parts.push(type.shared.name);
|
|
552
|
+
// Add type parameters for generic classes
|
|
553
|
+
if (type.shared.typeParams && type.shared.typeParams.length > 0) {
|
|
554
|
+
const typeParamNames = type.shared.typeParams.map((tp) => {
|
|
555
|
+
if (PyrightTypes.isTypeVar(tp)) {
|
|
556
|
+
return tp.shared.name;
|
|
557
|
+
}
|
|
558
|
+
return pyrightTypeToString(tp, context);
|
|
559
|
+
});
|
|
560
|
+
parts.push('[');
|
|
561
|
+
parts.push(typeParamNames.join(', '));
|
|
562
|
+
parts.push(']');
|
|
563
|
+
}
|
|
564
|
+
// Check if any base classes are synthesized (no declaration) and need their stubs generated
|
|
565
|
+
// We need to do this check before generating the base class list so we know which ones
|
|
566
|
+
// to reference with simple names vs fully qualified names
|
|
567
|
+
const synthesizedBaseClasses = new Set();
|
|
568
|
+
const baseClassStubs = [];
|
|
569
|
+
if (type.shared.baseClasses && type.shared.baseClasses.length > 0) {
|
|
570
|
+
for (const bc of type.shared.baseClasses) {
|
|
571
|
+
if (PyrightTypes.isClass(bc) && !bc.shared.declaration && bc.shared.name !== 'object') {
|
|
572
|
+
// Track this as a synthesized base class
|
|
573
|
+
synthesizedBaseClasses.add(bc.shared.name);
|
|
574
|
+
// Generate stub for synthesized base class
|
|
575
|
+
const baseStub = generateClassStub(evaluator, bc, context);
|
|
576
|
+
let bcStr = pyrightTypeToString(bc, context);
|
|
577
|
+
// Remove type[] wrapper if present
|
|
578
|
+
const typeWrapperMatch = bcStr.match(/^type\[(.+)\]$/);
|
|
579
|
+
if (typeWrapperMatch) {
|
|
580
|
+
bcStr = typeWrapperMatch[1];
|
|
581
|
+
}
|
|
582
|
+
baseClassStubs.push(`# ↓ Base class: ${bcStr}`);
|
|
583
|
+
baseClassStubs.push(baseStub);
|
|
584
|
+
baseClassStubs.push('');
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
// Add base classes
|
|
589
|
+
if (type.shared.baseClasses && type.shared.baseClasses.length > 0) {
|
|
590
|
+
const baseClassStrs = type.shared.baseClasses
|
|
591
|
+
.map((bc) => {
|
|
592
|
+
let bcStr = pyrightTypeToString(bc, context);
|
|
593
|
+
// Remove type[] wrapper if present
|
|
594
|
+
const typeWrapperMatch = bcStr.match(/^type\[(.+)\]$/);
|
|
595
|
+
if (typeWrapperMatch) {
|
|
596
|
+
bcStr = typeWrapperMatch[1];
|
|
597
|
+
}
|
|
598
|
+
// If this base class had its stub generated inline, use just the simple name
|
|
599
|
+
if (PyrightTypes.isClass(bc) && synthesizedBaseClasses.has(bc.shared.name)) {
|
|
600
|
+
return bc.shared.name;
|
|
601
|
+
}
|
|
602
|
+
return bcStr;
|
|
603
|
+
})
|
|
604
|
+
.filter((bc) => bc !== 'object'); // Exclude object base class
|
|
605
|
+
if (baseClassStrs.length > 0) {
|
|
606
|
+
parts.push('(');
|
|
607
|
+
parts.push(baseClassStrs.join(', '));
|
|
608
|
+
parts.push(')');
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
parts.push(':');
|
|
612
|
+
lines.push(parts.join(''));
|
|
613
|
+
// Add docstring if present
|
|
614
|
+
if (type.shared.docString) {
|
|
615
|
+
const docLines = type.shared.docString.split('\n');
|
|
616
|
+
lines.push(' """');
|
|
617
|
+
for (const docLine of docLines) {
|
|
618
|
+
lines.push(` ${docLine}`);
|
|
619
|
+
}
|
|
620
|
+
lines.push(' """');
|
|
621
|
+
}
|
|
622
|
+
// Generate class body
|
|
623
|
+
const bodyLines = generateClassBody(evaluator, type, context);
|
|
624
|
+
if (bodyLines.length === 0) {
|
|
625
|
+
lines.push(' ...');
|
|
626
|
+
}
|
|
627
|
+
else {
|
|
628
|
+
lines.push(...bodyLines);
|
|
629
|
+
}
|
|
630
|
+
const classStub = lines.join('\n');
|
|
631
|
+
// Combine base class stubs with main class stub
|
|
632
|
+
if (baseClassStubs.length > 0) {
|
|
633
|
+
const result = [];
|
|
634
|
+
result.push(...baseClassStubs);
|
|
635
|
+
result.push(classStub);
|
|
636
|
+
return result.join('\n');
|
|
637
|
+
}
|
|
638
|
+
return classStub;
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Generates the body of a class (fields and methods).
|
|
642
|
+
*/
|
|
643
|
+
function generateClassBody(evaluator, type, context) {
|
|
644
|
+
const lines = [];
|
|
645
|
+
// Get all fields from the symbol table, excluding private members
|
|
646
|
+
const fields = [];
|
|
647
|
+
for (const [name, symbol] of type.shared.fields) {
|
|
648
|
+
// Skip private fields (starting with single _), but keep dunder methods
|
|
649
|
+
if (name.startsWith('_') && !name.startsWith('__')) {
|
|
650
|
+
continue;
|
|
651
|
+
}
|
|
652
|
+
// Try to get the type from synthesized type, or from declarations
|
|
653
|
+
let fieldType;
|
|
654
|
+
const synthesizedType = symbol.getSynthesizedType();
|
|
655
|
+
if (synthesizedType) {
|
|
656
|
+
fieldType = synthesizedType.type;
|
|
657
|
+
}
|
|
658
|
+
else {
|
|
659
|
+
// Try to get declared type from declarations
|
|
660
|
+
fieldType = evaluator.getEffectiveTypeOfSymbol(symbol);
|
|
661
|
+
}
|
|
662
|
+
if (!fieldType) {
|
|
663
|
+
continue;
|
|
664
|
+
}
|
|
665
|
+
const isMethod = PyrightTypes.isFunction(fieldType) || PyrightTypes.isOverloaded(fieldType);
|
|
666
|
+
const isClassVar = symbol.isClassVar();
|
|
667
|
+
// Skip fields that are private but not dunder methods
|
|
668
|
+
if (name.startsWith('_') && !isMethod) {
|
|
669
|
+
continue;
|
|
670
|
+
}
|
|
671
|
+
fields.push({ name, type: fieldType, isMethod, isClassVar });
|
|
672
|
+
}
|
|
673
|
+
// Sort: class variables first, then instance variables, then methods
|
|
674
|
+
fields.sort((a, b) => {
|
|
675
|
+
if (a.isClassVar && !b.isClassVar)
|
|
676
|
+
return -1;
|
|
677
|
+
if (!a.isClassVar && b.isClassVar)
|
|
678
|
+
return 1;
|
|
679
|
+
if (!a.isMethod && b.isMethod)
|
|
680
|
+
return -1;
|
|
681
|
+
if (a.isMethod && !b.isMethod)
|
|
682
|
+
return 1;
|
|
683
|
+
return a.name.localeCompare(b.name);
|
|
684
|
+
});
|
|
685
|
+
// Generate field stubs
|
|
686
|
+
for (const field of fields) {
|
|
687
|
+
if (field.isMethod) {
|
|
688
|
+
// Generate method stub
|
|
689
|
+
if (PyrightTypes.isFunction(field.type)) {
|
|
690
|
+
const methodContext = { ...context, methodClass: type };
|
|
691
|
+
const methodStub = generateFunctionStub(field.type, methodContext, field.type.shared.docString, field.type.shared.deprecatedMessage);
|
|
692
|
+
// Indent the method
|
|
693
|
+
const indentedStub = methodStub
|
|
694
|
+
.split('\n')
|
|
695
|
+
.map((line) => ` ${line}`)
|
|
696
|
+
.join('\n');
|
|
697
|
+
lines.push(indentedStub);
|
|
698
|
+
}
|
|
699
|
+
else if (PyrightTypes.isOverloaded(field.type)) {
|
|
700
|
+
// Handle overloaded methods - just use first overload for stub
|
|
701
|
+
const overloads = PyrightTypes.OverloadedType.getOverloads(field.type);
|
|
702
|
+
if (overloads.length > 0) {
|
|
703
|
+
const methodContext = { ...context, methodClass: type };
|
|
704
|
+
const methodStub = generateFunctionStub(overloads[0], methodContext, overloads[0].shared.docString, overloads[0].shared.deprecatedMessage);
|
|
705
|
+
const indentedStub = methodStub
|
|
706
|
+
.split('\n')
|
|
707
|
+
.map((line) => ` ${line}`)
|
|
708
|
+
.join('\n');
|
|
709
|
+
lines.push(indentedStub);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
else if (PyrightTypes.isClass(field.type) && !field.type.shared.declaration) {
|
|
714
|
+
// Nested synthesized class - generate its stub recursively
|
|
715
|
+
const nestedStub = generateClassStub(evaluator, field.type, context);
|
|
716
|
+
const indentedStub = nestedStub
|
|
717
|
+
.split('\n')
|
|
718
|
+
.map((line) => ` ${line}`)
|
|
719
|
+
.join('\n');
|
|
720
|
+
lines.push(indentedStub);
|
|
721
|
+
}
|
|
722
|
+
else {
|
|
723
|
+
// Regular field with type annotation
|
|
724
|
+
const typeStr = pyrightTypeToString(field.type, context);
|
|
725
|
+
lines.push(` ${field.name}: ${typeStr}`);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
return lines;
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* Generates a TypedDict stub using special syntax.
|
|
732
|
+
*/
|
|
733
|
+
function generateTypedDictStub(type, context) {
|
|
734
|
+
const lines = [];
|
|
735
|
+
// TypedDict class definition
|
|
736
|
+
const parts = [];
|
|
737
|
+
parts.push('class ');
|
|
738
|
+
parts.push(type.shared.name);
|
|
739
|
+
parts.push('(TypedDict');
|
|
740
|
+
// Add total=False if not all fields are required
|
|
741
|
+
const entries = type.shared.typedDictEntries;
|
|
742
|
+
if (entries) {
|
|
743
|
+
const hasOptional = Array.from(entries.knownItems.values()).some((e) => !e.isRequired);
|
|
744
|
+
if (hasOptional) {
|
|
745
|
+
parts.push(', total=False');
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
parts.push('):');
|
|
749
|
+
lines.push(parts.join(''));
|
|
750
|
+
// Generate fields
|
|
751
|
+
if (entries && entries.knownItems.size > 0) {
|
|
752
|
+
for (const [name, entry] of entries.knownItems) {
|
|
753
|
+
const typeStr = pyrightTypeToString(entry.valueType, context);
|
|
754
|
+
lines.push(` ${name}: ${typeStr}`);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
else {
|
|
758
|
+
lines.push(' ...');
|
|
759
|
+
}
|
|
760
|
+
// Add TypedDict to imports
|
|
761
|
+
addImport(context, 'typing', 'TypedDict');
|
|
762
|
+
return lines.join('\n');
|
|
763
|
+
}
|
|
764
|
+
/**
|
|
765
|
+
* Generates a NamedTuple stub using special syntax.
|
|
766
|
+
*/
|
|
767
|
+
function generateNamedTupleStub(evaluator, type, context) {
|
|
768
|
+
const lines = [];
|
|
769
|
+
// NamedTuple class definition
|
|
770
|
+
const parts = [];
|
|
771
|
+
parts.push('class ');
|
|
772
|
+
parts.push(type.shared.name);
|
|
773
|
+
parts.push('(NamedTuple):');
|
|
774
|
+
lines.push(parts.join(''));
|
|
775
|
+
// Generate fields from namedTupleEntries
|
|
776
|
+
const entries = type.shared.namedTupleEntries;
|
|
777
|
+
if (entries && entries.size > 0) {
|
|
778
|
+
// Get field types from the symbol table
|
|
779
|
+
for (const [name, symbol] of type.shared.fields) {
|
|
780
|
+
if (entries.has(name)) {
|
|
781
|
+
const synthesizedType = symbol.getSynthesizedType();
|
|
782
|
+
if (synthesizedType) {
|
|
783
|
+
const typeStr = pyrightTypeToString(synthesizedType.type, context);
|
|
784
|
+
lines.push(` ${name}: ${typeStr}`);
|
|
785
|
+
}
|
|
786
|
+
else {
|
|
787
|
+
const type = evaluator.getEffectiveTypeOfSymbol(symbol);
|
|
788
|
+
if (type) {
|
|
789
|
+
const typeStr = pyrightTypeToString(type, context);
|
|
790
|
+
lines.push(` ${name}: ${typeStr}`);
|
|
791
|
+
}
|
|
792
|
+
else {
|
|
793
|
+
lines.push(` ${name}: Any`);
|
|
794
|
+
addImport(context, 'typing', 'Any');
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
if (lines.length === 1) {
|
|
801
|
+
lines.push(' ...');
|
|
802
|
+
}
|
|
803
|
+
// Add NamedTuple to imports
|
|
804
|
+
addImport(context, 'typing', 'NamedTuple');
|
|
805
|
+
return lines.join('\n');
|
|
806
|
+
}
|
|
807
|
+
/**
|
|
808
|
+
* Collects all TypeVars/ParamSpecs used in a function signature by walking through
|
|
809
|
+
* parameter types and return type.
|
|
810
|
+
*/
|
|
811
|
+
function collectTypeVarsFromFunctionSignature(type) {
|
|
812
|
+
const typeVars = new Map();
|
|
813
|
+
// Helper to recursively collect TypeVars from a type
|
|
814
|
+
const collectFromType = (t) => {
|
|
815
|
+
if (PyrightTypes.isTypeVar(t)) {
|
|
816
|
+
typeVars.set(t.shared.name, t);
|
|
817
|
+
}
|
|
818
|
+
else if (PyrightTypes.isClass(t) && t.priv.typeArgs) {
|
|
819
|
+
// Check type arguments (e.g., List[T])
|
|
820
|
+
for (const arg of t.priv.typeArgs) {
|
|
821
|
+
collectFromType(arg);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
else if (PyrightTypes.isUnion(t)) {
|
|
825
|
+
// Check union members
|
|
826
|
+
t.priv.subtypes.forEach((subtype) => collectFromType(subtype));
|
|
827
|
+
}
|
|
828
|
+
else if (PyrightTypes.isFunction(t)) {
|
|
829
|
+
// Check function parameters and return type
|
|
830
|
+
for (let i = 0; i < t.shared.parameters.length; i++) {
|
|
831
|
+
const param = t.shared.parameters[i];
|
|
832
|
+
if (param._type) {
|
|
833
|
+
collectFromType(param._type);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
const returnType = PyrightTypes.FunctionType.getEffectiveReturnType(t, true);
|
|
837
|
+
if (returnType) {
|
|
838
|
+
collectFromType(returnType);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
};
|
|
842
|
+
// Collect from parameters
|
|
843
|
+
for (let i = 0; i < type.shared.parameters.length; i++) {
|
|
844
|
+
const param = type.shared.parameters[i];
|
|
845
|
+
if (param._type) {
|
|
846
|
+
collectFromType(param._type);
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
// Collect from return type
|
|
850
|
+
const returnType = PyrightTypes.FunctionType.getEffectiveReturnType(type, true);
|
|
851
|
+
if (returnType) {
|
|
852
|
+
collectFromType(returnType);
|
|
853
|
+
}
|
|
854
|
+
return Array.from(typeVars.values());
|
|
855
|
+
}
|
|
856
|
+
/**
|
|
857
|
+
* Generates TypeVar/ParamSpec/TypeVarTuple declarations for a list of type parameters.
|
|
858
|
+
*/
|
|
859
|
+
function generateTypeVarDeclarations(typeParams, context) {
|
|
860
|
+
const lines = [];
|
|
861
|
+
for (const typeParam of typeParams) {
|
|
862
|
+
if (PyrightTypes.isTypeVar(typeParam)) {
|
|
863
|
+
const name = typeParam.shared.name;
|
|
864
|
+
// Check if it's a ParamSpec
|
|
865
|
+
if (PyrightTypes.isParamSpec(typeParam)) {
|
|
866
|
+
addImport(context, getTypingModule(context.pythonVersion, 'ParamSpec'), 'ParamSpec');
|
|
867
|
+
lines.push(`${name} = ParamSpec('${name}')`);
|
|
868
|
+
}
|
|
869
|
+
// Check if it's a TypeVarTuple
|
|
870
|
+
else if (PyrightTypes.isTypeVarTuple(typeParam)) {
|
|
871
|
+
addImport(context, getTypingModule(context.pythonVersion, 'TypeVarTuple'), 'TypeVarTuple');
|
|
872
|
+
lines.push(`${name} = TypeVarTuple('${name}')`);
|
|
873
|
+
}
|
|
874
|
+
// Regular TypeVar
|
|
875
|
+
else {
|
|
876
|
+
addImport(context, 'typing', 'TypeVar');
|
|
877
|
+
const parts = [`${name} = TypeVar('${name}'`];
|
|
878
|
+
// Add bound if present
|
|
879
|
+
if (typeParam.shared.boundType) {
|
|
880
|
+
const boundStr = pyrightTypeToString(typeParam.shared.boundType, context);
|
|
881
|
+
parts.push(`, bound=${boundStr}`);
|
|
882
|
+
}
|
|
883
|
+
// Add constraints if present
|
|
884
|
+
if (typeParam.shared.constraints && typeParam.shared.constraints.length > 0) {
|
|
885
|
+
const constraintStrs = typeParam.shared.constraints.map((c) => pyrightTypeToString(c, context));
|
|
886
|
+
parts.push(`, ${constraintStrs.join(', ')}`);
|
|
887
|
+
}
|
|
888
|
+
parts.push(')');
|
|
889
|
+
lines.push(parts.join(''));
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
return lines;
|
|
894
|
+
}
|
|
895
|
+
//# sourceMappingURL=stubGenerator.js.map
|