@zzzen/pyright-internal 1.2.0-dev.20260705 → 1.2.0-dev.20260712

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.
Files changed (94) hide show
  1. package/dist/tests/typeServer/inProcTypeServerTestUtils.d.ts +39 -0
  2. package/dist/tests/typeServer/inProcTypeServerTestUtils.js +366 -0
  3. package/dist/tests/typeServer/inProcTypeServerTestUtils.js.map +1 -0
  4. package/dist/tests/typeServer/notebook.typeServer.test.d.ts +1 -0
  5. package/dist/tests/typeServer/notebook.typeServer.test.js +315 -0
  6. package/dist/tests/typeServer/notebook.typeServer.test.js.map +1 -0
  7. package/dist/tests/typeServer/typeServer.inProc.test.d.ts +1 -0
  8. package/dist/tests/typeServer/typeServer.inProc.test.js +234 -0
  9. package/dist/tests/typeServer/typeServer.inProc.test.js.map +1 -0
  10. package/dist/tests/typeServer/typeServer.virtualFileRedirect.test.d.ts +1 -0
  11. package/dist/tests/typeServer/typeServer.virtualFileRedirect.test.js +298 -0
  12. package/dist/tests/typeServer/typeServer.virtualFileRedirect.test.js.map +1 -0
  13. package/dist/typeServer/cancellation.d.ts +5 -0
  14. package/dist/typeServer/cancellation.js +14 -0
  15. package/dist/typeServer/cancellation.js.map +1 -0
  16. package/dist/typeServer/diagnosticUtils.d.ts +4 -0
  17. package/dist/typeServer/diagnosticUtils.js +71 -0
  18. package/dist/typeServer/diagnosticUtils.js.map +1 -0
  19. package/dist/typeServer/enums.d.ts +14 -0
  20. package/dist/typeServer/enums.js +524 -0
  21. package/dist/typeServer/enums.js.map +1 -0
  22. package/dist/typeServer/eventEmitter.d.ts +12 -0
  23. package/dist/typeServer/eventEmitter.js +32 -0
  24. package/dist/typeServer/eventEmitter.js.map +1 -0
  25. package/dist/typeServer/nodeMain.d.ts +1 -0
  26. package/dist/typeServer/nodeMain.js +65 -0
  27. package/dist/typeServer/nodeMain.js.map +1 -0
  28. package/dist/typeServer/notebookCellChain.d.ts +34 -0
  29. package/dist/typeServer/notebookCellChain.js +182 -0
  30. package/dist/typeServer/notebookCellChain.js.map +1 -0
  31. package/dist/typeServer/notebookDocumentHandler.d.ts +23 -0
  32. package/dist/typeServer/notebookDocumentHandler.js +158 -0
  33. package/dist/typeServer/notebookDocumentHandler.js.map +1 -0
  34. package/dist/typeServer/notebookUriMapper.d.ts +39 -0
  35. package/dist/typeServer/notebookUriMapper.js +164 -0
  36. package/dist/typeServer/notebookUriMapper.js.map +1 -0
  37. package/dist/typeServer/profilingStub.d.ts +10 -0
  38. package/dist/typeServer/profilingStub.js +12 -0
  39. package/dist/typeServer/profilingStub.js.map +1 -0
  40. package/dist/typeServer/programTypes.d.ts +136 -0
  41. package/dist/typeServer/programTypes.js +3 -0
  42. package/dist/typeServer/programTypes.js.map +1 -0
  43. package/dist/typeServer/programWrapper.d.ts +134 -0
  44. package/dist/typeServer/programWrapper.js +731 -0
  45. package/dist/typeServer/programWrapper.js.map +1 -0
  46. package/dist/typeServer/protocol/tspSupplemental.d.ts +43 -0
  47. package/dist/typeServer/protocol/tspSupplemental.js +48 -0
  48. package/dist/typeServer/protocol/tspSupplemental.js.map +1 -0
  49. package/dist/typeServer/protocol/typeServerProtocol.d.ts +1102 -0
  50. package/dist/typeServer/protocol/typeServerProtocol.js +290 -0
  51. package/dist/typeServer/protocol/typeServerProtocol.js.map +1 -0
  52. package/dist/typeServer/server.d.ts +54 -0
  53. package/dist/typeServer/server.js +620 -0
  54. package/dist/typeServer/server.js.map +1 -0
  55. package/dist/typeServer/serverUtils.d.ts +4 -0
  56. package/dist/typeServer/serverUtils.js +21 -0
  57. package/dist/typeServer/serverUtils.js.map +1 -0
  58. package/dist/typeServer/stubGenerator.d.ts +42 -0
  59. package/dist/typeServer/stubGenerator.js +895 -0
  60. package/dist/typeServer/stubGenerator.js.map +1 -0
  61. package/dist/typeServer/typeCache.d.ts +31 -0
  62. package/dist/typeServer/typeCache.js +54 -0
  63. package/dist/typeServer/typeCache.js.map +1 -0
  64. package/dist/typeServer/typeEvalUtils.d.ts +13 -0
  65. package/dist/typeServer/typeEvalUtils.js +83 -0
  66. package/dist/typeServer/typeEvalUtils.js.map +1 -0
  67. package/dist/typeServer/typeGuards.d.ts +3 -0
  68. package/dist/typeServer/typeGuards.js +55 -0
  69. package/dist/typeServer/typeGuards.js.map +1 -0
  70. package/dist/typeServer/typeServerConversionTypes.d.ts +39 -0
  71. package/dist/typeServer/typeServerConversionTypes.js +448 -0
  72. package/dist/typeServer/typeServerConversionTypes.js.map +1 -0
  73. package/dist/typeServer/typeServerConversionUtils.d.ts +35 -0
  74. package/dist/typeServer/typeServerConversionUtils.js +524 -0
  75. package/dist/typeServer/typeServerConversionUtils.js.map +1 -0
  76. package/dist/typeServer/typeServerEvaluator.d.ts +16 -0
  77. package/dist/typeServer/typeServerEvaluator.js +36 -0
  78. package/dist/typeServer/typeServerEvaluator.js.map +1 -0
  79. package/dist/typeServer/typeServerFileSystem.d.ts +47 -0
  80. package/dist/typeServer/typeServerFileSystem.js +129 -0
  81. package/dist/typeServer/typeServerFileSystem.js.map +1 -0
  82. package/dist/typeServer/typeServerProtocolUtils.d.ts +2 -0
  83. package/dist/typeServer/typeServerProtocolUtils.js +7 -0
  84. package/dist/typeServer/typeServerProtocolUtils.js.map +1 -0
  85. package/dist/typeServer/typeServerServiceKeys.d.ts +7 -0
  86. package/dist/typeServer/typeServerServiceKeys.js +20 -0
  87. package/dist/typeServer/typeServerServiceKeys.js.map +1 -0
  88. package/dist/typeServer/typeUtils.d.ts +4 -0
  89. package/dist/typeServer/typeUtils.js +31 -0
  90. package/dist/typeServer/typeUtils.js.map +1 -0
  91. package/dist/typeServer/virtualFileOverlayFileSystem.d.ts +87 -0
  92. package/dist/typeServer/virtualFileOverlayFileSystem.js +181 -0
  93. package/dist/typeServer/virtualFileOverlayFileSystem.js.map +1 -0
  94. 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