@@ -6,8 +6,11 @@ import * as child_process from "child_process";
6
6
import * as settings from "./configSettings" ;
7
7
8
8
let pythonInterpretterDirectory : string = null ;
9
+ let previouslyIdentifiedPythonPath : string = null ;
10
+
9
11
export function getPythonInterpreterDirectory ( ) : Promise < string > {
10
- if ( pythonInterpretterDirectory ) {
12
+ // If we already have it and the python path hasn't changed, yay
13
+ if ( pythonInterpretterDirectory && previouslyIdentifiedPythonPath === settings . PythonSettings . getInstance ( ) . pythonPath ) {
11
14
return Promise . resolve ( pythonInterpretterDirectory ) ;
12
15
}
13
16
@@ -22,74 +25,46 @@ export function getPythonInterpreterDirectory(): Promise<string> {
22
25
23
26
// If we can execute the python, then get the path from the fullyqualitified name
24
27
child_process . execFile ( pythonFileName , [ "-c" , "print(1234)" ] , ( error , stdout , stderr ) => {
28
+ // Yes this is a valid python path
25
29
if ( stdout . startsWith ( "1234" ) ) {
26
30
return resolve ( path . dirname ( pythonFileName ) ) ;
27
31
}
32
+ // No idea, didn't work, hence don't reject, but return empty path
28
33
resolve ( "" ) ;
29
34
} ) ;
30
35
} ) . then ( value => {
36
+ // Cache and return
37
+ previouslyIdentifiedPythonPath = settings . PythonSettings . getInstance ( ) . pythonPath ;
31
38
return pythonInterpretterDirectory = value ;
32
39
} ) . catch ( ( ) => {
33
40
// Don't care what the error is, all we know is that this doesn't work
34
41
return pythonInterpretterDirectory = "" ;
35
42
} ) ;
36
43
}
37
44
38
- const IN_VALID_FILE_PATHS : Map < string , boolean > = new Map < string , boolean > ( ) ;
39
-
40
45
export function execPythonFile ( file : string , args : string [ ] , cwd : string , includeErrorAsResponse : boolean = false ) : Promise < string > {
41
- // Whether to try executing the command without prefixing it with the python path
42
- let tryUsingCommandArg = false ;
43
- let fullyQualifiedFilePromise = getPythonInterpreterDirectory ( ) . then ( pyPath => {
44
- let pythonIntepreterPath = pyPath ;
45
- let fullyQualifiedFile = file ;
46
+ return getPythonInterpreterDirectory ( ) . then ( pyPath => {
47
+ let options : any = { cwd : cwd } ;
46
48
47
- // Qualify the command with the python path
48
- if ( pythonIntepreterPath . length > 0 && ! file . startsWith ( pyPath ) ) {
49
- fullyQualifiedFile = pythonIntepreterPath + ( pythonIntepreterPath . endsWith ( path . sep ) ? "" : path . sep ) + file ;
50
-
51
- // Check if we know whether this trow ENONE errors
52
- if ( IN_VALID_FILE_PATHS . has ( fullyQualifiedFile ) ) {
53
- fullyQualifiedFile = file ;
54
- }
55
- else {
56
- tryUsingCommandArg = true ;
57
- }
49
+ // If we have a fully qualitified path to the python executable directory, then we can use that
50
+ // as the base path for all other executables
51
+ if ( pyPath . length > 0 ) {
52
+ options . env = mergeEnvVariables ( { PATH : pyPath + path . delimiter + process . env . PATH } ) ;
58
53
}
59
54
60
- return execFileInternal ( fullyQualifiedFile , args , cwd , includeErrorAsResponse , true ) ;
55
+ return execFileInternal ( file , args , options , includeErrorAsResponse ) ;
61
56
} ) ;
62
-
63
- if ( tryUsingCommandArg ) {
64
- return fullyQualifiedFilePromise . catch ( error => {
65
- if ( error && ( < any > error ) . code === "ENOENT" ) {
66
- // Re-execute the file, without the python path prefix
67
- // Only if we know that the previous one failed with ENOENT
68
- return execFileInternal ( file , args , cwd , includeErrorAsResponse , true ) ;
69
- }
70
- Promise . reject ( error ) ;
71
- } ) ;
72
- }
73
- else {
74
- return fullyQualifiedFilePromise ;
75
- }
76
57
}
77
58
78
- function execFileInternal ( file : string , args : string [ ] , cwd : string , includeErrorAsResponse : boolean , rejectIfENOENTErrors : boolean = false ) : Promise < string > {
59
+ function execFileInternal ( file : string , args : string [ ] , options : child_process . ExecFileOptions , includeErrorAsResponse : boolean ) : Promise < string > {
79
60
return new Promise < string > ( ( resolve , reject ) => {
80
- child_process . execFile ( file , args , { cwd : cwd } , ( error , stdout , stderr ) => {
61
+ child_process . execFile ( file , args , options , ( error , stdout , stderr ) => {
81
62
// pylint:
82
63
// In the case of pylint we have some messages (such as config file not found and using default etc...) being returned in stderr
83
64
// These error messages are useless when using pylint
84
65
if ( includeErrorAsResponse && ( stdout . length > 0 || stderr . length > 0 ) ) {
85
66
return resolve ( stdout + "\n" + stderr ) ;
86
67
}
87
- if ( error && ( < any > error ) . code === "ENOENT" && rejectIfENOENTErrors ) {
88
- if ( ! IN_VALID_FILE_PATHS . has ( file ) ) {
89
- IN_VALID_FILE_PATHS . set ( file , true ) ;
90
- }
91
- return reject ( error ) ;
92
- }
93
68
94
69
let hasErrors = ( error && error . message . length > 0 ) || ( stderr && stderr . length > 0 ) ;
95
70
if ( hasErrors && ( typeof stdout !== "string" || stdout . length === 0 ) ) {
@@ -101,3 +76,13 @@ function execFileInternal(file: string, args: string[], cwd: string, includeErro
101
76
} ) ;
102
77
} ) ;
103
78
}
79
+
80
+ export function mergeEnvVariables ( newVariables : { [ key : string ] : string } ) : any {
81
+ for ( let setting in process . env ) {
82
+ if ( ! newVariables [ setting ] ) {
83
+ newVariables [ setting ] = process . env [ setting ] ;
84
+ }
85
+ }
86
+
87
+ return newVariables ;
88
+ }
0 commit comments