Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--addons/gtkwidgettypefilter.py12
-rw-r--r--tests/constraintstests.py107
-rw-r--r--tests/propertiestests.py94
-rw-r--r--tutorius/constraints.py90
-rw-r--r--tutorius/properties.py26
-rw-r--r--tutorius/vault.py1
6 files changed, 298 insertions, 32 deletions
diff --git a/addons/gtkwidgettypefilter.py b/addons/gtkwidgettypefilter.py
index 8faf172..0339e74 100644
--- a/addons/gtkwidgettypefilter.py
+++ b/addons/gtkwidgettypefilter.py
@@ -15,7 +15,7 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
from ..filters import *
-from ..properties import *
+from ..properties import TStringProperty, TSequenceProperty
from ..services import ObjectStore
from ..gtkutils import find_widget
@@ -28,7 +28,7 @@ class GtkWidgetTypeFilter(EventFilter):
"""
object_id = TStringProperty("")
text = TStringProperty("")
- strokes = TArrayProperty([])
+ strokes = TSequenceProperty("")
def __init__(self, object_id, text=None, strokes=None):
"""Constructor
@@ -41,9 +41,9 @@ class GtkWidgetTypeFilter(EventFilter):
"""
super(GtkWidgetTypeFilter, self).__init__()
self.object_id = object_id
- self.text = text
+ self.text = text or ""
self._captext = ""
- self.strokes = strokes
+ self.strokes = strokes or []
self._capstrokes = []
self._widget = None
self._handler_id = None
@@ -86,9 +86,9 @@ class GtkWidgetTypeFilter(EventFilter):
self._captext = self._captext + keystr
logger.debug("~~~Current state: " + str(self._capstrokes) + ":" + str(self._captext))
- if not self.strokes is None and self.strokes in self._capstrokes:
+ if not self.strokes == [] and self.strokes in self._capstrokes:
self.do_callback()
- if not self.text is None and self.text in self._captext:
+ if not self.text is "" and self.text in self._captext:
self.do_callback()
__event__ = {
diff --git a/tests/constraintstests.py b/tests/constraintstests.py
index a5ccf26..f4318f8 100644
--- a/tests/constraintstests.py
+++ b/tests/constraintstests.py
@@ -36,6 +36,113 @@ class ValueConstraintTest(unittest.TestCase):
assert cons.limit == 12
+def try_good_values(constraint, type):
+ if type == "int":
+ constraint.validate(1)
+ if type == "float":
+ constraint.validate(1.2)
+ if type == "str":
+ constraint.validate("This is a string")
+ if type == "tuple":
+ constraint.validate((1,2.0,'3',[4], (5,)))
+ if type == "list":
+ constraint.validate([1,2.0,'3',[4],(5,)])
+ if type == "bool":
+ constraint.validate(True)
+ constraint.validate(False)
+
+def try_wrong_values(testcase, constraint, type):
+ if type != "int":
+ testcase.assertRaises(TypeConstraintError, constraint.validate, 1)
+ if type != "float":
+ testcase.assertRaises(TypeConstraintError, constraint.validate, 1.2)
+ if type != "str":
+ testcase.assertRaises(TypeConstraintError, constraint.validate, "1")
+ if type != "tuple":
+ testcase.assertRaises(TypeConstraintError, constraint.validate, (1,))
+ if type != "list":
+ testcase.assertRaises(TypeConstraintError, constraint.validate, [1,])
+ if type != "bool":
+ testcase.assertRaises(TypeConstraintError, constraint.validate, True)
+
+class TypeConstraintsTest(unittest.TestCase):
+ """
+ Tests all the type constraints at the same time to ensure that they
+ only allow a single type of item.
+ """
+ def test_int_type(self):
+ constraint = IntTypeConstraint()
+ try_good_values(constraint, 'int')
+ try_wrong_values(self, constraint, 'int')
+
+ def test_float_type(self):
+ constraint = FloatTypeConstraint()
+ try_good_values(constraint, 'float')
+ try_wrong_values(self, constraint, 'float')
+
+ def test_string_type(self):
+ constraint = StringTypeConstraint()
+ try_good_values(constraint, 'str')
+ try_wrong_values(self, constraint, 'str')
+
+ def test_tuple_type(self):
+ constraint = TupleTypeConstraint()
+ try_good_values(constraint, 'tuple')
+ try_wrong_values(self, constraint, 'tuple')
+
+ def test_list_type(self):
+ constraint = ListTypeConstraint()
+ try_good_values(constraint, 'list')
+ try_wrong_values(self, constraint, 'list')
+
+ def test_bool_type(self):
+ constraint = BoolTypeConstraint()
+ try_good_values(constraint, 'bool')
+ try_wrong_values(self, constraint, 'bool')
+
+class MultiTypeConstraintsTest(unittest.TestCase):
+ """
+ Tests all the type constraints at the same time to ensure that they
+ only allow the specified types of item.
+ """
+ def _try_good_values(self, constraint, types_list):
+ if "int" in types_list:
+ constraint.validate(1)
+ if "float" in types_list:
+ constraint.validate(1.0)
+ if "str" in types_list:
+ constraint.validate("This is a string.")
+ if "tuple" in types_list:
+ constraint.validate((1, 2.0, '3', [4], (5,)))
+ if "list" in types_list:
+ constraint.validate([1, 2.0, '3', [4], (5,)])
+ if "bool" in types_list:
+ constraint.validate(True)
+
+ def _try_wrong_values(self, constraint, types_list):
+ if "int" not in types_list:
+ self.assertRaises(TypeConstraintError, constraint.validate, 1)
+ if "float" not in types_list:
+ self.assertRaises(TypeConstraintError, constraint.validate, 1.0)
+ if "str" not in types_list:
+ self.assertRaises(TypeConstraintError, constraint.validate, "1")
+ if "tuple" not in types_list:
+ self.assertRaises(TypeConstraintError, constraint.validate, (1,))
+ if "list" not in types_list:
+ self.assertRaises(TypeConstraintError, constraint.validate, [1,])
+ if "bool" not in types_list:
+ self.assertRaises(TypeConstraintError, constraint.validate, True)
+
+ def test_array_type(self):
+ constraint = ArrayTypeConstraint()
+ self._try_good_values(constraint, ['tuple', 'list'])
+ self._try_wrong_values(constraint, ['tuple', 'list'])
+
+ def test_sequence_type(self):
+ constraint = SequenceTypeConstraint()
+ self._try_good_values(constraint, ['list', 'tuple', 'str'])
+ self._try_wrong_values(constraint, ['list', 'tuple', 'str'])
+
class UpperLimitConstraintTest(unittest.TestCase):
def test_empty_constraint(self):
cons = UpperLimitConstraint(None)
diff --git a/tests/propertiestests.py b/tests/propertiestests.py
index 49d2312..55c3818 100644
--- a/tests/propertiestests.py
+++ b/tests/propertiestests.py
@@ -29,36 +29,35 @@ def try_wrong_values(obj):
try:
obj.prop = 3
assert False, "Able to insert int value in property of type %s"%typ
- except ValueError:
+ except TypeConstraintError:
pass
if typ != "float":
try:
obj.prop = 1.1
assert False, "Able to insert float value in property of type %s"%typ
- except ValueError:
+ except TypeConstraintError:
pass
- if typ != "string":
+ if typ != "string" and typ != "sequence":
try:
obj.prop = "Fake string"
assert False, "Able to insert string value in property of type %s"%typ
- except ValueError:
+ except TypeConstraintError:
pass
- if typ != "array":
+ if typ != "array" and typ != "color" and typ != "sequence":
try:
obj.prop = [1, 2000, 3, 400]
assert False, "Able to insert array value in property of type %s"%typ
- except ValueError:
+ except TypeConstraintError:
pass
- if typ != "color":
+ if typ != "color" and typ != "sequence" and typ != "array":
try:
obj.prop = [1,2,3]
- if typ != "array":
- assert False, "Able to insert color value in property of type %s"%typ
- except ValueError:
+ assert False, "Able to insert color value in property of type %s"%typ
+ except TypeConstraintError:
pass
if typ != "boolean":
@@ -66,7 +65,7 @@ def try_wrong_values(obj):
obj.prop = True
if typ != "boolean":
assert False, "Able to set boolean value in property of type %s"%typ
- except ValueError:
+ except TypeConstraintError:
pass
class BasePropertyTest(unittest.TestCase):
@@ -216,7 +215,7 @@ class TIntPropertyTest(unittest.TestCase):
assert klass.prop.type == "int", "Wrong type on int property : %s" % prop.type
cons = klass.prop.get_constraints()
- assert len(cons) == 2, "Not enough constraints on the int property"
+ assert len(cons) == 3, "Not enough constraints on the int property"
obj.prop = 12
@@ -271,22 +270,22 @@ class TIntPropertyTest(unittest.TestCase):
class TFloatPropertyTest(unittest.TestCase):
def test_float_property(self):
class klass(TPropContainer):
- prop = TFloatProperty(22)
+ prop = TFloatProperty(22.0)
obj = klass()
- assert obj.prop == 22, "Could not set value on property via constructor"
+ assert obj.prop == 22.0, "Could not set value on property via constructor"
assert klass.prop.type == "float", "Wrong type on float property : %s" % klass.prop.type
cons = klass.prop.get_constraints()
- assert len(cons) == 2, "Not enough constraints on the float property"
+ assert len(cons) == 3, "Not enough constraints on the float property"
- obj.prop = 12
+ obj.prop = 12.0
- assert obj.prop == 12, "Could not set value"
+ assert obj.prop == 12.0, "Could not set value"
def test_wrong_values(self):
class klass(TPropContainer):
- prop = TFloatProperty(33)
+ prop = TFloatProperty(33.0)
obj = klass()
# Try setting values of other types
@@ -315,7 +314,7 @@ class TFloatPropertyTest(unittest.TestCase):
def test_failing_constructor(self):
try:
- prop = TFloatProperty(100, 0, 20)
+ prop = TFloatProperty(100.0, 0, 20)
assert False, "Creation of the property should fail."
except UpperLimitConstraintError:
pass
@@ -323,7 +322,7 @@ class TFloatPropertyTest(unittest.TestCase):
assert False, "Wrong exception type on failed constructor"
try:
- prop = TFloatProperty(-100, 0, 20)
+ prop = TFloatProperty(-100.0, 0, 20)
assert False, "Creation of the property should fail."
except LowerLimitConstraintError:
pass
@@ -419,6 +418,56 @@ class TArrayPropertyTest(unittest.TestCase):
except MinSizeConstraintError:
pass
+class TSequencePropertyTest(unittest.TestCase):
+ def test_basic_array(self):
+ class klass(TPropContainer):
+ prop = TSequenceProperty([1, 2, 3, 4])
+ obj = klass()
+
+ assert obj.prop == (1,2,3,4), "Unable to set initial value via constructor"
+
+ assert klass.prop.type == "sequence", "Wrong type for sequence : %s"%klass.prop.type
+
+ def test_wrong_values(self):
+ class klass(TPropContainer):
+ prop = TSequenceProperty([1,2,3,4,5])
+ obj = klass()
+
+ try_wrong_values(obj)
+
+ def test_size_limits(self):
+ class klass(TPropContainer):
+ prop = TSequenceProperty([1,2], None, 4)
+ obj = klass()
+
+ try:
+ obj.prop = [1,2,4,5,6,7]
+ assert False, "Maximum size limit constraint was not properly applied"
+ except MaxSizeConstraintError:
+ pass
+
+ class klass(TPropContainer):
+ prop = TSequenceProperty([1,2,3,4], 2)
+ obj = klass()
+
+ try:
+ obj.prop = [1]
+ assert False, "Minimum size limit constraint was not properly applied"
+ except MinSizeConstraintError:
+ pass
+
+ def test_failing_constructor(self):
+ try:
+ prop = TSequenceProperty([100, 0, 20], None, 2)
+ assert False, "Creation of the property should fail."
+ except MaxSizeConstraintError:
+ pass
+ try:
+ prop = TSequenceProperty([100, 0, 20], 4, None)
+ assert False, "Creation of the property should fail."
+ except MinSizeConstraintError:
+ pass
+
class TColorPropertyTest(unittest.TestCase):
def test_basic_color(self):
class klass(TPropContainer):
@@ -471,7 +520,7 @@ class TBooleanPropertyTest(unittest.TestCase):
try:
prop = TBooleanProperty(64)
assert False, "Creation of the property should fail with non-boolean value"
- except BooleanConstraintError:
+ except TypeConstraintError:
pass
except:
assert False, "Wrong exception type on failed constructor"
@@ -497,9 +546,6 @@ class TEnumPropertyTest(unittest.TestCase):
except EnumConstraintError:
pass
- def test_wrong_type(self):
- try_wrong_values(self.obj)
-
class TFilePropertyTest(unittest.TestCase):
root_folder = "/tmp/tutorius"
diff --git a/tutorius/constraints.py b/tutorius/constraints.py
index cd71167..9ac1ca4 100644
--- a/tutorius/constraints.py
+++ b/tutorius/constraints.py
@@ -47,6 +47,96 @@ class Constraint():
"""
raise NotImplementedError("Unable to validate a base Constraint")
+class TypeConstraintError(ConstraintException):
+ pass
+
+class TypeConstraint(Constraint):
+ """
+ Base class for ensuring that all the values have a specific Python type.
+ """
+ def __init__(self, expected_type):
+ """
+ Creates a new type constraint to restrict accepted values to a certain type.
+ @param expected_type A string describing the name of the type to enforce
+ """
+ self._expected_type = expected_type
+
+ def validate(self, value):
+ if self._expected_type == type(value).__name__:
+ return value
+ raise TypeConstraintError("Wrong type for value %s, got type %s; expected %s" % \
+ (str(value),
+ type(value).__name__,
+ self._expected_type))
+
+class IntTypeConstraint(TypeConstraint):
+ """
+ Forces the value to be a Python int.
+ """
+ def __init__(self):
+ TypeConstraint.__init__(self, 'int')
+
+class FloatTypeConstraint(TypeConstraint):
+ """
+ Forces the value to be a Python float.
+ """
+ def __init__(self):
+ TypeConstraint.__init__(self, 'float')
+
+class StringTypeConstraint(TypeConstraint):
+ """
+ Forces the value to be a Python string.
+ """
+ def __init__(self):
+ TypeConstraint.__init__(self, 'str')
+
+class TupleTypeConstraint(TypeConstraint):
+ """
+ Forces the value to be a Python tuple.
+ """
+ def __init__(self):
+ TypeConstraint.__init__(self, 'tuple')
+
+class ListTypeConstraint(TypeConstraint):
+ """
+ Forces the value to be a Python list.
+ """
+ def __init__(self):
+ TypeConstraint.__init__(self, 'list')
+
+class BoolTypeConstraint(TypeConstraint):
+ def __init__(self):
+ TypeConstraint.__init__(self, 'bool')
+
+class MultiTypeConstraint(Constraint):
+ """
+ Base class for ensuring that all the values have a specific Python type.
+ """
+ def __init__(self, expected_types_list):
+ """
+ Creates a new type constraint to restrict accepted values to a certain type.
+ @param expected_types_list A list of strings describing the possible names
+ of the types to enforce.
+ """
+ self._expected_types_list = expected_types_list
+
+ def validate(self, value):
+ if type(value).__name__ in self._expected_types_list:
+ return value
+ raise TypeConstraintError("Wrong type for value %s, got type %s; expected one in %s" % \
+ (str(value),
+ type(value).__name__,
+ str(self._expected_types_list)))
+
+class ArrayTypeConstraint(MultiTypeConstraint):
+ """ Forces the value to either be a list or a tuple."""
+ def __init__(self):
+ MultiTypeConstraint.__init__(self, ['list', 'tuple'])
+
+class SequenceTypeConstraint(MultiTypeConstraint):
+ def __init__(self):
+ MultiTypeConstraint.__init__(self, ['list', 'tuple', 'str'])
+
class ValueConstraint(Constraint):
"""
A value constraint contains a _limit member that can be used in a children
diff --git a/tutorius/properties.py b/tutorius/properties.py
index 07b1645..4a80e9c 100644
--- a/tutorius/properties.py
+++ b/tutorius/properties.py
@@ -26,7 +26,9 @@ from .constraints import Constraint, \
UpperLimitConstraint, LowerLimitConstraint, \
MaxSizeConstraint, MinSizeConstraint, \
ColorConstraint, FileConstraint, BooleanConstraint, EnumConstraint, \
- ResourceConstraint
+ ResourceConstraint, IntTypeConstraint, FloatTypeConstraint, \
+ StringTypeConstraint, ArrayTypeConstraint, BoolTypeConstraint, \
+ SequenceTypeConstraint
from .propwidgets import PropWidget, \
StringPropWidget, \
@@ -227,6 +229,7 @@ class TIntProperty(TutoriusProperty):
self.type = "int"
self.upper_limit = UpperLimitConstraint(upper_limit)
self.lower_limit = LowerLimitConstraint(lower_limit)
+ self.int_type = IntTypeConstraint()
self.default = self.validate(value)
@@ -242,6 +245,7 @@ class TFloatProperty(TutoriusProperty):
self.upper_limit = UpperLimitConstraint(upper_limit)
self.lower_limit = LowerLimitConstraint(lower_limit)
+ self.float_type = FloatTypeConstraint()
self.default = self.validate(value)
@@ -254,6 +258,7 @@ class TStringProperty(TutoriusProperty):
TutoriusProperty.__init__(self)
self.type = "string"
self.size_limit = MaxSizeConstraint(size_limit)
+ self.string_type = StringTypeConstraint()
self.default = self.validate(value)
@@ -268,6 +273,7 @@ class TArrayProperty(TutoriusProperty):
self.type = "array"
self.max_size_limit = MaxSizeConstraint(max_size_limit)
self.min_size_limit = MinSizeConstraint(min_size_limit)
+ self.array_type = ArrayTypeConstraint()
self.default = tuple(self.validate(value))
#Make this thing hashable
@@ -283,6 +289,22 @@ class TArrayProperty(TutoriusProperty):
value=self.value,
)
+class TSequenceProperty(TutoriusProperty):
+ """
+ Represents a data type that can be accessed with indices (via []). Those
+ are mainly for structures that only expect a list of elements that may
+ also be specified using a string. E.g. the strokes in a text type filter.
+ Since it is more convenient to specify a list of keystrokes as a string
+ rather than a list of characters, a sequence is the best option.
+ """
+ def __init__(self, value, min_size_limit=None, max_size_limit=None):
+ TutoriusProperty.__init__(self)
+ self.type = "sequence"
+ self.max_size_limit = MaxSizeConstraint(max_size_limit)
+ self.min_size_limit = MinSizeConstraint(min_size_limit)
+ self.sequence_type = SequenceTypeConstraint()
+ self.default = tuple(self.validate(value))
+
class TColorProperty(TutoriusProperty):
"""
Represents a RGB color with 3 8-bit integer values.
@@ -293,6 +315,7 @@ class TColorProperty(TutoriusProperty):
TutoriusProperty.__init__(self)
self.type = "color"
+ self.array_type = ArrayTypeConstraint()
self.color_constraint = ColorConstraint()
self._red = red or 0
@@ -384,6 +407,7 @@ class TBooleanProperty(TutoriusProperty):
self.type = "boolean"
+ self.bool_type = BoolTypeConstraint()
self.boolean_constraint = BooleanConstraint()
self.default = self.validate(value)
diff --git a/tutorius/vault.py b/tutorius/vault.py
index 1c1e33c..2b9c5b9 100644
--- a/tutorius/vault.py
+++ b/tutorius/vault.py
@@ -663,7 +663,6 @@ class XMLSerializer(Serializer):
if getattr(type(comp), propname).type == "addonlist":
compNode.appendChild(cls._create_addonlist_component_node(propname, propval, doc))
elif getattr(type(comp), propname).type == "addon":
- #import rpdb2; rpdb2.start_embedded_debugger('pass')
compNode.appendChild(cls._create_addon_component_node(propname, propval, doc))
else:
# repr instead of str, as we want to be able to eval() it into a