@zzzen/pyright-internal 1.2.0-dev.20250112 → 1.2.0-dev.20250126

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 (103) hide show
  1. package/dist/analyzer/backgroundAnalysisProgram.d.ts +1 -0
  2. package/dist/analyzer/backgroundAnalysisProgram.js +3 -0
  3. package/dist/analyzer/backgroundAnalysisProgram.js.map +1 -1
  4. package/dist/analyzer/binder.js +7 -1
  5. package/dist/analyzer/binder.js.map +1 -1
  6. package/dist/analyzer/checker.d.ts +1 -1
  7. package/dist/analyzer/checker.js +34 -42
  8. package/dist/analyzer/checker.js.map +1 -1
  9. package/dist/analyzer/constructors.js +1 -1
  10. package/dist/analyzer/constructors.js.map +1 -1
  11. package/dist/analyzer/enums.js +140 -126
  12. package/dist/analyzer/enums.js.map +1 -1
  13. package/dist/analyzer/importResolver.d.ts +3 -2
  14. package/dist/analyzer/importResolver.js +80 -79
  15. package/dist/analyzer/importResolver.js.map +1 -1
  16. package/dist/analyzer/pythonPathUtils.d.ts +2 -0
  17. package/dist/analyzer/pythonPathUtils.js +20 -11
  18. package/dist/analyzer/pythonPathUtils.js.map +1 -1
  19. package/dist/analyzer/service.js +2 -5
  20. package/dist/analyzer/service.js.map +1 -1
  21. package/dist/analyzer/typeCacheUtils.js +1 -1
  22. package/dist/analyzer/typeCacheUtils.js.map +1 -1
  23. package/dist/analyzer/typeEvaluator.js +72 -44
  24. package/dist/analyzer/typeEvaluator.js.map +1 -1
  25. package/dist/analyzer/typeEvaluatorTypes.d.ts +3 -1
  26. package/dist/analyzer/typeEvaluatorTypes.js.map +1 -1
  27. package/dist/analyzer/typeGuards.js +24 -17
  28. package/dist/analyzer/typeGuards.js.map +1 -1
  29. package/dist/backgroundAnalysis.js +2 -0
  30. package/dist/backgroundAnalysis.js.map +1 -1
  31. package/dist/backgroundThreadBase.d.ts +1 -0
  32. package/dist/backgroundThreadBase.js +5 -8
  33. package/dist/backgroundThreadBase.js.map +1 -1
  34. package/dist/common/fullAccessHost.d.ts +1 -1
  35. package/dist/common/fullAccessHost.js +1 -1
  36. package/dist/common/languageServerInterface.d.ts +1 -1
  37. package/dist/common/realFileSystem.d.ts +1 -1
  38. package/dist/common/realFileSystem.js +10 -2
  39. package/dist/common/realFileSystem.js.map +1 -1
  40. package/dist/common/serviceProvider.d.ts +1 -0
  41. package/dist/common/serviceProvider.js +8 -0
  42. package/dist/common/serviceProvider.js.map +1 -1
  43. package/dist/languageServerBase.d.ts +3 -3
  44. package/dist/languageServerBase.js +7 -4
  45. package/dist/languageServerBase.js.map +1 -1
  46. package/dist/languageService/analyzerServiceExecutor.js +4 -1
  47. package/dist/languageService/analyzerServiceExecutor.js.map +1 -1
  48. package/dist/languageService/hoverProvider.js +5 -3
  49. package/dist/languageService/hoverProvider.js.map +1 -1
  50. package/dist/languageService/tooltipUtils.d.ts +1 -1
  51. package/dist/languageService/tooltipUtils.js +14 -1
  52. package/dist/languageService/tooltipUtils.js.map +1 -1
  53. package/dist/localization/localize.d.ts +9 -7
  54. package/dist/localization/localize.js +6 -2
  55. package/dist/localization/localize.js.map +1 -1
  56. package/dist/localization/package.nls.cs.json +8 -7
  57. package/dist/localization/package.nls.de.json +8 -7
  58. package/dist/localization/package.nls.en-us.json +21 -5
  59. package/dist/localization/package.nls.es.json +8 -7
  60. package/dist/localization/package.nls.fr.json +8 -7
  61. package/dist/localization/package.nls.it.json +8 -7
  62. package/dist/localization/package.nls.ja.json +8 -7
  63. package/dist/localization/package.nls.ko.json +8 -7
  64. package/dist/localization/package.nls.pl.json +6 -5
  65. package/dist/localization/package.nls.pt-br.json +8 -7
  66. package/dist/localization/package.nls.qps-ploc.json +4 -3
  67. package/dist/localization/package.nls.ru.json +8 -7
  68. package/dist/localization/package.nls.tr.json +6 -5
  69. package/dist/localization/package.nls.zh-cn.json +8 -7
  70. package/dist/localization/package.nls.zh-tw.json +8 -7
  71. package/dist/pyright.js +11 -4
  72. package/dist/pyright.js.map +1 -1
  73. package/dist/server.d.ts +2 -1
  74. package/dist/server.js +1 -1
  75. package/dist/server.js.map +1 -1
  76. package/dist/tests/config.test.js +451 -427
  77. package/dist/tests/config.test.js.map +1 -1
  78. package/dist/tests/harness/fourslash/testState.d.ts +3 -1
  79. package/dist/tests/harness/fourslash/testState.js +23 -23
  80. package/dist/tests/harness/fourslash/testState.js.map +1 -1
  81. package/dist/tests/hoverProvider.test.js +51 -0
  82. package/dist/tests/hoverProvider.test.js.map +1 -1
  83. package/dist/tests/importResolver.test.js +537 -527
  84. package/dist/tests/importResolver.test.js.map +1 -1
  85. package/dist/tests/sourceFile.test.js +1 -0
  86. package/dist/tests/sourceFile.test.js.map +1 -1
  87. package/dist/tests/testUtils.js +1 -0
  88. package/dist/tests/testUtils.js.map +1 -1
  89. package/dist/tests/typeEvaluator1.test.js +1 -1
  90. package/dist/tests/typeEvaluator3.test.js +5 -1
  91. package/dist/tests/typeEvaluator3.test.js.map +1 -1
  92. package/dist/tests/typeEvaluator5.test.js +1 -1
  93. package/dist/tests/typeEvaluator6.test.js +8 -0
  94. package/dist/tests/typeEvaluator6.test.js.map +1 -1
  95. package/dist/tests/typeEvaluator7.test.js +4 -0
  96. package/dist/tests/typeEvaluator7.test.js.map +1 -1
  97. package/dist/tests/uri.test.js +3 -0
  98. package/dist/tests/uri.test.js.map +1 -1
  99. package/dist/tests/zipfs.test.js +2 -0
  100. package/dist/tests/zipfs.test.js.map +1 -1
  101. package/dist/workspaceFactory.d.ts +2 -1
  102. package/dist/workspaceFactory.js.map +1 -1
  103. package/package.json +1 -1
@@ -26,32 +26,235 @@ const libraryRoot = (0, pathUtils_1.combinePaths)((0, pathUtils_1.normalizeSlash
26
26
  function usingTrueVenv() {
27
27
  return process.env.CI_IMPORT_TEST_VENVPATH !== undefined || process.env.CI_IMPORT_TEST_PYTHONPATH !== undefined;
28
28
  }
29
- if (!usingTrueVenv()) {
30
- describe('Import tests that cannot run in a true venv', () => {
31
- test('partial stub file exists', () => {
29
+ describe('Import tests with fake venv', () => {
30
+ const tempFile = new realFileSystem_1.RealTempFile();
31
+ afterAll(() => tempFile.dispose());
32
+ if (!usingTrueVenv()) {
33
+ describe('Import tests that cannot run in a true venv', () => {
34
+ test('partial stub file exists', () => {
35
+ const files = [
36
+ {
37
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib-stubs', 'partialStub.pyi'),
38
+ content: 'def test(): ...',
39
+ },
40
+ {
41
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib-stubs', 'py.typed'),
42
+ content: 'partial\n',
43
+ },
44
+ {
45
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', 'partialStub.py'),
46
+ content: 'def test(): pass',
47
+ },
48
+ ];
49
+ const importResult = getImportResult(files, ['myLib', 'partialStub']);
50
+ (0, assert_1.default)(importResult.isImportFound);
51
+ (0, assert_1.default)(importResult.isStubFile);
52
+ assert_1.default.strictEqual(1, importResult.resolvedUris.filter((f) => !f.isEmpty() && f.getFilePath() === (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', 'partialStub.pyi')).length);
53
+ });
54
+ test('partial stub __init__ exists', () => {
55
+ const files = [
56
+ {
57
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib-stubs', '__init__.pyi'),
58
+ content: 'def test(): ...',
59
+ },
60
+ {
61
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib-stubs', 'py.typed'),
62
+ content: 'partial\n',
63
+ },
64
+ {
65
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', '__init__.py'),
66
+ content: 'def test(): pass',
67
+ },
68
+ ];
69
+ const importResult = getImportResult(files, ['myLib']);
70
+ (0, assert_1.default)(importResult.isImportFound);
71
+ (0, assert_1.default)(importResult.isStubFile);
72
+ assert_1.default.strictEqual(1, importResult.resolvedUris.filter((f) => f.getFilePath() === (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', '__init__.pyi')).length);
73
+ });
74
+ test('stub package', () => {
75
+ const files = [
76
+ {
77
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib-stubs', 'stub.pyi'),
78
+ content: '# empty',
79
+ },
80
+ {
81
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib-stubs', '__init__.pyi'),
82
+ content: '# empty',
83
+ },
84
+ {
85
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', 'partialStub.py'),
86
+ content: 'def test(): pass',
87
+ },
88
+ ];
89
+ // If fully typed stub package exists, that wins over the real package.
90
+ const importResult = getImportResult(files, ['myLib', 'partialStub']);
91
+ (0, assert_1.default)(!importResult.isImportFound);
92
+ });
93
+ test('partial stub package in typing folder', () => {
94
+ const typingFolder = (0, pathUtils_1.combinePaths)((0, pathUtils_1.normalizeSlashes)('/'), 'typing');
95
+ const files = [
96
+ {
97
+ path: (0, pathUtils_1.combinePaths)(typingFolder, 'myLib-stubs', '__init__.pyi'),
98
+ content: 'def test(): ...',
99
+ },
100
+ {
101
+ path: (0, pathUtils_1.combinePaths)(typingFolder, 'myLib-stubs', 'py.typed'),
102
+ content: 'partial\n',
103
+ },
104
+ {
105
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', '__init__.py'),
106
+ content: 'def test(): pass',
107
+ },
108
+ ];
109
+ const importResult = getImportResult(files, ['myLib'], (c) => (c.stubPath = uriUtils_1.UriEx.file(typingFolder)));
110
+ (0, assert_1.default)(importResult.isImportFound);
111
+ (0, assert_1.default)(importResult.isStubFile);
112
+ assert_1.default.strictEqual(1, importResult.resolvedUris.filter((f) => f.getFilePath() === (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', '__init__.pyi')).length);
113
+ });
114
+ test('typeshed folder', () => {
115
+ const typeshedFolder = (0, pathUtils_1.combinePaths)((0, pathUtils_1.normalizeSlashes)('/'), 'ts');
116
+ const files = [
117
+ {
118
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib-stubs', '__init__.pyi'),
119
+ content: 'def test(): ...',
120
+ },
121
+ {
122
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib-stubs', 'py.typed'),
123
+ content: 'partial\n',
124
+ },
125
+ {
126
+ path: (0, pathUtils_1.combinePaths)(typeshedFolder, 'stubs', 'myLibPackage', 'myLib.pyi'),
127
+ content: '# empty',
128
+ },
129
+ {
130
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', '__init__.py'),
131
+ content: 'def test(): pass',
132
+ },
133
+ ];
134
+ // Stub packages win over typeshed.
135
+ const importResult = getImportResult(files, ['myLib'], (c) => (c.typeshedPath = uriUtils_1.UriEx.file(typeshedFolder)));
136
+ (0, assert_1.default)(importResult.isImportFound);
137
+ (0, assert_1.default)(importResult.isStubFile);
138
+ assert_1.default.strictEqual(1, importResult.resolvedUris.filter((f) => f.getFilePath() === (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', '__init__.pyi')).length);
139
+ });
140
+ test('typeshed fallback folder', () => {
141
+ const files = [
142
+ {
143
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib-stubs', '__init__.pyi'),
144
+ content: 'def test(): ...',
145
+ },
146
+ {
147
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib-stubs', 'py.typed'),
148
+ content: 'partial\n',
149
+ },
150
+ {
151
+ path: (0, pathUtils_1.combinePaths)('/', pathConsts_1.typeshedFallback, 'stubs', 'myLibPackage', 'myLib.pyi'),
152
+ content: '# empty',
153
+ },
154
+ {
155
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', '__init__.py'),
156
+ content: 'def test(): pass',
157
+ },
158
+ ];
159
+ // Stub packages win over typeshed.
160
+ const importResult = getImportResult(files, ['myLib']);
161
+ (0, assert_1.default)(importResult.isImportFound);
162
+ (0, assert_1.default)(importResult.isStubFile);
163
+ assert_1.default.strictEqual(1, importResult.resolvedUris.filter((f) => f.getFilePath() === (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', '__init__.pyi')).length);
164
+ });
165
+ test('py.typed file', () => {
166
+ const files = [
167
+ {
168
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib-stubs', '__init__.pyi'),
169
+ content: 'def test(): ...',
170
+ },
171
+ {
172
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib-stubs', 'py.typed'),
173
+ content: 'partial\n',
174
+ },
175
+ {
176
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', '__init__.py'),
177
+ content: 'def test(): pass',
178
+ },
179
+ {
180
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', 'py.typed'),
181
+ content: '# typed',
182
+ },
183
+ ];
184
+ // Partial stub package always overrides original package.
185
+ const importResult = getImportResult(files, ['myLib']);
186
+ (0, assert_1.default)(importResult.isImportFound);
187
+ (0, assert_1.default)(importResult.isStubFile);
188
+ });
189
+ test('py.typed library', () => {
190
+ const files = [
191
+ {
192
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'os', '__init__.py'),
193
+ content: 'def test(): ...',
194
+ },
195
+ {
196
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'os', 'py.typed'),
197
+ content: '',
198
+ },
199
+ {
200
+ path: (0, pathUtils_1.combinePaths)('/', pathConsts_1.typeshedFallback, 'stubs', 'os', 'os', '__init__.pyi'),
201
+ content: '# empty',
202
+ },
203
+ ];
204
+ const importResult = getImportResult(files, ['os']);
205
+ (0, assert_1.default)(importResult.isImportFound);
206
+ assert_1.default.strictEqual(files[0].path, importResult.resolvedUris[importResult.resolvedUris.length - 1].getFilePath());
207
+ });
208
+ test('import side by side file sub under lib folder', () => {
209
+ const files = [
210
+ {
211
+ path: (0, pathUtils_1.combinePaths)('/lib/site-packages/myLib', 'file1.py'),
212
+ content: 'def test1(): ...',
213
+ },
214
+ {
215
+ path: (0, pathUtils_1.combinePaths)('/lib/site-packages/myLib', 'file2.py'),
216
+ content: 'def test2(): ...',
217
+ },
218
+ ];
219
+ const importResult = getImportResult(files, ['file1']);
220
+ (0, assert_1.default)(!importResult.isImportFound);
221
+ });
222
+ });
223
+ test('getModuleNameForImport library file', () => {
32
224
  const files = [
33
225
  {
34
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib-stubs', 'partialStub.pyi'),
35
- content: 'def test(): ...',
226
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', 'myModule', 'file1.py'),
227
+ content: '# empty',
36
228
  },
229
+ ];
230
+ const moduleImportInfo = getModuleNameForImport(files);
231
+ assert_1.default.strictEqual(moduleImportInfo.importType, 1 /* ImportType.ThirdParty */);
232
+ (0, assert_1.default)(!moduleImportInfo.isThirdPartyPyTypedPresent);
233
+ (0, assert_1.default)(!moduleImportInfo.isLocalTypingsFile);
234
+ });
235
+ test('getModuleNameForImport py.typed library file', () => {
236
+ const files = [
37
237
  {
38
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib-stubs', 'py.typed'),
39
- content: 'partial\n',
238
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', 'py.typed'),
239
+ content: '',
40
240
  },
41
241
  {
42
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', 'partialStub.py'),
43
- content: 'def test(): pass',
242
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', 'myModule', 'file1.py'),
243
+ content: '# empty',
44
244
  },
45
245
  ];
46
- const importResult = getImportResult(files, ['myLib', 'partialStub']);
47
- (0, assert_1.default)(importResult.isImportFound);
48
- (0, assert_1.default)(importResult.isStubFile);
49
- assert_1.default.strictEqual(1, importResult.resolvedUris.filter((f) => !f.isEmpty() && f.getFilePath() === (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', 'partialStub.pyi')).length);
246
+ const moduleImportInfo = getModuleNameForImport(files);
247
+ assert_1.default.strictEqual(moduleImportInfo.importType, 1 /* ImportType.ThirdParty */);
248
+ (0, assert_1.default)(moduleImportInfo.isThirdPartyPyTypedPresent);
249
+ (0, assert_1.default)(!moduleImportInfo.isLocalTypingsFile);
50
250
  });
51
- test('partial stub __init__ exists', () => {
251
+ }
252
+ describe('Import tests that can run with or without a true venv', () => {
253
+ test('side by side files', () => {
254
+ const myFile = (0, pathUtils_1.combinePaths)('src', 'file.py');
52
255
  const files = [
53
256
  {
54
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib-stubs', '__init__.pyi'),
257
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib-stubs', 'partialStub.pyi'),
55
258
  content: 'def test(): ...',
56
259
  },
57
260
  {
@@ -59,25 +262,58 @@ if (!usingTrueVenv()) {
59
262
  content: 'partial\n',
60
263
  },
61
264
  {
62
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', '__init__.py'),
265
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', 'partialStub.pyi'),
266
+ content: '# empty',
267
+ },
268
+ {
269
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', 'partialStub.py'),
63
270
  content: 'def test(): pass',
64
271
  },
272
+ {
273
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib-stubs', 'partialStub2.pyi'),
274
+ content: 'def test(): ...',
275
+ },
276
+ {
277
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', 'partialStub2.py'),
278
+ content: 'def test(): pass',
279
+ },
280
+ {
281
+ path: myFile,
282
+ content: '# not used',
283
+ },
65
284
  ];
66
- const importResult = getImportResult(files, ['myLib']);
67
- (0, assert_1.default)(importResult.isImportFound);
68
- (0, assert_1.default)(importResult.isStubFile);
69
- assert_1.default.strictEqual(1, importResult.resolvedUris.filter((f) => f.getFilePath() === (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', '__init__.pyi')).length);
285
+ const sp = createServiceProviderFromFiles(files);
286
+ const configOptions = new configOptions_1.ConfigOptions(uriUtils_1.UriEx.file('/'));
287
+ const importResolver = new importResolver_1.ImportResolver(sp, configOptions, new testAccessHost_1.TestAccessHost(sp.fs().getModulePath(), [uriUtils_1.UriEx.file(libraryRoot)]));
288
+ // Stub package wins over original package (per PEP 561 rules).
289
+ const myUri = uriUtils_1.UriEx.file(myFile);
290
+ const sideBySideResult = importResolver.resolveImport(myUri, configOptions.findExecEnvironment(myUri), {
291
+ leadingDots: 0,
292
+ nameParts: ['myLib', 'partialStub'],
293
+ importedSymbols: new Set(),
294
+ });
295
+ (0, assert_1.default)(sideBySideResult.isImportFound);
296
+ (0, assert_1.default)(sideBySideResult.isStubFile);
297
+ const sideBySideStubFile = uriUtils_1.UriEx.file((0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', 'partialStub.pyi'));
298
+ assert_1.default.strictEqual(1, sideBySideResult.resolvedUris.filter((f) => f.key === sideBySideStubFile.key).length);
299
+ assert_1.default.strictEqual('def test(): ...', sp.fs().readFileSync(sideBySideStubFile, 'utf8'));
300
+ // Side by side stub doesn't completely disable partial stub.
301
+ const partialStubResult = importResolver.resolveImport(myUri, configOptions.findExecEnvironment(myUri), {
302
+ leadingDots: 0,
303
+ nameParts: ['myLib', 'partialStub2'],
304
+ importedSymbols: new Set(),
305
+ });
306
+ (0, assert_1.default)(partialStubResult.isImportFound);
307
+ (0, assert_1.default)(partialStubResult.isStubFile);
308
+ const partialStubFile = uriUtils_1.UriEx.file((0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', 'partialStub2.pyi'));
309
+ assert_1.default.strictEqual(1, partialStubResult.resolvedUris.filter((f) => f.key === partialStubFile.key).length);
70
310
  });
71
- test('stub package', () => {
311
+ test('stub namespace package', () => {
72
312
  const files = [
73
313
  {
74
314
  path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib-stubs', 'stub.pyi'),
75
315
  content: '# empty',
76
316
  },
77
- {
78
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib-stubs', '__init__.pyi'),
79
- content: '# empty',
80
- },
81
317
  {
82
318
  path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', 'partialStub.py'),
83
319
  content: 'def test(): pass',
@@ -85,31 +321,37 @@ if (!usingTrueVenv()) {
85
321
  ];
86
322
  // If fully typed stub package exists, that wins over the real package.
87
323
  const importResult = getImportResult(files, ['myLib', 'partialStub']);
88
- (0, assert_1.default)(!importResult.isImportFound);
324
+ (0, assert_1.default)(importResult.isImportFound);
325
+ (0, assert_1.default)(!importResult.isStubFile);
326
+ assert_1.default.strictEqual(1, importResult.resolvedUris.filter((f) => !f.isEmpty() && f.getFilePath() === (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', 'partialStub.py')).length);
89
327
  });
90
- test('partial stub package in typing folder', () => {
328
+ test('py.typed namespace package plus stubs', () => {
91
329
  const typingFolder = (0, pathUtils_1.combinePaths)((0, pathUtils_1.normalizeSlashes)('/'), 'typing');
92
330
  const files = [
93
331
  {
94
- path: (0, pathUtils_1.combinePaths)(typingFolder, 'myLib-stubs', '__init__.pyi'),
95
- content: 'def test(): ...',
332
+ path: (0, pathUtils_1.combinePaths)(typingFolder, 'myLib/core', 'foo.pyi'),
333
+ content: 'def test(): pass',
96
334
  },
97
335
  {
98
- path: (0, pathUtils_1.combinePaths)(typingFolder, 'myLib-stubs', 'py.typed'),
99
- content: 'partial\n',
336
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', 'py.typed'),
337
+ content: '',
100
338
  },
101
339
  {
102
340
  path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', '__init__.py'),
103
341
  content: 'def test(): pass',
104
342
  },
343
+ {
344
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', '__init__.pyi'),
345
+ content: 'def test(): pass',
346
+ },
105
347
  ];
106
348
  const importResult = getImportResult(files, ['myLib'], (c) => (c.stubPath = uriUtils_1.UriEx.file(typingFolder)));
107
349
  (0, assert_1.default)(importResult.isImportFound);
108
350
  (0, assert_1.default)(importResult.isStubFile);
109
- assert_1.default.strictEqual(1, importResult.resolvedUris.filter((f) => f.getFilePath() === (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', '__init__.pyi')).length);
351
+ assert_1.default.strictEqual(1, importResult.resolvedUris.filter((f) => !f.isEmpty() && f.getFilePath() === (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', '__init__.pyi')).length);
110
352
  });
111
- test('typeshed folder', () => {
112
- const typeshedFolder = (0, pathUtils_1.combinePaths)((0, pathUtils_1.normalizeSlashes)('/'), 'ts');
353
+ test('stub in typing folder over partial stub package', () => {
354
+ const typingFolder = (0, pathUtils_1.combinePaths)((0, pathUtils_1.normalizeSlashes)('/'), 'typing');
113
355
  const files = [
114
356
  {
115
357
  path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib-stubs', '__init__.pyi'),
@@ -120,7 +362,7 @@ if (!usingTrueVenv()) {
120
362
  content: 'partial\n',
121
363
  },
122
364
  {
123
- path: (0, pathUtils_1.combinePaths)(typeshedFolder, 'stubs', 'myLibPackage', 'myLib.pyi'),
365
+ path: (0, pathUtils_1.combinePaths)(typingFolder, 'myLib.pyi'),
124
366
  content: '# empty',
125
367
  },
126
368
  {
@@ -128,561 +370,329 @@ if (!usingTrueVenv()) {
128
370
  content: 'def test(): pass',
129
371
  },
130
372
  ];
131
- // Stub packages win over typeshed.
132
- const importResult = getImportResult(files, ['myLib'], (c) => (c.typeshedPath = uriUtils_1.UriEx.file(typeshedFolder)));
373
+ // If the package exists in typing folder, that gets picked up first.
374
+ const importResult = getImportResult(files, ['myLib'], (c) => (c.stubPath = uriUtils_1.UriEx.file(typingFolder)));
133
375
  (0, assert_1.default)(importResult.isImportFound);
134
376
  (0, assert_1.default)(importResult.isStubFile);
135
- assert_1.default.strictEqual(1, importResult.resolvedUris.filter((f) => f.getFilePath() === (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', '__init__.pyi')).length);
377
+ assert_1.default.strictEqual(0, importResult.resolvedUris.filter((f) => f.getFilePath() === (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', '__init__.pyi')).length);
136
378
  });
137
- test('typeshed fallback folder', () => {
379
+ test('non py.typed library', () => {
138
380
  const files = [
139
381
  {
140
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib-stubs', '__init__.pyi'),
382
+ path: (0, pathUtils_1.combinePaths)(libraryRoot, 'os', '__init__.py'),
141
383
  content: 'def test(): ...',
142
384
  },
143
385
  {
144
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib-stubs', 'py.typed'),
145
- content: 'partial\n',
386
+ path: (0, pathUtils_1.combinePaths)('/', pathConsts_1.typeshedFallback, 'stubs', 'os', 'os', '__init__.pyi'),
387
+ content: '# empty',
146
388
  },
389
+ ];
390
+ const importResult = getImportResult(files, ['os']);
391
+ (0, assert_1.default)(importResult.isImportFound);
392
+ assert_1.default.strictEqual(files[1].path, importResult.resolvedUris[importResult.resolvedUris.length - 1].getFilePath());
393
+ });
394
+ test('no empty import roots', () => {
395
+ const sp = createServiceProviderFromFiles([]);
396
+ const configOptions = new configOptions_1.ConfigOptions(uri_1.Uri.empty()); // Empty, like open-file mode.
397
+ const importResolver = new importResolver_1.ImportResolver(sp, configOptions, new testAccessHost_1.TestAccessHost(sp.fs().getModulePath(), [uriUtils_1.UriEx.file(libraryRoot)]));
398
+ importResolver.getImportRoots(configOptions.getDefaultExecEnvironment()).forEach((path) => (0, assert_1.default)(path));
399
+ });
400
+ test('multiple typeshedFallback', () => {
401
+ const files = [
147
402
  {
148
- path: (0, pathUtils_1.combinePaths)('/', pathConsts_1.typeshedFallback, 'stubs', 'myLibPackage', 'myLib.pyi'),
403
+ path: (0, pathUtils_1.combinePaths)('/', pathConsts_1.typeshedFallback, 'stubs', 'aLib', 'aLib', '__init__.pyi'),
149
404
  content: '# empty',
150
405
  },
151
406
  {
152
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', '__init__.py'),
153
- content: 'def test(): pass',
407
+ path: (0, pathUtils_1.combinePaths)('/', pathConsts_1.typeshedFallback, 'stubs', 'bLib', 'bLib', '__init__.pyi'),
408
+ content: '# empty',
154
409
  },
155
410
  ];
156
- // Stub packages win over typeshed.
157
- const importResult = getImportResult(files, ['myLib']);
158
- (0, assert_1.default)(importResult.isImportFound);
159
- (0, assert_1.default)(importResult.isStubFile);
160
- assert_1.default.strictEqual(1, importResult.resolvedUris.filter((f) => f.getFilePath() === (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', '__init__.pyi')).length);
411
+ const sp = createServiceProviderFromFiles(files);
412
+ const configOptions = new configOptions_1.ConfigOptions(uri_1.Uri.empty()); // Empty, like open-file mode.
413
+ const importResolver = new importResolver_1.ImportResolver(sp, configOptions, new testAccessHost_1.TestAccessHost(sp.fs().getModulePath(), [uriUtils_1.UriEx.file(libraryRoot)]));
414
+ const importRoots = importResolver.getImportRoots(configOptions.getDefaultExecEnvironment());
415
+ assert_1.default.strictEqual(1, importRoots.filter((f) => !f.isEmpty() && f.getFilePath() === (0, pathUtils_1.combinePaths)('/', pathConsts_1.typeshedFallback, 'stubs', 'aLib')).length);
416
+ assert_1.default.strictEqual(1, importRoots.filter((f) => !f.isEmpty() && f.getFilePath() === (0, pathUtils_1.combinePaths)('/', pathConsts_1.typeshedFallback, 'stubs', 'bLib')).length);
161
417
  });
162
- test('py.typed file', () => {
418
+ test('import side by side file root', () => {
163
419
  const files = [
164
420
  {
165
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib-stubs', '__init__.pyi'),
166
- content: 'def test(): ...',
421
+ path: (0, pathUtils_1.combinePaths)('/', 'file1.py'),
422
+ content: 'def test1(): ...',
167
423
  },
168
424
  {
169
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib-stubs', 'py.typed'),
170
- content: 'partial\n',
425
+ path: (0, pathUtils_1.combinePaths)('/', 'file2.py'),
426
+ content: 'def test2(): ...',
171
427
  },
428
+ ];
429
+ const importResult = getImportResult(files, ['file1']);
430
+ (0, assert_1.default)(importResult.isImportFound);
431
+ assert_1.default.strictEqual(1, importResult.resolvedUris.filter((f) => f.getFilePath() === (0, pathUtils_1.combinePaths)('/', 'file1.py')).length);
432
+ });
433
+ test('import side by side file sub folder', () => {
434
+ const files = [
172
435
  {
173
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', '__init__.py'),
174
- content: 'def test(): pass',
436
+ path: (0, pathUtils_1.combinePaths)('/test', 'file1.py'),
437
+ content: 'def test1(): ...',
175
438
  },
176
439
  {
177
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', 'py.typed'),
178
- content: '# typed',
440
+ path: (0, pathUtils_1.combinePaths)('/test', 'file2.py'),
441
+ content: 'def test2(): ...',
179
442
  },
180
443
  ];
181
- // Partial stub package always overrides original package.
182
- const importResult = getImportResult(files, ['myLib']);
444
+ const importResult = getImportResult(files, ['file1']);
183
445
  (0, assert_1.default)(importResult.isImportFound);
184
- (0, assert_1.default)(importResult.isStubFile);
446
+ assert_1.default.strictEqual(1, importResult.resolvedUris.filter((f) => f.getFilePath() === (0, pathUtils_1.combinePaths)('/test', 'file1.py')).length);
185
447
  });
186
- test('py.typed library', () => {
448
+ test('import side by side file sub under src folder', () => {
187
449
  const files = [
188
450
  {
189
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'os', '__init__.py'),
190
- content: 'def test(): ...',
191
- },
192
- {
193
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'os', 'py.typed'),
194
- content: '',
451
+ path: (0, pathUtils_1.combinePaths)('/src/nested', 'file1.py'),
452
+ content: 'def test1(): ...',
195
453
  },
196
454
  {
197
- path: (0, pathUtils_1.combinePaths)('/', pathConsts_1.typeshedFallback, 'stubs', 'os', 'os', '__init__.pyi'),
198
- content: '# empty',
455
+ path: (0, pathUtils_1.combinePaths)('/src/nested', 'file2.py'),
456
+ content: 'def test2(): ...',
199
457
  },
200
458
  ];
201
- const importResult = getImportResult(files, ['os']);
459
+ const importResult = getImportResult(files, ['file1']);
202
460
  (0, assert_1.default)(importResult.isImportFound);
203
- assert_1.default.strictEqual(files[0].path, importResult.resolvedUris[importResult.resolvedUris.length - 1].getFilePath());
461
+ assert_1.default.strictEqual(1, importResult.resolvedUris.filter((f) => f.getFilePath() === (0, pathUtils_1.combinePaths)('/src/nested', 'file1.py'))
462
+ .length);
204
463
  });
205
- test('import side by side file sub under lib folder', () => {
464
+ test('import file sub under containing folder', () => {
206
465
  const files = [
207
466
  {
208
- path: (0, pathUtils_1.combinePaths)('/lib/site-packages/myLib', 'file1.py'),
467
+ path: (0, pathUtils_1.combinePaths)('/src/nested', 'file1.py'),
209
468
  content: 'def test1(): ...',
210
469
  },
211
470
  {
212
- path: (0, pathUtils_1.combinePaths)('/lib/site-packages/myLib', 'file2.py'),
471
+ path: (0, pathUtils_1.combinePaths)('/src/nested/nested2', 'file2.py'),
213
472
  content: 'def test2(): ...',
214
473
  },
215
474
  ];
216
475
  const importResult = getImportResult(files, ['file1']);
217
- (0, assert_1.default)(!importResult.isImportFound);
218
- });
219
- });
220
- test('getModuleNameForImport library file', () => {
221
- const files = [
222
- {
223
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', 'myModule', 'file1.py'),
224
- content: '# empty',
225
- },
226
- ];
227
- const moduleImportInfo = getModuleNameForImport(files);
228
- assert_1.default.strictEqual(moduleImportInfo.importType, 1 /* ImportType.ThirdParty */);
229
- (0, assert_1.default)(!moduleImportInfo.isThirdPartyPyTypedPresent);
230
- (0, assert_1.default)(!moduleImportInfo.isLocalTypingsFile);
231
- });
232
- test('getModuleNameForImport py.typed library file', () => {
233
- const files = [
234
- {
235
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', 'py.typed'),
236
- content: '',
237
- },
238
- {
239
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', 'myModule', 'file1.py'),
240
- content: '# empty',
241
- },
242
- ];
243
- const moduleImportInfo = getModuleNameForImport(files);
244
- assert_1.default.strictEqual(moduleImportInfo.importType, 1 /* ImportType.ThirdParty */);
245
- (0, assert_1.default)(moduleImportInfo.isThirdPartyPyTypedPresent);
246
- (0, assert_1.default)(!moduleImportInfo.isLocalTypingsFile);
247
- });
248
- }
249
- describe('Import tests that can run with or without a true venv', () => {
250
- test('side by side files', () => {
251
- const myFile = (0, pathUtils_1.combinePaths)('src', 'file.py');
252
- const files = [
253
- {
254
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib-stubs', 'partialStub.pyi'),
255
- content: 'def test(): ...',
256
- },
257
- {
258
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib-stubs', 'py.typed'),
259
- content: 'partial\n',
260
- },
261
- {
262
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', 'partialStub.pyi'),
263
- content: '# empty',
264
- },
265
- {
266
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', 'partialStub.py'),
267
- content: 'def test(): pass',
268
- },
269
- {
270
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib-stubs', 'partialStub2.pyi'),
271
- content: 'def test(): ...',
272
- },
273
- {
274
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', 'partialStub2.py'),
275
- content: 'def test(): pass',
276
- },
277
- {
278
- path: myFile,
279
- content: '# not used',
280
- },
281
- ];
282
- const sp = createServiceProviderFromFiles(files);
283
- const configOptions = new configOptions_1.ConfigOptions(uriUtils_1.UriEx.file('/'));
284
- const importResolver = new importResolver_1.ImportResolver(sp, configOptions, new testAccessHost_1.TestAccessHost(sp.fs().getModulePath(), [uriUtils_1.UriEx.file(libraryRoot)]));
285
- // Stub package wins over original package (per PEP 561 rules).
286
- const myUri = uriUtils_1.UriEx.file(myFile);
287
- const sideBySideResult = importResolver.resolveImport(myUri, configOptions.findExecEnvironment(myUri), {
288
- leadingDots: 0,
289
- nameParts: ['myLib', 'partialStub'],
290
- importedSymbols: new Set(),
476
+ (0, assert_1.default)(importResult.isImportFound);
477
+ assert_1.default.strictEqual(1, importResult.resolvedUris.filter((f) => f.getFilePath() === (0, pathUtils_1.combinePaths)('/src/nested', 'file1.py'))
478
+ .length);
291
479
  });
292
- (0, assert_1.default)(sideBySideResult.isImportFound);
293
- (0, assert_1.default)(sideBySideResult.isStubFile);
294
- const sideBySideStubFile = uriUtils_1.UriEx.file((0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', 'partialStub.pyi'));
295
- assert_1.default.strictEqual(1, sideBySideResult.resolvedUris.filter((f) => f.key === sideBySideStubFile.key).length);
296
- assert_1.default.strictEqual('def test(): ...', sp.fs().readFileSync(sideBySideStubFile, 'utf8'));
297
- // Side by side stub doesn't completely disable partial stub.
298
- const partialStubResult = importResolver.resolveImport(myUri, configOptions.findExecEnvironment(myUri), {
299
- leadingDots: 0,
300
- nameParts: ['myLib', 'partialStub2'],
301
- importedSymbols: new Set(),
480
+ test("don't walk up the root", () => {
481
+ const files = [
482
+ {
483
+ path: (0, pathUtils_1.combinePaths)('/', 'file1.py'),
484
+ content: 'def test1(): ...',
485
+ },
486
+ ];
487
+ const importResult = getImportResult(files, ['notExist'], (c) => (c.projectRoot = uri_1.Uri.empty()));
488
+ (0, assert_1.default)(!importResult.isImportFound);
302
489
  });
303
- (0, assert_1.default)(partialStubResult.isImportFound);
304
- (0, assert_1.default)(partialStubResult.isStubFile);
305
- const partialStubFile = uriUtils_1.UriEx.file((0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', 'partialStub2.pyi'));
306
- assert_1.default.strictEqual(1, partialStubResult.resolvedUris.filter((f) => f.key === partialStubFile.key).length);
307
- });
308
- test('stub namespace package', () => {
309
- const files = [
310
- {
311
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib-stubs', 'stub.pyi'),
312
- content: '# empty',
313
- },
314
- {
315
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', 'partialStub.py'),
316
- content: 'def test(): pass',
317
- },
318
- ];
319
- // If fully typed stub package exists, that wins over the real package.
320
- const importResult = getImportResult(files, ['myLib', 'partialStub']);
321
- (0, assert_1.default)(importResult.isImportFound);
322
- (0, assert_1.default)(!importResult.isStubFile);
323
- assert_1.default.strictEqual(1, importResult.resolvedUris.filter((f) => !f.isEmpty() && f.getFilePath() === (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', 'partialStub.py')).length);
324
- });
325
- test('py.typed namespace package plus stubs', () => {
326
- const typingFolder = (0, pathUtils_1.combinePaths)((0, pathUtils_1.normalizeSlashes)('/'), 'typing');
327
- const files = [
328
- {
329
- path: (0, pathUtils_1.combinePaths)(typingFolder, 'myLib/core', 'foo.pyi'),
330
- content: 'def test(): pass',
331
- },
332
- {
333
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', 'py.typed'),
334
- content: '',
335
- },
336
- {
337
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', '__init__.py'),
338
- content: 'def test(): pass',
339
- },
340
- {
341
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', '__init__.pyi'),
342
- content: 'def test(): pass',
343
- },
344
- ];
345
- const importResult = getImportResult(files, ['myLib'], (c) => (c.stubPath = uriUtils_1.UriEx.file(typingFolder)));
346
- (0, assert_1.default)(importResult.isImportFound);
347
- (0, assert_1.default)(importResult.isStubFile);
348
- assert_1.default.strictEqual(1, importResult.resolvedUris.filter((f) => !f.isEmpty() && f.getFilePath() === (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', '__init__.pyi')).length);
349
- });
350
- test('stub in typing folder over partial stub package', () => {
351
- const typingFolder = (0, pathUtils_1.combinePaths)((0, pathUtils_1.normalizeSlashes)('/'), 'typing');
352
- const files = [
353
- {
354
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib-stubs', '__init__.pyi'),
355
- content: 'def test(): ...',
356
- },
357
- {
358
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib-stubs', 'py.typed'),
359
- content: 'partial\n',
360
- },
361
- {
362
- path: (0, pathUtils_1.combinePaths)(typingFolder, 'myLib.pyi'),
363
- content: '# empty',
364
- },
365
- {
366
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', '__init__.py'),
367
- content: 'def test(): pass',
368
- },
369
- ];
370
- // If the package exists in typing folder, that gets picked up first.
371
- const importResult = getImportResult(files, ['myLib'], (c) => (c.stubPath = uriUtils_1.UriEx.file(typingFolder)));
372
- (0, assert_1.default)(importResult.isImportFound);
373
- (0, assert_1.default)(importResult.isStubFile);
374
- assert_1.default.strictEqual(0, importResult.resolvedUris.filter((f) => f.getFilePath() === (0, pathUtils_1.combinePaths)(libraryRoot, 'myLib', '__init__.pyi')).length);
375
- });
376
- test('non py.typed library', () => {
377
- const files = [
378
- {
379
- path: (0, pathUtils_1.combinePaths)(libraryRoot, 'os', '__init__.py'),
380
- content: 'def test(): ...',
381
- },
382
- {
383
- path: (0, pathUtils_1.combinePaths)('/', pathConsts_1.typeshedFallback, 'stubs', 'os', 'os', '__init__.pyi'),
384
- content: '# empty',
385
- },
386
- ];
387
- const importResult = getImportResult(files, ['os']);
388
- (0, assert_1.default)(importResult.isImportFound);
389
- assert_1.default.strictEqual(files[1].path, importResult.resolvedUris[importResult.resolvedUris.length - 1].getFilePath());
390
- });
391
- test('no empty import roots', () => {
392
- const sp = createServiceProviderFromFiles([]);
393
- const configOptions = new configOptions_1.ConfigOptions(uri_1.Uri.empty()); // Empty, like open-file mode.
394
- const importResolver = new importResolver_1.ImportResolver(sp, configOptions, new testAccessHost_1.TestAccessHost(sp.fs().getModulePath(), [uriUtils_1.UriEx.file(libraryRoot)]));
395
- importResolver.getImportRoots(configOptions.getDefaultExecEnvironment()).forEach((path) => (0, assert_1.default)(path));
396
- });
397
- test('multiple typeshedFallback', () => {
398
- const files = [
399
- {
400
- path: (0, pathUtils_1.combinePaths)('/', pathConsts_1.typeshedFallback, 'stubs', 'aLib', 'aLib', '__init__.pyi'),
401
- content: '# empty',
402
- },
403
- {
404
- path: (0, pathUtils_1.combinePaths)('/', pathConsts_1.typeshedFallback, 'stubs', 'bLib', 'bLib', '__init__.pyi'),
405
- content: '# empty',
406
- },
407
- ];
408
- const sp = createServiceProviderFromFiles(files);
409
- const configOptions = new configOptions_1.ConfigOptions(uri_1.Uri.empty()); // Empty, like open-file mode.
410
- const importResolver = new importResolver_1.ImportResolver(sp, configOptions, new testAccessHost_1.TestAccessHost(sp.fs().getModulePath(), [uriUtils_1.UriEx.file(libraryRoot)]));
411
- const importRoots = importResolver.getImportRoots(configOptions.getDefaultExecEnvironment());
412
- assert_1.default.strictEqual(1, importRoots.filter((f) => !f.isEmpty() && f.getFilePath() === (0, pathUtils_1.combinePaths)('/', pathConsts_1.typeshedFallback, 'stubs', 'aLib')).length);
413
- assert_1.default.strictEqual(1, importRoots.filter((f) => !f.isEmpty() && f.getFilePath() === (0, pathUtils_1.combinePaths)('/', pathConsts_1.typeshedFallback, 'stubs', 'bLib')).length);
414
- });
415
- test('import side by side file root', () => {
416
- const files = [
417
- {
418
- path: (0, pathUtils_1.combinePaths)('/', 'file1.py'),
419
- content: 'def test1(): ...',
420
- },
421
- {
422
- path: (0, pathUtils_1.combinePaths)('/', 'file2.py'),
423
- content: 'def test2(): ...',
424
- },
425
- ];
426
- const importResult = getImportResult(files, ['file1']);
427
- (0, assert_1.default)(importResult.isImportFound);
428
- assert_1.default.strictEqual(1, importResult.resolvedUris.filter((f) => f.getFilePath() === (0, pathUtils_1.combinePaths)('/', 'file1.py')).length);
429
- });
430
- test('import side by side file sub folder', () => {
431
- const files = [
432
- {
433
- path: (0, pathUtils_1.combinePaths)('/test', 'file1.py'),
434
- content: 'def test1(): ...',
435
- },
436
- {
437
- path: (0, pathUtils_1.combinePaths)('/test', 'file2.py'),
438
- content: 'def test2(): ...',
439
- },
440
- ];
441
- const importResult = getImportResult(files, ['file1']);
442
- (0, assert_1.default)(importResult.isImportFound);
443
- assert_1.default.strictEqual(1, importResult.resolvedUris.filter((f) => f.getFilePath() === (0, pathUtils_1.combinePaths)('/test', 'file1.py')).length);
444
- });
445
- test('import side by side file sub under src folder', () => {
446
- const files = [
447
- {
448
- path: (0, pathUtils_1.combinePaths)('/src/nested', 'file1.py'),
449
- content: 'def test1(): ...',
450
- },
451
- {
452
- path: (0, pathUtils_1.combinePaths)('/src/nested', 'file2.py'),
453
- content: 'def test2(): ...',
454
- },
455
- ];
456
- const importResult = getImportResult(files, ['file1']);
457
- (0, assert_1.default)(importResult.isImportFound);
458
- assert_1.default.strictEqual(1, importResult.resolvedUris.filter((f) => f.getFilePath() === (0, pathUtils_1.combinePaths)('/src/nested', 'file1.py')).length);
459
- });
460
- test('import file sub under containing folder', () => {
461
- const files = [
462
- {
463
- path: (0, pathUtils_1.combinePaths)('/src/nested', 'file1.py'),
464
- content: 'def test1(): ...',
465
- },
466
- {
467
- path: (0, pathUtils_1.combinePaths)('/src/nested/nested2', 'file2.py'),
468
- content: 'def test2(): ...',
469
- },
470
- ];
471
- const importResult = getImportResult(files, ['file1']);
472
- (0, assert_1.default)(importResult.isImportFound);
473
- assert_1.default.strictEqual(1, importResult.resolvedUris.filter((f) => f.getFilePath() === (0, pathUtils_1.combinePaths)('/src/nested', 'file1.py')).length);
474
- });
475
- test("don't walk up the root", () => {
476
- const files = [
477
- {
478
- path: (0, pathUtils_1.combinePaths)('/', 'file1.py'),
479
- content: 'def test1(): ...',
480
- },
481
- ];
482
- const importResult = getImportResult(files, ['notExist'], (c) => (c.projectRoot = uri_1.Uri.empty()));
483
- (0, assert_1.default)(!importResult.isImportFound);
484
- });
485
- test('nested namespace package 1', () => {
486
- const files = [
487
- {
488
- path: (0, pathUtils_1.combinePaths)('/', 'packages1', 'a', 'b', 'c', 'd.py'),
489
- content: 'def f(): pass',
490
- },
491
- {
492
- path: (0, pathUtils_1.combinePaths)('/', 'packages1', 'a', '__init__.py'),
493
- content: '',
494
- },
495
- {
496
- path: (0, pathUtils_1.combinePaths)('/', 'packages2', 'a', '__init__.py'),
497
- content: '',
498
- },
499
- ];
500
- const importResult = getImportResult(files, ['a', 'b', 'c', 'd'], (config) => {
501
- config.defaultExtraPaths = [
502
- uriUtils_1.UriEx.file((0, pathUtils_1.combinePaths)('/', 'packages1')),
503
- uriUtils_1.UriEx.file((0, pathUtils_1.combinePaths)('/', 'packages2')),
490
+ test('nested namespace package 1', () => {
491
+ const files = [
492
+ {
493
+ path: (0, pathUtils_1.combinePaths)('/', 'packages1', 'a', 'b', 'c', 'd.py'),
494
+ content: 'def f(): pass',
495
+ },
496
+ {
497
+ path: (0, pathUtils_1.combinePaths)('/', 'packages1', 'a', '__init__.py'),
498
+ content: '',
499
+ },
500
+ {
501
+ path: (0, pathUtils_1.combinePaths)('/', 'packages2', 'a', '__init__.py'),
502
+ content: '',
503
+ },
504
504
  ];
505
+ const importResult = getImportResult(files, ['a', 'b', 'c', 'd'], (config) => {
506
+ config.defaultExtraPaths = [
507
+ uriUtils_1.UriEx.file((0, pathUtils_1.combinePaths)('/', 'packages1')),
508
+ uriUtils_1.UriEx.file((0, pathUtils_1.combinePaths)('/', 'packages2')),
509
+ ];
510
+ });
511
+ (0, assert_1.default)(importResult.isImportFound);
505
512
  });
506
- (0, assert_1.default)(importResult.isImportFound);
507
- });
508
- test('nested namespace package 2', () => {
509
- const files = [
510
- {
511
- path: (0, pathUtils_1.combinePaths)('/', 'packages1', 'a', 'b', 'c', 'd.py'),
512
- content: 'def f(): pass',
513
- },
514
- {
515
- path: (0, pathUtils_1.combinePaths)('/', 'packages1', 'a', 'b', 'c', '__init__.py'),
516
- content: '',
517
- },
518
- {
519
- path: (0, pathUtils_1.combinePaths)('/', 'packages2', 'a', 'b', 'c', '__init__.py'),
520
- content: '',
521
- },
522
- ];
523
- const importResult = getImportResult(files, ['a', 'b', 'c', 'd'], (config) => {
524
- config.defaultExtraPaths = [
525
- uriUtils_1.UriEx.file((0, pathUtils_1.combinePaths)('/', 'packages1')),
526
- uriUtils_1.UriEx.file((0, pathUtils_1.combinePaths)('/', 'packages2')),
513
+ test('nested namespace package 2', () => {
514
+ const files = [
515
+ {
516
+ path: (0, pathUtils_1.combinePaths)('/', 'packages1', 'a', 'b', 'c', 'd.py'),
517
+ content: 'def f(): pass',
518
+ },
519
+ {
520
+ path: (0, pathUtils_1.combinePaths)('/', 'packages1', 'a', 'b', 'c', '__init__.py'),
521
+ content: '',
522
+ },
523
+ {
524
+ path: (0, pathUtils_1.combinePaths)('/', 'packages2', 'a', 'b', 'c', '__init__.py'),
525
+ content: '',
526
+ },
527
527
  ];
528
+ const importResult = getImportResult(files, ['a', 'b', 'c', 'd'], (config) => {
529
+ config.defaultExtraPaths = [
530
+ uriUtils_1.UriEx.file((0, pathUtils_1.combinePaths)('/', 'packages1')),
531
+ uriUtils_1.UriEx.file((0, pathUtils_1.combinePaths)('/', 'packages2')),
532
+ ];
533
+ });
534
+ (0, assert_1.default)(importResult.isImportFound);
528
535
  });
529
- (0, assert_1.default)(importResult.isImportFound);
530
- });
531
- test('nested namespace package 3', () => {
532
- const files = [
533
- {
534
- path: (0, pathUtils_1.combinePaths)('/', 'packages1', 'a', 'b', 'c', 'd.py'),
535
- content: 'def f(): pass',
536
- },
537
- {
538
- path: (0, pathUtils_1.combinePaths)('/', 'packages2', 'a', '__init__.py'),
539
- content: '',
540
- },
541
- ];
542
- const importResult = getImportResult(files, ['a', 'b', 'c', 'd'], (config) => {
543
- config.defaultExtraPaths = [
544
- uriUtils_1.UriEx.file((0, pathUtils_1.combinePaths)('/', 'packages1')),
545
- uriUtils_1.UriEx.file((0, pathUtils_1.combinePaths)('/', 'packages2')),
536
+ test('nested namespace package 3', () => {
537
+ const files = [
538
+ {
539
+ path: (0, pathUtils_1.combinePaths)('/', 'packages1', 'a', 'b', 'c', 'd.py'),
540
+ content: 'def f(): pass',
541
+ },
542
+ {
543
+ path: (0, pathUtils_1.combinePaths)('/', 'packages2', 'a', '__init__.py'),
544
+ content: '',
545
+ },
546
546
  ];
547
+ const importResult = getImportResult(files, ['a', 'b', 'c', 'd'], (config) => {
548
+ config.defaultExtraPaths = [
549
+ uriUtils_1.UriEx.file((0, pathUtils_1.combinePaths)('/', 'packages1')),
550
+ uriUtils_1.UriEx.file((0, pathUtils_1.combinePaths)('/', 'packages2')),
551
+ ];
552
+ });
553
+ (0, assert_1.default)(!importResult.isImportFound);
547
554
  });
548
- (0, assert_1.default)(!importResult.isImportFound);
549
- });
550
- test('nested namespace package 4', () => {
551
- const files = [
552
- {
553
- path: (0, pathUtils_1.combinePaths)('/', 'packages1', 'a', 'b', '__init__.py'),
554
- content: '',
555
- },
556
- {
557
- path: (0, pathUtils_1.combinePaths)('/', 'packages1', 'a', 'b', 'c.py'),
558
- content: 'def f(): pass',
559
- },
560
- {
561
- path: (0, pathUtils_1.combinePaths)('/', 'packages2', 'a', '__init__.py'),
562
- content: '',
563
- },
564
- {
565
- path: (0, pathUtils_1.combinePaths)('/', 'packages2', 'a', 'b', '__init__.py'),
566
- content: '',
567
- },
568
- ];
569
- const importResult = getImportResult(files, ['a', 'b', 'c'], (config) => {
570
- config.defaultExtraPaths = [
571
- uriUtils_1.UriEx.file((0, pathUtils_1.combinePaths)('/', 'packages1')),
572
- uriUtils_1.UriEx.file((0, pathUtils_1.combinePaths)('/', 'packages2')),
555
+ test('nested namespace package 4', () => {
556
+ const files = [
557
+ {
558
+ path: (0, pathUtils_1.combinePaths)('/', 'packages1', 'a', 'b', '__init__.py'),
559
+ content: '',
560
+ },
561
+ {
562
+ path: (0, pathUtils_1.combinePaths)('/', 'packages1', 'a', 'b', 'c.py'),
563
+ content: 'def f(): pass',
564
+ },
565
+ {
566
+ path: (0, pathUtils_1.combinePaths)('/', 'packages2', 'a', '__init__.py'),
567
+ content: '',
568
+ },
569
+ {
570
+ path: (0, pathUtils_1.combinePaths)('/', 'packages2', 'a', 'b', '__init__.py'),
571
+ content: '',
572
+ },
573
573
  ];
574
+ const importResult = getImportResult(files, ['a', 'b', 'c'], (config) => {
575
+ config.defaultExtraPaths = [
576
+ uriUtils_1.UriEx.file((0, pathUtils_1.combinePaths)('/', 'packages1')),
577
+ uriUtils_1.UriEx.file((0, pathUtils_1.combinePaths)('/', 'packages2')),
578
+ ];
579
+ });
580
+ (0, assert_1.default)(!importResult.isImportFound);
574
581
  });
575
- (0, assert_1.default)(!importResult.isImportFound);
576
- });
577
- test('default workspace importing side by side file', () => {
578
- const files = [
579
- {
580
- path: (0, pathUtils_1.combinePaths)('/', 'src', 'a', 'b', 'file1.py'),
581
- content: 'import file2',
582
- },
583
- {
584
- path: (0, pathUtils_1.combinePaths)('/', 'src', 'a', 'b', 'file2.py'),
585
- content: 'def f(): pass',
586
- },
587
- ];
588
- const importResult = getImportResult(files, ['file2'], (config) => {
589
- config.projectRoot = uri_1.Uri.defaultWorkspace({ isCaseSensitive: () => true });
582
+ test('default workspace importing side by side file', () => {
583
+ const files = [
584
+ {
585
+ path: (0, pathUtils_1.combinePaths)('/', 'src', 'a', 'b', 'file1.py'),
586
+ content: 'import file2',
587
+ },
588
+ {
589
+ path: (0, pathUtils_1.combinePaths)('/', 'src', 'a', 'b', 'file2.py'),
590
+ content: 'def f(): pass',
591
+ },
592
+ ];
593
+ const importResult = getImportResult(files, ['file2'], (config) => {
594
+ config.projectRoot = uri_1.Uri.defaultWorkspace({ isCaseSensitive: () => true });
595
+ });
596
+ (0, assert_1.default)(importResult.isImportFound);
590
597
  });
591
- (0, assert_1.default)(importResult.isImportFound);
592
- });
593
- test('getModuleNameForImport user file', () => {
594
- const files = [
595
- {
596
- path: (0, pathUtils_1.combinePaths)('/', 'src', 'file1.py'),
597
- content: '# empty',
598
- },
599
- ];
600
- const moduleImportInfo = getModuleNameForImport(files);
601
- assert_1.default.strictEqual(moduleImportInfo.importType, 2 /* ImportType.Local */);
602
- (0, assert_1.default)(!moduleImportInfo.isThirdPartyPyTypedPresent);
603
- (0, assert_1.default)(!moduleImportInfo.isLocalTypingsFile);
604
- });
605
- });
606
- if (usingTrueVenv()) {
607
- describe('Import tests that have to run with a venv', () => {
608
- test('venv can find imports', () => {
598
+ test('getModuleNameForImport user file', () => {
609
599
  const files = [
610
600
  {
611
- path: (0, pathUtils_1.combinePaths)('/', 'file1.py'),
612
- content: 'import pytest',
601
+ path: (0, pathUtils_1.combinePaths)('/', 'src', 'file1.py'),
602
+ content: '# empty',
613
603
  },
614
604
  ];
615
- const importResult = getImportResult(files, ['pytest']);
616
- (0, assert_1.default)(importResult.isImportFound, `Import not found: ${importResult.importFailureInfo?.join('\n')}`);
605
+ const moduleImportInfo = getModuleNameForImport(files);
606
+ assert_1.default.strictEqual(moduleImportInfo.importType, 2 /* ImportType.Local */);
607
+ (0, assert_1.default)(!moduleImportInfo.isThirdPartyPyTypedPresent);
608
+ (0, assert_1.default)(!moduleImportInfo.isLocalTypingsFile);
617
609
  });
618
610
  });
619
- }
620
- function getImportResult(files, nameParts, setup) {
621
- const { importResolver, uri, configOptions } = setupImportResolver(files, setup);
622
- const importResult = importResolver.resolveImport(uri, configOptions.findExecEnvironment(uri), {
623
- leadingDots: 0,
624
- nameParts: nameParts,
625
- importedSymbols: new Set(),
626
- });
627
- // Add the config venvpath to the import result so we can output it on failure.
628
- if (!importResult.isImportFound) {
629
- importResult.importFailureInfo = importResult.importFailureInfo ?? [];
630
- importResult.importFailureInfo.push(`venvPath: ${configOptions.venvPath}`);
611
+ if (usingTrueVenv()) {
612
+ describe('Import tests that have to run with a venv', () => {
613
+ test('venv can find imports', () => {
614
+ const tempFile = new realFileSystem_1.RealTempFile();
615
+ const files = [
616
+ {
617
+ path: (0, pathUtils_1.combinePaths)('/', 'file1.py'),
618
+ content: 'import pytest',
619
+ },
620
+ ];
621
+ const importResult = getImportResult(files, ['pytest']);
622
+ (0, assert_1.default)(importResult.isImportFound, `Import not found: ${importResult.importFailureInfo?.join('\n')}`);
623
+ tempFile.dispose();
624
+ });
625
+ });
631
626
  }
632
- return importResult;
633
- }
634
- function getModuleNameForImport(files, setup) {
635
- const { importResolver, uri, configOptions } = setupImportResolver(files, setup);
636
- const moduleImportInfo = importResolver.getModuleNameForImport(uri, configOptions.findExecEnvironment(uri), undefined,
637
- /* detectPyTyped */ true);
638
- return moduleImportInfo;
639
- }
640
- function setupImportResolver(files, setup) {
641
- const defaultHostFactory = (sp) => new testAccessHost_1.TestAccessHost(sp.fs().getModulePath(), [uriUtils_1.UriEx.file(libraryRoot)]);
642
- const defaultSetup = setup ??
643
- ((c) => {
644
- /* empty */
627
+ function getImportResult(files, nameParts, setup) {
628
+ const { importResolver, uri, configOptions } = setupImportResolver(files, setup);
629
+ const importResult = importResolver.resolveImport(uri, configOptions.findExecEnvironment(uri), {
630
+ leadingDots: 0,
631
+ nameParts: nameParts,
632
+ importedSymbols: new Set(),
645
633
  });
646
- const defaultSpFactory = (files) => createServiceProviderFromFiles(files);
647
- // Use environment variables to determine how to create a host and how to modify the config options.
648
- // These are set in the CI to test imports with different options.
649
- let hostFactory = defaultHostFactory;
650
- let configModifier = defaultSetup;
651
- let spFactory = defaultSpFactory;
652
- if (process.env.CI_IMPORT_TEST_VENVPATH) {
653
- configModifier = (c) => {
654
- defaultSetup(c);
655
- c.venvPath = uriUtils_1.UriEx.file(process.env.CI_IMPORT_TEST_VENVPATH,
656
- /* isCaseSensitive */ true,
657
- /* checkRelative */ true);
658
- c.venv = process.env.CI_IMPORT_TEST_VENV;
659
- };
660
- spFactory = (files) => createServiceProviderWithCombinedFs(files);
634
+ // Add the config venvpath to the import result so we can output it on failure.
635
+ if (!importResult.isImportFound) {
636
+ importResult.importFailureInfo = importResult.importFailureInfo ?? [];
637
+ importResult.importFailureInfo.push(`venvPath: ${configOptions.venvPath}`);
638
+ }
639
+ return importResult;
661
640
  }
662
- else if (process.env.CI_IMPORT_TEST_PYTHONPATH) {
663
- configModifier = (c) => {
664
- defaultSetup(c);
665
- c.pythonPath = uriUtils_1.UriEx.file(process.env.CI_IMPORT_TEST_PYTHONPATH,
666
- /* isCaseSensitive */ true,
667
- /* checkRelative */ true);
668
- };
669
- hostFactory = (sp) => new TruePythonTestAccessHost(sp);
670
- spFactory = (files) => createServiceProviderWithCombinedFs(files);
641
+ function getModuleNameForImport(files, setup) {
642
+ const { importResolver, uri, configOptions } = setupImportResolver(files, setup);
643
+ const moduleImportInfo = importResolver.getModuleNameForImport(uri, configOptions.findExecEnvironment(uri), undefined,
644
+ /* detectPyTyped */ true);
645
+ return moduleImportInfo;
671
646
  }
672
- const sp = spFactory(files);
673
- const configOptions = new configOptions_1.ConfigOptions(uriUtils_1.UriEx.file('/'));
674
- configModifier(configOptions);
675
- const file = files.length > 0 ? files[files.length - 1].path : (0, pathUtils_1.combinePaths)('src', 'file.py');
676
- if (files.length === 0) {
677
- files.push({
678
- path: file,
679
- content: '# not used',
680
- });
647
+ function setupImportResolver(files, setup) {
648
+ const defaultHostFactory = (sp) => new testAccessHost_1.TestAccessHost(sp.fs().getModulePath(), [uriUtils_1.UriEx.file(libraryRoot)]);
649
+ const defaultSetup = setup ??
650
+ ((c) => {
651
+ /* empty */
652
+ });
653
+ const defaultSpFactory = (files) => createServiceProviderFromFiles(files);
654
+ // Use environment variables to determine how to create a host and how to modify the config options.
655
+ // These are set in the CI to test imports with different options.
656
+ let hostFactory = defaultHostFactory;
657
+ let configModifier = defaultSetup;
658
+ let spFactory = defaultSpFactory;
659
+ if (process.env.CI_IMPORT_TEST_VENVPATH) {
660
+ configModifier = (c) => {
661
+ defaultSetup(c);
662
+ c.venvPath = uriUtils_1.UriEx.file(process.env.CI_IMPORT_TEST_VENVPATH,
663
+ /* isCaseSensitive */ true,
664
+ /* checkRelative */ true);
665
+ c.venv = process.env.CI_IMPORT_TEST_VENV;
666
+ };
667
+ spFactory = (files) => createServiceProviderWithCombinedFs(files);
668
+ }
669
+ else if (process.env.CI_IMPORT_TEST_PYTHONPATH) {
670
+ configModifier = (c) => {
671
+ defaultSetup(c);
672
+ c.pythonPath = uriUtils_1.UriEx.file(process.env.CI_IMPORT_TEST_PYTHONPATH,
673
+ /* isCaseSensitive */ true,
674
+ /* checkRelative */ true);
675
+ };
676
+ hostFactory = (sp) => {
677
+ return new TruePythonTestAccessHost(sp, tempFile);
678
+ };
679
+ spFactory = (files) => createServiceProviderWithCombinedFs(files);
680
+ }
681
+ const sp = spFactory(files);
682
+ const configOptions = new configOptions_1.ConfigOptions(uriUtils_1.UriEx.file('/'));
683
+ configModifier(configOptions);
684
+ const file = files.length > 0 ? files[files.length - 1].path : (0, pathUtils_1.combinePaths)('src', 'file.py');
685
+ if (files.length === 0) {
686
+ files.push({
687
+ path: file,
688
+ content: '# not used',
689
+ });
690
+ }
691
+ const uri = uriUtils_1.UriEx.file(file);
692
+ const importResolver = new importResolver_1.ImportResolver(sp, configOptions, hostFactory(sp));
693
+ return { importResolver, uri, configOptions };
681
694
  }
682
- const uri = uriUtils_1.UriEx.file(file);
683
- const importResolver = new importResolver_1.ImportResolver(sp, configOptions, hostFactory(sp));
684
- return { importResolver, uri, configOptions };
685
- }
695
+ });
686
696
  function createTestFileSystem(files) {
687
697
  const fs = new filesystem_1.TestFileSystem(/* ignoreCase */ false, { cwd: (0, pathUtils_1.normalizeSlashes)('/') });
688
698
  for (const file of files) {
@@ -704,11 +714,11 @@ function createServiceProviderWithCombinedFs(files) {
704
714
  return (0, serviceProviderExtensions_1.createServiceProvider)(testFS, fs);
705
715
  }
706
716
  class TruePythonTestAccessHost extends fullAccessHost_1.FullAccessHost {
707
- constructor(sp) {
708
- // Make sure the service provide in use is using a real file system and real temporary file provider.
717
+ constructor(sp, tempFile) {
709
718
  const clone = sp.clone();
710
- clone.add(serviceKeys_1.ServiceKeys.fs, (0, realFileSystem_1.createFromRealFileSystem)(sp.get(serviceKeys_1.ServiceKeys.caseSensitivityDetector)));
711
- clone.add(serviceKeys_1.ServiceKeys.tempFile, new realFileSystem_1.RealTempFile());
719
+ // Make sure the service provide in use is using a real file system and real temporary file provider.
720
+ clone.add(serviceKeys_1.ServiceKeys.tempFile, tempFile);
721
+ clone.add(serviceKeys_1.ServiceKeys.fs, (0, realFileSystem_1.createFromRealFileSystem)(tempFile));
712
722
  super(clone);
713
723
  }
714
724
  }