Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 55 additions & 8 deletions DSPythonNet3/DSPythonNet3Evaluator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
internal class DynamoCPythonHandleComparer : IEqualityComparer<DynamoCPythonHandle>
{

public bool Equals(DynamoCPythonHandle x, DynamoCPythonHandle y)

Check warning on line 23 in DSPythonNet3/DSPythonNet3Evaluator.cs

View workflow job for this annotation

GitHub Actions / Build and Test

Nullability of reference types in type of parameter 'y' of 'bool DynamoCPythonHandleComparer.Equals(DynamoCPythonHandle x, DynamoCPythonHandle y)' doesn't match implicitly implemented member 'bool IEqualityComparer<DynamoCPythonHandle>.Equals(DynamoCPythonHandle? x, DynamoCPythonHandle? y)' (possibly because of nullability attributes).

Check warning on line 23 in DSPythonNet3/DSPythonNet3Evaluator.cs

View workflow job for this annotation

GitHub Actions / Build and Test

Nullability of reference types in type of parameter 'x' of 'bool DynamoCPythonHandleComparer.Equals(DynamoCPythonHandle x, DynamoCPythonHandle y)' doesn't match implicitly implemented member 'bool IEqualityComparer<DynamoCPythonHandle>.Equals(DynamoCPythonHandle? x, DynamoCPythonHandle? y)' (possibly because of nullability attributes).
{
return x.PythonObjectID.Equals(y.PythonObjectID);
}
Expand Down Expand Up @@ -74,7 +74,7 @@
try
{
var pyobj = DSPythonNet3Evaluator.globalScope.Get(PythonObjectID.ToString());
return pyobj.ToString();

Check warning on line 77 in DSPythonNet3/DSPythonNet3Evaluator.cs

View workflow job for this annotation

GitHub Actions / Build and Test

Possible null reference return.
}
catch (Exception e)
{
Expand Down Expand Up @@ -143,8 +143,8 @@
{ // Session is null when running unit tests.
if (ExecutionEvents.ActiveSession != null)
{
dynamoLogger = ExecutionEvents.ActiveSession.GetParameterValue(ParameterKeys.Logger) as DynamoLogger;

Check warning on line 146 in DSPythonNet3/DSPythonNet3Evaluator.cs

View workflow job for this annotation

GitHub Actions / Build and Test

Possible null reference assignment.
return dynamoLogger;

Check warning on line 147 in DSPythonNet3/DSPythonNet3Evaluator.cs

View workflow job for this annotation

GitHub Actions / Build and Test

Possible null reference return.
}
return dynamoLogger;
}
Expand Down Expand Up @@ -351,6 +351,8 @@
private static bool isPythonInstalled = false;
/// <summary>
/// Makes sure Python is installed on the system and its location added to the path.
/// Extracts non-pip wheels directly from the embedded resource stream into site-packages,
/// adds a zip-slip guard (normalize & validate paths), and uses pip only for pywin32.

Check warning on line 355 in DSPythonNet3/DSPythonNet3Evaluator.cs

View workflow job for this annotation

GitHub Actions / Build and Test

XML comment has badly formed XML -- 'Whitespace is not allowed at this location.'
/// NOTE: Calling SetupPython multiple times will add the install location to the path many times,
/// potentially causing the environment variable to overflow.
/// </summary>
Expand All @@ -370,21 +372,66 @@

Assembly wheelsAssembly = context.LoadFromAssemblyPath(Path.Join(Path.GetDirectoryName(assembly.Location), "DSPythonNet3Wheels.dll"));

string sitePkgs = Path.Combine(Python.Included.Installer.EmbeddedPythonHome, "Lib", "site-packages");
var normalizedBase = Path.GetFullPath(sitePkgs) + Path.DirectorySeparatorChar;

Directory.CreateDirectory(sitePkgs);

List<string> pipWheelInstall = new List<string>();
await Task.WhenAll(wheelsAssembly.GetManifestResourceNames().Where(x =>

// Extract non-pip wheels directly from the resource stream
foreach (var resName in wheelsAssembly.GetManifestResourceNames())
{
bool isWheel = x.Contains(".whl");
if (isWheel && x.Contains("pywin32-"))
bool isWheel = resName.EndsWith(".whl");
if (!isWheel) continue;

if (resName.Contains("pywin32-"))
{
pipWheelInstall.Add(x);
return false;
pipWheelInstall.Add(resName);
continue;
}

return isWheel;
}).Select(wheel => Python.Included.Installer.InstallWheel(wheelsAssembly, wheel))).ConfigureAwait(false);
using (var stream = wheelsAssembly.GetManifestResourceStream(resName))
{
if (stream == null || stream.Length == 0)
{
continue;
}

using (var zip = new System.IO.Compression.ZipArchive(stream, System.IO.Compression.ZipArchiveMode.Read, false))
{
foreach (var entry in zip.Entries)
{
if (string.IsNullOrEmpty(entry.Name)) continue;

var relPath = entry.FullName.Replace('/', Path.DirectorySeparatorChar);
var tentative = Path.Combine(sitePkgs, relPath);
var destPath = Path.GetFullPath(tentative);

if (!destPath.StartsWith(normalizedBase, StringComparison.OrdinalIgnoreCase))
{
dynamoLogger?.LogWarning($"[PyInit] Skipped suspicious wheel entry: {entry.FullName}", WarningLevel.Mild);
continue;
}

var destDir = Path.GetDirectoryName(destPath);
if (!string.IsNullOrEmpty(destDir))
{
Directory.CreateDirectory(destDir);
}

using (var inStream = entry.Open())
using (var outStream = new FileStream(destPath, FileMode.Create, FileAccess.Write, FileShare.None))
{
await inStream.CopyToAsync(outStream).ConfigureAwait(false);
}
}
}
}
}

foreach (var pipWheelResource in pipWheelInstall)
{
{
var pipWheelName = pipWheelResource.Remove(0, "DSPythonNet3Wheels.Resources.".Count());
string wheelPath = Path.Combine(Python.Included.Installer.EmbeddedPythonHome, "Lib", pipWheelName);
using (Stream? stream = wheelsAssembly.GetManifestResourceStream(pipWheelResource))
Expand Down
Loading