4
4
import { cloneDeep } from 'lodash' ;
5
5
import * as path from 'path' ;
6
6
import {
7
- FileInfo ,
8
- PythonDistroInfo ,
9
- PythonEnvInfo , PythonEnvKind , PythonVersion ,
7
+ FileInfo , PythonDistroInfo , PythonEnvInfo , PythonEnvKind , PythonReleaseLevel , PythonVersion
10
8
} from '.' ;
11
9
import { Architecture } from '../../../common/utils/platform' ;
12
10
import { arePathsSame } from '../../common/externalDependencies' ;
13
11
import { areEqualVersions , areEquivalentVersions } from './pythonVersion' ;
14
12
13
+ /**
14
+ * Create a new info object with all values empty.
15
+ *
16
+ * @param init - if provided, these values are applied to the new object
17
+ */
18
+ export function buildEnvInfo ( init ?: {
19
+ kind ?: PythonEnvKind ;
20
+ executable ?: string ;
21
+ location ?: string ;
22
+ version ?: PythonVersion ;
23
+ } ) : PythonEnvInfo {
24
+ const env = {
25
+ kind : PythonEnvKind . Unknown ,
26
+ executable : {
27
+ filename : '' ,
28
+ sysPrefix : '' ,
29
+ ctime : - 1 ,
30
+ mtime : - 1 ,
31
+ } ,
32
+ name : '' ,
33
+ location : '' ,
34
+ searchLocation : undefined ,
35
+ defaultDisplayName : undefined ,
36
+ version : {
37
+ major : - 1 ,
38
+ minor : - 1 ,
39
+ micro : - 1 ,
40
+ release : {
41
+ level : PythonReleaseLevel . Final ,
42
+ serial : 0 ,
43
+ } ,
44
+ } ,
45
+ arch : Architecture . Unknown ,
46
+ distro : {
47
+ org : '' ,
48
+ } ,
49
+ } ;
50
+ if ( init !== undefined ) {
51
+ updateEnv ( env , init ) ;
52
+ }
53
+ return env ;
54
+ }
55
+
56
+ /**
57
+ * Return a deep copy of the given env info.
58
+ *
59
+ * @param updates - if provided, these values are applied to the copy
60
+ */
61
+ export function copyEnvInfo (
62
+ env : PythonEnvInfo ,
63
+ updates ?: {
64
+ kind ?: PythonEnvKind ;
65
+ } ,
66
+ ) : PythonEnvInfo {
67
+ // We don't care whether or not extra/hidden properties
68
+ // get preserved, so we do the easy thing here.
69
+ const copied = cloneDeep ( env ) ;
70
+ if ( updates !== undefined ) {
71
+ updateEnv ( copied , updates ) ;
72
+ }
73
+ return copied ;
74
+ }
75
+
76
+ function updateEnv (
77
+ env : PythonEnvInfo ,
78
+ updates : {
79
+ kind ?: PythonEnvKind ;
80
+ executable ?: string ;
81
+ location ?: string ;
82
+ version ?: PythonVersion ;
83
+ } ,
84
+ ) : void {
85
+ if ( updates . kind !== undefined ) {
86
+ env . kind = updates . kind ;
87
+ }
88
+ if ( updates . executable !== undefined ) {
89
+ env . executable . filename = updates . executable ;
90
+ }
91
+ if ( updates . location !== undefined ) {
92
+ env . location = updates . location ;
93
+ }
94
+ if ( updates . version !== undefined ) {
95
+ env . version = updates . version ;
96
+ }
97
+ }
98
+
99
+ /**
100
+ * For the given data, build a normalized partial info object.
101
+ *
102
+ * If insufficient data is provided to generate a minimal object, such
103
+ * that it is not identifiable, then `undefined` is returned.
104
+ */
105
+ export function getMinimalPartialInfo ( env : string | Partial < PythonEnvInfo > ) : Partial < PythonEnvInfo > | undefined {
106
+ if ( typeof env === 'string' ) {
107
+ if ( env === '' ) {
108
+ return undefined ;
109
+ }
110
+ return {
111
+ executable : {
112
+ filename : env , sysPrefix : '' , ctime : - 1 , mtime : - 1 ,
113
+ } ,
114
+ } ;
115
+ }
116
+ if ( env . executable === undefined ) {
117
+ return undefined ;
118
+ }
119
+ if ( env . executable . filename === '' ) {
120
+ return undefined ;
121
+ }
122
+ return env ;
123
+ }
124
+
15
125
/**
16
126
* Checks if two environments are same.
17
127
* @param {string | PythonEnvInfo } left: environment to compare.
@@ -24,14 +134,20 @@ import { areEqualVersions, areEquivalentVersions } from './pythonVersion';
24
134
* to be same environment. This later case is needed for comparing windows store python,
25
135
* where multiple versions of python executables are all put in the same directory.
26
136
*/
27
- export function areSameEnvironment (
28
- left : string | PythonEnvInfo ,
29
- right : string | PythonEnvInfo ,
30
- allowPartialMatch ?: boolean ,
31
- ) : boolean {
32
- const leftFilename = typeof left === 'string' ? left : left . executable . filename ;
33
- const rightFilename = typeof right === 'string' ? right : right . executable . filename ;
137
+ export function areSameEnv (
138
+ left : string | Partial < PythonEnvInfo > ,
139
+ right : string | Partial < PythonEnvInfo > ,
140
+ allowPartialMatch = true ,
141
+ ) : boolean | undefined {
142
+ const leftInfo = getMinimalPartialInfo ( left ) ;
143
+ const rightInfo = getMinimalPartialInfo ( right ) ;
144
+ if ( leftInfo === undefined || rightInfo === undefined ) {
145
+ return undefined ;
146
+ }
147
+ const leftFilename = leftInfo . executable ! . filename ;
148
+ const rightFilename = rightInfo . executable ! . filename ;
34
149
150
+ // For now we assume that matching executable means they are the same.
35
151
if ( arePathsSame ( leftFilename , rightFilename ) ) {
36
152
return true ;
37
153
}
@@ -58,7 +174,7 @@ export function areSameEnvironment(
58
174
* weighted by most important to least important fields.
59
175
* Wn > Wn-1 + Wn-2 + ... W0
60
176
*/
61
- function getPythonVersionInfoHeuristic ( version :PythonVersion ) : number {
177
+ function getPythonVersionInfoHeuristic ( version : PythonVersion ) : number {
62
178
let infoLevel = 0 ;
63
179
if ( version . major > 0 ) {
64
180
infoLevel += 20 ; // W4
@@ -72,11 +188,11 @@ function getPythonVersionInfoHeuristic(version:PythonVersion): number {
72
188
infoLevel += 5 ; // W2
73
189
}
74
190
75
- if ( version . release . level ) {
191
+ if ( version . release ? .level ) {
76
192
infoLevel += 3 ; // W1
77
193
}
78
194
79
- if ( version . release . serial || version . sysVersion ) {
195
+ if ( version . release ? .serial || version . sysVersion ) {
80
196
infoLevel += 1 ; // W0
81
197
}
82
198
@@ -90,7 +206,7 @@ function getPythonVersionInfoHeuristic(version:PythonVersion): number {
90
206
* weighted by most important to least important fields.
91
207
* Wn > Wn-1 + Wn-2 + ... W0
92
208
*/
93
- function getFileInfoHeuristic ( file :FileInfo ) : number {
209
+ function getFileInfoHeuristic ( file : FileInfo ) : number {
94
210
let infoLevel = 0 ;
95
211
if ( file . filename . length > 0 ) {
96
212
infoLevel += 5 ; // W2
@@ -114,7 +230,7 @@ function getFileInfoHeuristic(file:FileInfo): number {
114
230
* weighted by most important to least important fields.
115
231
* Wn > Wn-1 + Wn-2 + ... W0
116
232
*/
117
- function getDistroInfoHeuristic ( distro :PythonDistroInfo ) :number {
233
+ function getDistroInfoHeuristic ( distro : PythonDistroInfo ) : number {
118
234
let infoLevel = 0 ;
119
235
if ( distro . org . length > 0 ) {
120
236
infoLevel += 20 ; // W3
@@ -135,62 +251,6 @@ function getDistroInfoHeuristic(distro:PythonDistroInfo):number {
135
251
return infoLevel ;
136
252
}
137
253
138
- /**
139
- * Gets a prioritized list of environment types for identification.
140
- * @returns {PythonEnvKind[] } : List of environments ordered by identification priority
141
- *
142
- * Remarks: This is the order of detection based on how the various distributions and tools
143
- * configure the environment, and the fall back for identification.
144
- * Top level we have the following environment types, since they leave a unique signature
145
- * in the environment or * use a unique path for the environments they create.
146
- * 1. Conda
147
- * 2. Windows Store
148
- * 3. PipEnv
149
- * 4. Pyenv
150
- * 5. Poetry
151
- *
152
- * Next level we have the following virtual environment tools. The are here because they
153
- * are consumed by the tools above, and can also be used independently.
154
- * 1. venv
155
- * 2. virtualenvwrapper
156
- * 3. virtualenv
157
- *
158
- * Last category is globally installed python, or system python.
159
- */
160
- export function getPrioritizedEnvironmentKind ( ) : PythonEnvKind [ ] {
161
- return [
162
- PythonEnvKind . CondaBase ,
163
- PythonEnvKind . Conda ,
164
- PythonEnvKind . WindowsStore ,
165
- PythonEnvKind . Pipenv ,
166
- PythonEnvKind . Pyenv ,
167
- PythonEnvKind . Poetry ,
168
- PythonEnvKind . Venv ,
169
- PythonEnvKind . VirtualEnvWrapper ,
170
- PythonEnvKind . VirtualEnv ,
171
- PythonEnvKind . OtherVirtual ,
172
- PythonEnvKind . OtherGlobal ,
173
- PythonEnvKind . MacDefault ,
174
- PythonEnvKind . System ,
175
- PythonEnvKind . Custom ,
176
- PythonEnvKind . Unknown ,
177
- ] ;
178
- }
179
-
180
- /**
181
- * Selects an environment based on the environment selection priority. This should
182
- * match the priority in the environment identifier.
183
- */
184
- export function sortEnvInfoByPriority ( ...envs : PythonEnvInfo [ ] ) : PythonEnvInfo [ ] {
185
- // tslint:disable-next-line: no-suspicious-comment
186
- // TODO: When we consolidate the PythonEnvKind and EnvironmentType we should have
187
- // one location where we define priority and
188
- const envKindByPriority :PythonEnvKind [ ] = getPrioritizedEnvironmentKind ( ) ;
189
- return envs . sort (
190
- ( a :PythonEnvInfo , b :PythonEnvInfo ) => envKindByPriority . indexOf ( a . kind ) - envKindByPriority . indexOf ( b . kind ) ,
191
- ) ;
192
- }
193
-
194
254
/**
195
255
* Merges properties of the `target` environment and `other` environment and returns the merged environment.
196
256
* if the value in the `target` environment is not defined or has less information. This does not mutate
@@ -203,18 +263,19 @@ export function mergeEnvironments(target: PythonEnvInfo, other: PythonEnvInfo):
203
263
204
264
const version = cloneDeep (
205
265
getPythonVersionInfoHeuristic ( target . version ) > getPythonVersionInfoHeuristic ( other . version )
206
- ? target . version : other . version ,
266
+ ? target . version
267
+ : other . version ,
207
268
) ;
208
269
209
270
const executable = cloneDeep (
210
271
getFileInfoHeuristic ( target . executable ) > getFileInfoHeuristic ( other . executable )
211
- ? target . executable : other . executable ,
272
+ ? target . executable
273
+ : other . executable ,
212
274
) ;
213
275
executable . sysPrefix = target . executable . sysPrefix ?? other . executable . sysPrefix ;
214
276
215
277
const distro = cloneDeep (
216
- getDistroInfoHeuristic ( target . distro ) > getDistroInfoHeuristic ( other . distro )
217
- ? target . distro : other . distro ,
278
+ getDistroInfoHeuristic ( target . distro ) > getDistroInfoHeuristic ( other . distro ) ? target . distro : other . distro ,
218
279
) ;
219
280
220
281
merged . arch = merged . arch === Architecture . Unknown ? other . arch : target . arch ;
@@ -226,8 +287,8 @@ export function mergeEnvironments(target: PythonEnvInfo, other: PythonEnvInfo):
226
287
// preferred env based on kind.
227
288
merged . kind = target . kind ;
228
289
229
- merged . location = merged . location ?? other . location ;
230
- merged . name = merged . name ?? other . name ;
290
+ merged . location = merged . location . length ? merged . location : other . location ;
291
+ merged . name = merged . name . length ? merged . name : other . name ;
231
292
merged . searchLocation = merged . searchLocation ?? other . searchLocation ;
232
293
merged . version = version ;
233
294
0 commit comments