diff --git a/.gitignore b/.gitignore index 42a5852..aa49493 100644 --- a/.gitignore +++ b/.gitignore @@ -94,4 +94,8 @@ Alligator/Alligator_REMOTE_9848.csproj # VSCode User File # #################### -.vscode/ \ No newline at end of file +.vscode/ + +# User defined files # +###################### +build.ps1 \ No newline at end of file diff --git a/Python_Engine/Compute/BasePythonEnvironment.cs b/Python_Engine/Compute/BasePythonEnvironment.cs new file mode 100644 index 0000000..0209ef0 --- /dev/null +++ b/Python_Engine/Compute/BasePythonEnvironment.cs @@ -0,0 +1,106 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2023, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using BH.oM.Base.Attributes; +using BH.oM.Python; +using BH.oM.Python.Enums; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; +using System.Linq; + +namespace BH.Engine.Python +{ + public static partial class Compute + { + [Description("Retrieve or reinstall the base Python Environment for BHoM workflows.")] + [Input("reload", "Reload the base Python environment rather than recreating it, if it already exists.")] + [Output("env", "The base Python Environment for all BHoM workflows.")] + [PreviousVersion("6.3", "BH.Engine.Python.Compute.InstallBasePythonEnvironment(System.Boolean)")] + public static PythonEnvironment BasePythonEnvironment( + bool reload = true + ) + { + if (!Directory.Exists(Query.DirectoryEnvironments())) + { + // create PythonEnvironments directory if it doesnt already exist + Directory.CreateDirectory(Query.DirectoryEnvironments()); + } + + // determine whether the base environment already exists + string targetExecutable = Path.Combine(Query.DirectoryBaseEnvironment(), "python.exe"); + bool exists = Directory.Exists(Query.DirectoryBaseEnvironment()) && File.Exists(targetExecutable); + + if (exists && reload) + return new PythonEnvironment() { Name = Query.ToolkitName(), Executable = targetExecutable }; + + if (exists && !reload) + // remove all existing environments and kernels + RemoveEverything(); + + // download the target Python version and convert into a "full" python installation bypassing admin rights + string executable = PythonVersion.v3_10_5.DownloadPython(Query.ToolkitName()); + string pipInstaller = DownloadGetPip(Path.GetDirectoryName(executable)); + string baseEnvironmentDirectory = Path.GetDirectoryName(executable); + + // install pip into the python installation + Process process = new Process() + { + StartInfo = new ProcessStartInfo() + { + FileName = Modify.AddQuotesIfRequired(executable), + Arguments = Modify.AddQuotesIfRequired(pipInstaller) + " --no-warn-script-location", + RedirectStandardError=true, + UseShellExecute=false, + } + }; + using (Process p = Process.Start(process.StartInfo)) + { + p.WaitForExit(); + if (p.ExitCode != 0) + BH.Engine.Base.Compute.RecordError($"Error installing pip.\n{p.StandardError.ReadToEnd()}"); + File.Delete(pipInstaller); + } + + // delete files with the suffix ._pth from installedDirectory + List pthFiles = Directory.GetFiles(baseEnvironmentDirectory, "*.*", SearchOption.TopDirectoryOnly).Where(s => s.EndsWith("._pth")).ToList(); + foreach (string pthFile in pthFiles) + { + File.Delete(pthFile); + } + + // move files with the suffix .dll and .pyd from installedDirectory into a DLLs directory + string libDirectory = Directory.CreateDirectory(Path.Combine(baseEnvironmentDirectory, "DLLs")).FullName; + List libFiles = Directory.GetFiles(baseEnvironmentDirectory, "*.*", SearchOption.TopDirectoryOnly).Where(s => (s.EndsWith(".dll") || s.EndsWith(".pyd")) && !Path.GetFileName(s).Contains("python") && !Path.GetFileName(s).Contains("vcruntime")).ToList(); + foreach (string libFile in libFiles) + { + File.Move(libFile, Path.Combine(libDirectory, Path.GetFileName(libFile))); + } + + // install essential packages into base environment + InstallPackages(executable, new List() { "virtualenv", "jupyterlab", "black", "pylint" }); + + return new PythonEnvironment() { Name = Query.ToolkitName(), Executable = executable }; + } + } +} diff --git a/Python_Engine/Compute/Download.cs b/Python_Engine/Compute/Download.cs new file mode 100644 index 0000000..3d72037 --- /dev/null +++ b/Python_Engine/Compute/Download.cs @@ -0,0 +1,119 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2023, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using BH.oM.Base.Attributes; +using BH.oM.Python.Enums; +using System; +using System.ComponentModel; +using System.IO; +using System.Xml.Linq; + +namespace BH.Engine.Python +{ + public static partial class Compute + { + [Description("Download a file from a Url.")] + [Input("fileUrl", "The file to download.")] + [Input("targetDirectory", "The destination directory to download the file to.")] + [Input("filename", "An optional input to rename the downloaded file.")] + [Input("overwrite", "An optional input to overwrite the file if it already exists, otherwise if it does then return the existing file.")] + [Output("filePath", "The path to the downloaded file.")] + public static string DownloadFile( + string fileUrl, + string targetDirectory, + string filename = null, + bool overwrite = true + ) + { + if (string.IsNullOrWhiteSpace(fileUrl)) + { + BH.Engine.Base.Compute.RecordError($"The fileUrl cannot be null or empty."); + return null; + } + + + if (string.IsNullOrWhiteSpace(targetDirectory)) + { + BH.Engine.Base.Compute.RecordError($"The targetDirectory cannot be null or empty."); + return null; + } + + + if (!Directory.Exists(targetDirectory)) + { + BH.Engine.Base.Compute.RecordError($"The targetDirectory \"{targetDirectory}\" does not exist."); + return null; + } + + + if (string.IsNullOrWhiteSpace(filename)) + filename = Path.GetFileName(fileUrl); + + string filePath = Path.Combine(targetDirectory, filename); + + if (File.Exists(filePath)) + { + if (!overwrite) + return filePath; + File.Delete(filePath); + } + + using (var client = new System.Net.WebClient()) + { + client.DownloadFile(fileUrl, filePath); + } + + return filePath; + } + + // TODO - THIS METHOD HAS CHANGED BUT IS STILL USED, SO NEEDS DEPRECATING + // changed from what to what ? + [Description("Download the target version of Python.")] + [Input("version", "A Python version.")] + [Input("name", "Name of target exe file.")] + [Output("executablePath", "The path of the executable for the downloaded Python.")] + [PreviousVersion("6.3", "BH.Engine.Python.Compute.DownloadPython(BH.oM.Python.Enums.PythonVersion)")] + public static string DownloadPython(this PythonVersion version, string name = null) + { + string url = version.EmbeddableURL(); + if (string.IsNullOrEmpty(name)) + name = Path.GetFileNameWithoutExtension(url); + string targetExecutable = Path.Combine(Query.DirectoryEnvironments(), name, "python.exe"); + + if (File.Exists(targetExecutable)) + return targetExecutable; + + string zipfile = DownloadFile(url, Query.DirectoryEnvironments()); + UnzipFile(zipfile, Query.DirectoryEnvironments(), name, true); + + return targetExecutable; + } + + [Description("Download the pip installer")] + [Input("targetDirectory", "The directory into which get-pip.py will be downloaded.")] + [Output("getpipPath", "The path of the file used to install pip into an embedded Python environment.")] + public static string DownloadGetPip(string targetDirectory) + { + return DownloadFile("https://bootstrap.pypa.io/get-pip.py", targetDirectory); + } + } +} diff --git a/Python_Engine/Compute/DownloadPython.cs b/Python_Engine/Compute/DownloadPython.cs deleted file mode 100644 index 474e654..0000000 --- a/Python_Engine/Compute/DownloadPython.cs +++ /dev/null @@ -1,65 +0,0 @@ -/* - * This file is part of the Buildings and Habitats object Model (BHoM) - * Copyright (c) 2015 - 2023, the respective contributors. All rights reserved. - * - * Each contributor holds copyright over their respective contributions. - * The project versioning (Git) records all such contribution source information. - * - * - * The BHoM is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * The BHoM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this code. If not, see . - */ - -using BH.oM.Base.Attributes; -using System.Collections.Generic; -using System.ComponentModel; -using System.IO; - -namespace BH.Engine.Python -{ - public static partial class Compute - { - [Description("Download a target version of Python.")] - [Input("version", "A Python version.")] - [Output("executablePath", "The path of the executable for the downloaded Python.")] - public static string DownloadPython(this BH.oM.Python.Enums.PythonVersion version) - { - string targetDirectory = Query.EnvironmentsDirectory(); - string versionString = version.ToString().Replace("v", ""); - string resultantDirectory = Path.Combine(targetDirectory, versionString); - string executable = Path.Combine(resultantDirectory, "python.exe"); - - if (File.Exists(executable)) - return executable; - - string pythonUrl = version.EmbeddableURL(); - string pythonZipFile = Path.Combine(targetDirectory, Path.GetFileName(pythonUrl)); - - List commands = new List() - { - $"curl {pythonUrl} -o {pythonZipFile}", - $"mkdir {resultantDirectory}", - $"tar -v -xf {pythonZipFile} -C {resultantDirectory}", - $"del {pythonZipFile}", - }; - foreach (string command in commands) - { - RunCommandStdout(command, hideWindows: true); - } - - return executable; - } - } -} - - diff --git a/Python_Engine/Compute/InstallBasePythonEnvironment.cs b/Python_Engine/Compute/InstallBasePythonEnvironment.cs deleted file mode 100644 index ea87f05..0000000 --- a/Python_Engine/Compute/InstallBasePythonEnvironment.cs +++ /dev/null @@ -1,121 +0,0 @@ -/* - * This file is part of the Buildings and Habitats object Model (BHoM) - * Copyright (c) 2015 - 2023, the respective contributors. All rights reserved. - * - * Each contributor holds copyright over their respective contributions. - * The project versioning (Git) records all such contribution source information. - * - * - * The BHoM is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * The BHoM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this code. If not, see . - */ - -using BH.oM.Base.Attributes; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.IO; -using System.Linq; - -namespace BH.Engine.Python -{ - public static partial class Compute - { - [Description("Install the base Python Environment for BHoM workflows.")] - [Input("run", "Run the installation process for the BHoM Python Environment.")] - [Output("env", "The base Python Environment for BHoM workflows.")] - public static oM.Python.PythonEnvironment InstallBasePythonEnvironment(bool run = false) - { - if (!run) - { - BH.Engine.Base.Compute.RecordWarning($"Please toggle `{nameof(run)}` to true."); - return null; - } - - // Python version for base environment - oM.Python.Enums.PythonVersion version = oM.Python.Enums.PythonVersion.v3_10_5; - - // set location where base Python env will be created - string targetDirectory = Path.Combine(Query.EnvironmentsDirectory(), Query.ToolkitName()); - if (!Directory.Exists(targetDirectory)) - Directory.CreateDirectory(targetDirectory); - - // set executable path for later use in installation - string executable = Path.Combine(targetDirectory, "python.exe"); - oM.Python.PythonEnvironment env = new oM.Python.PythonEnvironment() { Name = Query.ToolkitName(), Executable = executable }; - - if (env.EnvironmentExists()) - return env; - - // create log of installation as process continues - useful for debugging if things go wrong! - string logFile = Path.Combine(targetDirectory, "installation.log"); - - // prepare constants for use in installation process - string pythonUrl = version.EmbeddableURL(); - string pythonZipFile = Path.Combine(targetDirectory, "embeddable_python.zip"); - string pipInstallerUrl = "https://bootstrap.pypa.io/get-pip.py"; - string pipInstallerFile = Path.Combine(targetDirectory, "get-pip.py"); - - using (StreamWriter sw = File.AppendText(logFile)) - { - sw.WriteLine(LoggingHeader("Installation started for BHoM base Python environment")); - - // download and unpack files - List installationCommands = new List() { - $"curl {pythonUrl} -o {pythonZipFile}", // download Python zip file - $"tar -v -xf {pythonZipFile} -C {targetDirectory}", // unzip files - $"del {pythonZipFile}", // remove zip file - $"curl {pipInstallerUrl} -o {pipInstallerFile}", // download PIP installer - $"{executable} {pipInstallerFile} --no-warn-script-location", // install PIP - $"del {pipInstallerFile}", // remove PIP installer file - }; - - foreach (string command in installationCommands) - { - sw.WriteLine($"[{System.DateTime.Now.ToString("s")}] {command}"); - sw.WriteLine(Compute.RunCommandStdout($"{command}", hideWindows: true)); - } - - // modify directory to replicate "normal" Python installation - List pthFiles = System.IO.Directory.GetFiles(targetDirectory, "*.*", SearchOption.TopDirectoryOnly).Where(s => s.EndsWith("._pth")).ToList(); - string libDirectory = System.IO.Directory.CreateDirectory(Path.Combine(targetDirectory, "DLLs")).FullName; - List libFiles = System.IO.Directory.GetFiles(targetDirectory, "*.*", SearchOption.TopDirectoryOnly).Where(s => (s.EndsWith(".dll") || s.EndsWith(".pyd")) && !Path.GetFileName(s).Contains("python") && !Path.GetFileName(s).Contains("vcruntime")).ToList(); - - List modificationCommands = new List() { }; - foreach (string pthFile in pthFiles) - { - modificationCommands.Add($"del {pthFile}"); // delete *._pth file/s - } - foreach (string libFile in libFiles) - { - modificationCommands.Add($"move /y {libFile} {libDirectory}"); // move specific *._pyd and *.dll file/s into DLLs directory - } - - foreach (string command in modificationCommands) - { - sw.WriteLine($"[{System.DateTime.Now.ToString("s")}] {command}"); - sw.WriteLine(Compute.RunCommandStdout($"{command}", hideWindows: true)); - } - - // install packages into base environment - string baseBHoMPackage = Path.Combine(Query.CodeDirectory(), Query.ToolkitName()); - string packageInstallationCommand = $"{executable} -m pip install --no-warn-script-location -e {baseBHoMPackage}"; - sw.WriteLine($"[{System.DateTime.Now.ToString("s")}] {packageInstallationCommand}"); - sw.WriteLine(Compute.RunCommandStdout($"{packageInstallationCommand}", hideWindows: true)); // install packages into this environment - } - - return env; - } - } -} - diff --git a/Python_Engine/Compute/InstallPackage.cs b/Python_Engine/Compute/InstallPackage.cs new file mode 100644 index 0000000..358ce2e --- /dev/null +++ b/Python_Engine/Compute/InstallPackage.cs @@ -0,0 +1,176 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2023, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using BH.oM.Base.Attributes; +using BH.oM.Python; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; +using System.Windows.Forms; +using System.Xml.Linq; + +namespace BH.Engine.Python +{ + public static partial class Compute + { + [Description("Install a list of packages to a given Python executable.")] + [Input("executable", "The Python executable to install the packages to.")] + [Input("packages", "The packages to install.")] + public static void InstallPackages( + string executable, + List packages + ) + { + if (!File.Exists(executable)) + { + BH.Engine.Base.Compute.RecordError("Python executable not found at " + executable); + } + + if (packages.Count == 0) + { + BH.Engine.Base.Compute.RecordError("No packages to install."); + } + + string packagesString = string.Join(" ", packages); + System.Diagnostics.Process process = new System.Diagnostics.Process() + { + StartInfo = new System.Diagnostics.ProcessStartInfo() + { + FileName = executable, + Arguments = $"-m pip install --no-warn-script-location {packagesString}", + UseShellExecute = false, + RedirectStandardError = true, + } + }; + using (Process p = Process.Start(process.StartInfo)) + { + p.WaitForExit(); + if (p.ExitCode != 0) + BH.Engine.Base.Compute.RecordError($"Error installing packages [{packagesString}].\n{p.StandardError.ReadToEnd()}"); + } + } + + [Description("Install a list of packages to a given Python executable.")] + [Input("environment", "The Python environment to install the packages to.")] + [Input("packages", "The packages to install.")] + public static void InstallPackages( + this PythonEnvironment environment, + List packages + ) + { + InstallPackages(environment.Executable, packages); + } + + [Description("Install packages from a requirements.txt file to a given Python executable.")] + [Input("executable", "The Python executable to install the packages to.")] + [Input("requirements", "The requirements.txt file to install.")] + public static void InstallRequirements( + string executable, + string requirements + ) + { + if (!File.Exists(executable)) + { + BH.Engine.Base.Compute.RecordError($"Python executable not found at {executable}"); + } + + if (!File.Exists(requirements)) + { + BH.Engine.Base.Compute.RecordError($"requirements.txt not found at {requirements}"); + } + + + System.Diagnostics.Process process = new System.Diagnostics.Process() + { + StartInfo = new System.Diagnostics.ProcessStartInfo() + { + FileName = executable, + Arguments = $"-m pip install --no-warn-script-location -r {Modify.AddQuotesIfRequired(requirements)}", + UseShellExecute = false, + RedirectStandardError = true, + } + }; + using (Process p = Process.Start(process.StartInfo)) + { + p.WaitForExit(); + if (p.ExitCode != 0) + BH.Engine.Base.Compute.RecordError($"Error installing packages from {requirements}.\n{p.StandardError.ReadToEnd()}"); + } + } + + [Description("Install packages from a requirements.txt file to a given Python environment.")] + [Input("environment", "The Python environment to install the packages to.")] + [Input("requirements", "The requirements.txt file to install.")] + public static void InstallRequirements( + this PythonEnvironment environment, + string requirements + ) + { + InstallRequirements(environment.Executable, requirements); + } + + + [Description("Install a local Python package to a given Python executable.")] + [Input("executable", "The Python executable to install the package to.")] + [Input("packageDirectory", "The package to install.")] + public static void InstallPackageLocal( + string executable, + string packageDirectory + ) + { + if (!File.Exists(executable)) + { + BH.Engine.Base.Compute.RecordError("Python executable not found at " + executable); + } + + System.Diagnostics.Process process = new System.Diagnostics.Process() + { + StartInfo = new System.Diagnostics.ProcessStartInfo() + { + FileName = executable, + Arguments = $"-m pip install --no-warn-script-location -e {Modify.AddQuotesIfRequired(packageDirectory)}", + UseShellExecute = false, + RedirectStandardError = true, + } + }; + using (Process p = Process.Start(process.StartInfo)) + { + p.WaitForExit(); + if (p.ExitCode != 0) + BH.Engine.Base.Compute.RecordError($"Error installing package from \"{packageDirectory}\".\n{p.StandardError.ReadToEnd()}"); + } + } + + [Description("Install a local Python package to a given Python environment.")] + [Input("environment", "The Python environment to install the packages to.")] + [Input("packageDirectory", "The package to install.")] + public static void InstallPackageLocal( + this PythonEnvironment environment, + string packageDirectory + ) + { + InstallPackageLocal(environment.Executable, packageDirectory); + } + } +} diff --git a/Python_Engine/Compute/InstallReferencedVirtualenv.cs b/Python_Engine/Compute/InstallReferencedVirtualenv.cs deleted file mode 100644 index fd8af61..0000000 --- a/Python_Engine/Compute/InstallReferencedVirtualenv.cs +++ /dev/null @@ -1,118 +0,0 @@ -/* - * This file is part of the Buildings and Habitats object Model (BHoM) - * Copyright (c) 2015 - 2023, the respective contributors. All rights reserved. - * - * Each contributor holds copyright over their respective contributions. - * The project versioning (Git) records all such contribution source information. - * - * - * The BHoM is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * The BHoM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this code. If not, see . - */ - -using BH.oM.Base.Attributes; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.IO; -using System.Linq; - -namespace BH.Engine.Python -{ - public static partial class Compute - { - [Description("Install the Python Environment into BHoM for a referenced existing Python environment.")] - [Input("name", "The name of this BHoM Python Environment.")] - [Input("executable", "The Python executable for the referenced environment. From this, the version of Python will be determined in order to recreate it for BHoM as a virtualenv.")] - [Input("localPackage", "A local package to be included in the resultant environment. This is where custom BHoM code can be added into this environment.")] - [Input("run", "Run the installation process for the BHoM Python Environment.")] - [Output("env", "The virtualenv replication of Python Environment for BHoM workflows.")] - public static oM.Python.PythonEnvironment InstallReferencedVirtualenv( - string name, - string executable, - string localPackage = null, - bool run = false - ) - { - if (!IsValidEnvironmentName(name)) - { - BH.Engine.Base.Compute.RecordError("A BHoM Python virtualenv cannot cannot contain invalid filepath characters."); - return null; - } - - if (!File.Exists(executable)) - { - BH.Engine.Base.Compute.RecordError($"{executable} doesn't exist."); - return null; - } - - if (!run) - { - BH.Engine.Base.Compute.RecordWarning($"Please toggle \"{nameof(run)}\" to true."); - return null; - } - - // install base BHoM Python environment if it's not already installed - oM.Python.PythonEnvironment baseEnv = Compute.InstallBasePythonEnvironment(run); - string bhomPythonExecutable = baseEnv.Executable; - - // set location where virtual env will be created - string targetDirectory = Path.Combine(Query.EnvironmentsDirectory(), name); - if (!Directory.Exists(targetDirectory)) - { - Directory.CreateDirectory(targetDirectory); - } - - // return existing env if it already exists - oM.Python.PythonEnvironment env = new oM.Python.PythonEnvironment() { Name = name, Executable = Path.Combine(targetDirectory, "Scripts", "python.exe") }; - if (env.EnvironmentExists()) - { - BH.Engine.Base.Compute.RecordNote($"The {name} environment already exists and is being returned here instead of installing it again. To install a fresh version of this environment, remove this environment first."); - return env; - } - - // create paths for use in this process - executable = Modify.AddQuotesIfRequired(executable); - - // create log of installation as process continues - useful for debugging if things go wrong! - string logFile = Path.Combine(targetDirectory, "installation.log"); - - // create installation commands - string baseBHoMPackage = Path.Combine(Query.CodeDirectory(), baseEnv.Name); - List installationCommands = new List() { - $"{bhomPythonExecutable} -m virtualenv --python={executable} {targetDirectory}", // create the virtualenv of the target executable - $"{executable} -m pip freeze > {Path.Combine(targetDirectory, "requirements.txt")}", // get the installed packages from the referenced executable - $"{Path.Combine(targetDirectory, "Scripts", "activate")} && python -m pip install -r {Path.Combine(targetDirectory, "requirements.txt")}", // install requirements into virtualenv - $"{Path.Combine(targetDirectory, "Scripts", "activate")} && python -m pip install ipykernel pytest", // install ipykernel and pytest into virtualenv - $"{Path.Combine(targetDirectory, "Scripts", "activate")} && python -m ipykernel install --name={name}", // register environment with ipykernel - $"{Path.Combine(targetDirectory, "Scripts", "activate")} && python -m pip install --no-warn-script-location -e {baseBHoMPackage}", // install base BHoM package to virtualenv - }; - if (localPackage != null) - installationCommands.Add($"{Path.Combine(targetDirectory, "Scripts", "activate")} && python -m pip install -e {Modify.AddQuotesIfRequired(localPackage)}"); // install local package into virtualenv - - using (StreamWriter sw = File.AppendText(logFile)) - { - sw.WriteLine(LoggingHeader($"Installation started for referenced {name} Python environment")); - - foreach (string command in installationCommands) - { - sw.WriteLine($"[{System.DateTime.Now.ToString("s")}] {command}"); - sw.WriteLine(Compute.RunCommandStdout($"{command}", hideWindows: true)); - } - } - - return env; - } - } -} - diff --git a/Python_Engine/Compute/InstallVirtualenv.cs b/Python_Engine/Compute/InstallVirtualenv.cs deleted file mode 100644 index 7a050d2..0000000 --- a/Python_Engine/Compute/InstallVirtualenv.cs +++ /dev/null @@ -1,110 +0,0 @@ -/* - * This file is part of the Buildings and Habitats object Model (BHoM) - * Copyright (c) 2015 - 2023, the respective contributors. All rights reserved. - * - * Each contributor holds copyright over their respective contributions. - * The project versioning (Git) records all such contribution source information. - * - * - * The BHoM is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * The BHoM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this code. If not, see . - */ - -using BH.oM.Base.Attributes; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.IO; -using System.Linq; - -namespace BH.Engine.Python -{ - public static partial class Compute - { - [Description("Install a virtualenv of the given configuration.")] - [Input("name", "The name of this virtualenv.")] - [Input("pythonVersion", "The version of Python to use for this environment.")] - [Input("localPackage", "A local package to be included in the resultant virtualenv. This are where custom BHoM code can be added into this environment.")] - [Input("run", "Run the installation process for this virtualenv.")] - [Output("env", "The exectuble for the resultant virtualenv.")] - public static oM.Python.PythonEnvironment InstallVirtualenv( - string name, - oM.Python.Enums.PythonVersion pythonVersion, - string localPackage = null, - bool run = false - ) - { - - if (!IsValidEnvironmentName(name)) - { - BH.Engine.Base.Compute.RecordError("A BHoM Python virtualenv cannot cannot contain invalid filepath characters."); - return null; - } - - if (!run) - { - BH.Engine.Base.Compute.RecordWarning($"Please toggle \"{nameof(run)}\" to true."); - return null; - } - - // install base BHoM Python environment if it's not already installed - oM.Python.PythonEnvironment baseEnv = Compute.InstallBasePythonEnvironment(run); - string bhomPythonExecutable = baseEnv.Executable; - - // set location where virtual env will be created - string targetDirectory = Path.Combine(Query.EnvironmentsDirectory(), name); - if (!Directory.Exists(targetDirectory)) - Directory.CreateDirectory(targetDirectory); - - // return the existing environment if it already exists - oM.Python.PythonEnvironment env = new oM.Python.PythonEnvironment() { Name = name, Executable = Path.Combine(targetDirectory, "Scripts", "python.exe") }; - if (env.EnvironmentExists()) - { - BH.Engine.Base.Compute.RecordNote($"The {name} environment already exists and is being returned here instead of installing it again. To install a fresh version of this environment, remove this environment first."); - return env; - } - - // get the python version executable to reference for this virtualenv - string executable = pythonVersion.DownloadPython(); - - // create log of installation as process continues - useful for debugging if things go wrong! - string logFile = Path.Combine(targetDirectory, "installation.log"); - - // create installation commands - string baseBHoMPackage = Path.Combine(Query.CodeDirectory(), baseEnv.Name); - List installationCommands = new List() { - $"{bhomPythonExecutable} -m virtualenv --python={executable} {targetDirectory}", // create the virtualenv of the target executable - $"{Path.Combine(targetDirectory, "Scripts", "activate")} && python -m pip install ipykernel pytest", // install ipykernel and pytest into virtualenv - $"{Path.Combine(targetDirectory, "Scripts", "activate")} && python -m ipykernel install --name={name}", // register environment with ipykernel - $"{Path.Combine(targetDirectory, "Scripts", "activate")} && python -m pip install --no-warn-script-location -e {baseBHoMPackage}", // install base BHoM package to virtualenv - }; - if (localPackage != null) - installationCommands.Add($"{Path.Combine(targetDirectory, "Scripts", "activate")} && python -m pip install -e {Modify.AddQuotesIfRequired(localPackage)}"); // install local package into virtualenv - - - using (StreamWriter sw = File.AppendText(logFile)) - { - sw.WriteLine(LoggingHeader($"Installation started for standalone {name} Python environment")); - - foreach (string command in installationCommands) - { - sw.WriteLine($"[{System.DateTime.Now.ToString("s")}] {command}"); - sw.WriteLine(Compute.RunCommandStdout($"{command}", hideWindows: true)); - } - } - - return env; - } - } -} - diff --git a/Python_Engine/Compute/Invoke.cs b/Python_Engine/Compute/Invoke.cs deleted file mode 100644 index e206e85..0000000 --- a/Python_Engine/Compute/Invoke.cs +++ /dev/null @@ -1,44 +0,0 @@ -/* - * This file is part of the Buildings and Habitats object Model (BHoM) - * Copyright (c) 2015 - 2023, the respective contributors. All rights reserved. - * - * Each contributor holds copyright over their respective contributions. - * The project versioning (Git) records all such contribution source information. - * - * - * The BHoM is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * The BHoM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this code. If not, see . - */ - -using BH.oM.Base.Attributes; -using Python.Runtime; -using System.Collections.Generic; -using System.Linq; - -namespace BH.Engine.Python -{ - public static partial class Compute - { - [ToBeRemoved("5.3", "This method included in order to fool versioning into playing nicely.")] - public static PyObject Invoke(PyObject module, string method, Dictionary args) - { - return module; - } - - [ToBeRemoved("5.3", "This method included in order to fool versioning into playing nicely.")] - public static PyObject Invoke(PyObject module, string method, IEnumerable args, Dictionary kwargs) - { - return module; - } - } -} diff --git a/Python_Engine/Compute/Logging.cs b/Python_Engine/Compute/Logging.cs deleted file mode 100644 index c8fe65c..0000000 --- a/Python_Engine/Compute/Logging.cs +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This file is part of the Buildings and Habitats object Model (BHoM) - * Copyright (c) 2015 - 2023, the respective contributors. All rights reserved. - * - * Each contributor holds copyright over their respective contributions. - * The project versioning (Git) records all such contribution source information. - * - * - * The BHoM is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * The BHoM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this code. If not, see . - */ - -using BH.oM.Base.Attributes; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace BH.Engine.Python -{ - public static partial class Compute - { - [Description("Create a header for a logging document.")] - [Input("text", "A string to place into the header.")] - [Output("header", "A header string.")] - public static string LoggingHeader(string text) - { - StringBuilder sb = new StringBuilder(); - int maxLength = new List() { text.Length, 54 }.Max(); - int innerLength = maxLength - 4; - string topBottom = String.Concat(Enumerable.Repeat("#", maxLength + 4)); - sb.AppendLine(); - sb.AppendLine(topBottom); - sb.AppendLine($"# {String.Format($"{{0,-{maxLength}}}", text)} #"); - sb.AppendLine($"# {String.Format($"{{0,-{maxLength}}}", System.DateTime.Now.ToString("s"))} #"); - sb.AppendLine($"# {String.Format($"{{0,-{maxLength}}}", $"BHoM version {BH.Engine.Base.Query.BHoMVersion()}")} #"); - sb.AppendLine(topBottom); - return sb.ToString(); - } - } -} - diff --git a/Python_Engine/Compute/Remove.cs b/Python_Engine/Compute/Remove.cs new file mode 100644 index 0000000..009bbd1 --- /dev/null +++ b/Python_Engine/Compute/Remove.cs @@ -0,0 +1,95 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2023, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using BH.oM.Base.Attributes; +using System.ComponentModel; +using System.IO; + +namespace BH.Engine.Python +{ + public static partial class Compute + { + + [Description("Remove a named BHoM Python kernel.")] + [Input("kernelName", "The name of the kernel to remove.")] + public static void RemoveKernel(string kernelName) + { + string kernelPath = Path.Combine(Query.DirectoryKernels(), kernelName); + if (Directory.Exists(kernelPath)) + Directory.Delete(kernelPath, true); + } + + [Description("Remove all BHoM Python kernels.")] + public static void RemoveAllKernels() + { + DirectoryInfo di = new DirectoryInfo(Query.DirectoryKernels()); + foreach (FileInfo file in di.EnumerateFiles()) + { + file.Delete(); + } + foreach (DirectoryInfo dir in di.EnumerateDirectories()) + { + dir.Delete(true); + } + } + + [Description("Remove the named BHoM Python virtual environment.")] + [Input("envName", "The name of the BHoM Python virtual environment to remove.")] + public static void RemoveVirtualEnvironment(string envName) + { + string envPath = Path.Combine(Query.DirectoryEnvironments(), envName); + if (Directory.Exists(envPath)) + Directory.Delete(envPath, true); + + RemoveKernel(envName); + } + + [Description("Remove all BHoM Python virtual environments.")] + public static void RemoveAllVirtualEnvironments() + { + DirectoryInfo di = new DirectoryInfo(Query.DirectoryEnvironments()); + foreach (DirectoryInfo dir in di.EnumerateDirectories()) + { + if (dir.Name != Query.ToolkitName()) + { + RemoveVirtualEnvironment(dir.Name); + } + + } + } + + [Description("Completely remove the base BHoM Python environment.")] + public static void RemoveBaseEnvironment() + { + string basePath = Path.Combine(Query.DirectoryEnvironments(), Query.ToolkitName()); + if (Directory.Exists(basePath)) + Directory.Delete(basePath, true); + } + + [Description("Completely remove all BHoM-related Python environments and kernels.")] + public static void RemoveEverything() + { + RemoveAllVirtualEnvironments(); + RemoveBaseEnvironment(); + } + } +} diff --git a/Python_Engine/Compute/RemoveVirtualenv.cs b/Python_Engine/Compute/RemoveVirtualenv.cs deleted file mode 100644 index 4c82e05..0000000 --- a/Python_Engine/Compute/RemoveVirtualenv.cs +++ /dev/null @@ -1,79 +0,0 @@ -/* - * This file is part of the Buildings and Habitats object Model (BHoM) - * Copyright (c) 2015 - 2023, the respective contributors. All rights reserved. - * - * Each contributor holds copyright over their respective contributions. - * The project versioning (Git) records all such contribution source information. - * - * - * The BHoM is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * The BHoM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this code. If not, see . - */ - -using BH.oM.Base.Attributes; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.IO; -using System.Linq; - -namespace BH.Engine.Python -{ - public static partial class Compute - { - [Description("Remove a virtualenv of the given name.")] - [Input("name", "The name of this virtualenv to remove.")] - [Input("run", "Run the installation process for this virtualenv.")] - [Output("success", "True if the environment of the given name has been removed.")] - public static bool RemoveVirtualenv( - string name, - bool run = false - ) - { - if (run) - { - string bhomPythonExecutable = Path.Combine(Query.EnvironmentsDirectory(), Query.ToolkitName(), "python.exe"); - - string targetDirectory = Path.Combine(Query.EnvironmentsDirectory(), name); - oM.Python.PythonEnvironment env = new oM.Python.PythonEnvironment() { Name = name, Executable = Path.Combine(targetDirectory, "Scripts", "python.exe") }; - - if (env.EnvironmentExists()) - { - string logFile = Path.Combine(Query.EnvironmentsDirectory(), "removal.log"); - - List uninstallationCommands = new List() { - $"rmdir {Modify.AddQuotesIfRequired(targetDirectory)} /S /Q", // delete folder and contents of given env name - $"{bhomPythonExecutable} -m jupyter kernelspec remove {name.ToLower()} -y", // remove the registered kernel - }; - using (StreamWriter sw = File.AppendText(logFile)) - { - sw.WriteLine(LoggingHeader($"Uninstalling {name} BHoM Python environment")); - - foreach (string command in uninstallationCommands) - { - sw.WriteLine($"[{System.DateTime.Now.ToString("s")}] {command}"); - Compute.RunCommandStdout($"{command}", hideWindows: true); - } - } - return true; - } - else - { - BH.Engine.Base.Compute.RecordError($"{Query.ToString(env)} wasn't found and can not be removed."); - } - } - return false; - } - } -} - diff --git a/Python_Engine/Compute/RequirementsTxt.cs b/Python_Engine/Compute/RequirementsTxt.cs new file mode 100644 index 0000000..541fe47 --- /dev/null +++ b/Python_Engine/Compute/RequirementsTxt.cs @@ -0,0 +1,85 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2023, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using BH.oM.Base.Attributes; +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; + +namespace BH.Engine.Python +{ + public static partial class Compute + { + [Description("Create a requirements.txt file from an existing Python executable.")] + [Input("executable", "The path to the Python executable to create the requirements.txt file from.")] + [Input("targetPath", "The path to the requirements.txt file to create. If not specified, the file will be created in the same directory as the executable.")] + [Output("requirementsTxt", "The path to the requirements.txt file.")] + public static string RequirementsTxt( + string executable, + string targetPath = null + ) + { + if (!File.Exists(executable)) + { + BH.Engine.Base.Compute.RecordError($"The executable '{executable}' does not exist."); + return null; + } + + if (string.IsNullOrEmpty(targetPath)) + { + targetPath = Path.Combine(Path.GetDirectoryName(executable), "requirements.txt"); + } + else + { + if (!Directory.Exists(Path.GetDirectoryName(targetPath))) + { + BH.Engine.Base.Compute.RecordError($"The executable '{executable}' does not exist."); + return null; + } + } + + System.Diagnostics.Process process = new System.Diagnostics.Process() + { + StartInfo = new System.Diagnostics.ProcessStartInfo() + { + FileName = executable, + Arguments = $"-m pip freeze", + UseShellExecute = false, + RedirectStandardError = true, + RedirectStandardOutput = true + } + }; + using (Process p = Process.Start(process.StartInfo)) + { + StreamWriter sr = new StreamWriter(targetPath); + p.WaitForExit(); + if (p.ExitCode != 0) + BH.Engine.Base.Compute.RecordError($"Error creating requirements.txt from \"{executable}\".\n{p.StandardError.ReadToEnd()}"); + sr.Write(p.StandardOutput.ReadToEnd()); + sr.Close(); + } + + return targetPath; + } + } +} diff --git a/Python_Engine/Compute/Run.cs b/Python_Engine/Compute/Run.cs index 5cc7839..56a3642 100644 --- a/Python_Engine/Compute/Run.cs +++ b/Python_Engine/Compute/Run.cs @@ -20,85 +20,21 @@ * along with this code. If not, see . */ +using BH.oM.Base; using BH.oM.Base.Attributes; -using System.Threading; -using System.Threading.Tasks; +using BH.oM.Python; +using BH.oM.Python.Enums; +using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics; using System.IO; using System.Linq; -using BH.oM.Python; -using BH.oM.Base; -using System.Collections.Generic; +using System.Threading.Tasks; namespace BH.Engine.Python { public static partial class Compute { - [Description("Run a command via CMD and return stdout.")] - [Input("command", "The command to be run.")] - [Input("hideWindows", "Set to True to hide cmd windows.")] - [Input("startDirectory", "The directory in which the command should be run.")] - [Output("stdout", "The StandardOutput from the command that was run. If the process failed, then StandardError will be returned here instead.")] - public static async void RunCommandAsync(string command, bool hideWindows = true, string startDirectory = null) - { - await Task.Run(() => - { - System.Diagnostics.Process process = new System.Diagnostics.Process(); - System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo(); - - string commandMode = "/K"; - if (hideWindows) - { - startInfo.CreateNoWindow = true; - startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; - commandMode = "/C"; - } - - startInfo.FileName = "cmd.exe"; - startInfo.RedirectStandardOutput = true; - startInfo.RedirectStandardError = true; - startInfo.UseShellExecute = false; - startInfo.Arguments = $"{commandMode} {command} && exit"; - - process.StartInfo = startInfo; - process.Start(); - }); - } - - [Description("Run a command via CMD and return True if successful and False if not.")] - [Input("command", "The command to be run.")] - [Input("hideWindows", "Set to True to hide cmd windows.")] - [Input("startDirectory", "The directory in which the command should be run.")] - [Input("timeoutMinutes", "A number of minutes beyond which this command will timeout.")] - [Output("success", "True if successful and False if not.")] - public static bool RunCommandBool(string command, bool hideWindows = false, string startDirectory = null, double timeoutMinutes = 5) - { - System.Diagnostics.Process process = new System.Diagnostics.Process(); - - string commandMode = "/K"; - if (hideWindows) - { - process.StartInfo.CreateNoWindow = true; - process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden; - commandMode = "/C"; - } - process.StartInfo.FileName = "cmd.exe"; - process.StartInfo.RedirectStandardOutput = true; - process.StartInfo.UseShellExecute = false; - process.StartInfo.Arguments = $"{commandMode} {command}"; - process.Start(); - - int millisecondsToWait = (int)timeoutMinutes * 60 * 1000; - process.WaitForExit(millisecondsToWait); - - if (process.ExitCode != 0) - { - return false; - } - - return true; - } - [Description("Run a command via CMD and return stdout.")] [Input("command", "The command to be run.")] [Input("hideWindows", "Set to True to hide cmd windows.")] @@ -160,72 +96,5 @@ public static string RunPythonString(this oM.Python.PythonEnvironment env, strin return RunCommandStdout(cmd, hideWindows: true); } - - [Description("Run a Python script using the given BHoM PythonEnvironment, and return a BHoM object containing results.")] - [Input("pythonEnvironment", "The Python environment with which to run the Python script.")] - [Input("pythonScript", "A path to a Python script (a *.py file containing a __main__ executable function).")] - [Input("arguments", "A list of optional arguments to pass to the script.")] - [Output("obj", "A BHoM CustomObject containing results from this script.")] - public static oM.Base.CustomObject RunPythonScript(this PythonEnvironment pythonEnvironment, string pythonScript, List arguments = null) - { - if (!File.Exists(pythonScript)) - { - BH.Engine.Base.Compute.RecordError($"{pythonScript} does not exist."); - return null; - } - - string contents = File.ReadAllText(pythonScript); - List executableStrings = new List() - { - "if __name__ == \"__main__\":", - "if __name__ is \"__main__\":", - "if __name__ == '__main__':", - "if __name__ is '__main__':", - "if __name__ == \'__main__\':", - "if __name__ is \'__main__\':", - }; - if (!executableStrings.Any(contents.Contains)) - { - BH.Engine.Base.Compute.RecordError($"The script passed does not seem to be directly executable using Python. It should contain an 'if __name__ == \"__main__\"' to enable the file to be called directly."); - return null; - } - - string cmd = $"{pythonEnvironment.Executable} {pythonScript}"; - if (arguments.Count > 0) - { - for (int i = 0; i < arguments.Count; i++) - { - cmd += $" {arguments[i]}"; - } - } - - string tempFile = RunCommandStdout(cmd, hideWindows: true); - - if (!File.Exists(tempFile)) - { - if (arguments.Contains("-h")) - { - BH.Engine.Base.Compute.RecordNote($"It looks like you've asked for some documentation. Here it is!"); - } - else - { - BH.Engine.Base.Compute.RecordError($"Something went wrong! The object returned contains the error message given by the Python code."); - } - - return new CustomObject() - { - CustomData = new Dictionary() - { - { "output", (object)tempFile } - } - }; - } - else - { - string tempFileContent = File.ReadAllText(tempFile); - return Serialiser.Convert.FromJson(tempFileContent) as CustomObject; - } - } } } - diff --git a/Python_Engine/Compute/UnzipFile.cs b/Python_Engine/Compute/UnzipFile.cs new file mode 100644 index 0000000..8c86406 --- /dev/null +++ b/Python_Engine/Compute/UnzipFile.cs @@ -0,0 +1,82 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2023, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using BH.oM.Base.Attributes; +using System; +using System.ComponentModel; +using System.IO; + +namespace BH.Engine.Python +{ + public static partial class Compute + { + [Description("Extract the contents of an archive.")] + [Input("archivePath", "The archive to extract.")] + [Input("targetDirectory", "The destination directory to extract into.")] + [Input("targetName", "An optional input to rename the archive directory.")] + [Input("delete", "An optional input to delete the archive after extraction.")] + public static void UnzipFile( + string archivePath, + string targetDirectory, + string targetName = null, + bool delete = false + ) + { + if (string.IsNullOrWhiteSpace(archivePath)) + { + BH.Engine.Base.Compute.RecordError($"The archivePath cannot be null or empty."); + } + + if (string.IsNullOrWhiteSpace(targetDirectory)) + { + BH.Engine.Base.Compute.RecordError($"The targetDirectory cannot be null or empty."); + } + + if (!File.Exists(archivePath)) + { + BH.Engine.Base.Compute.RecordError($"The archivePath '{archivePath}' does not exist."); + } + + if (!Directory.Exists(targetDirectory)) + { + BH.Engine.Base.Compute.RecordError($"The targetDirectory '{targetDirectory}' does not exist."); + } + + if (string.IsNullOrWhiteSpace(targetName)) + { + targetName = Path.GetFileNameWithoutExtension(archivePath); + } + + string targetPath = Path.Combine(targetDirectory, targetName); + + if (Directory.Exists(targetPath)) + { + BH.Engine.Base.Compute.RecordError($"The targetPath '{targetPath}' already exists."); + } + + System.IO.Compression.ZipFile.ExtractToDirectory(archivePath, targetPath); + + if (delete) + File.Delete(archivePath); + } + } +} diff --git a/Python_Engine/Compute/VirtualEnvironment.cs b/Python_Engine/Compute/VirtualEnvironment.cs new file mode 100644 index 0000000..4ad74d7 --- /dev/null +++ b/Python_Engine/Compute/VirtualEnvironment.cs @@ -0,0 +1,135 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2023, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using BH.oM.Base.Attributes; +using BH.oM.Python; +using BH.oM.Python.Enums; +using Microsoft.SqlServer.Server; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; +using System.Linq; + +namespace BH.Engine.Python +{ + public static partial class Compute + { + [Description("Install a Python Virtual Environment of a given version and name.")] + [Input("version", "The version of Python to use for this environment.")] + [Input("name", "The name of this virtualenv.")] + [Input("reload", "Reload the virtual environment rather than recreating it, if it already exists.")] + [Output("env", "The exectuble for the resultant virtualenv.")] + public static PythonEnvironment VirtualEnvironment(this PythonVersion version, string name, bool reload = true) + { + if (!Query.IsValidEnvironmentName(name)) + { + BH.Engine.Base.Compute.RecordError("A BHoM Python virtual environment cannot cannot contain invalid filepath characters."); + return null; + } + if (version == PythonVersion.Undefined) + { + BH.Engine.Base.Compute.RecordError("Please provide a version of Python."); + return null; + } + + // check that base environment is installed and return null and raise error if it isn't + string baseEnvironmentExecutable = Path.Combine(Query.DirectoryBaseEnvironment(), "python.exe"); + if (!File.Exists(baseEnvironmentExecutable)) + { + BH.Engine.Base.Compute.RecordWarning("The base Python environment doesnt seem to be installed. Install it first in order to run this method."); + return null; + } + + string targetExecutable = Query.VirtualEnvironmentExecutable(name); + string targetDirectory = Query.VirtualEnvironmentDirectory(name); + bool exists = Query.VirtualEnvironmentExists(name); + + if (exists && reload) + return new PythonEnvironment() { Name = name, Executable = targetExecutable }; + + if (exists && !reload) + { + // remove all existing environments and kernels + RemoveVirtualEnvironment(name); + RemoveKernel(name); + } + + // download the target version of Python + string referencedExecutable = version.DownloadPython(); + + // move the directory containing referencedExecutable into Query.DirectoryBaseEnvironment() using the same name + string sourceDirectory = Path.GetDirectoryName(referencedExecutable); + string destinationDirectory = Path.Combine(Query.DirectoryBaseEnvironment(), new DirectoryInfo(Path.GetDirectoryName(referencedExecutable)).Name); + if (!Directory.Exists(destinationDirectory)) + { + Directory.Move(sourceDirectory, destinationDirectory); + } + if (Directory.Exists(sourceDirectory)) + Directory.Delete(sourceDirectory, true); + referencedExecutable = Path.Combine(destinationDirectory, "python.exe"); + + // create the venv from the base environment + Process process = new Process() + { + StartInfo = new ProcessStartInfo() + { + FileName = Modify.AddQuotesIfRequired(baseEnvironmentExecutable), + Arguments = $"-m virtualenv --python={Modify.AddQuotesIfRequired(referencedExecutable)} {Modify.AddQuotesIfRequired(targetDirectory)}", + RedirectStandardError = true, + UseShellExecute = false, + } + }; + using (Process p = Process.Start(process.StartInfo)) + { + p.WaitForExit(); + if (p.ExitCode != 0) + BH.Engine.Base.Compute.RecordError($"Error creating virtual environment.\n{p.StandardError.ReadToEnd()}"); + } + + // install ipykernel, pytest and black into new virtualenv + InstallPackages(targetExecutable, new List() { "ipykernel", "pytest", "black" }); + + // register the virtualenv with the base environment + Process process2 = new Process() + { + StartInfo = new ProcessStartInfo() + { + FileName = targetExecutable, + Arguments = $"-m ipykernel install --name={name}", + RedirectStandardError = true, + UseShellExecute = false, + } + }; + using (Process p = Process.Start(process2.StartInfo)) + { + p.WaitForExit(); + if (p.ExitCode != 0) + BH.Engine.Base.Compute.RecordError($"Error registering the \"{name}\" virtual environment.\n{p.StandardError.ReadToEnd()}"); + } + // replace text in a file + + + return new PythonEnvironment() { Executable = targetExecutable, Name = name }; + } + } +} diff --git a/Python_Engine/Convert/ToPython.cs b/Python_Engine/Convert/ToPython.cs index d157f8d..4acdb1b 100644 --- a/Python_Engine/Convert/ToPython.cs +++ b/Python_Engine/Convert/ToPython.cs @@ -31,11 +31,11 @@ namespace BH.Engine.Python { public static partial class Convert { + // TODO - REMOVE THIS METHOD, NO LONGER REQUIRED, PLUS IT HAS AN OLD ToBeRemoved TAG [ToBeRemoved("5.3", "This method included in order to fool versioning into playing nicely.")] public static bool ToPython(this T[,] input) { return false; } } -} - +} \ No newline at end of file diff --git a/Python_Engine/Properties/AssemblyInfo.cs b/Python_Engine/Properties/AssemblyInfo.cs index 0f31ecc..b5e1cff 100644 --- a/Python_Engine/Properties/AssemblyInfo.cs +++ b/Python_Engine/Properties/AssemblyInfo.cs @@ -55,6 +55,6 @@ // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("6.0.0.0")] -[assembly: AssemblyFileVersion("6.2.0.0")] +[assembly: AssemblyFileVersion("6.3.0.0")] diff --git a/Python_Engine/Python/README.md b/Python_Engine/Python/README.md index 3e8dd22..bf482e2 100644 --- a/Python_Engine/Python/README.md +++ b/Python_Engine/Python/README.md @@ -12,7 +12,7 @@ A BHoM toolkit can be associated with a Python environment. The toolkit needs to **In the example below we'll go through creating a Python environment for the `Example_Toolkit`**. -### Creating the Python code structure +### Structure All Python code within BHoM should be placed within the hosting toolkit, inside its `*_Engine` folder. Within this folder, the following directory structure should be created (files have been included here within `./example_toolkit`, though these are purely indicative and struictures can vary based on toolkit need): @@ -22,63 +22,69 @@ All Python code within BHoM should be placed within the hosting toolkit, inside ├── Example_Engine │ └── Python | ├── README.md -| ├── setup.py | ├── requirements.txt -| └── src -| └── example_toolkit -| ├── __init__.py -| ├── code_directory_a -| | ├── __init__.py__ -| | └── method.py -| ├── code_directory_b -| | └── __init__.py -| | └── method.py -| └── helpers -| ├── __init__.py -| └── helper_method_a.py +| ├── setup.py +| ├── src +| | └── example_toolkit +| | ├── __init__.py +| | ├── module_a +| | | ├── __init__.py +| | | └── ... +| | └── ... +| └── tests +| ├── __init__.py +| ├── test_module_a.py +| └── ... └── ... ``` -### Populating the `requirements.txt` file content +### `setup.py` -If you've used Python before you may be familiar with `requirements.txt` files. These contain the packages required by a Python environment, and can specify the version of those packages also. In BHoM, an environment may be defined for a specific toolkit, associated with an external Python environment (replicating it for use in BHoM workflowsrequirements), or even as a standalone environment used for a specific purpose or project. +The `setup.py` file must be created alongside the code within a toolkit. This should contain teh following: -Toolkit-specific environments, and standalone environments should be explicit about which versions of packages to install. - -If the Python environment created is referencing an external environment (i.e. it recreates the external environment for use/development in BHoM) then packages listed in the `requirements.txt` file should not state versions. This is so that the external "source" environment has control over packages installed, rather than the BHoM copy of that environment. +```python +## setup.py ## +from pathlib import Path -By default, `ipykernel` is included in all BHoM created Python environments in order to use that environments Python kernel within a [Jupyter Notebook](https://jupyter.org/) UI (you can run one of these using the instructions [here](#scriptingnotebooks). `pytest` is also installed to aid in automated unit testing, and `pylint` is included to aid in finding errors in code during development. +import setuptools +from win32api import HIWORD, LOWORD, GetFileVersionInfo -### Populating the `setup.py` file +TOOLKIT_NAME = "Example_Toolkit" -All BHoM Python code should be stored within the `./Python/src/example_toolkit` directory, and can be further organised within this to split different packages up to better manage code. The `setup.py` file defines what folders to include when installing this code into the environment that will be created. In the code below, `src` is set as the location where the Python code is stored, and `requirements.txt` is also being used to state what packages to install alongside the local code. +def _bhom_version() -> str: + """Return the version of BHoM installed (using the BHoM.dll in the root BHoM directory.""" + info = GetFileVersionInfo("C:/ProgramData/BHoM/Assemblies/BHoM.dll", "\\") # pylint: disable=[no-name-in-module] + ms = info["FileVersionMS"] + ls = info["FileVersionLS"] + return f"{HIWORD(ms)}.{LOWORD(ms)}.{HIWORD(ls)}.{LOWORD(ls)}" # pylint: disable=[no-name-in-module] -```python -## setup.py ## -from setuptools import setup, find_packages -import pathlib +BHOM_VERSION = _bhom_version() -here = pathlib.Path(__file__).parent.resolve() +here = Path(__file__).parent.resolve() long_description = (here / "README.md").read_text(encoding="utf-8") -requirements = [i.strip() for i in (here / "requirements.txt").read_text(encoding="utf-8-sig").splitlines()] +requirements = [ + i.strip() + for i in (here / "requirements.txt").read_text(encoding="utf-8-sig").splitlines() +] -setup( - name="example_toolkit", +setuptools.setup( + name=TOOLKIT_NAME.lower(), author="BHoM", author_email="bhombot@burohappold.com", - description="A Python library that enables Example Toolkit python code to be used within BHoM workflows.", + description=f"A Python library that contains code intended to be used by the {TOOLKIT_NAME} Python environment for BHoM workflows.", long_description=long_description, long_description_content_type="text/markdown", - url="https://github.com/BHoM/Example_Toolkit", + url=f"https://github.com/BHoM/{TOOLKIT_NAME}", package_dir={"": "src"}, - packages=find_packages(where="src", exclude=['tests']), + packages=setuptools.find_packages(where="src", exclude=['tests']), install_requires=requirements, + version=BHOM_VERSION, ) ``` -### Installing the Python environment +### Enabling installation -Once the Example_Toolkit has been given a Python folder containing the requisite files and directory structure, two methods are required in the toolkit to which Python code is being added. The first is a `Query` method for the name of that toolkit: +Two methods are required in the toolkit to which Python code is being added. The first is a `Query` method for the name of that toolkit: ```C# using BH.oM.Base.Attributes; diff --git a/Python_Engine/Python/requirements.txt b/Python_Engine/Python/requirements.txt index e47f553..5f28270 100644 --- a/Python_Engine/Python/requirements.txt +++ b/Python_Engine/Python/requirements.txt @@ -1,12 +1 @@ -black -Jinja2 -jupyterlab -matplotlib -numpy -pandas -pymongo -pyodbc -pytest -SQLAlchemy -tables -virtualenv \ No newline at end of file + \ No newline at end of file diff --git a/Python_Engine/Python/setup.py b/Python_Engine/Python/setup.py index 73063bc..51c6de9 100644 --- a/Python_Engine/Python/setup.py +++ b/Python_Engine/Python/setup.py @@ -1,20 +1,36 @@ -from setuptools import setup, find_packages -import pathlib +from pathlib import Path -here = pathlib.Path(__file__).parent.resolve() +import setuptools +from win32api import HIWORD, LOWORD, GetFileVersionInfo + +TOOLKIT_NAME = "Python_Toolkit" + +def _bhom_version() -> str: + """Return the version of BHoM installed (using the BHoM.dll in the root BHoM directory.""" + info = GetFileVersionInfo("C:/ProgramData/BHoM/Assemblies/BHoM.dll", "\\") # pylint: disable=[no-name-in-module] + ms = info["FileVersionMS"] + ls = info["FileVersionLS"] + return f"{HIWORD(ms)}.{LOWORD(ms)}.{HIWORD(ls)}.{LOWORD(ls)}" # pylint: disable=[no-name-in-module] + +BHOM_VERSION = _bhom_version() + +here = Path(__file__).parent.resolve() long_description = (here / "README.md").read_text(encoding="utf-8") -requirements = [i.strip() for i in (here / "requirements.txt").read_text(encoding="utf-8-sig").splitlines()] +requirements = [ + i.strip() + for i in (here / "requirements.txt").read_text(encoding="utf-8-sig").splitlines() +] -# TODO - populate values here with values from global config instead -setup( - name="python_toolkit", +setuptools.setup( + name=TOOLKIT_NAME.lower(), author="BHoM", author_email="bhombot@burohappold.com", - description="A Python library that enables Python to be used within BHoM workflows", + description=f"A Python library that contains minimal code intended to be used by the {TOOLKIT_NAME} Python environment for BHoM workflows.", long_description=long_description, long_description_content_type="text/markdown", - url="https://github.com/BHoM/Python_Toolkit", + url=f"https://github.com/BHoM/{TOOLKIT_NAME}", package_dir={"": "src"}, - packages=find_packages(where="src", exclude=['tests']), + packages=setuptools.find_packages(where="src", exclude=['tests']), install_requires=requirements, + version=BHOM_VERSION, ) diff --git a/Python_Engine/Python/src/python_toolkit/bhom/analytics.py b/Python_Engine/Python/src/python_toolkit/bhom/analytics.py deleted file mode 100644 index 739a7b9..0000000 --- a/Python_Engine/Python/src/python_toolkit/bhom/analytics.py +++ /dev/null @@ -1,76 +0,0 @@ -import functools -import inspect -import sys -import traceback -from pathlib import Path -from typing import Callable -from uuid import uuid4 - -from python_toolkit.bhom.encoder import Encoder -from python_toolkit.bhom.ticks import ticks -from python_toolkit.bhom.version import version - -ASSEMBLIES_DIR = Path(r"C:\ProgramData\BHoM\Assemblies") -LOGGING_DIR = Path(r"C:\ProgramData\BHoM\Logs") -LOGGING_DIR.mkdir(exist_ok=True, parents=True) -BHOM_VERSION = ".".join([str(i) for i in version((ASSEMBLIES_DIR / "BHoM.dll").as_posix())][:2]) -UI_NAME = "PythonBHoM" -TICKS = ticks() - - -def analytics(f: Callable): - """A wrapper used to capture usage analytics of the decorated function.""" - - @functools.wraps(f) - def wrapper(*args, **kwargs): - - _, module_stack, _, file_path = str(inspect.getmodule(f)).split(" ") - module_stack = module_stack[1:-1] - - log_file = LOGGING_DIR / f"Usage_{UI_NAME}_{TICKS}.log" - - # gather metadata around current callable instance - d = { - "BHoMVersion": BHOM_VERSION, - "BHoM_Guid": str(uuid4()), - "CallerName": f"{module_stack}", - "ComponentId": str(uuid4()), - "CustomData": {}, - "Errors": [], - "FileId": "", - "FileName": "", - "Fragments": [], - "Name": "", - "ProjectID": "", - "SelectedItem": - { - "Name": f.__name__, - "_bhomVersion": BHOM_VERSION, - "_t": "Python" - }, - "Tags": [], - "Time": - { - "$date": TICKS - }, - "UI": UI_NAME, - "UiVersion": sys.version, - "_bhomVersion": BHOM_VERSION, - "_t": "BH.oM.UI.UsageLogEntry" - } - - # run method and capture errors - try: - f(*args, **kwargs) - except Exception: - d["Errors"].append(traceback.format_exc()) - - with open(log_file, "a+") as fp: - fp.write(str(d) + "\n") - - # print function here to enable downstream processes to access the location where the data is stored for loading into BHoM. - #print(log_file, file=sys.stdout) - - return f(*args, **kwargs) - - return wrapper diff --git a/Python_Engine/Python/src/python_toolkit/bhom/anonymise_filepath.py b/Python_Engine/Python/src/python_toolkit/bhom/anonymise_filepath.py deleted file mode 100644 index 8b44aa2..0000000 --- a/Python_Engine/Python/src/python_toolkit/bhom/anonymise_filepath.py +++ /dev/null @@ -1,21 +0,0 @@ -from pathlib import Path - -def anonymise_filepath(filepath): - """Remove user identifying information from a filepath - NOTE: This currently only removes from after "Users". - """ - - fp = Path(filepath) - file_parts = list(fp.parts) - - username_index = None - for n, part in enumerate(file_parts): - if part.lower() == "users": - username_index = n + 1 - - if username_index is None: - return fp.as_posix() - - file_parts[username_index] = "" - - return Path(*file_parts).as_posix() diff --git a/Python_Engine/Python/src/python_toolkit/bhom/decorate_all_in_module.py b/Python_Engine/Python/src/python_toolkit/bhom/decorate_all_in_module.py deleted file mode 100644 index 47f8988..0000000 --- a/Python_Engine/Python/src/python_toolkit/bhom/decorate_all_in_module.py +++ /dev/null @@ -1,8 +0,0 @@ -import types - - -def decorate_all_in_module(module, decorator): - for name in dir(module): - obj = getattr(module, name) - if isinstance(obj, types.FunctionType): - setattr(module, name, decorator(obj)) diff --git a/Python_Engine/Python/src/python_toolkit/bhom/encoder.py b/Python_Engine/Python/src/python_toolkit/bhom/encoder.py deleted file mode 100644 index 0267d1d..0000000 --- a/Python_Engine/Python/src/python_toolkit/bhom/encoder.py +++ /dev/null @@ -1,21 +0,0 @@ -from datetime import date, datetime -from json import JSONEncoder -from pathlib import Path - -import numpy as np -import pandas as pd - - -class Encoder(JSONEncoder): - """A custom BHoM JSONEncoder class capable of serialising non-native Python datatypes into a JSONable object.""" - - def default(self, obj): - if isinstance(obj, np.ndarray): - return obj.tolist() - if isinstance(obj, (pd.Series, pd.DataFrame)): - return obj.to_dict() - if isinstance(obj, (datetime, date)): - return obj.isoformat() - if isinstance(obj, Path): - return obj.as_posix() - return JSONEncoder.default(self, obj) diff --git a/Python_Engine/Python/src/python_toolkit/bhom/package_contents.py b/Python_Engine/Python/src/python_toolkit/bhom/package_contents.py deleted file mode 100644 index 4a7e49f..0000000 --- a/Python_Engine/Python/src/python_toolkit/bhom/package_contents.py +++ /dev/null @@ -1,32 +0,0 @@ -import importlib.util -from pathlib import Path -import os - -MODULE_EXTENSIONS = '.py' - -def package_contents(package_name, exclude = None): - """Recursively traverse a module to find all packages within, and return these as a list of strings""" - - spec = importlib.util.find_spec(package_name) - if spec is None: - return set() - - pathname = Path(spec.origin).parent - ret = set() - with os.scandir(pathname) as entries: - for entry in entries: - if entry.name.startswith('__'): - continue - current = '.'.join((package_name, entry.name.partition('.')[0])) - if entry.is_file(): - if exclude is not None: - for x in exclude: - if x in entry.name: - continue - if entry.name.endswith(MODULE_EXTENSIONS): - ret.add(current) - elif entry.is_dir(): - ret.add(current) - ret |= package_contents(current) - - return ret diff --git a/Python_Engine/Python/src/python_toolkit/bhom/ticks.py b/Python_Engine/Python/src/python_toolkit/bhom/ticks.py deleted file mode 100644 index 78fd381..0000000 --- a/Python_Engine/Python/src/python_toolkit/bhom/ticks.py +++ /dev/null @@ -1,5 +0,0 @@ -from datetime import datetime - -def ticks(): - """Return the number of ticks since 0001:01:01 00:00:00""" - return int((datetime.utcnow() - datetime(1, 1, 1)).total_seconds()) \ No newline at end of file diff --git a/Python_Engine/Python/src/python_toolkit/bhom/version.py b/Python_Engine/Python/src/python_toolkit/bhom/version.py deleted file mode 100644 index b0adeb1..0000000 --- a/Python_Engine/Python/src/python_toolkit/bhom/version.py +++ /dev/null @@ -1,8 +0,0 @@ -from win32api import GetFileVersionInfo, LOWORD, HIWORD - -def version(filename): - """Return the version of a compiled Windows DLL""" - info = GetFileVersionInfo(filename, "\\") - ms = info['FileVersionMS'] - ls = info['FileVersionLS'] - return HIWORD (ms), LOWORD (ms), HIWORD (ls), LOWORD (ls) diff --git a/Python_Engine/Python/src/python_toolkit/bhom/__init__.py b/Python_Engine/Python/src/python_toolkit/bhomutil/__init__.py similarity index 100% rename from Python_Engine/Python/src/python_toolkit/bhom/__init__.py rename to Python_Engine/Python/src/python_toolkit/bhomutil/__init__.py diff --git a/Python_Engine/Python/src/python_toolkit/example/__init__.py b/Python_Engine/Python/src/python_toolkit/example/__init__.py deleted file mode 100644 index 5f28270..0000000 --- a/Python_Engine/Python/src/python_toolkit/example/__init__.py +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/Python_Engine/Python/src/python_toolkit/example/example_1.py b/Python_Engine/Python/src/python_toolkit/example/example_1.py deleted file mode 100644 index c954471..0000000 --- a/Python_Engine/Python/src/python_toolkit/example/example_1.py +++ /dev/null @@ -1,35 +0,0 @@ -import argparse - -from python_toolkit.bhom.analytics import analytics - - -@analytics -def example_1(a: float = 23, b: int = 42) -> str: - """An example method that generates and returns some text. - This method is also decorated with a ToBHoM method, which - pushes its output into a JSON for use in downstream BHoM - operations. - - Arguments: - a (float, optional): The first number. Default is 23. - b (int, optional): The second number. Default is 42. - - Returns: - string: Some really interesting text. - - """ - - return f"Hello there! You entered {a} and {b}.\n\nWhen added those equal {a + b}. Isn't that nice. Give yourself a pat on the back for such an excellent job.\n\nMaybe go make a nice cup of tea (or coffee, I'm not here to dictate your beverage choice).\n\nI've always been partial to an Irish Breakfast blend, though Assam is a close second.\n\nEither way, after all that hard work you're probably thirsty, so relax, put your feet up and take a break.\n\nYou could even go outside, or watch a movie. You could read a book.\n\nA sci-fi book, or a tragedy perhaps.\n\nDid you ever hear the tragedy of Darth Plagueis The Wise? I thought not. It's not a story the Jedi would tell you. It's a Sith legend. Darth Plagueis was a Dark Lord of the Sith, so powerful and so wise he could use the Force to influence the midichlorians to create life… He had such a knowledge of the dark side that he could even keep the ones he cared about from dying. The dark side of the Force is a pathway to many abilities some consider to be unnatural. He became so powerful… the only thing he was afraid of was losing his power, which eventually, of course, he did. Unfortunately, he taught his apprentice everything he knew, then his apprentice killed him in his sleep. Ironic. He could save others from death, but not himself." - - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument( - "-a", required=False, help="A float value", type=float, default=23 - ) - parser.add_argument( - "-b", required=False, help="Another int value", type=int, default=42 - ) - args = parser.parse_args() - - print(example_1(args.a, args.b)) diff --git a/Python_Engine/Python/src/python_toolkit/example/example_2.py b/Python_Engine/Python/src/python_toolkit/example/example_2.py deleted file mode 100644 index f1bd0ec..0000000 --- a/Python_Engine/Python/src/python_toolkit/example/example_2.py +++ /dev/null @@ -1,57 +0,0 @@ -import argparse -from pathlib import Path - -import matplotlib.pyplot as plt -import numpy as np -from python_toolkit.bhom.analytics import analytics - - -@analytics -def example_2(save_path: Path, n_steps: int = 25) -> Path: - """An example method to generate a chart and returns a dictionary. - - Arguments: - save_path (Path): The path where the generated chart should be saved. - n_steps (int): The number of steps between 0 and 2*Pi to plot. Default is 25. - - Returns: - Dict[str, Union[Path, List[float]]]: An interesting dictionary. - - """ - - x = np.linspace(0, 2 * np.pi, n_steps) - sin_x = np.sin(x) - cos_x = np.cos(x) - - fig, ax = plt.subplots(1, 1, figsize=(8, 4)) - - for y in [sin_x, cos_x]: - ax.plot(x, y, lw=1, zorder=2) - ax.scatter(x, y, c="k", s=2, zorder=3) - ax.grid(ls="--") - plt.tight_layout() - ax.set_title("Woohoo, a chart!") - plt.savefig(save_path.absolute(), dpi=150, transparent=False) - - return {"save_path": save_path.absolute(), "x": x, "sin_x": sin_x, "cos_x": cos_x} - - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument( - "-save_path", - required=True, - help="The path where the generated chart should be saved.", - type=lambda p: Path(p).absolute(), - ) - parser.add_argument( - "-n_steps", - required=False, - help="The number of steps between 0 and 2*Pi to plot. Default is 25.", - type=int, - default=25, - ) - - args = parser.parse_args() - - print(example_2(args.save_path, args.n_steps)) diff --git a/Python_Engine/Python_Engine.csproj b/Python_Engine/Python_Engine.csproj index dc2b735..0e6eb2a 100644 --- a/Python_Engine/Python_Engine.csproj +++ b/Python_Engine/Python_Engine.csproj @@ -33,11 +33,11 @@ False - C:\ProgramData\BHoM\Assemblies\BHoM.dll + $(ProgramData)\BHoM\Assemblies\BHoM.dll False - C:\ProgramData\BHoM\Assemblies\BHoM_Engine.dll + $(ProgramData)\BHoM\Assemblies\BHoM_Engine.dll False False @@ -47,12 +47,12 @@ False - C:\ProgramData\BHoM\Assemblies\Reflection_Engine.dll + $(ProgramData)\BHoM\Assemblies\Reflection_Engine.dll False False - C:\ProgramData\BHoM\Assemblies\Serialiser_Engine.dll + $(ProgramData)\BHoM\Assemblies\Serialiser_Engine.dll False @@ -67,26 +67,21 @@ - - - - - - - + + + + + + + - - - - - - - - + + + + - @@ -105,17 +100,7 @@ - - - - - - - - - - - + @@ -124,23 +109,20 @@ - xcopy "$(TargetDir)$(TargetFileName)" "C:\\ProgramData\\BHoM\\Assemblies" /Y - xcopy "$(TargetDir)Python.Runtime.dll" "C:\\ProgramData\\BHoM\\Assemblies" /Y - - :: create infrastructure necessary to support Python environments + xcopy "$(TargetDir)Python.Runtime.dll" "C:\\ProgramData\\BHoM\\Assemblies" /Y + + + + if not exist "C:\ProgramData\BHoM\Extensions\PythonEnvironments" mkdir "C:\ProgramData\BHoM\Extensions\PythonEnvironments" if not exist "C:\ProgramData\BHoM\Extensions\PythonCode" mkdir "C:\ProgramData\BHoM\Extensions\PythonCode" - - :: remove any old versions within target directory for current toolkits Python code + if exist "C:\ProgramData\BHoM\Extensions\PythonCode\$(SolutionName)" rmdir "C:\ProgramData\BHoM\Extensions\PythonCode\$(SolutionName)" /S /Q - - :: move Python code over to toolkit extensions folder, redirecting output to temp log file to silence it - robocopy "$(ProjectDir)Python" "C:\ProgramData\BHoM\Extensions\PythonCode\$(SolutionName)" /s /purge /xf "*.pyc" /xd "\__pycache__\" > output.log - - :: remove temporary log file - del output.log + mkdir "C:\ProgramData\BHoM\Extensions\PythonCode\$(SolutionName)" - + robocopy "$(ProjectDir)Python" "C:\ProgramData\BHoM\Extensions\PythonCode\$(SolutionName)" /mir /xf "*.pyc" "*.ipynb" /xd "__*__" ".*" > output.log + del output.log + \ No newline at end of file diff --git a/Python_Engine/Query/CodeDirectory.cs b/Python_Engine/Query/CodeDirectory.cs deleted file mode 100644 index 655402d..0000000 --- a/Python_Engine/Query/CodeDirectory.cs +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This file is part of the Buildings and Habitats object Model (BHoM) - * Copyright (c) 2015 - 2023, the respective contributors. All rights reserved. - * - * Each contributor holds copyright over their respective contributions. - * The project versioning (Git) records all such contribution source information. - * - * - * The BHoM is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * The BHoM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this code. If not, see . - */ - -using BH.oM.Base.Attributes; - -using System.ComponentModel; -using System.IO; - -namespace BH.Engine.Python -{ - public static partial class Query - { - [Description("The path where any BHoM generated Python code is placed prior to inclusion in a BHoM Python Environment.")] - [Output("The path where any BHoM generated Python code is placed prior to inclusion in a BHoM Python Environment.")] - public static string CodeDirectory() - { - return Path.Combine(Query.ExtensionsDirectory(), "PythonCode"); - } - } -} - - diff --git a/Python_Engine/Query/Directory.cs b/Python_Engine/Query/Directory.cs new file mode 100644 index 0000000..df18193 --- /dev/null +++ b/Python_Engine/Query/Directory.cs @@ -0,0 +1,69 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2023, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using BH.oM.Base.Attributes; + +using System.ComponentModel; +using System.IO; + +namespace BH.Engine.Python +{ + public static partial class Query + { + [Description("The location where BHoM extensions reside.")] + [Output("The location where BHoM extensions reside.")] + public static string DirectoryExtensions() + { + return @"C:\ProgramData\BHoM\Extensions"; + } + + [Description("The location where any BHoM-related Python kernels reside.")] + [Output("The location where any BHoM-related Python kernels reside.")] + public static string DirectoryKernels() + { + return @"C:\ProgramData\jupyter\kernels"; + } + + [Description("The location where any BHoM-related Python code resides.")] + [Output("The location where any BHoM-related Python code resides.")] + public static string DirectoryCode() + { + return Path.Combine(Query.DirectoryExtensions(), "PythonCode"); + } + + [Description("The location where any BHoM-related Python environment (or virtualenv) resides.")] + [Output("The location where any BHoM-related Python environment (or virtualenv) resides.")] + public static string DirectoryEnvironments() + { + return Path.Combine(Query.DirectoryExtensions(), "PythonEnvironments"); + } + + [Description("The location where the base Python environment exists.")] + [Output("The location where the base Python environment exists.")] + public static string DirectoryBaseEnvironment() + { + return Path.Combine(Query.DirectoryEnvironments(), Query.ToolkitName()); + } + } +} + + diff --git a/Python_Engine/Query/EmbeddableURL.cs b/Python_Engine/Query/EmbeddableURL.cs index ab3036c..4601e29 100644 --- a/Python_Engine/Query/EmbeddableURL.cs +++ b/Python_Engine/Query/EmbeddableURL.cs @@ -22,7 +22,6 @@ using BH.oM.Python.Enums; using BH.oM.Base.Attributes; - using System.Collections.Generic; using System.ComponentModel; @@ -37,43 +36,52 @@ public static string EmbeddableURL(this PythonVersion version) { Dictionary versions = new Dictionary() { - { oM.Python.Enums.PythonVersion.v3_7_0, "https://www.python.org/ftp/python/3.7.0/python-3.7.0-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_7_1, "https://www.python.org/ftp/python/3.7.1/python-3.7.1-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_7_2, "https://www.python.org/ftp/python/3.7.2/python-3.7.2.post1-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_7_3, "https://www.python.org/ftp/python/3.7.3/python-3.7.3-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_7_4, "https://www.python.org/ftp/python/3.7.4/python-3.7.4-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_7_5, "https://www.python.org/ftp/python/3.7.5/python-3.7.5-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_7_6, "https://www.python.org/ftp/python/3.7.6/python-3.7.6-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_7_7, "https://www.python.org/ftp/python/3.7.7/python-3.7.7-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_7_8, "https://www.python.org/ftp/python/3.7.8/python-3.7.8-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_7_9, "https://www.python.org/ftp/python/3.7.9/python-3.7.9-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_8_0, "https://www.python.org/ftp/python/3.8.0/python-3.8.0-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_8_1, "https://www.python.org/ftp/python/3.8.1/python-3.8.1-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_8_2, "https://www.python.org/ftp/python/3.8.2/python-3.8.2-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_8_3, "https://www.python.org/ftp/python/3.8.3/python-3.8.3-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_8_4, "https://www.python.org/ftp/python/3.8.4/python-3.8.4-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_8_5, "https://www.python.org/ftp/python/3.8.5/python-3.8.5-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_8_6, "https://www.python.org/ftp/python/3.8.6/python-3.8.6-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_8_7, "https://www.python.org/ftp/python/3.8.7/python-3.8.7-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_8_8, "https://www.python.org/ftp/python/3.8.8/python-3.8.8-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_8_9, "https://www.python.org/ftp/python/3.8.9/python-3.8.9-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_8_10, "https://www.python.org/ftp/python/3.8.10/python-3.8.10-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_9_0, "https://www.python.org/ftp/python/3.9.0/python-3.9.0-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_9_1, "https://www.python.org/ftp/python/3.9.1/python-3.9.1-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_9_2, "https://www.python.org/ftp/python/3.9.2/python-3.9.2-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_9_4, "https://www.python.org/ftp/python/3.9.4/python-3.9.4-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_9_5, "https://www.python.org/ftp/python/3.9.5/python-3.9.5-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_9_6, "https://www.python.org/ftp/python/3.9.6/python-3.9.6-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_9_7, "https://www.python.org/ftp/python/3.9.7/python-3.9.7-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_9_8, "https://www.python.org/ftp/python/3.9.8/python-3.9.8-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_9_9, "https://www.python.org/ftp/python/3.9.9/python-3.9.9-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_9_10, "https://www.python.org/ftp/python/3.9.10/python-3.9.10-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_10_0, "https://www.python.org/ftp/python/3.10.0/python-3.10.0-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_10_1, "https://www.python.org/ftp/python/3.10.1/python-3.10.1-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_10_2, "https://www.python.org/ftp/python/3.10.2/python-3.10.2-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_10_3, "https://www.python.org/ftp/python/3.10.3/python-3.10.3-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_10_4, "https://www.python.org/ftp/python/3.10.4/python-3.10.4-embed-amd64.zip" }, - { oM.Python.Enums.PythonVersion.v3_10_5, "https://www.python.org/ftp/python/3.10.5/python-3.10.5-embed-amd64.zip" }, + //{ PythonVersion.v3_7_2, "https://www.python.org/ftp/python/3.7.2/python-3.7.2rc1-embed-amd64.zip" }, + { PythonVersion.v3_7_3, "https://www.python.org/ftp/python/3.7.3/python-3.7.3rc1-embed-amd64.zip" }, + { PythonVersion.v3_7_4, "https://www.python.org/ftp/python/3.7.4/python-3.7.4rc2-embed-amd64.zip" }, + { PythonVersion.v3_7_5, "https://www.python.org/ftp/python/3.7.5/python-3.7.5rc1-embed-amd64.zip" }, + { PythonVersion.v3_7_6, "https://www.python.org/ftp/python/3.7.6/python-3.7.6rc1-embed-amd64.zip" }, + { PythonVersion.v3_7_7, "https://www.python.org/ftp/python/3.7.7/python-3.7.7rc1-embed-amd64.zip" }, + { PythonVersion.v3_7_8, "https://www.python.org/ftp/python/3.7.8/python-3.7.8rc1-embed-amd64.zip" }, + { PythonVersion.v3_7_9, "https://www.python.org/ftp/python/3.7.9/python-3.7.9-embed-amd64.zip" }, + { PythonVersion.v3_8_0, "https://www.python.org/ftp/python/3.8.0/python-3.8.0rc1-embed-amd64.zip" }, + { PythonVersion.v3_8_1, "https://www.python.org/ftp/python/3.8.1/python-3.8.1rc1-embed-amd64.zip" }, + { PythonVersion.v3_8_2, "https://www.python.org/ftp/python/3.8.2/python-3.8.2rc2-embed-amd64.zip" }, + { PythonVersion.v3_8_3, "https://www.python.org/ftp/python/3.8.3/python-3.8.3rc1-embed-amd64.zip" }, + { PythonVersion.v3_8_4, "https://www.python.org/ftp/python/3.8.4/python-3.8.4rc1-embed-amd64.zip" }, + { PythonVersion.v3_8_5, "https://www.python.org/ftp/python/3.8.5/python-3.8.5-embed-amd64.zip" }, + { PythonVersion.v3_8_6, "https://www.python.org/ftp/python/3.8.6/python-3.8.6rc1-embed-amd64.zip" }, + { PythonVersion.v3_8_7, "https://www.python.org/ftp/python/3.8.7/python-3.8.7rc1-embed-amd64.zip" }, + { PythonVersion.v3_8_8, "https://www.python.org/ftp/python/3.8.8/python-3.8.8rc1-embed-amd64.zip" }, + { PythonVersion.v3_8_9, "https://www.python.org/ftp/python/3.8.9/python-3.8.9-embed-amd64.zip" }, + { PythonVersion.v3_9_0, "https://www.python.org/ftp/python/3.9.0/python-3.9.0rc2-embed-amd64.zip" }, + { PythonVersion.v3_9_1, "https://www.python.org/ftp/python/3.9.1/python-3.9.1rc1-embed-amd64.zip" }, + { PythonVersion.v3_9_2, "https://www.python.org/ftp/python/3.9.2/python-3.9.2rc1-embed-amd64.zip" }, + { PythonVersion.v3_9_3, "https://www.python.org/ftp/python/3.9.3/python-3.9.3-embed-amd64.zip" }, + { PythonVersion.v3_9_4, "https://www.python.org/ftp/python/3.9.4/python-3.9.4-embed-amd64.zip" }, + { PythonVersion.v3_9_5, "https://www.python.org/ftp/python/3.9.5/python-3.9.5-embed-amd64.zip" }, + { PythonVersion.v3_9_6, "https://www.python.org/ftp/python/3.9.6/python-3.9.6-embed-amd64.zip" }, + { PythonVersion.v3_9_7, "https://www.python.org/ftp/python/3.9.7/python-3.9.7-embed-amd64.zip" }, + { PythonVersion.v3_9_8, "https://www.python.org/ftp/python/3.9.8/python-3.9.8-embed-amd64.zip" }, + { PythonVersion.v3_9_9, "https://www.python.org/ftp/python/3.9.9/python-3.9.9-embed-amd64.zip" }, + { PythonVersion.v3_9_10, "https://www.python.org/ftp/python/3.9.10/python-3.9.10-embed-amd64.zip" }, + { PythonVersion.v3_10_0, "https://www.python.org/ftp/python/3.10.0/python-3.10.0rc2-embed-amd64.zip" }, + { PythonVersion.v3_10_1, "https://www.python.org/ftp/python/3.10.1/python-3.10.1-embed-amd64.zip" }, + { PythonVersion.v3_10_2, "https://www.python.org/ftp/python/3.10.2/python-3.10.2-embed-amd64.zip" }, + { PythonVersion.v3_10_3, "https://www.python.org/ftp/python/3.10.3/python-3.10.3-embed-amd64.zip" }, + { PythonVersion.v3_10_4, "https://www.python.org/ftp/python/3.10.4/python-3.10.4-embed-amd64.zip" }, + { PythonVersion.v3_10_5, "https://www.python.org/ftp/python/3.10.5/python-3.10.5-embed-amd64.zip" }, + { PythonVersion.v3_10_6, "https://www.python.org/ftp/python/3.10.6/python-3.10.6-embed-amd64.zip" }, + { PythonVersion.v3_10_7, "https://www.python.org/ftp/python/3.10.7/python-3.10.7-embed-amd64.zip" }, + { PythonVersion.v3_10_8, "https://www.python.org/ftp/python/3.10.8/python-3.10.8-embed-amd64.zip" }, + { PythonVersion.v3_10_9, "https://www.python.org/ftp/python/3.10.9/python-3.10.9-embed-amd64.zip" }, + { PythonVersion.v3_10_10, "https://www.python.org/ftp/python/3.10.10/python-3.10.10-embed-amd64.zip" }, + { PythonVersion.v3_10_11, "https://www.python.org/ftp/python/3.10.11/python-3.10.11-embed-amd64.zip" }, + { PythonVersion.v3_11_0, "https://www.python.org/ftp/python/3.11.0/python-3.11.0rc2-embed-amd64.zip" }, + { PythonVersion.v3_11_1, "https://www.python.org/ftp/python/3.11.1/python-3.11.1-embed-amd64.zip" }, + { PythonVersion.v3_11_2, "https://www.python.org/ftp/python/3.11.2/python-3.11.2-embed-amd64.zip" }, + { PythonVersion.v3_11_3, "https://www.python.org/ftp/python/3.11.3/python-3.11.3-embed-amd64.zip" }, + { PythonVersion.v3_11_4, "https://www.python.org/ftp/python/3.11.4/python-3.11.4-embed-amd64.zip" }, }; return versions[version]; diff --git a/Python_Engine/Query/EnvironmentExists.cs b/Python_Engine/Query/EnvironmentExists.cs deleted file mode 100644 index d026d02..0000000 --- a/Python_Engine/Query/EnvironmentExists.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* - * This file is part of the Buildings and Habitats object Model (BHoM) - * Copyright (c) 2015 - 2023, the respective contributors. All rights reserved. - * - * Each contributor holds copyright over their respective contributions. - * The project versioning (Git) records all such contribution source information. - * - * - * The BHoM is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * The BHoM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this code. If not, see . - */ - -using BH.oM.Python.Enums; -using BH.oM.Base.Attributes; - -using System.Collections.Generic; -using System.ComponentModel; -using System.IO; - -namespace BH.Engine.Python -{ - public static partial class Query - { - [Description("Check whether the given BHoM Python Environment already exists.")] - [Input("env", "A BHoM Python environment.")] - [Output("exists", "True if the environment exists, False if not.")] - public static bool EnvironmentExists(this oM.Python.PythonEnvironment env) - { - if (File.Exists(env.Executable)) - { - return true; - } - - return false; - } - } -} - diff --git a/Python_Engine/Query/EnvironmentsDirectory.cs b/Python_Engine/Query/EnvironmentsDirectory.cs deleted file mode 100644 index 203e818..0000000 --- a/Python_Engine/Query/EnvironmentsDirectory.cs +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This file is part of the Buildings and Habitats object Model (BHoM) - * Copyright (c) 2015 - 2023, the respective contributors. All rights reserved. - * - * Each contributor holds copyright over their respective contributions. - * The project versioning (Git) records all such contribution source information. - * - * - * The BHoM is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * The BHoM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this code. If not, see . - */ - -using BH.oM.Base.Attributes; - -using System.ComponentModel; -using System.IO; - -namespace BH.Engine.Python -{ - public static partial class Query - { - [Description("The location where any BHoM generated Python environments reside.")] - [Output("The location where any BHoM generated Python environments reside.")] - public static string EnvironmentsDirectory() - { - return Path.Combine(Query.ExtensionsDirectory(), "PythonEnvironments"); - } - } -} - - diff --git a/Python_Engine/Query/ExtensionsDirectory.cs b/Python_Engine/Query/ExtensionsDirectory.cs deleted file mode 100644 index c753cd7..0000000 --- a/Python_Engine/Query/ExtensionsDirectory.cs +++ /dev/null @@ -1,40 +0,0 @@ -/* - * This file is part of the Buildings and Habitats object Model (BHoM) - * Copyright (c) 2015 - 2023, the respective contributors. All rights reserved. - * - * Each contributor holds copyright over their respective contributions. - * The project versioning (Git) records all such contribution source information. - * - * - * The BHoM is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * The BHoM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this code. If not, see . - */ - -using BH.oM.Base.Attributes; - -using System.ComponentModel; - -namespace BH.Engine.Python -{ - public static partial class Query - { - [Description("The location where Python_Toolkit associated code and environments reside.")] - [Output("The location where Python_Toolkit associated code and environments resides")] - public static string ExtensionsDirectory() - { - return @"C:\ProgramData\BHoM\Extensions"; - } - } -} - - diff --git a/Python_Engine/Compute/IsValidEnvironmentName.cs b/Python_Engine/Query/IsValidEnvironmentName.cs similarity index 94% rename from Python_Engine/Compute/IsValidEnvironmentName.cs rename to Python_Engine/Query/IsValidEnvironmentName.cs index a84aa2a..9a7aedd 100644 --- a/Python_Engine/Compute/IsValidEnvironmentName.cs +++ b/Python_Engine/Query/IsValidEnvironmentName.cs @@ -32,11 +32,12 @@ namespace BH.Engine.Python { - public static partial class Compute + public static partial class Query { [Description("Check whether a string is valid as BHoM Python Environment name.")] [Input("name", "The name given to the BHoM Python Environment.")] [Output("valid", "True if valid, False if not.")] + [PreviousVersion("6.3","BH.Engine.Python.Compute.IsValidEnvironmentName(System.String)")] public static bool IsValidEnvironmentName(string name) { List invalidChars = new List() { ' ' }; diff --git a/Python_Engine/Query/ToString.cs b/Python_Engine/Query/ToString.cs deleted file mode 100644 index 8e3c196..0000000 --- a/Python_Engine/Query/ToString.cs +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of the Buildings and Habitats object Model (BHoM) - * Copyright (c) 2015 - 2023, the respective contributors. All rights reserved. - * - * Each contributor holds copyright over their respective contributions. - * The project versioning (Git) records all such contribution source information. - * - * - * The BHoM is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * The BHoM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this code. If not, see . - */ - -using BH.oM.Base.Attributes; - -using System.ComponentModel; -using System.IO; - -namespace BH.Engine.Python -{ - public static partial class Query - { - [Description("The string representation of a BHoM PythonEnvironment object.")] - [Input("env", "A BHoM PythonEnvironment object.")] - [Output("The string representation of the input BHoM PythonEnvironment object")] - public static string ToString(this oM.Python.PythonEnvironment env) - { - return $"{env.GetType().Name}[{env.Name}]"; - } - } -} - - diff --git a/Python_Engine/Query/Version.cs b/Python_Engine/Query/Version.cs new file mode 100644 index 0000000..3267497 --- /dev/null +++ b/Python_Engine/Query/Version.cs @@ -0,0 +1,62 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2023, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using BH.oM.Base.Attributes; +using BH.oM.Python.Enums; +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; + +namespace BH.Engine.Python +{ + public static partial class Query + { + [Description("Gets the Python version for the requested Python executable.")] + [Input("pythonExecutable", "Path to python.exe.")] + [Output("version", "Python version of the requested Python executable.")] + public static PythonVersion Version(string pythonExecutable) + { + Process process = new Process() + { + StartInfo = new ProcessStartInfo() + { + FileName = pythonExecutable, + Arguments = $"--version", + RedirectStandardError = true, + RedirectStandardOutput = true, + UseShellExecute = false, + } + }; + string versionString; + using (Process p = Process.Start(process.StartInfo)) + { + p.WaitForExit(); + if (p.ExitCode != 0) + BH.Engine.Base.Compute.RecordError($"Error getting Python version.\n{p.StandardError.ReadToEnd()}"); + versionString = p.StandardOutput.ReadToEnd().TrimEnd(); + } + + return (PythonVersion) Enum.Parse(typeof(PythonVersion), "v" + versionString.Replace("Python ", "").Replace(".", "_")); + } + } +} diff --git a/Python_Engine/Query/VirtualEnv.cs b/Python_Engine/Query/VirtualEnv.cs deleted file mode 100644 index 36502cd..0000000 --- a/Python_Engine/Query/VirtualEnv.cs +++ /dev/null @@ -1,52 +0,0 @@ -/* - * This file is part of the Buildings and Habitats object Model (BHoM) - * Copyright (c) 2015 - 2023, the respective contributors. All rights reserved. - * - * Each contributor holds copyright over their respective contributions. - * The project versioning (Git) records all such contribution source information. - * - * - * The BHoM is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * The BHoM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this code. If not, see . - */ - -using BH.oM.Base.Attributes; -using BH.oM.Python; -using System.ComponentModel; -using System.IO; -using System.Xml.Linq; - -namespace BH.Engine.Python -{ - public static partial class Query - { - [Description("Get an installed environment without the overhead of attempting to install that environment.")] - [Input("envName","The virtual environment name to request.")] - [Output("The virtual environment, if it exists.")] - public static PythonEnvironment VirtualEnv(string envName) - { - string exePath = Query.VirtualEnvPythonExePath(envName); - if (File.Exists(exePath)) - { - return new PythonEnvironment() { Name = envName, Executable = exePath }; - } - else - { - BH.Engine.Base.Compute.RecordError($"No environment could be found for {envName}. Use the appropriate InstallPythonEnv to install this environment."); - return null; - } - } - } -} - - diff --git a/Python_Engine/Query/VirtualEnvDirectory.cs b/Python_Engine/Query/VirtualEnvDirectory.cs deleted file mode 100644 index dbc954b..0000000 --- a/Python_Engine/Query/VirtualEnvDirectory.cs +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This file is part of the Buildings and Habitats object Model (BHoM) - * Copyright (c) 2015 - 2023, the respective contributors. All rights reserved. - * - * Each contributor holds copyright over their respective contributions. - * The project versioning (Git) records all such contribution source information. - * - * - * The BHoM is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * The BHoM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this code. If not, see . - */ - -using BH.oM.Base.Attributes; - -using System.ComponentModel; -using System.IO; -using System.Xml.Linq; - -namespace BH.Engine.Python -{ - public static partial class Query - { - [Description("The location where any BHoM generated Python environments reside.")] - [Input("envName","The virtual environment name.")] - [Output("The location where any BHoM generated Python environments reside.")] - public static string VirtualEnvDirectory(string envName) - { - string directory = Path.Combine(Query.EnvironmentsDirectory(), envName); - - if (Directory.Exists(directory)) - { - return directory; - } - - BH.Engine.Base.Compute.RecordError($"The virtual environment directory for \"{envName}\" does not exist at \"{directory}\"."); - return null; - } - } -} - - diff --git a/Python_Engine/Query/VirtualEnvPythonExePath.cs b/Python_Engine/Query/VirtualEnvPythonExePath.cs deleted file mode 100644 index 43d6a20..0000000 --- a/Python_Engine/Query/VirtualEnvPythonExePath.cs +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This file is part of the Buildings and Habitats object Model (BHoM) - * Copyright (c) 2015 - 2023, the respective contributors. All rights reserved. - * - * Each contributor holds copyright over their respective contributions. - * The project versioning (Git) records all such contribution source information. - * - * - * The BHoM is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3.0 of the License, or - * (at your option) any later version. - * - * The BHoM is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this code. If not, see . - */ - -using BH.oM.Base.Attributes; - -using System.ComponentModel; -using System.IO; -using System.Xml.Linq; - -namespace BH.Engine.Python -{ - public static partial class Query - { - [Description("The location where Python.exe for a named virtual environment would reside. This method does not check if that environment actually exists or not.")] - [Input("envName","The virtual environment name.")] - [Output("The location where a Python virtual environment would reside.")] - public static string VirtualEnvPythonExePath(string envName) - { - string exePath = Path.Combine(VirtualEnvDirectory(envName), "Scripts", "python.exe"); - - if (File.Exists(exePath)) - { - return exePath; - } - - BH.Engine.Base.Compute.RecordError($"The executable for \"{envName}\" does not exist at \"{exePath}\"."); - return null; - } - } -} - - diff --git a/Python_Engine/Query/VirtualEnvironment.cs b/Python_Engine/Query/VirtualEnvironment.cs new file mode 100644 index 0000000..2a740ea --- /dev/null +++ b/Python_Engine/Query/VirtualEnvironment.cs @@ -0,0 +1,85 @@ +/* + * This file is part of the Buildings and Habitats object Model (BHoM) + * Copyright (c) 2015 - 2023, the respective contributors. All rights reserved. + * + * Each contributor holds copyright over their respective contributions. + * The project versioning (Git) records all such contribution source information. + * + * + * The BHoM is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3.0 of the License, or + * (at your option) any later version. + * + * The BHoM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this code. If not, see . + */ + +using BH.oM.Python.Enums; +using BH.oM.Python; +using BH.oM.Base.Attributes; + +using System.ComponentModel; +using System.IO; +using System.Linq; +using System; +using System.Collections.Generic; + +namespace BH.Engine.Python +{ + public static partial class Query + { + [Description("Check whether a named BHoM Python virtual environment exists.")] + [Input("envName", "The name given to the BHoM Python Environment.")] + [Output("exists", "True if exists, False if not.")] + public static bool VirtualEnvironmentExists(string envName) + { + return Directory.Exists(VirtualEnvironmentDirectory(envName)) && File.Exists(VirtualEnvironmentExecutable(envName)) && Directory.Exists(VirtualEnvironmentKernel(envName)); + } + + [Description("Get the path to the named BHoM Python virtual environment kernel.")] + [Input("envName", "The name given to the BHoM Python Environment.")] + [Output("kernelDirectory", "The path to the kernel directory.")] + public static string VirtualEnvironmentKernel(string envName) + { + return Path.Combine(Query.DirectoryKernels(), envName); + } + + [Description("Get the path to the named BHoM Python virtual environment executable.")] + [Input("envName", "The name given to the BHoM Python Environment.")] + [Output("executable", "The path to the executable.")] + public static string VirtualEnvironmentExecutable(string envName) + { + return Path.Combine(Query.DirectoryEnvironments(), envName, "Scripts", "python.exe"); + } + + [Description("Get the path to the named BHoM Python virtual environment.")] + [Input("envName", "The name given to the BHoM Python Environment.")] + [Output("directory", "The directory where the virtual environment is located.")] + public static string VirtualEnvironmentDirectory(string envName) + { + return Path.Combine(Query.DirectoryEnvironments(), envName); + } + + [Description("Get the named BHoM Python virtual environment.")] + [Input("envName", "The name given to the BHoM Python Environment.")] + [Output("environment", "The BHoM Python Environment.")] + public static PythonEnvironment VirtualEnvironment(string envName) + { + if (!VirtualEnvironmentExists(envName)) + return null; + + return new PythonEnvironment() + { + Name = envName, + Executable = VirtualEnvironmentExecutable(envName), + }; + } + } +} + diff --git a/Python_Engine/Versioning_63.json b/Python_Engine/Versioning_63.json new file mode 100644 index 0000000..cbd5c9a --- /dev/null +++ b/Python_Engine/Versioning_63.json @@ -0,0 +1,22 @@ +{ + "MessageForDeleted": { + "BH.Engine.Python.Compute.InstallBasePythonEnvironment(System.Boolean)": "This method has been replaced. Please use the BasePythonEnvironment method instead.", + "BH.Engine.Python.Compute.InstallReferencedVirtualenv(System.String, System.String, System.String, System.Boolean)": "This method has been removed.", + "BH.Engine.Python.Compute.InstallVirtualenv(System.String, BH.oM.Python.Enums.PythonVersion, System.String, System.Boolean)": "This method has been removed.", + "BH.Engine.Python.Compute.LoggingHeader(System.String)": "This method has been removed.", + "BH.Engine.Python.Compute.RemoveVirtualenv(System.String, System.Boolean)": "This method has been removed.", + "BH.Engine.Python.Query.CodeDirectory()": "This method has been removed.", + "BH.Engine.Python.Query.EnvironmentExists(BH.oM.Python.PythonEnvironment)": "This method has been removed.", + "BH.Engine.Python.Query.ExtensionsDirectory()": "This method has been removed.", + "BH.Engine.Python.Query.EnvironmentsDirectory()": "This method has been removed.", + "BH.Engine.Python.Query.ToString(BH.oM.Python.PythonEnvironment)": "This method has been removed.", + "BH.Engine.Python.Query.VirtualEnv(System.String)": "This method has been removed.", + "BH.Engine.Python.Query.VirtualEnvPythonExePath(System.String)": "This method has been removed.", + "BH.Engine.Python.Query.VirtualEnvDirectory(System.String)": "This method has been removed.", + "BH.Engine.Python.Compute.RunCommandAsync(System.String, System.Boolean, System.String)": "This method has been removed.", + "BH.Engine.Python.Compute.RunCommandBool(System.String, System.Boolean, System.String, System.Double)": "This method has been removed.", + "BH.Engine.Python.Compute.RunPythonScript(BH.oM.Python.PythonEnvironment, System.String, System.Collections.Generic.List)": "This method has been removed.", + "BH.Engine.Python.Compute.Invoke(Python.Runtime.PyObject, System.String, System.Collections.Generic.Dictionary)" : "This method has been removed.", + "BH.Engine.Python.Compute.Invoke(Python.Runtime.PyObject, System.String, System.Collections.Generic.IEnumerable, System.Collections.Generic.Dictionary)" : "This method has been removed.", + } +} \ No newline at end of file diff --git a/Python_oM/Enums/PythonVersion.cs b/Python_oM/Enums/PythonVersion.cs index 3903580..4e9c51d 100644 --- a/Python_oM/Enums/PythonVersion.cs +++ b/Python_oM/Enums/PythonVersion.cs @@ -32,8 +32,8 @@ public enum PythonVersion v3_7_0, [Description("3.7.1")] v3_7_1, - [Description("3.7.2")] - v3_7_2, + //[Description("3.7.2")] + //v3_7_2, [Description("3.7.3")] v3_7_3, [Description("3.7.4")] @@ -76,6 +76,8 @@ public enum PythonVersion v3_9_1, [Description("3.9.2")] v3_9_2, + [Description("3.9.3")] + v3_9_3, [Description("3.9.4")] v3_9_4, [Description("3.9.5")] @@ -102,6 +104,27 @@ public enum PythonVersion v3_10_4, [Description("3.10.5")] v3_10_5, + [Description("3.10.6")] + v3_10_6, + [Description("3.10.7")] + v3_10_7, + [Description("3.10.8")] + v3_10_8, + [Description("3.10.9")] + v3_10_9, + [Description("3.10.10")] + v3_10_10, + [Description("3.10.11")] + v3_10_11, + [Description("3.11.0")] + v3_11_0, + [Description("3.11.1")] + v3_11_1, + [Description("3.11.2")] + v3_11_2, + [Description("3.11.3")] + v3_11_3, + [Description("3.11.4")] + v3_11_4, } } - diff --git a/Python_oM/Properties/AssemblyInfo.cs b/Python_oM/Properties/AssemblyInfo.cs index 2baa2ae..8194005 100644 --- a/Python_oM/Properties/AssemblyInfo.cs +++ b/Python_oM/Properties/AssemblyInfo.cs @@ -55,6 +55,6 @@ // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("6.0.0.0")] -[assembly: AssemblyFileVersion("6.2.0.0")] +[assembly: AssemblyFileVersion("6.3.0.0")]