mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-04-29 10:17:27 +02:00
[core][graphIO] Improve node type version handling
* Deserialization: Replace the logic that defaulted the node type version to "0.0" when unspecified, and assume that unspecified version on a node is targetting current node type version. * Serialization: Only serialize node type versions for which a version info is available. * Test suites: * Add helper context manager to manually override the version of a given node type. * Add new unit tests to cover version conflicts handling is various scenarios.
This commit is contained in:
parent
d9e59e330a
commit
87fbcee06d
7 changed files with 98 additions and 9 deletions
|
@ -326,12 +326,13 @@ class Graph(BaseObject):
|
|||
return graphContent
|
||||
|
||||
def _deserializeNode(self, nodeData: dict, nodeName: str, fromGraph: "Graph"):
|
||||
# Retrieve version from
|
||||
# Retrieve version info from:
|
||||
# 1. nodeData: node saved from a CompatibilityNode
|
||||
# 2. nodesVersion in file header: node saved from a Node
|
||||
# 3. fallback behavior: default to "0.0"
|
||||
# If unvailable, the "version" field will not be set in `nodeData`.
|
||||
if "version" not in nodeData:
|
||||
nodeData["version"] = fromGraph._getNodeTypeVersionFromHeader(nodeData["nodeType"], "0.0")
|
||||
if version := fromGraph._getNodeTypeVersionFromHeader(nodeData["nodeType"]):
|
||||
nodeData["version"] = version
|
||||
inTemplate = fromGraph.header.get(GraphIO.Keys.Template, False)
|
||||
node = nodeFactory(nodeData, nodeName, inTemplate=inTemplate)
|
||||
self._addNode(node, nodeName)
|
||||
|
|
|
@ -100,7 +100,9 @@ class GraphSerializer:
|
|||
"""Get registered versions of each node types in `nodes`, excluding CompatibilityNode instances."""
|
||||
nodeTypes = set([node.nodeDesc.__class__ for node in self.nodes if isinstance(node, Node)])
|
||||
nodeTypesVersions = {
|
||||
nodeType.__name__: meshroom.core.nodeVersion(nodeType, "0.0") for nodeType in nodeTypes
|
||||
nodeType.__name__: version
|
||||
for nodeType in nodeTypes
|
||||
if (version := meshroom.core.nodeVersion(nodeType)) is not None
|
||||
}
|
||||
# Sort them by name (to avoid random order changing from one save to another).
|
||||
return dict(sorted(nodeTypesVersions.items()))
|
||||
|
|
|
@ -1608,7 +1608,8 @@ class CompatibilityNode(BaseNode):
|
|||
# Make a deepcopy of nodeDict to handle CompatibilityNode duplication
|
||||
# and be able to change modified inputs (see CompatibilityNode.toDict)
|
||||
self.nodeDict = copy.deepcopy(nodeDict)
|
||||
self.version = Version(self.nodeDict.get("version", None))
|
||||
version = self.nodeDict.get("version")
|
||||
self.version = Version(version) if version else None
|
||||
|
||||
self._inputs = self.nodeDict.get("inputs", {})
|
||||
self._internalInputs = self.nodeDict.get("internalInputs", {})
|
||||
|
|
|
@ -95,7 +95,10 @@ class _NodeCreator:
|
|||
nodeCreatedFromCurrentVersion = self.version is None
|
||||
if nodeCreatedFromCurrentVersion:
|
||||
return True
|
||||
nodeTypeCurrentVersion = meshroom.core.nodeVersion(self.nodeDesc, "0.0")
|
||||
nodeTypeCurrentVersion = meshroom.core.nodeVersion(self.nodeDesc)
|
||||
# If the node type has not current version information, assume compatibility.
|
||||
if nodeTypeCurrentVersion is None:
|
||||
return True
|
||||
return Version(self.version).major == Version(nodeTypeCurrentVersion).major
|
||||
|
||||
def _checkDescriptionCompatibility(self) -> bool:
|
||||
|
|
|
@ -13,7 +13,7 @@ from meshroom.core.exception import GraphCompatibilityError, NodeUpgradeError
|
|||
from meshroom.core.graph import Graph, loadGraph
|
||||
from meshroom.core.node import CompatibilityNode, CompatibilityIssue, Node
|
||||
|
||||
from .utils import registeredNodeTypes
|
||||
from .utils import registeredNodeTypes, overrideNodeTypeVersion
|
||||
|
||||
|
||||
SampleGroupV1 = [
|
||||
|
@ -473,6 +473,36 @@ class TestGraphTemplateLoading:
|
|||
|
||||
loadGraph(graph.filepath, strictCompatibility=True)
|
||||
|
||||
class TestVersionConflict:
|
||||
|
||||
def test_loadingConflictingNodeVersionCreatesCompatibilityNodes(self, graphSavedOnDisk):
|
||||
graph: Graph = graphSavedOnDisk
|
||||
|
||||
with registeredNodeTypes([SampleNodeV1]):
|
||||
with overrideNodeTypeVersion(SampleNodeV1, "1.0"):
|
||||
node = graph.addNewNode(SampleNodeV1.__name__)
|
||||
graph.save()
|
||||
|
||||
with overrideNodeTypeVersion(SampleNodeV1, "2.0"):
|
||||
otherGraph = Graph("")
|
||||
otherGraph.load(graph.filepath)
|
||||
|
||||
assert len(otherGraph.compatibilityNodes) == 1
|
||||
assert otherGraph.node(node.name).issue is CompatibilityIssue.VersionConflict
|
||||
|
||||
def test_loadingUnspecifiedNodeVersionAssumesCurrentVersion(self, graphSavedOnDisk):
|
||||
graph: Graph = graphSavedOnDisk
|
||||
|
||||
with registeredNodeTypes([SampleNodeV1]):
|
||||
graph.addNewNode(SampleNodeV1.__name__)
|
||||
graph.save()
|
||||
|
||||
with overrideNodeTypeVersion(SampleNodeV1, "2.0"):
|
||||
otherGraph = Graph("")
|
||||
otherGraph.load(graph.filepath)
|
||||
|
||||
assert len(otherGraph.compatibilityNodes) == 0
|
||||
|
||||
|
||||
class UidTestingNodeV1(desc.Node):
|
||||
inputs = [
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
import json
|
||||
from textwrap import dedent
|
||||
|
||||
from meshroom.core import desc
|
||||
from meshroom.core.graph import Graph
|
||||
from meshroom.core.node import CompatibilityIssue
|
||||
|
||||
from .utils import registeredNodeTypes
|
||||
from .utils import registeredNodeTypes, overrideNodeTypeVersion
|
||||
|
||||
|
||||
class SimpleNode(desc.Node):
|
||||
|
@ -193,6 +197,21 @@ class TestImportGraphContent:
|
|||
assert len(otherGraph.compatibilityNodes) == 2
|
||||
assert not compareGraphsContent(graph, otherGraph)
|
||||
|
||||
def test_importingDifferentNodeVersionCreatesCompatibilityNodes(self, graphSavedOnDisk):
|
||||
graph: Graph = graphSavedOnDisk
|
||||
|
||||
with registeredNodeTypes([SimpleNode]):
|
||||
with overrideNodeTypeVersion(SimpleNode, "1.0"):
|
||||
node = graph.addNewNode(SimpleNode.__name__)
|
||||
graph.save()
|
||||
|
||||
with overrideNodeTypeVersion(SimpleNode, "2.0"):
|
||||
otherGraph = Graph("")
|
||||
nodes = otherGraph.importGraphContentFromFile(graph.filepath)
|
||||
|
||||
assert len(nodes) == 1
|
||||
assert len(otherGraph.compatibilityNodes) == 1
|
||||
assert otherGraph.node(node.name).issue is CompatibilityIssue.VersionConflict
|
||||
|
||||
class TestGraphPartialSerialization:
|
||||
def test_emptyGraph(self):
|
||||
|
@ -297,3 +316,23 @@ class TestGraphCopy:
|
|||
|
||||
graphCopy = graph.copy()
|
||||
assert not compareGraphsContent(graph, graphCopy)
|
||||
|
||||
|
||||
class TestImportGraphContentFromMinimalGraphData:
|
||||
def test_nodeWithoutVersionInfoIsUpgraded(self):
|
||||
graph = Graph("")
|
||||
|
||||
with (
|
||||
registeredNodeTypes([SimpleNode]),
|
||||
overrideNodeTypeVersion(SimpleNode, "2.0"),
|
||||
):
|
||||
sampleGraphContent = dedent("""
|
||||
{
|
||||
"SimpleNode_1": { "nodeType": "SimpleNode" }
|
||||
}
|
||||
""")
|
||||
graph._deserialize(json.loads(sampleGraphContent))
|
||||
|
||||
assert len(graph.nodes) == 1
|
||||
assert len(graph.compatibilityNodes) == 0
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
from contextlib import contextmanager
|
||||
from unittest.mock import patch
|
||||
from typing import Type
|
||||
from meshroom.core import registerNodeType, unregisterNodeType
|
||||
|
||||
import meshroom
|
||||
from meshroom.core import registerNodeType, unregisterNodeType
|
||||
from meshroom.core import desc
|
||||
|
||||
@contextmanager
|
||||
|
@ -13,3 +15,14 @@ def registeredNodeTypes(nodeTypes: list[Type[desc.Node]]):
|
|||
|
||||
for nodeType in nodeTypes:
|
||||
unregisterNodeType(nodeType)
|
||||
|
||||
@contextmanager
|
||||
def overrideNodeTypeVersion(nodeType: Type[desc.Node], version: str):
|
||||
"""Helper context manager to override the version of a given node type."""
|
||||
unpatchedFunc = meshroom.core.nodeVersion
|
||||
with patch.object(
|
||||
meshroom.core,
|
||||
"nodeVersion",
|
||||
side_effect=lambda type: version if type is nodeType else unpatchedFunc(type),
|
||||
):
|
||||
yield
|
||||
|
|
Loading…
Add table
Reference in a new issue