Skip to content

Commit

Permalink
Efficiently solve dependency metadata on install (#10544)
Browse files Browse the repository at this point in the history
When resolving dependencies from the workspace dependency extension,
the entire metadata of dependent packages was requested. However, only
information about the version was truly needed, and for packages with a
lot of versions this was causing latency issues.

Now only the package version of dependencies is obtained. As a result,
several code changes were needed, as part of the code path is shared
with the standard installation of packages through search, but with
different data requirements in each case. Several properties and
functions no longer used were marked as 'Obsolete'.

Added some basic unit tests for the new functions in package manager
client.
  • Loading branch information
mmisol authored Apr 6, 2020
1 parent 62ba1c9 commit 6c994f6
Show file tree
Hide file tree
Showing 9 changed files with 248 additions and 114 deletions.
17 changes: 11 additions & 6 deletions src/DynamoCoreWpf/Properties/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions src/DynamoCoreWpf/Properties/Resources.en-US.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1361,11 +1361,7 @@ You can always redownload the package.</value>
<value>The package or one of its dependencies contains Python scripts or binaries. Do you want to continue?</value>
</data>
<data name="MessagePackageNewerDynamo" xml:space="preserve">
<value>The following packages use a newer version of {0} than you are currently using:

{1}

Do you want to continue?</value>
<value>The package or one of its dependencies use a newer version of {0} than you are currently using. Do you want to continue?</value>
</data>
<data name="MessageSelectAtLeastOneNode" xml:space="preserve">
<value>You must select at least one custom node.</value>
Expand Down Expand Up @@ -2233,6 +2229,10 @@ Uninstall the following packages: {0}?</value>
<data name="DynamoViewSettingsMenuShowCodeBlockNodeLineNumber" xml:space="preserve">
<value>Show CodeBlockNode Line Numbers</value>
</data>
<data name="MessageFailedToDownloadPackageVersion" xml:space="preserve">
<value>Failed to download version {0} of package with id: {1}. Please try again and report the package if you continue to have problems.</value>
<comment>Message box content. {0} = 1.2.3, {1} = 57d576e8f615e7725800001d</comment>
</data>
<data name="NodeTooltipDescription" xml:space="preserve">
<value>Description: </value>
</data>
Expand Down
10 changes: 5 additions & 5 deletions src/DynamoCoreWpf/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -952,11 +952,7 @@ You can always redownload the package.</value>
<value>The package or one of its dependencies contains Python scripts or binaries. Do you want to continue?</value>
</data>
<data name="MessagePackageNewerDynamo" xml:space="preserve">
<value>The following packages use a newer version of {0} than you are currently using:

{1}

Do you want to continue?</value>
<value>The package or one of its dependencies use a newer version of {0} than you are currently using. Do you want to continue?</value>
</data>
<data name="MessageSelectAtLeastOneNode" xml:space="preserve">
<value>You must select at least one custom node.</value>
Expand Down Expand Up @@ -2233,6 +2229,10 @@ Uninstall the following packages: {0}?</value>
<data name="DynamoViewSettingsMenuShowCodeBlockNodeLineNumber" xml:space="preserve">
<value>Show CodeBlockNode Line Numbers</value>
</data>
<data name="MessageFailedToDownloadPackageVersion" xml:space="preserve">
<value>Failed to download version {0} of package with id: {1}. Please try again and report the package if you continue to have problems.</value>
<comment>Message box content. {0} = 1.2.3, {1} = 57d576e8f615e7725800001d</comment>
</data>
<data name="NodeTooltipDescription" xml:space="preserve">
<value>Description: </value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System.Windows;
using System.Windows.Input;
using Dynamo.Core;
using Dynamo.Exceptions;
using Dynamo.Graph.Nodes.CustomNodes;
using Dynamo.Graph.Workspaces;
using Dynamo.Models;
Expand Down Expand Up @@ -459,24 +460,20 @@ public void DownloadAndInstallPackage(IPackageInfo packageInfo, string downloadP
return;
}
}

// Try to get the package header for this package
var header = Model.GetPackageHeader(packageInfo);
if (header == null)
{
var result = MessageBox.Show(string.Format(Resources.MessagePackageNotFound, packageInfo.Name),
Resources.PackageDownloadErrorMessageBoxTitle,
MessageBoxButton.OKCancel, MessageBoxImage.Question);
return;
}

// Try to get the package version for this package
var version = Model.GetGregPackageVersion(header, packageInfo.Version);
if (version == null)
PackageVersion version;
try
{
var result = MessageBox.Show(string.Format(Resources.MessagePackageVersionNotFound, packageInfo.Version.ToString(), packageInfo.Name),
Resources.PackageDownloadErrorMessageBoxTitle,
MessageBoxButton.OKCancel, MessageBoxImage.Question);
version = Model.GetPackageVersionHeader(packageInfo);
}
catch
{
MessageBox.Show(
string.Format(Resources.MessagePackageVersionNotFound, packageInfo.Version.ToString(), packageInfo.Name),
Resources.PackageDownloadErrorMessageBoxTitle,
MessageBoxButton.OK,
MessageBoxImage.Error);
return;
}

Expand All @@ -501,39 +498,39 @@ internal void ExecutePackageDownload(string name, PackageVersion version, string
var pmExt = DynamoViewModel.Model.GetPackageManagerExtension();
if (result == MessageBoxResult.OK)
{
// get all of the headers
var headers = version.full_dependency_ids.Select(dep => dep._id).Select((id) =>
// get all of the dependency version headers
var dependencyVersionHeaders = version.full_dependency_ids.Select((dep, i) =>
{
PackageHeader pkgHeader;
var res = Model.DownloadPackageHeader(id, out pkgHeader);

if (!res.Success)
MessageBox.Show(String.Format(Resources.MessageFailedToDownloadPackage, id),
try
{
var depVersion = version.full_dependency_versions[i];
var res = Model.GetPackageVersionHeader(dep._id, depVersion);
return res;
}
catch
{
MessageBox.Show(
String.Format(Resources.MessageFailedToDownloadPackageVersion, dep._id),
Resources.PackageDownloadErrorMessageBoxTitle,
MessageBoxButton.OK, MessageBoxImage.Error);

return pkgHeader;
return null;
}
}).ToList();

// if any header download fails, abort
if (headers.Any(x => x == null))
if (dependencyVersionHeaders.Any(x => x == null))
{
return;
}

var allPackageVersions = PackageManagerSearchElement.ListRequiredPackageVersions(headers, version);

// determine if any of the packages contain binaries or python scripts.
var containsBinaries =
allPackageVersions.Any(
x => x.Item2.contents.Contains(PackageManagerClient.PackageContainsBinariesConstant) || x.Item2.contains_binaries);

var containsPythonScripts =
allPackageVersions.Any(
x => x.Item2.contents.Contains(PackageManagerClient.PackageContainsPythonScriptsConstant));
var containsBinariesOrPythonScripts = dependencyVersionHeaders.Any(x =>
x.contains_binaries ||
x.contents.Contains(PackageManagerClient.PackageContainsBinariesConstant) ||
x.contents.Contains(PackageManagerClient.PackageContainsPythonScriptsConstant));

// if any do, notify user and allow cancellation
if (containsBinaries || containsPythonScripts)
if (containsBinariesOrPythonScripts)
{
var res = MessageBox.Show(Resources.MessagePackageContainPythonScript,
Resources.PackageDownloadMessageBoxTitle,
Expand All @@ -544,21 +541,15 @@ internal void ExecutePackageDownload(string name, PackageVersion version, string

// Determine if there are any dependencies that are made with a newer version
// of Dynamo (this includes the root package)
var dynamoVersion = DynamoViewModel.Model.Version;
var dynamoVersionParsed = VersionUtilities.PartialParse(dynamoVersion, 3);
var futureDeps = allPackageVersions.FilterFuturePackages(dynamoVersionParsed);
var dynamoVersion = VersionUtilities.PartialParse(DynamoViewModel.Model.Version);
var futureDeps = dependencyVersionHeaders.Where(dep => VersionUtilities.PartialParse(dep.engine_version) > dynamoVersion);

// If any of the required packages use a newer version of Dynamo, show a dialog to the user
// allowing them to cancel the package download
if (futureDeps.Any())
{
var versionList = FormatPackageVersionList(futureDeps);

if (MessageBox.Show(String.Format(Resources.MessagePackageNewerDynamo,
DynamoViewModel.BrandingResourceProvider.ProductName,
versionList),
string.Format(Resources.PackageUseNewerDynamoMessageBoxTitle,
DynamoViewModel.BrandingResourceProvider.ProductName),
if (MessageBox.Show(string.Format(Resources.MessagePackageNewerDynamo, DynamoViewModel.BrandingResourceProvider.ProductName),
string.Format(Resources.PackageUseNewerDynamoMessageBoxTitle, DynamoViewModel.BrandingResourceProvider.ProductName),
MessageBoxButton.OKCancel,
MessageBoxImage.Warning) == MessageBoxResult.Cancel)
{
Expand All @@ -574,7 +565,7 @@ internal void ExecutePackageDownload(string name, PackageVersion version, string

// if a package is already installed we need to uninstall it, allowing
// the user to cancel if they do not want to uninstall the package
foreach (var localPkg in headers.Select(x => localPkgs.FirstOrDefault(v => v.Name == x.name)))
foreach (var localPkg in version.full_dependency_ids.Select(dep => localPkgs.FirstOrDefault(v => v.ID == dep._id)))
{
if (localPkg == null) continue;

Expand Down Expand Up @@ -651,16 +642,28 @@ internal void ExecutePackageDownload(string name, PackageVersion version, string
}

// form header version pairs and download and install all packages
allPackageVersions
.Select(x => new PackageDownloadHandle(x.Item1, x.Item2))
.ToList()
.ForEach(x => DownloadAndInstall(x, downloadPath));
dependencyVersionHeaders
.Select((dep, i) => {
// Note that Name will be empty when calling from DownloadAndInstallPackage.
// This is currently not an issue, as the code path belongs to the Workspace Dependency
// extension, which does not display dependencies names.
var dependencyPackageHeader = version.full_dependency_ids[i];
return new PackageDownloadHandle()
{
Id = dependencyPackageHeader._id,
VersionName = dep.version,
Name = dependencyPackageHeader.name
};
})
.ToList()
.ForEach(x => DownloadAndInstall(x, downloadPath));
}
}

/// <summary>
/// Returns a newline delimited string representing the package name and version of the argument
/// </summary>
[Obsolete("No longer used. Remove in 3.0.")]
public static string FormatPackageVersionList(IEnumerable<Tuple<PackageHeader, PackageVersion>> packages)
{
return String.Join("\r\n", packages.Select(x => x.Item1.name + " " + x.Item2.version));
Expand All @@ -682,7 +685,7 @@ internal void DownloadAndInstall(PackageDownloadHandle packageDownloadHandle, st
{
// Attempt to download package
string pathDl;
var res = Model.DownloadPackage(packageDownloadHandle.Header._id, packageDownloadHandle.VersionName, out pathDl);
var res = Model.DownloadPackage(packageDownloadHandle.Id, packageDownloadHandle.VersionName, out pathDl);

// if you fail, update download handle and return
if (!res.Success)
Expand All @@ -701,7 +704,7 @@ internal void DownloadAndInstall(PackageDownloadHandle packageDownloadHandle, st
Package dynPkg;

var pmExtension = DynamoViewModel.Model.GetPackageManagerExtension();
var firstOrDefault = pmExtension.PackageLoader.LocalPackages.FirstOrDefault(pkg => pkg.Name == packageDownloadHandle.Name);
var firstOrDefault = pmExtension.PackageLoader.LocalPackages.FirstOrDefault(pkg => pkg.ID == packageDownloadHandle.Id);
if (firstOrDefault != null)
{
var dynModel = DynamoViewModel.Model;
Expand All @@ -721,18 +724,13 @@ internal void DownloadAndInstall(PackageDownloadHandle packageDownloadHandle, st

if (packageDownloadHandle.Extract(DynamoViewModel.Model, downloadPath, out dynPkg))
{
var p = Package.FromDirectory(dynPkg.RootDirectory, DynamoViewModel.Model.Logger);

if (p != null)
{
pmExtension.PackageLoader.LoadPackages(new List<Package> { p });
packageDownloadHandle.DownloadState = PackageDownloadHandle.State.Installed;
}
else
{
packageDownloadHandle.DownloadState = PackageDownloadHandle.State.Error;
packageDownloadHandle.Error(Resources.MessageInvalidPackage);
}
pmExtension.PackageLoader.LoadPackages(new List<Package> {dynPkg});
packageDownloadHandle.DownloadState = PackageDownloadHandle.State.Installed;
}
else
{
packageDownloadHandle.DownloadState = PackageDownloadHandle.State.Error;
packageDownloadHandle.Error(Resources.MessageInvalidPackage);
}
}
catch (Exception e)
Expand Down
Loading

0 comments on commit 6c994f6

Please sign in to comment.