diff --git a/meshroom/core/desc.py b/meshroom/core/desc.py index 3422a8b8..ce69601a 100755 --- a/meshroom/core/desc.py +++ b/meshroom/core/desc.py @@ -1,5 +1,7 @@ from meshroom.common import BaseObject, Property, Variant from enum import Enum # available by default in python3. For python2: "pip install enum34" +import collections +import os class Attribute(BaseObject): @@ -23,6 +25,9 @@ class Attribute(BaseObject): isOutput = Property(bool, lambda self: self._isOutput, constant=True) isInput = Property(bool, lambda self: not self._isOutput, constant=True) + def validateValue(self, value): + return value + class ListAttribute(Attribute): """ A list of Attributes """ @@ -35,6 +40,11 @@ class ListAttribute(Attribute): uid = Property(Variant, lambda self: self.elementDesc.uid, constant=True) + def validateValue(self, value): + if not (isinstance(value, collections.Iterable) and isinstance(value, basestring)): + raise ValueError('ListAttribute only supports iterable input values.') + return value + class GroupAttribute(Attribute): """ A macro Attribute composed of several Attributes """ @@ -45,6 +55,11 @@ class GroupAttribute(Attribute): self.groupDesc = groupDesc super(GroupAttribute, self).__init__(label=label, description=description, value=None, uid=(), group=group) + def validateValue(self, value): + if not (isinstance(value, collections.Iterable) and isinstance(value, basestring)): + raise ValueError('GroupAttribute only supports iterable input values.') + return value + def retrieveChildrenUids(self): allUids = [] for desc in self.groupDesc.values(): @@ -68,6 +83,11 @@ class File(Attribute): super(File, self).__init__(label=label, description=description, value=value, uid=uid, group=group) self._isOutput = isOutput + def validateValue(self, value): + if not isinstance(value, basestring): + raise ValueError('File only supports string input: "{}".'.format(value)) + return os.path.normpath(value).replace('\\', '/') + class BoolParam(Param): """ @@ -75,6 +95,12 @@ class BoolParam(Param): def __init__(self, label, description, value, uid, group='allParams'): super(BoolParam, self).__init__(label=label, description=description, value=value, uid=uid, group=group) + def validateValue(self, value): + try: + return bool(value) + except: + raise ValueError('BoolParam only supports bool value.') + class IntParam(Param): """ @@ -83,6 +109,12 @@ class IntParam(Param): self._range = range super(IntParam, self).__init__(label=label, description=description, value=value, uid=uid, group=group) + def validateValue(self, value): + try: + return int(value) + except: + raise ValueError('IntParam only supports int value.') + range = Property(Variant, lambda self: self._range, constant=True) @@ -93,6 +125,12 @@ class FloatParam(Param): self._range = range super(FloatParam, self).__init__(label=label, description=description, value=value, uid=uid, group=group) + def validateValue(self, value): + try: + return float(value) + except: + raise ValueError('FloatParam only supports float value.') + range = Property(Variant, lambda self: self._range, constant=True) @@ -105,6 +143,19 @@ class ChoiceParam(Param): self._joinChar = joinChar super(ChoiceParam, self).__init__(label=label, description=description, value=value, uid=uid, group=group) + def validateValue(self, value): + newValues = None + if self.exclusive: + newValues = [value] + else: + if not isinstance(value, collections.Iterable): + raise ValueError('Non exclusive ChoiceParam value "{}" should be iterable.'.format(value)) + newValues = value + for newValue in newValues: + if newValue not in self.values: + raise ValueError('ChoiceParam value "{}" is not in "{}".'.format(newValue, str(self.values))) + return value + values = Property(Variant, lambda self: self._values, constant=True) exclusive = Property(bool, lambda self: self._exclusive, constant=True) joinChar = Property(str, lambda self: self._joinChar, constant=True) @@ -116,6 +167,11 @@ class StringParam(Param): def __init__(self, label, description, value, uid, group='allParams'): super(StringParam, self).__init__(label=label, description=description, value=value, uid=uid, group=group) + def validateValue(self, value): + if not isinstance(value, basestring): + raise ValueError('StringParam value "{}" should be a string.'.format(value)) + return value + class Level(Enum): NONE = 0 diff --git a/meshroom/core/graph.py b/meshroom/core/graph.py index e6e15131..83c876d8 100644 --- a/meshroom/core/graph.py +++ b/meshroom/core/graph.py @@ -14,6 +14,7 @@ from collections import defaultdict from contextlib import contextmanager from enum import Enum # available by default in python3. For python2: "pip install enum34" import logging +import re from . import stats from . import desc @@ -48,6 +49,7 @@ else: bytes = str basestring = basestring +stringIsLinkRe = re.compile('^\{.+\}$') @contextmanager def GraphModification(graph): @@ -143,7 +145,15 @@ class Attribute(BaseObject): def _set_value(self, value): if self._value == value: return - self._value = value + + if isinstance(value, Attribute) or (isinstance(value, basestring) and stringIsLinkRe.match(value)): + # if we set a link to another attribute + self._value = value + else: + # if we set a new value, we use the attribute descriptor validator to check the validity of the value + # and apply some conversion if needed + convertedValue = self.desc.validateValue(value) + self._value = convertedValue # Request graph update when input parameter value is set # and parent node belongs to a graph # Output attributes value are set internally during the update process,