|
| 1 | +'use strict'; |
| 2 | + |
1 | 3 | import * as fs from 'fs-extra'; |
2 | 4 | import * as path from 'path'; |
| 5 | +import { coerce, SemVer } from 'semver'; |
3 | 6 | import { ConfigurationTarget, Uri, workspace } from 'vscode'; |
4 | 7 | import { PythonSettings } from '../client/common/configSettings'; |
5 | 8 | import { EXTENSION_ROOT_DIR } from '../client/common/constants'; |
| 9 | +import { traceError } from '../client/common/logger'; |
| 10 | +import { BufferDecoder } from '../client/common/process/decoder'; |
| 11 | +import { ProcessService } from '../client/common/process/proc'; |
| 12 | +import { IProcessService } from '../client/common/process/types'; |
| 13 | +import { getOSType, OSType } from '../client/common/utils/platform'; |
6 | 14 | import { IS_MULTI_ROOT_TEST } from './initialize'; |
| 15 | + |
7 | 16 | export { sleep } from './core'; |
8 | 17 |
|
9 | 18 | // tslint:disable:no-invalid-this no-any |
@@ -128,3 +137,153 @@ function getPythonPath(): string { |
128 | 137 | } |
129 | 138 | return 'python'; |
130 | 139 | } |
| 140 | + |
| 141 | +/** |
| 142 | + * Determine if the current platform is included in a list of platforms. |
| 143 | + * |
| 144 | + * @param {OSes} OSType[] List of operating system Ids to check within. |
| 145 | + * @return true if the current OS matches one from the list, false otherwise. |
| 146 | + */ |
| 147 | +export function isOs(...OSes: OSType[]): boolean { |
| 148 | + // get current OS |
| 149 | + const currentOS: OSType = getOSType(); |
| 150 | + // compare and return |
| 151 | + if (OSes.indexOf(currentOS) === -1) { |
| 152 | + return false; |
| 153 | + } |
| 154 | + return true; |
| 155 | +} |
| 156 | + |
| 157 | +/** |
| 158 | + * Get the current Python interpreter version. |
| 159 | + * |
| 160 | + * @param {procService} IProcessService Optionally specify the IProcessService implementation to use to execute with. |
| 161 | + * @return `SemVer` version of the Python interpreter, or `undefined` if an error occurs. |
| 162 | + */ |
| 163 | +export async function getPythonSemVer(procService?: IProcessService): Promise<SemVer | undefined> { |
| 164 | + const pythonProcRunner = procService ? procService : new ProcessService(new BufferDecoder()); |
| 165 | + const pyVerArgs = ['-c', 'import sys;print("{0}.{1}.{2}".format(*sys.version_info[:3]))']; |
| 166 | + |
| 167 | + return pythonProcRunner.exec(PYTHON_PATH, pyVerArgs) |
| 168 | + .then(strVersion => new SemVer(strVersion.stdout.trim())) |
| 169 | + .catch((err) => { |
| 170 | + // if the call fails this should make it loudly apparent. |
| 171 | + traceError('getPythonSemVer', err); |
| 172 | + return undefined; |
| 173 | + }); |
| 174 | +} |
| 175 | + |
| 176 | +/** |
| 177 | + * Match a given semver version specification with a list of loosely defined |
| 178 | + * version strings. |
| 179 | + * |
| 180 | + * Specify versions by their major version at minimum - the minor and patch |
| 181 | + * version numbers are optional. |
| 182 | + * |
| 183 | + * '3', '3.6', '3.6.6', are all vald and only the portions specified will be matched |
| 184 | + * against the current running Python interpreter version. |
| 185 | + * |
| 186 | + * Example scenarios: |
| 187 | + * '3' will match version 3.5.6, 3.6.4, 3.6.6, and 3.7.0. |
| 188 | + * '3.6' will match version 3.6.4 and 3.6.6. |
| 189 | + * '3.6.4' will match version 3.6.4 only. |
| 190 | + * |
| 191 | + * @param {version} SemVer the version to look for. |
| 192 | + * @param {searchVersions} string[] List of loosely-specified versions to match against. |
| 193 | + */ |
| 194 | +export function isVersionInList(version: SemVer, ...searchVersions: string[]): boolean { |
| 195 | + // see if the major/minor version matches any member of the skip-list. |
| 196 | + const isPresent = searchVersions.findIndex(ver => { |
| 197 | + const semverChecker = coerce(ver); |
| 198 | + if (semverChecker) { |
| 199 | + if (semverChecker.compare(version) === 0) { |
| 200 | + return true; |
| 201 | + } else { |
| 202 | + // compare all the parts of the version that we have, we know we have |
| 203 | + // at minimum the major version or semverChecker would be 'null'... |
| 204 | + const versionParts = ver.split('.'); |
| 205 | + let matches = parseInt(versionParts[0], 10) === version.major; |
| 206 | + |
| 207 | + if (matches && versionParts.length >= 2) { |
| 208 | + matches = parseInt(versionParts[1], 10) === version.minor; |
| 209 | + } |
| 210 | + |
| 211 | + if (matches && versionParts.length >= 3) { |
| 212 | + matches = parseInt(versionParts[2], 10) === version.patch; |
| 213 | + } |
| 214 | + |
| 215 | + return matches; |
| 216 | + } |
| 217 | + } |
| 218 | + return false; |
| 219 | + }); |
| 220 | + |
| 221 | + if (isPresent >= 0) { |
| 222 | + return true; |
| 223 | + } |
| 224 | + return false; |
| 225 | +} |
| 226 | + |
| 227 | +/** |
| 228 | + * Determine if the Python interpreter version running in a given `IProcessService` |
| 229 | + * is in a selection of versions. |
| 230 | + * |
| 231 | + * You can specify versions by specifying the major version at minimum - the minor and |
| 232 | + * patch version numbers are optional. |
| 233 | + * |
| 234 | + * '3', '3.6', '3.6.6', are all vald and only the portions specified will be matched |
| 235 | + * against the current running Python interpreter version. |
| 236 | + * |
| 237 | + * Example scenarios: |
| 238 | + * '3' will match version 3.5.6, 3.6.4, 3.6.6, and 3.7.0. |
| 239 | + * '3.6' will match version 3.6.4 and 3.6.6. |
| 240 | + * '3.6.4' will match version 3.6.4 only. |
| 241 | + * |
| 242 | + * If you don't need to specify the environment (ie. the workspace) that the Python |
| 243 | + * interpreter is running under, use the simpler `isPythonVersion` instead. |
| 244 | + * |
| 245 | + * @param {skipForVersions} string[] List of versions of python that are to be skipped. |
| 246 | + * @param {resource} vscode.Uri Current workspace resource Uri or undefined. |
| 247 | + * @return true if the current Python version matches a version in the skip list, false otherwise. |
| 248 | + */ |
| 249 | +export async function isPythonVersionInProcess(procService?: IProcessService, ...versions: string[]): Promise<boolean> { |
| 250 | + // get the current python version major/minor |
| 251 | + const currentPyVersion = await getPythonSemVer(procService); |
| 252 | + if (currentPyVersion) { |
| 253 | + return isVersionInList(currentPyVersion, ...versions); |
| 254 | + } else { |
| 255 | + traceError(`Failed to determine the current Python version when comparing against list [${versions.join(', ')}].`); |
| 256 | + return false; |
| 257 | + } |
| 258 | +} |
| 259 | + |
| 260 | +/** |
| 261 | + * Determine if the current interpreter version is in a given selection of versions. |
| 262 | + * |
| 263 | + * You can specify versions by using up to the first three semver parts of a python |
| 264 | + * version. |
| 265 | + * |
| 266 | + * '3', '3.6', '3.6.6', are all vald and only the portions specified will be matched |
| 267 | + * against the current running Python interpreter version. |
| 268 | + * |
| 269 | + * Example scenarios: |
| 270 | + * '3' will match version 3.5.6, 3.6.4, 3.6.6, and 3.7.0. |
| 271 | + * '3.6' will match version 3.6.4 and 3.6.6. |
| 272 | + * '3.6.4' will match version 3.6.4 only. |
| 273 | + * |
| 274 | + * If you need to specify the environment (ie. the workspace) that the Python |
| 275 | + * interpreter is running under, use `isPythonVersionInProcess` instead. |
| 276 | + * |
| 277 | + * @param {skipForVersions} string[] List of versions of python that are to be skipped. |
| 278 | + * @param {resource} vscode.Uri Current workspace resource Uri or undefined. |
| 279 | + * @return true if the current Python version matches a version in the skip list, false otherwise. |
| 280 | + */ |
| 281 | +export async function isPythonVersion(...versions: string[]): Promise<boolean> { |
| 282 | + const currentPyVersion = await getPythonSemVer(); |
| 283 | + if (currentPyVersion) { |
| 284 | + return isVersionInList(currentPyVersion, ...versions); |
| 285 | + } else { |
| 286 | + traceError(`Failed to determine the current Python version when comparing against list [${versions.join(', ')}].`); |
| 287 | + return false; |
| 288 | + } |
| 289 | +} |
0 commit comments