Browse Source

Recreate venvs on migration

pull/14/head
Ionite 1 year ago
parent
commit
14f5626f32
No known key found for this signature in database
  1. 11
      StabilityMatrix/Helper/PrerequisiteHelper.cs
  2. 9
      StabilityMatrix/Models/FileInterfaces/DirectoryPath.cs
  3. 3
      StabilityMatrix/Models/FileInterfaces/FilePath.cs
  4. 12
      StabilityMatrix/Models/FileInterfaces/FileSystemPath.cs
  5. 34
      StabilityMatrix/Python/PyVenvRunner.cs
  6. 36
      StabilityMatrix/ViewModels/DataDirectoryMigrationViewModel.cs

11
StabilityMatrix/Helper/PrerequisiteHelper.cs

@ -158,9 +158,9 @@ public class PrerequisiteHelper : IPrerequisiteHelper
logger.LogDebug("Python already installed at {PythonDllPath}", PythonDllPath);
return;
}
logger.LogInformation("Python not found at {PythonDllPath}, downloading...", PythonDllPath);
Directory.CreateDirectory(AssetsDir);
if (!File.Exists(PythonDownloadPath))
@ -171,6 +171,13 @@ public class PrerequisiteHelper : IPrerequisiteHelper
progress?.Report(new ProgressReport(-1, "Installing Python...", isIndeterminate: true));
// We also need 7z if it's not already unpacked
if (!File.Exists(SevenZipPath))
{
await ExtractEmbeddedResource("StabilityMatrix.Assets.7za.exe", AssetsDir);
await ExtractEmbeddedResource("StabilityMatrix.Assets.7za - LICENSE.txt", AssetsDir);
}
// Delete existing python dir
if (Directory.Exists(PythonDir))
{

9
StabilityMatrix/Models/FileInterfaces/DirectoryPath.cs

@ -72,6 +72,15 @@ public class DirectoryPath : FileSystemPath, IPathObject
/// <summary> Deletes the directory asynchronously. </summary>
public Task DeleteAsync(bool recursive) => Task.Run(() => Delete(recursive));
// DirectoryPath + DirectoryPath = DirectoryPath
public static DirectoryPath operator +(DirectoryPath path, DirectoryPath other) => new(Path.Combine(path, other.FullPath));
// DirectoryPath + FilePath = FilePath
public static FilePath operator +(DirectoryPath path, FilePath other) => new(Path.Combine(path, other.FullPath));
// DirectoryPath + string = string
public static string operator +(DirectoryPath path, string other) => Path.Combine(path, other);
// Implicit conversions to and from string
public static implicit operator string(DirectoryPath path) => path.FullPath;
public static implicit operator DirectoryPath(string path) => new(path);

3
StabilityMatrix/Models/FileInterfaces/FilePath.cs

@ -1,5 +1,4 @@
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace StabilityMatrix.Models.FileInterfaces;
@ -55,7 +54,7 @@ public class FilePath : FileSystemPath, IPathObject
/// <summary> Deletes the file </summary>
public void Delete() => File.Delete(FullPath);
// Implicit conversions to and from string
public static implicit operator string(FilePath path) => path.FullPath;
public static implicit operator FilePath(string path) => new(path);

12
StabilityMatrix/Models/FileInterfaces/FileSystemPath.cs

@ -18,16 +18,4 @@ public class FileSystemPath
protected FileSystemPath(params string[] paths) : this(Path.Combine(paths))
{
}
// Add operators to join other paths or strings
public static FileSystemPath operator +(FileSystemPath path, FileSystemPath other) => new(Path.Combine(path.FullPath, other.FullPath));
public static FileSystemPath operator +(FileSystemPath path, string other) => new(Path.Combine(path.FullPath, other));
// Adding directory or file results in their types
public static DirectoryPath operator +(FileSystemPath path, DirectoryPath other) => new(Path.Combine(path.FullPath, other.FullPath));
public static FilePath operator +(FileSystemPath path, FilePath other) => new(Path.Combine(path.FullPath, other.FullPath));
// Implicit conversions to and from string
public static implicit operator string(FileSystemPath path) => path.FullPath;
public static implicit operator FileSystemPath(string path) => new(path);
}

34
StabilityMatrix/Python/PyVenvRunner.cs

@ -7,6 +7,7 @@ using System.Threading.Tasks;
using NLog;
using Salaros.Configuration;
using StabilityMatrix.Helper;
using StabilityMatrix.Models.FileInterfaces;
namespace StabilityMatrix.Python;
@ -25,17 +26,17 @@ public class PyVenvRunner : IDisposable
/// <summary>
/// The path to the venv root directory.
/// </summary>
public string RootPath { get; private set; }
public DirectoryPath RootPath { get; }
/// <summary>
/// The path to the python executable.
/// </summary>
public string PythonPath => RootPath + @"\Scripts\python.exe";
public FilePath PythonPath => RootPath + @"\Scripts\python.exe";
/// <summary>
/// The path to the pip executable.
/// </summary>
public string PipPath => RootPath + @"\Scripts\pip.exe";
public FilePath PipPath => RootPath + @"\Scripts\pip.exe";
/// <summary>
/// List of substrings to suppress from the output.
@ -44,13 +45,13 @@ public class PyVenvRunner : IDisposable
/// </summary>
public List<string> SuppressOutput { get; } = new() { "fatal: not a git repository" };
public PyVenvRunner(string path)
public PyVenvRunner(DirectoryPath path)
{
RootPath = path;
}
/// <returns>True if the venv has a Scripts\python.exe file</returns>
public bool Exists() => File.Exists(PythonPath);
public bool Exists() => PythonPath.Exists;
/// <summary>
/// Creates a venv at the configured path.
@ -63,10 +64,11 @@ public class PyVenvRunner : IDisposable
}
// Create RootPath if it doesn't exist
Directory.CreateDirectory(RootPath);
RootPath.Create();
// Create venv
var venvProc = ProcessRunner.StartProcess(PyRunner.PythonExePath, $"-m virtualenv --always-copy \"{RootPath}\"");
var args = new string[] { "-m", "virtualenv", "--always-copy", RootPath };
var venvProc = ProcessRunner.StartProcess(PyRunner.PythonExePath, args);
await venvProc.WaitForExitAsync();
// Check return code
@ -93,14 +95,6 @@ public class PyVenvRunner : IDisposable
return "torch torchvision torchaudio";
}
/// <summary>
/// Install torch with pip, automatically chooses between Cuda and CPU.
/// </summary>
public async Task InstallTorch(Action<string?>? outputDataReceived = null)
{
await PipInstall(GetTorchInstallCommand(), outputDataReceived: outputDataReceived);
}
/// <summary>
/// Set current python path to pyvenv.cfg
/// This should be called before using the venv, in case user moves the venv directory.
@ -134,6 +128,14 @@ public class PyVenvRunner : IDisposable
File.WriteAllText(cfgPath, cfgString);
}
/// <summary>
/// Install torch with pip, automatically chooses between Cuda and CPU.
/// </summary>
public async Task InstallTorch(Action<string?>? outputDataReceived = null)
{
await PipInstall(GetTorchInstallCommand(), outputDataReceived: outputDataReceived);
}
/// <summary>
/// Run a pip install command. Waits for the process to exit.
/// workingDirectory defaults to RootPath.

36
StabilityMatrix/ViewModels/DataDirectoryMigrationViewModel.cs

@ -1,9 +1,11 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using StabilityMatrix.Helper;
using StabilityMatrix.Python;
using Wpf.Ui.Controls.ContentDialogControl;
namespace StabilityMatrix.ViewModels;
@ -11,6 +13,7 @@ namespace StabilityMatrix.ViewModels;
public partial class DataDirectoryMigrationViewModel : ObservableObject
{
private readonly ISettingsManager settingsManager;
private readonly IPrerequisiteHelper prerequisiteHelper;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(AutoMigrateText))]
@ -31,12 +34,19 @@ public partial class DataDirectoryMigrationViewModel : ObservableObject
public string NeedsMoveMigrateText => NeedsMoveMigrateCount == 0 ? string.Empty :
$"{NeedsMoveMigrateCount} Packages are not relative to the Data Directory and will be moved, this may take a few minutes";
public string MigrateProgressText => $"Migrating {MigrateProgressCount} of {AutoMigrateCount + NeedsMoveMigrateCount} Packages";
public DataDirectoryMigrationViewModel(ISettingsManager settingsManager)
[ObservableProperty]
private string migrateProgressText = "";
partial void OnMigrateProgressCountChanged(int value)
{
MigrateProgressText = value > 0 ? $"Migrating {value} of {AutoMigrateCount + NeedsMoveMigrateCount} Packages" : string.Empty;
}
public DataDirectoryMigrationViewModel(ISettingsManager settingsManager, IPrerequisiteHelper prerequisiteHelper)
{
this.settingsManager = settingsManager;
this.prerequisiteHelper = prerequisiteHelper;
}
public void OnLoaded()
@ -56,13 +66,29 @@ public partial class DataDirectoryMigrationViewModel : ObservableObject
private async Task MigrateAsync()
{
await using var delay = new MinimumDelay(200, 300);
// Since we are going to recreate venvs, need python to be installed
if (!prerequisiteHelper.IsPythonInstalled)
{
MigrateProgressText = "Preparing Environment";
await prerequisiteHelper.InstallPythonIfNecessary();
}
var libraryPath = settingsManager.LibraryDir;
var oldPackages = settingsManager.GetOldInstalledPackages();
foreach (var package in oldPackages)
{
MigrateProgressCount++;
await package.MigratePath();
// Save after each step in case interrupted
settingsManager.SaveSettings();
// Also recreate the venv
var venvPath = Path.Combine(libraryPath, package.FullPath!);
var venv = new PyVenvRunner(venvPath);
await venv.Setup(existsOk: true);
}
// Need to save settings to commit our changes to installed packages
settingsManager.SaveSettings();
}
}

Loading…
Cancel
Save