Skip to content

Commit 11fed04

Browse files
8.0 Deployment (#152)
2 parents 92d6789 + 891b039 commit 11fed04

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+11425
-188
lines changed

.ci/BHoMBot/Nuget/BHoM.Interop.Python.nuspec

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
</dependencies>
2222
</metadata>
2323
<files>
24+
<file src="C:\ProgramData\BHoM\Extensions\PythonCode\Python_Toolkit\src\python_toolkit\bhom\wrapped\**\*.*" exclude="**\__pycache__\*;**\docs\**\*" target="contentFiles\any\any\PythonCode\Python_Toolkit\src\python_toolkit\bhom\wrapped"/>
25+
<file src="C:\ProgramData\BHoM\Extensions\PythonCode\Python_Toolkit\src\**\*.*" exclude="**\__pycache__\*;**\docs\**\*" target="contentFiles\any\any\PythonEnvironment\Lib\site-packages" />
2426
<file src="licence/licence.txt" target="" />
2527
<file src="images/icon.png" target="" />
2628
<file src="docs/readme.md" target="" />

.gitignore

+120-1
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,123 @@ Alligator/Alligator_REMOTE_9848.csproj
9898

9999
# User defined files #
100100
######################
101-
build.ps1
101+
build.ps1
102+
103+
# Testing files
104+
.dev/
105+
prototypes/
106+
107+
# PyInstaller
108+
# Usually these files are written by a python script from a template
109+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
110+
*.manifest
111+
*.spec
112+
113+
# Installer logs
114+
pip-log.txt
115+
pip-delete-this-directory.txt
116+
117+
# Unit test / coverage reports
118+
htmlcov/
119+
.tox/
120+
.nox/
121+
.coverage
122+
.coverage.*
123+
.cache
124+
nosetests.xml
125+
coverage.xml
126+
*.cover
127+
*.py,cover
128+
.hypothesis/
129+
.pytest_cache/
130+
cover/
131+
132+
# Translations
133+
*.mo
134+
*.pot
135+
136+
# Django stuff:
137+
*.log
138+
local_settings.py
139+
db.sqlite3
140+
db.sqlite3-journal
141+
142+
# Flask stuff:
143+
instance/
144+
.webassets-cache
145+
146+
# Scrapy stuff:
147+
.scrapy
148+
149+
# Sphinx documentation
150+
docs/_build/
151+
152+
# PyBuilder
153+
.pybuilder/
154+
target/
155+
156+
# Jupyter Notebook
157+
*.ipynb
158+
.ipynb_checkpoints
159+
160+
# IPython
161+
profile_default/
162+
ipython_config.py
163+
164+
# pyenv
165+
# For a library or package, you might want to ignore these files since the code is
166+
# intended to run in multiple environments; otherwise, check them in:
167+
# .python-version
168+
169+
# pipenv
170+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
171+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
172+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
173+
# install all needed dependencies.
174+
#Pipfile.lock
175+
176+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
177+
__pypackages__/
178+
179+
# Celery stuff
180+
celerybeat-schedule
181+
celerybeat.pid
182+
183+
# SageMath parsed files
184+
*.sage.py
185+
186+
# Environments
187+
.env
188+
.venv
189+
env/
190+
venv/
191+
ENV/
192+
env.bak/
193+
venv.bak/
194+
195+
# Spyder project settings
196+
.spyderproject
197+
.spyproject
198+
199+
# Rope project settings
200+
.ropeproject
201+
202+
# mkdocs documentation
203+
/site
204+
205+
# mypy
206+
.mypy_cache/
207+
.dmypy.json
208+
dmypy.json
209+
210+
# Pyre type checker
211+
.pyre/
212+
213+
# pytype static type analyzer
214+
.pytype/
215+
216+
# Cython debug symbols
217+
cython_debug/
218+
219+
# unignored files
220+
!example.sql

Python_Engine/Compute/BasePythonEnvironment.cs

+11-44
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,14 @@ namespace BH.Engine.Python
3333
{
3434
public static partial class Compute
3535
{
36+
[PreviousVersion("8.0", "BH.Engine.Python.Compute.BasePythonEnvironment(System.Boolean, System.Boolean)")]
3637
[Description("Retrieve or reinstall the base Python Environment for BHoM workflows.")]
38+
[Input("version", "The target version of python to be installed or retrieved.")]
3739
[Input("reload", "Reload the base Python environment rather than recreating it, if it already exists.")]
3840
[Input("run", "Start the installation/retrieval of the BHoM Base Python Environment.")]
3941
[Output("env", "The base Python Environment for all BHoM workflows.")]
4042
public static PythonEnvironment BasePythonEnvironment(
43+
PythonVersion version = PythonVersion.v3_10,
4144
bool reload = true,
4245
bool run = false
4346
)
@@ -52,10 +55,10 @@ public static PythonEnvironment BasePythonEnvironment(
5255
// create PythonEnvironments directory if it doesnt already exist
5356
Directory.CreateDirectory(Query.DirectoryEnvironments());
5457
}
55-
58+
5659
// determine whether the base environment already exists
57-
string targetExecutable = Path.Combine(Query.DirectoryBaseEnvironment(), "python.exe");
58-
bool exists = Directory.Exists(Query.DirectoryBaseEnvironment()) && File.Exists(targetExecutable);
60+
string targetExecutable = Path.Combine(Query.DirectoryBaseEnvironment(version), "python.exe");
61+
bool exists = File.Exists(targetExecutable);
5962

6063
if (exists && reload)
6164
return new PythonEnvironment() { Name = Query.ToolkitName(), Executable = targetExecutable };
@@ -64,50 +67,14 @@ public static PythonEnvironment BasePythonEnvironment(
6467
// remove all existing environments and kernels
6568
RemoveEverything();
6669

67-
// download the target Python version and convert into a "full" python installation bypassing admin rights
68-
string executable = PythonVersion.v3_10_5.DownloadPython(Query.ToolkitName());
69-
string pipInstaller = DownloadGetPip(Path.GetDirectoryName(executable));
70-
string baseEnvironmentDirectory = Path.GetDirectoryName(executable);
71-
72-
// install pip into the python installation
73-
Process process = new Process()
74-
{
75-
StartInfo = new ProcessStartInfo()
76-
{
77-
FileName = Modify.AddQuotesIfRequired(executable),
78-
Arguments = Modify.AddQuotesIfRequired(pipInstaller) + " --no-warn-script-location",
79-
RedirectStandardError=true,
80-
UseShellExecute=false,
81-
}
82-
};
83-
using (Process p = Process.Start(process.StartInfo))
84-
{
85-
string standardError = p.StandardError.ReadToEnd();
86-
p.WaitForExit();
87-
if (p.ExitCode != 0)
88-
BH.Engine.Base.Compute.RecordError($"Error installing pip.\n{standardError}");
89-
File.Delete(pipInstaller);
90-
}
91-
92-
// delete files with the suffix ._pth from installedDirectory
93-
List<string> pthFiles = Directory.GetFiles(baseEnvironmentDirectory, "*.*", SearchOption.TopDirectoryOnly).Where(s => s.EndsWith("._pth")).ToList();
94-
foreach (string pthFile in pthFiles)
95-
{
96-
File.Delete(pthFile);
97-
}
98-
99-
// move files with the suffix .dll and .pyd from installedDirectory into a DLLs directory
100-
string libDirectory = Directory.CreateDirectory(Path.Combine(baseEnvironmentDirectory, "DLLs")).FullName;
101-
List<string> 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();
102-
foreach (string libFile in libFiles)
103-
{
104-
File.Move(libFile, Path.Combine(libDirectory, Path.GetFileName(libFile)));
105-
}
70+
// download and run the installer for the target Python version
71+
string exe = version.DownloadPythonVersion();
10672

10773
// install essential packages into base environment
108-
InstallPackages(executable, new List<string>() { "virtualenv", "jupyterlab", "black", "pylint" });
74+
InstallPackages(exe, new List<string>() { "virtualenv", "jupyterlab", "black", "pylint" });
75+
InstallPackageLocal(exe, Path.Combine(Query.DirectoryCode(), Query.ToolkitName()));
10976

110-
return new PythonEnvironment() { Name = Query.ToolkitName(), Executable = executable };
77+
return new PythonEnvironment() { Name = Query.ToolkitName(), Executable = exe };
11178
}
11279
}
11380
}

Python_Engine/Compute/Download.cs

+46-25
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@
2424
using BH.oM.Python.Enums;
2525
using System;
2626
using System.ComponentModel;
27+
using System.Diagnostics;
2728
using System.IO;
29+
using System.Reflection;
2830
using System.Xml.Linq;
2931

3032
namespace BH.Engine.Python
@@ -85,34 +87,53 @@ public static string DownloadFile(
8587
return filePath;
8688
}
8789

88-
// TODO - THIS METHOD HAS CHANGED BUT IS STILL USED, SO NEEDS DEPRECATING
89-
// changed from what to what ?
90-
[Description("Download the target version of Python.")]
91-
[Input("version", "A Python version.")]
92-
[Input("name", "Name of target exe file.")]
93-
[Output("executablePath", "The path of the executable for the downloaded Python.")]
94-
public static string DownloadPython(this PythonVersion version, string name = null)
90+
/******************************************************/
91+
92+
[PreviousVersion("8.0", "BH.Engine.Python.Compute.DownloadPython(BH.oM.Python.Enums.PythonVersion, System.String)")]
93+
[Description("Download and install a specified version of python, and return the executable for it.")]
94+
[Input("version", "The version of python to download.")]
95+
[Output("pythonExecutable", "The executable (python.exe) for the python version that was installed")]
96+
public static string DownloadPythonVersion(this PythonVersion version)
9597
{
9698
string url = version.EmbeddableURL();
97-
if (string.IsNullOrEmpty(name))
98-
name = Path.GetFileNameWithoutExtension(url);
99-
string targetExecutable = Path.Combine(Query.DirectoryEnvironments(), name, "python.exe");
100-
101-
if (File.Exists(targetExecutable))
102-
return targetExecutable;
103-
104-
string zipfile = DownloadFile(url, Query.DirectoryEnvironments());
105-
UnzipFile(zipfile, Query.DirectoryEnvironments(), name, true);
106-
107-
return targetExecutable;
108-
}
10999

110-
[Description("Download the pip installer")]
111-
[Input("targetDirectory", "The directory into which get-pip.py will be downloaded.")]
112-
[Output("getpipPath", "The path of the file used to install pip into an embedded Python environment.")]
113-
public static string DownloadGetPip(string targetDirectory)
114-
{
115-
return DownloadFile("https://bootstrap.pypa.io/get-pip.py", targetDirectory);
100+
string basePath = Path.Combine(Query.DirectoryBaseEnvironment(version));
101+
102+
if (File.Exists(Path.Combine(basePath, "python.exe")))
103+
return Path.Combine(basePath, "python.exe");
104+
105+
if (!Directory.Exists(basePath))
106+
Directory.CreateDirectory(basePath);
107+
else
108+
{
109+
Directory.Delete(basePath, true); //if there are any files here already for some reason, remove them.
110+
Directory.CreateDirectory(basePath);
111+
}
112+
113+
string installerFile = DownloadFile(url, basePath, "installer.exe");
114+
115+
using (Process install = new Process()
116+
{
117+
StartInfo = new ProcessStartInfo()
118+
{
119+
FileName = installerFile,
120+
Arguments = $"/passive InstallAllUsers=0 InstallLauncherAllUsers=0 Include_launcher=0 Shortcuts=0 AssociateFiles=0 Include_tools=0 Include_test=0 TargetDir={Modify.AddQuotesIfRequired(basePath)}",
121+
RedirectStandardError = true,
122+
UseShellExecute = false,
123+
}
124+
})
125+
{
126+
install.Start();
127+
string stderr = install.StandardError.ReadToEnd();
128+
install.WaitForExit();
129+
if (install.ExitCode != 0)
130+
{
131+
BH.Engine.Base.Compute.RecordError($"Error installing python: {stderr}");
132+
return null;
133+
}
134+
}
135+
136+
return Path.Combine(basePath, "python.exe");
116137
}
117138
}
118139
}

Python_Engine/Compute/Remove.cs

+50-5
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@
2121
*/
2222

2323
using BH.oM.Base.Attributes;
24+
using BH.oM.Python.Enums;
25+
using System;
26+
using System.Collections.Generic;
2427
using System.ComponentModel;
28+
using System.Diagnostics;
2529
using System.IO;
2630

2731
namespace BH.Engine.Python
@@ -77,19 +81,60 @@ public static void RemoveAllVirtualEnvironments()
7781
}
7882
}
7983

80-
[Description("Completely remove the base BHoM Python environment.")]
81-
public static void RemoveBaseEnvironment()
84+
[PreviousVersion("8.0", "BH.Engine.Python.Compute.RemoveBaseEnvironment()")]
85+
[Description("Remove the base install for the python version specified.")]
86+
[Input("version", "The base python version to remove.")]
87+
public static void RemoveBaseVersion(PythonVersion version = PythonVersion.v3_10)
8288
{
83-
string basePath = Path.Combine(Query.DirectoryEnvironments(), Query.ToolkitName());
84-
if (Directory.Exists(basePath))
89+
string basePath = Path.Combine(Query.DirectoryEnvironments(), Query.ToolkitName(), version.ToString());
90+
string baseInstaller = Path.Combine(basePath, "installer.exe");
91+
92+
// If the installer does not exist and the folder does - assume a bad/invalid install and just delete the entire folder.
93+
if (!File.Exists(baseInstaller) && Directory.Exists(basePath))
94+
{
8595
Directory.Delete(basePath, true);
96+
return;
97+
}
98+
else if (!Directory.Exists(basePath))
99+
// If the base path doesn't exist there is nothing to remove.
100+
return;
101+
102+
using (Process uninstall = new Process()
103+
{
104+
StartInfo = new ProcessStartInfo()
105+
{
106+
FileName = baseInstaller,
107+
Arguments = "/uninstall /passive",
108+
RedirectStandardError = true,
109+
UseShellExecute = false,
110+
}
111+
})
112+
{
113+
uninstall.Start();
114+
string stderr = uninstall.StandardError.ReadToEnd();
115+
uninstall.WaitForExit();
116+
if (uninstall.ExitCode != 0)
117+
{
118+
BH.Engine.Base.Compute.RecordError($"Error uninstalling python: {stderr}");
119+
return;
120+
}
121+
}
122+
123+
// Finally remove base folder as the installer may have missed something.
124+
Directory.Delete(basePath, true);
86125
}
87126

88127
[Description("Completely remove all BHoM-related Python environments and kernels.")]
89128
public static void RemoveEverything()
90129
{
91130
RemoveAllVirtualEnvironments();
92-
RemoveBaseEnvironment();
131+
132+
//remove all python versions installed in base directory.
133+
foreach (PythonVersion e in Enum.GetValues(typeof(PythonVersion)))
134+
{
135+
if (e != PythonVersion.Undefined)
136+
RemoveBaseVersion(e);
137+
}
93138
}
94139
}
95140
}

Python_Engine/Compute/UnzipFile.cs

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ namespace BH.Engine.Python
2929
{
3030
public static partial class Compute
3131
{
32+
//This method is no longer used by python toolkit, and perhaps should be removed or moved to the file toolkit instead.
3233
[Description("Extract the contents of an archive.")]
3334
[Input("archivePath", "The archive to extract.")]
3435
[Input("targetDirectory", "The destination directory to extract into.")]

0 commit comments

Comments
 (0)