@@ -9,13 +9,14 @@ import { getOSType, getUserHomeDir, OSType } from '../../../../common/utils/plat
9
9
import {
10
10
getPythonSetting ,
11
11
isParentPath ,
12
- pathExists ,
13
- readFile ,
12
+ pathExistsSync ,
13
+ readFileSync ,
14
14
shellExecute ,
15
15
} from '../../../common/externalDependencies' ;
16
16
import { getEnvironmentDirFromPath } from '../../../common/commonUtils' ;
17
17
import { isVirtualenvEnvironment } from './virtualEnvironmentIdentifier' ;
18
18
import { StopWatch } from '../../../../common/utils/stopWatch' ;
19
+ import { cache } from '../../../../common/utils/decorators' ;
19
20
20
21
/**
21
22
* Global virtual env dir for a project is named as:
@@ -59,7 +60,7 @@ async function isLocalPoetryEnvironment(interpreterPath: string): Promise<boolea
59
60
return false ;
60
61
}
61
62
const project = path . dirname ( envDir ) ;
62
- if ( ! ( await hasValidPyprojectToml ( project ) ) ) {
63
+ if ( ! hasValidPyprojectToml ( project ) ) {
63
64
return false ;
64
65
}
65
66
// The assumption is that we need to be able to run poetry CLI for an environment in order to mark it as poetry.
@@ -111,8 +112,16 @@ export class Poetry {
111
112
this . fixCwd ( ) ;
112
113
}
113
114
115
+ /**
116
+ * Returns a Poetry instance corresponding to the binary which can be used to run commands for the cwd.
117
+ *
118
+ * Poetry commands can be slow and so can be bottleneck to overall discovery time. So trigger command
119
+ * execution as soon as possible. To do that we need to ensure the operations before the command are
120
+ * performed synchronously.
121
+ */
114
122
public static async getPoetry ( cwd : string ) : Promise < Poetry | undefined > {
115
- if ( ! ( await hasValidPyprojectToml ( cwd ) ) ) {
123
+ // Following check should be performed synchronously so we trigger poetry execution as soon as possible.
124
+ if ( ! hasValidPyprojectToml ( cwd ) ) {
116
125
// This check is not expensive and may change during a session, so we need not cache it.
117
126
return undefined ;
118
127
}
@@ -123,12 +132,12 @@ export class Poetry {
123
132
return Poetry . _poetryPromise . get ( cwd ) ;
124
133
}
125
134
126
- /**
127
- * Returns a Poetry instance corresponding to the binary.
128
- */
129
135
private static async locate ( cwd : string ) : Promise < Poetry | undefined > {
136
+ // First thing this method awaits on should be poetry command execution, hence perform all operations
137
+ // before that synchronously.
138
+
130
139
// Produce a list of candidate binaries to be probed by exec'ing them.
131
- async function * getCandidates ( ) {
140
+ function * getCandidates ( ) {
132
141
const customPoetryPath = getPythonSetting < string > ( 'poetryPath' ) ;
133
142
if ( customPoetryPath && customPoetryPath !== 'poetry' ) {
134
143
// If user has specified a custom poetry path, use it first.
@@ -139,26 +148,21 @@ export class Poetry {
139
148
const home = getUserHomeDir ( ) ;
140
149
if ( home ) {
141
150
const defaultPoetryPath = path . join ( home , '.poetry' , 'bin' , 'poetry' ) ;
142
- if ( await pathExists ( defaultPoetryPath ) ) {
151
+ if ( pathExistsSync ( defaultPoetryPath ) ) {
143
152
yield defaultPoetryPath ;
144
153
}
145
154
}
146
155
}
147
156
148
157
// Probe the candidates, and pick the first one that exists and does what we need.
149
- for await ( const poetryPath of getCandidates ( ) ) {
158
+ for ( const poetryPath of getCandidates ( ) ) {
150
159
traceVerbose ( `Probing poetry binary for ${ cwd } : ${ poetryPath } ` ) ;
151
160
const poetry = new Poetry ( poetryPath , cwd ) ;
152
- const stopWatch = new StopWatch ( ) ;
153
161
const virtualenvs = await poetry . getEnvList ( ) ;
154
162
if ( virtualenvs !== undefined ) {
155
163
traceVerbose ( `Found poetry via filesystem probing for ${ cwd } : ${ poetryPath } ` ) ;
156
164
return poetry ;
157
165
}
158
- traceVerbose (
159
- `Time taken to run ${ poetryPath } env list --full-path in ms for ${ cwd } ` ,
160
- stopWatch . elapsedTime ,
161
- ) ;
162
166
}
163
167
164
168
// Didn't find anything.
@@ -176,6 +180,15 @@ export class Poetry {
176
180
* Corresponds to "poetry env list --full-path". Swallows errors if any.
177
181
*/
178
182
public async getEnvList ( ) : Promise < string [ ] | undefined > {
183
+ return this . getEnvListCached ( this . cwd ) ;
184
+ }
185
+
186
+ /**
187
+ * Method created to faciliate caching. The caching decorator uses function arguments as cache key,
188
+ * so pass in cwd on which we need to cache.
189
+ */
190
+ @cache ( 30_000 , true , 10_000 )
191
+ private async getEnvListCached ( _cwd : string ) : Promise < string [ ] | undefined > {
179
192
const result = await this . safeShellExecute ( `${ this . _command } env list --full-path` ) ;
180
193
if ( ! result ) {
181
194
return undefined ;
@@ -203,6 +216,15 @@ export class Poetry {
203
216
* Corresponds to "poetry env info -p". Swallows errors if any.
204
217
*/
205
218
public async getActiveEnvPath ( ) : Promise < string | undefined > {
219
+ return this . getActiveEnvPathCached ( this . cwd ) ;
220
+ }
221
+
222
+ /**
223
+ * Method created to faciliate caching. The caching decorator uses function arguments as cache key,
224
+ * so pass in cwd on which we need to cache.
225
+ */
226
+ @cache ( 20_000 , true , 10_000 )
227
+ private async getActiveEnvPathCached ( _cwd : string ) : Promise < string | undefined > {
206
228
const result = await this . safeShellExecute ( `${ this . _command } env info -p` , true ) ;
207
229
if ( ! result ) {
208
230
return undefined ;
@@ -288,12 +310,12 @@ export async function isPoetryEnvironmentRelatedToFolder(
288
310
*
289
311
* @param folder Folder to look for pyproject.toml file in.
290
312
*/
291
- async function hasValidPyprojectToml ( folder : string ) : Promise < boolean > {
313
+ function hasValidPyprojectToml ( folder : string ) : boolean {
292
314
const pyprojectToml = path . join ( folder , 'pyproject.toml' ) ;
293
- if ( ! ( await pathExists ( pyprojectToml ) ) ) {
315
+ if ( ! pathExistsSync ( pyprojectToml ) ) {
294
316
return false ;
295
317
}
296
- const content = await readFile ( pyprojectToml ) ;
318
+ const content = readFileSync ( pyprojectToml ) ;
297
319
if ( ! content . includes ( '[tool.poetry]' ) ) {
298
320
return false ;
299
321
}
0 commit comments