public static async Task <WorkerLanguageVersionInfo> GetEnvironmentPythonVersion() { // By circuiting here, we avoid computing the Python version multiple times // in the scope of one command run if (_pythonVersionCache != null) { return(_pythonVersionCache); } // If users are overriding this value, we will use the path it's pointing to. // This also allows for an escape path for complicated envrionments. string pythonDefaultExecutablePath = Environment.GetEnvironmentVariable(_pythonDefaultExecutableVar); if (!string.IsNullOrEmpty(pythonDefaultExecutablePath)) { return(await GetVersion(pythonDefaultExecutablePath)); } var pythonGetVersionTask = GetVersion("python"); var python3GetVersionTask = GetVersion("python3"); var python36GetVersionTask = GetVersion("python3.6"); var python37GetVersionTask = GetVersion("python3.7"); var versions = new List <WorkerLanguageVersionInfo> { await pythonGetVersionTask, await python3GetVersionTask, await python36GetVersionTask, await python37GetVersionTask }; // Highest preference -- Go through the list, if we find the first python 3.6 or python 3.7 worker, we prioritize that. WorkerLanguageVersionInfo python36_37worker = versions.FirstOrDefault(w => (w?.Major == 3 && w?.Minor == 6) || (w?.Major == 3 && w?.Minor == 7)); if (python36_37worker != null) { _pythonVersionCache = python36_37worker; return(_pythonVersionCache); } // If any of the possible python executables are 3.x, we will take that. WorkerLanguageVersionInfo python3worker = versions.FirstOrDefault(w => w?.Major == 3); if (python3worker != null) { _pythonVersionCache = python3worker; return(_pythonVersionCache); } // Least preferred -- If we found any python versions at all we return that WorkerLanguageVersionInfo anyPythonWorker = versions.FirstOrDefault(w => !string.IsNullOrEmpty(w?.Version)); _pythonVersionCache = anyPythonWorker ?? new WorkerLanguageVersionInfo(WorkerRuntime.python, null, null); return(_pythonVersionCache); }
public async void WorkerInfoRuntimeShouldBePython() { WorkerLanguageVersionInfo worker = await PythonHelpers.GetEnvironmentPythonVersion(); if (worker.Runtime != WorkerRuntime.python) { throw new Exception("Worker runtime should always be python"); } }
public async void InterpreterShouldHaveMajorVersion() { WorkerLanguageVersionInfo worker = await PythonHelpers.GetEnvironmentPythonVersion(); if (worker.Major != 2 && worker.Major != 3) { throw new Exception("Python major version should be 2 or 3"); } }
public async void InterpreterShouldHaveExecutablePath() { WorkerLanguageVersionInfo worker = await PythonHelpers.GetEnvironmentPythonVersion(); if (worker.ExecutablePath == null) { throw new Exception("Python executable path should not be empty"); } }
public static void SetWorkerRuntimeVersionPython(WorkerLanguageVersionInfo version) { if (version?.Version == null) { throw new ArgumentNullException(nameof(version), "Version must not be null."); } var versionStr = $"{version.Major}.{version.Minor}"; Environment.SetEnvironmentVariable(Constants.FunctionsWorkerRuntimeVersion, versionStr, EnvironmentVariableTarget.Process); }
private static async Task WritePythonDockerFile() { WorkerLanguageVersionInfo worker = await PythonHelpers.GetEnvironmentPythonVersion(); if (worker?.Major == 3 && worker?.Minor == 7) { await WriteFiles("Dockerfile", await StaticResources.DockerfilePython37); } else { await WriteFiles("Dockerfile", await StaticResources.DockerfilePython36); } }
private static async Task <string> ChoosePythonBuildEnvImage() { WorkerLanguageVersionInfo workerInfo = await GetEnvironmentPythonVersion(); if (workerInfo?.Major == 3 && workerInfo?.Minor == 7) { return(Constants.DockerImages.LinuxPython37ImageAmd64); } else { return(Constants.DockerImages.LinuxPython36ImageAmd64); } }
public async Task init_with_python_Dockerfile() { WorkerLanguageVersionInfo worker = await PythonHelpers.GetEnvironmentPythonVersion(); Skip.If(worker == null); await CliTester.Run(new RunConfiguration { Commands = new[] { $"init . --worker-runtime python --docker" }, CheckFiles = new[] { new FileResult { Name = "Dockerfile", ContentContains = new[] { $"FROM mcr.microsoft.com/azure-functions/python:2.0-python{worker.Major}.{worker.Minor}" } } }, OutputContains = new[] { "Dockerfile" } }, _output); }
public static void AssertPythonVersion(WorkerLanguageVersionInfo pythonVersion, bool errorIfNotSupported = false, bool errorIfNoVersion = true) { if (pythonVersion?.Version == null) { var message = "Could not find a Python version. Python 3.6.x or 3.7.x is recommended, and used in Azure Functions."; if (errorIfNoVersion) { throw new CliException(message); } ColoredConsole.WriteLine(WarningColor(message)); return; } ColoredConsole.WriteLine(AdditionalInfoColor($"Found Python version {pythonVersion.Version} ({pythonVersion.ExecutablePath}).")); // Python 3.6 or 3.7 (supported) if (pythonVersion.Major == 3 && (pythonVersion.Minor == 6 || pythonVersion.Minor == 7)) { return; } // Python 3.x (but not 3.6 or 3.7), not recommended, may fail if (pythonVersion.Major == 3) { if (errorIfNotSupported) { throw new CliException($"Python 3.6.x or 3.7.x is required for this operation. " + "Please install Python 3.6 or 3.7, and use a virtual environment to switch to Python 3.6 or 3.7."); } ColoredConsole.WriteLine(WarningColor("Python 3.6.x or 3.7.x is recommended, and used in Azure Functions.")); } // No Python 3 var error = "Python 3.x (recommended version 3.6 or 3.7) is required."; if (errorIfNoVersion) { throw new CliException(error); } ColoredConsole.WriteLine(WarningColor(error)); }