Skip to content
Open
Show file tree
Hide file tree
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
7 changes: 5 additions & 2 deletions meshroom/core/desc/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -422,8 +422,11 @@ def getMrNodeType(self):

def buildCommandLine(self, chunk) -> str:
cmdLineVars = chunk.node.createCmdLineVars()
cmdPrefix = chunk.node.nodeDesc.plugin.commandPrefix
cmdSuffix = chunk.node.nodeDesc.plugin.commandSuffix
cmdPrefix = ""
cmdSuffix = ""
if chunk.node.nodeDesc.plugin:
cmdPrefix = chunk.node.nodeDesc.plugin.commandPrefix
cmdSuffix = chunk.node.nodeDesc.plugin.commandSuffix
if chunk.node.isParallelized and chunk.node.size > 1:
cmdSuffix = " " + self.commandLineRange.format(**chunk.range.toDict()) + " " + cmdSuffix

Expand Down
1 change: 1 addition & 0 deletions meshroom/core/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -1155,6 +1155,7 @@ def _buildAttributeExpVars(expVars, name, attr):
self._expVars = {
"uid": self._uid,
"nodeCacheFolder": self._internalFolder,
"node": self,
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existing test for expression variables in tests/test_nodes.py::test_expVariables should be extended to verify that the new 'node' key is present in _expVars and that it correctly references the node object. This would ensure that the feature works as intended and prevent regressions.

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

}

# Evaluate input params
Expand Down
10 changes: 7 additions & 3 deletions meshroom/core/plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -561,19 +561,23 @@ def runtimeEnv(self) -> dict:
@property
def commandPrefix(self) -> str:
""" Return the command prefix for the NodePlugin's execution. """
if not self.processEnv:
return ""
return self.processEnv.getCommandPrefix()

@property
def commandSuffix(self) -> str:
""" Return the command suffix for the NodePlugin's execution. """
if not self.processEnv:
return ""
return self.processEnv.getCommandSuffix()

@property
def configFullEnv(self) -> dict[str: str]:
""" Return the plugin's full environment dictionary. """
if self.plugin:
return self.plugin.configFullEnv
return {}
if not self.plugin:
return {}
return self.plugin.configFullEnv

class NodePluginManager(BaseObject):
"""
Expand Down
189 changes: 189 additions & 0 deletions tests/test_nodeAttributesFormatting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
#!/usr/bin/env python
# coding:utf-8

from meshroom.core.graph import Graph
from meshroom.core import desc

from .utils import registerNodeDesc, unregisterNodeDesc


class NodeWithAttributesNeedingFormatting(desc.Node):
"""
A node containing list, file, choice and group attributes in order to test the
formatting of the command line.
"""
inputs = [
desc.ListAttribute(
name="images",
label="Images",
description="List of images.",
elementDesc=desc.File(
name="image",
label="Image",
description="Path to an image.",
value="",
),
),
desc.File(
name="input",
label="Input File",
description="An input file.",
value="",
),
desc.ChoiceParam(
name="method",
label="Method",
description="Method to choose from a list of available methods.",
value="MethodC",
values=["MethodA", "MethodB", "MethodC"],
),
desc.GroupAttribute(
name="firstGroup",
label="First Group",
description="Group with boolean and integer parameters.",
joinChar=":",
items=[
desc.BoolParam(
name="enableFirstGroup",
label="Enable",
description="Enable other parameter in the group.",
value=False,
),
desc.IntParam(
name="width",
label="Width",
description="Width setting.",
value=3,
range=(1, 10, 1),
enabled=lambda node: node.firstGroup.enableFirstGroup.value,
),
]
),
desc.GroupAttribute(
name="secondGroup",
label="Second Group",
description="Group with boolean, choice and float parameters.",
joinChar=",",
items=[
desc.BoolParam(
name="enableSecondGroup",
label="Enable",
description="Enable other parameters in the group.",
value=False,
),
desc.ChoiceParam(
name="groupChoice",
label="Grouped Choice",
description="Value to choose from a group.",
value="second_value",
values=["first_value", "second_value", "third_value"],
enabled=lambda node: node.secondGroup.enableSecondGroup.value,
),
desc.FloatParam(
name="floatWidth",
label="Width",
description="Width setting (but with a float).",
value=3.0,
range=(1.0, 10.0, 0.5),
enabled=lambda node: node.secondGroup.enableSecondGroup.value,
),
],
),
]

outputs = [
desc.File(
name="output",
label="Output",
description="Output file.",
value="{nodeCacheFolder}",
),
]


class TestAttributesFormatting:

@classmethod
def setup_class(cls):
registerNodeDesc(NodeWithAttributesNeedingFormatting)

@classmethod
def teardown_class(cls):
unregisterNodeDesc(NodeWithAttributesNeedingFormatting)

def test_formatting_listOfFiles(self):
inputImages = ["/non/existing/fileA", "/non/existing/with space/fileB"]

graph = Graph("")
node = graph.addNewNode("NodeWithAttributesNeedingFormatting")

# Assert that an empty list gives an empty string
assert node.images.getValueStr() == ""

# Assert that values in a list a correctly concatenated
node.images.extend([i for i in inputImages])
assert node.images.getValueStr() == '"/non/existing/fileA" "/non/existing/with space/fileB"'

# Reset list content and add a single value that contains spaces
node.images.resetToDefaultValue()
assert node.images.getValueStr() == "" # The value has been correctly reset
node.images.extend("single value with space")
assert node.images.getValueStr() == '"single value with space"'

# Assert that extending values when the list is not empty is working
node.images.extend(inputImages)
assert node.images.getValueStr() == \
'"single value with space" "{}" "{}"'.format(inputImages[0],
inputImages[1])

# Values are not retrieved as strings in the command line, so quotes around them are
# not expected
assert node._expVars["imagesValue"] == \
'single value with space {} {}'.format(inputImages[0],
inputImages[1])

def test_formatting_strings(self):
graph = Graph("")
node = graph.addNewNode("NodeWithAttributesNeedingFormatting")
node._buildExpVars()

# Assert an empty File attribute generates empty quotes when requesting its value as
# a string
assert node.input.getValueStr() == '""'
assert node._expVars["inputValue"] == ""

# Assert a Choice attribute with a non-empty default value is surrounded with quotes
# when requested as a string
assert node.method.getValueStr() == '"MethodC"'
assert node._expVars["methodValue"] == "MethodC"

# Assert that the empty list is really empty (no quotes)
assert node.images.getValueStr() == ""
assert node._expVars["imagesValue"] == "", "Empty list should become fully empty"

# Assert that the list with one empty value generates empty quotes
node.images.extend("")
assert node.images.getValueStr() == '""', \
"A list with one empty string should generate empty quotes"
assert node._expVars["imagesValue"] == "", \
"The value is always only the value, so empty here"

# Assert that a list with 2 empty strings generates quotes
node.images.extend("")
assert node.images.getValueStr() == '"" ""', \
"A list with 2 empty strings should generate quotes"
assert node._expVars["imagesValue"] == ' ', \
"The value is always only the value, so 2 empty strings with the " \
"space separator in the middle"

def test_formatting_groups(self):
graph = Graph("")
node = graph.addNewNode("NodeWithAttributesNeedingFormatting")
node._buildExpVars()

assert node.firstGroup.getValueStr() == '"False:3"'
assert node._expVars["firstGroupValue"] == 'False:3', \
"There should be no quotes here as the value is not formatted as a string"

assert node.secondGroup.getValueStr() == '"False,second_value,3.0"'
assert node._expVars["secondGroupValue"] == 'False,second_value,3.0'
Loading
Loading