-
Notifications
You must be signed in to change notification settings - Fork 665
DYN-9720: Upgrade nodes to PythonNet3 and save backup #16680
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
DYN-9720: Upgrade nodes to PythonNet3 and save backup #16680
Conversation
This reverts commit 0389294.
…kage-does-not-load
cpython still present but does not load
…com/ivaylo-matov/Dynamo into DYN-9388-Net3-package-does-not-load
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See the ticket for this pull request: https://jira.autodesk.com/browse/DYN-9720
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR implements automatic backup and migration of CPython3 nodes to PythonNet3 in Dynamo graphs and custom nodes. When a graph containing CPython3 nodes is opened, Dynamo creates a backup file and upgrades all Python nodes to PythonNet3, addressing the deprecation of the CPython3 engine.
- Automatic detection and in-memory upgrade of CPython3 nodes to PythonNet3 when graphs are opened
- Backup creation for both graphs (.dyn) and custom node definitions (.dyf) before migration
- Toast notifications informing users about automatic upgrades and backup file locations
Reviewed Changes
Copilot reviewed 7 out of 9 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| PythonMigrationViewExtension.cs | Core logic for detecting CPython nodes, performing temporary upgrades, managing workspace events, and displaying toast notifications |
| PythonEngineUpgradeService.cs | New service class handling Python engine detection, in-memory node upgrades, and permanent migration commits with backup creation |
| Resources.resx | Added localized strings for backup messages, node/definition labels, and combined upgrade notifications |
| Resources.en-US.resx | English localization for new backup and upgrade notification strings |
| PythonMigrationAssistantViewModel.cs | Added title parameter to backup notification message box |
| DynamoView.xaml.cs | Added event handler cleanup for Python engine change notifications |
| DynamoViewModel.cs | Reset CPython notification flag when clearing home workspace |
Files not reviewed (1)
- src/PythonMigrationViewExtension/Properties/Resources.Designer.cs: Language not supported
Comments suppressed due to low confidence (1)
src/PythonMigrationViewExtension/PythonMigrationViewExtension.cs:1
- Corrected spacing in constructor parameter declaration.
using System;
src/PythonMigrationViewExtension/PythonMigrationViewExtension.cs
Outdated
Show resolved
Hide resolved
src/PythonMigrationViewExtension/PythonMigrationViewExtension.cs
Outdated
Show resolved
Hide resolved
src/PythonMigrationViewExtension/PythonMigrationViewExtension.cs
Outdated
Show resolved
Hide resolved
src/PythonMigrationViewExtension/PythonMigrationViewExtension.cs
Outdated
Show resolved
Hide resolved
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
fixed bug where: - user opens graph A that contains custom node A (with cpython) -> user sees notification and a backup file of the graph is saved - user closes without saving -> the graph or node are not permanently saved - user opens graph B that contains the same custom node A -> no notification or backup for graph B !!!
|
@ivaylo-matov Please take a look at the updated message in the task comments. Also, this task: https://jira.autodesk.com/browse/DYN-9828 which I think can be included in this PR, but of not let me know, and you can submit another one for that and others. |
Hi @zeusongit , I've addressed DYN-9828 in this PR. |
benglin
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work as always, @ivaylo-matov! I debugged through your code halfway and added a few observations. I hope these help in some ways, I'll let the other members of the team take a closer look at it. 👍
src/PythonMigrationViewExtension/PythonMigrationViewExtension.cs
Outdated
Show resolved
Hide resolved
src/PythonMigrationViewExtension/PythonMigrationViewExtension.cs
Outdated
Show resolved
Hide resolved
src/PythonMigrationViewExtension/PythonMigrationViewExtension.cs
Outdated
Show resolved
Hide resolved
…tov/Dynamo into DYN-9720-plus-DYN-9719
|
@benglin , thank you very much for the review and the comments - super useful! 🙏 |
…251107' into DYN-9720-plus-DYN-9719
cpython notifications were showing regardless of preferences setting
benglin
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @ivaylo-matov, thanks for accommodating the requested changes. I went on a short vacation right after leaving those comments, so I could only review them today. I was in a rush at that time and couldn't finish the review, but today I debugged through your latest code. I left some comments as I went through the code—if you could take a look at them, that'd be great.
While reviewing the latest changes, I found some behaviors that might be good candidates for fixing before we merge this. Generally, I think the code around notification setting/hiding could use some simplification. If we can find a way to more reliably track which workspace has shown its notifications, that would be ideal. Alternatively, we could consider always showing toast notifications when a workspace is upgraded to use PythonNet3, which might simplify the code further.
What I found with a new DYN file is shown in the video below. The notifications do show sometimes for the home workspace but are then hidden (maybe that's intentional). The notifications themselves are secondary -- what the video highlights is that the custom node workspace is not marked as "modified" when it's opened from the new workspace. This likely needs fixing before merging. As I mentioned, we could always show the notification toasts to simplify the code (please check with Achintya), but the custom workspace not being marked as modified is a higher priority.
cpython-upgrade-notifications.mp4
Thanks again for the great work, as always! Looking forward to the updated code—please let me know.
P/S: I have also attached the files I used, just in case it helps.
| </data> | ||
| <data name="CombinedUpgradeToastSuffix" xml:space="preserve"> | ||
| <value>have been automatically converted to PythonNet3 to run in Dynamo 4.0. CPython is no longer supported.</value> | ||
| </data> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AndConjunction, CombinedUpgradeToastSuffix, CPythonUpgradeToastMessagePlural and CPythonUpgradeToastMessageSingular can now be removed.
| private void TryShowPythonEngineUpgradeToast(int cpythonNodeCount, int customDefCount, string backupPath = "") | ||
| { | ||
| ShowPythonEngineUpgradeToast(cpythonNodeCount, customDefCount, backupPath); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think we can remove TryShowPythonEngineUpgradeToast and use ShowPythonEngineUpgradeToast since both methods have exactly the same signature and the additional redirection does not make any actual changes?
| Dispatcher.BeginInvoke( | ||
| DispatcherPriority.Background, | ||
| new Action(() => DynamoViewModel.ShowPythonEngineUpgradeCanvasToast(msg, stayOpen: true))); | ||
| CurrentWorkspace.ShowCPythonNotifications = !preferenceSettings.HideCPython3Notifications; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In order to keep this PR focused, you don’t need to make this change now. I’ve logged a follow-up task so we can address it later. If we name this ShowCPython3Notifications, it will read more naturally and avoid a double-negative 🙂
| { | ||
| CurrentWorkspace.ShowCPythonNotifications = false; | ||
| return; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the code above, relying on RecomputeEngineFlags being called before this line to update hasCPython3Engine (which is defined elsewhere) feels fragile -- it’s possible that RecomputeEngineFlags could be skipped due to future changes. Since RecomputeEngineFlags is lightweight, let’s introduce a HasEngine() method and use that here instead:
if (preferenceSettings == null)
{
if (PythonEngineManager.Instance.HasEngine(PythonEngineManager.CPython3EngineName))
{
CurrentWorkspace.ShowCPythonNotifications = false;
return;
}
}| { | ||
| upgradeService.SaveGraphBackup(CurrentWorkspace, PythonServices.PythonEngineManager.CPython3EngineName); | ||
| saveBackup = false; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of relying on a class-level saveBackup field that's only set when direct/first-level Python nodes are detected in RecomputeCPython3NotificationForWorkspace, we can return a boolean from that method and use it locally. This improves readability and avoids issues if saveBackup is inadvertently modified elsewhere:
private void OnCurrentWorkspaceChanged(IWorkspaceModel workspace)
{
bool workspaceModified = false;
if (CurrentWorkspace is HomeWorkspaceModel hws)
{
// Home workspace related logic...
workspaceModified = RecomputeCPython3NotificationForWorkspace();
}
else if (CurrentWorkspace is ICustomNodeWorkspaceModel cws)
{
// Custom node workspace related logic...
workspaceModified = RecomputeCPython3NotificationForWorkspace();
}
if (workspaceModified)
{
upgradeService.SaveGraphBackup(CurrentWorkspace, ...);
}
}As a side note, Cursor suggest MigrateCPythonNodesForWorkspace based on what is done in RecomputeCPython3NotificationForWorkspace method 🙂
| /// <summary> | ||
| /// Custom node workspaces touched during this session | ||
| /// </summary> | ||
| public readonly HashSet<WorkspaceModel> TouchedCustomWorkspaces = new HashSet<WorkspaceModel>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both TouchedCustomWorkspaces and TempMigratedCustomDefs maintain identical information (just in different forms), which makes keeping them in sync challenging and error-prone. For example, it's unclear whether we need to remove TouchedCustomWorkspaces entries in CommitCustomNodeMigrationsOnSave after the loop, when entries are removed from TempMigratedCustomDefs.
Here's what I'd suggest to simplify their usage:
-
Keep only the lightweight
TempMigratedCustomDefshash set, which stores onlyGuidvalues. -
When
CommitCustomNodeMigrationsOnSaveneeds the correspondingWorkspaceModel, it can retrieve the heavyweightWorkspaceModelfromCustomNodeManagerusing theGuid.
This way we only need to maintain one HashSet.
| // Remove tracking if this was the last instance of that custom node | ||
| var anyRemaining = CurrentWorkspace?.Nodes | ||
| .OfType<Dynamo.Graph.Nodes.CustomNodes.Function>() | ||
| .Any(f => (f.Definition?.FunctionId ?? Guid.Empty) == defId) == true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any already returns bool so we can simplify it to:
var anyRemaining = CurrentWorkspace?.Nodes
.OfType<Dynamo.Graph.Nodes.CustomNodes.Function>()
.Any(f => f.Definition?.FunctionId == defId);| } | ||
|
|
||
| return false; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see TryGetFunctionWorkspace calls being used in various places. For consistency and avoid repetition, what do you think of this approach?
// New method other components can use
internal ICustomNodeWorkspaceModel TryGetFunctionWorkspace(DynamoModel dynamoModel, Guid guid)
{
ICustomNodeWorkspaceModel ws;
var cnm = dynamoModel?.CustomNodeManager;
if (cnm != null && cnm.TryGetFunctionWorkspace(guid, dynamoModel.IsTestMode, out ws))
{
return ws;
}
return null;
}Then we can update CustomNodeHasPython to be this way:
private bool CustomNodeHasPython(Guid defId, Func<NodeModel, bool> isPythonNode)
{
var cws = this.TryGetFunctionWorkspace(DynamoModel, defId) as CustomNodeWorkspaceModel;
return cws?.Nodes != null && cws.Nodes.Any(isPythonNode);
}Other callers can also be simplified, too.
| workspace, | ||
| SetEngine); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the new upgrade service method TryGetFunctionWorkspace, we can now do this:
var workspace = upgradeService.TryGetFunctionWorkspace(Models.DynamoModel, defId) as WorkspaceModel;
if (workspace != null)
{
// Existing logic goes here...
}
Purpose
Tis PR is response to DYN-9720 & DYN-9719.
This update makes Dynamo automatically create backups and upgrade Python nodes when a graph or custom node still uses CPython3.
When a graph (.dyn) with CPython3 nodes is opened, Dynamo saves a backup copy of the file in the Backup folder. It then updates all Python nodes in the file to use PythonNet3.
When the graph is saved, Dynamo also makes backups of any custom nodes that are part of it. These backups are copied to the backup folder. The original .dyf files in the package folder are updated in place so they now use PythonNet3.
Check these if you believe they are true
Release Notes
Dynamo now automatically backs up and upgrades any graphs or custom nodes using CPython3 to PythonNet3.
Reviewers
@zeusongit
@DynamoDS/eidos
FYIs
@achintyabhat
@dnenov