Refactor gauge trait to match description of it

This commit is contained in:
Griatch 2020-04-19 12:51:05 +02:00
parent 7c12e4d362
commit 5571ef8a7d
2 changed files with 146 additions and 52 deletions

View file

@ -514,7 +514,7 @@ class TestTraitCounter(_TraitHandlerBase):
self.assertEqual(self.trait1.min, None) self.assertEqual(self.trait1.min, None)
class TestTraitGauge(TestTraitCounter): class TestTraitGauge(_TraitHandlerBase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
@ -522,66 +522,137 @@ class TestTraitGauge(TestTraitCounter):
"test2", "test2",
name="Test1", name="Test1",
trait_type='gauge', trait_type='gauge',
base=1, base=8, # max = base + mod
mod=2, mod=2,
min=-10,
max=10,
extra_val1="xvalue1", extra_val1="xvalue1",
extra_val2="xvalue2" extra_val2="xvalue2"
) )
self.trait1 = self.traithandler.get("test2") self.trait1 = self.traithandler.get("test2")
def test_boundaries__change_boundaries(self): def _get_values(self):
"""Change boundaries after base/mod change""" return (self.trait1.base, self.trait1.mod, self.trait1.actual,
self.trait1.base = 5 self.trait1.min, self.trait1.max)
self.trait1.mod = -100
def test_init(self):
self.assertEqual(
self._get_dbstore("test1"),
{"name": "Test1",
"trait_type": 'counter',
"base": 8,
"mod": 2,
"extra_val1": "xvalue1",
"extra_val2": "xvalue2"
}
)
def test_actual(self):
"""Actual is current, where current defaults to base + mod"""
# current unset - follows base + mod
self.assertEqual(self._get_values(), (8, 2, 10, 0, 10))
self.trait1.base += 4
self.assertEqual(self._get_values(), (12, 2, 14, 0, 14))
self.trait1.mod -= 1
self.assertEqual(self._get_values(), (12, 1, 13, 0, 13))
# set current, decouple from base + mod
self.trait1.current = 5
self.assertEqual(self._get_values(), (12, 1, 5, 0, 13))
self.trait1.mod += 1
self.trait1.base -= 4
self.assertEqual(self._get_values(), (8, 2, 5, 0, 10))
self.trait1.min = -100
self.trait.base = -20
self.assertEqual(self._get_values(), (-20, 2, -18, -100, 10))
def test_boundaries__minmax(self):
"""Test range"""
# current unset - tied to base + mod
self.trait1.base += 20
self.assertEqual(self._get_values(), (28, 2, 30, 0, 30))
# set current - decouple from base + mod
self.trait1.current = 19
self.assertEqual(self._get_values(), (28, 2, 19, 0, 30))
# test upper bound
self.trait1.current = 100
self.assertEqual(self._get_values(), (28, 2, 30, 0, 30))
# min defaults to 0
self.trait1.current = -10
self.assertEqual(self._get_values(), (28, 2, 0, 0, 30))
self.trait1.min = -20 self.trait1.min = -20
# from pudb import debugger;debugger.Debugger().set_trace() self.assertEqual(self._get_values(), (28, 2, 0, -20, 30))
self.assertEqual(self._get_values(), (5, -100, -20)) self.trait1.current = -10
self.assertEqual(self._get_values(), (28, 2, -10, -20, 30))
def test_boundaries__bigmod(self):
"""add a big mod"""
self.trait1.base = 5
self.trait1.mod = 100 self.trait1.mod = 100
self.trait1.max = 20 self.assertEqual(self._get_values(), (5, 100, 105, 0, 105))
self.assertEqual(self._get_values(), (5, 100, 20)) # restricted by min
self.trait1.mod = -100
self.assertEqual(self._get_values(), (5, -5, 0, 0, 0))
self.trait1.min = -200
self.assertEqual(self._get_values(), (5, -5, 0, -200, 0))
def test_boundaries__change_boundaries(self):
"""Change boundaries after current change"""
self.trait1.current = 20
self.assertEqual(self._get_values(), (8, 2, 10, 0, 10))
self.trait1.mod = 102
self.assertEqual(self._get_values(), (8, 102, 10, 0, 110))
# raising min past current value will force it upwards
self.trait1.min = 20
self.assertEqual(self._get_values(), (8, 102, 20, 20, 110))
def test_boundaries__disable(self): def test_boundaries__disable(self):
"""Disable and re-enable boundaries""" """Disable and re-enable boundary"""
self.trait1.base = 5 self.trait1.base = 5
self.trait1.mod = 100 self.trait1.min = 1
del self.trait1.max self.assertEqual(self._get_values(), (5, 2, 7, 1, 7))
self.assertEqual(self.trait1.max, None)
del self.trait1.min del self.trait1.min
self.assertEqual(self.trait1.min, None) self.assertEqual(self._get_values(), (5, 2, 7, 0, 7))
self.trait1.base = 100 del self.trait1.base
# this won't change since current is not changed del self.trait1.mod
self.assertEqual(self._get_values(), (100, 100, 10)) self.assertEqual(self._get_values(), (0, 0, 0, 0, 0))
self.trait1.current = 150 with self.assertRaises(traits.TraitException):
self.assertEqual(self._get_values(), (100, 100, 150)) del self.trait1.max
self.trait1.base = -10
self.assertEqual(self._get_values(), (-10, 100, 150))
# re-activate boundaries
self.trait1.max = 15
self.trait1.min = 10
self.assertEqual(self._get_values(), (-10, 100, 15))
def test_boundaries__inverse(self): def test_boundaries__inverse(self):
"""Set inverse boundaries - limited by base""" """Try to set reversed boundaries"""
self.trait1.mod = 0
self.trait1.base = -10 # limited by min
self.assertEqual(self._get_values(), (0, 0, 0, 0, 0))
self.trait1.min = -10
self.assertEqual(self._get_values(), (0, 0, 0, -10, 0))
self.trait1.base = -10 self.trait1.base = -10
self.trait1.mod = 100 self.assertEqual(self._get_values(), (-10, 0, -10, -10, -10))
self.trait1.min = 20 # will be set to base self.min = 0 # limited by base + mod
self.assertEqual(self.trait1.min, -10) self.assertEqual(self._get_values(), (-10, 0, -10, -10, -10))
self.trait1.max = -20 # this is <base so ok
self.assertEqual(self.trait1.max, -20)
self.assertEqual(self._get_values(), (-10, 100, -10))
def test_current(self): def test_current(self):
"""For a gauge, mod applies to base and not to current.""" """Modifying current value"""
self.trait1.base = 10
self.trait1.current = 5 self.trait1.current = 5
self.assertEqual(self._get_values(), (1, 2, 5)) self.assertEqual(self._get_values(), (10, 2, 5, 0, 12))
self.trait1.current = 14 self.trait1.current = 10
self.assertEqual(self._get_values(), (1, 2, 10)) self.assertEqual(self._get_values(), (10, 2, 10, 0, 12))
self.trait1.current = -14 self.trait1.current = 12
self.assertEqual(self._get_values(), (1, 2, -10)) self.assertEqual(self._get_values(), (10, 2, 12, 0, 12))
self.trait1.current = 0
self.assertEqual(self._get_values(), (10, 2, 0, 0, 12))
self.trait1.current = -1
self.assertEqual(self._get_values(), (10, 2, 0, 0, 12))
def test_delete(self):
"""Deleting resets to default."""
del self.trait1.mod
self.assertEqual(self._get_values(), (8, 0, 8, 0, 8))
self.trait1.mod = 2
del self.trait1.base
self.assertEqual(self._get_values(), (0, 2, 2, 0, 2))
del self.trait1.min
self.assertEqual(self._get_values(), (0, 2, 2, 0, 2))
self.trait1.min = -10
self.assertEqual(self._get_values(), (0, 2, 2, -10, 2))
del self.trait1.min
self.assertEqual(self._get_values(), (0, 2, 2, 0, 2))
class TestNumericTraitOperators(TestCase): class TestNumericTraitOperators(TestCase):

View file

@ -289,6 +289,7 @@ def _delayed_import_trait_classes():
_GA = object.__getattribute__ _GA = object.__getattribute__
_SA = object.__setattr__ _SA = object.__setattr__
_DA = object.__delattr__
# this is the default we offer in TraitHandler.add # this is the default we offer in TraitHandler.add
DEFAULT_TRAIT_TYPE = "static" DEFAULT_TRAIT_TYPE = "static"
@ -682,8 +683,18 @@ class Trait:
# set to default # set to default
self._data[key] = self.data_keys[key] self._data[key] = self.data_keys[key]
elif key in self._data: elif key in self._data:
# an extra property. Delete as normal. try:
del self._data[key] # check if we have a custom deleter
_DA(self, key)
except AttributeHandler:
# delete normally
del self._data[key]
else:
try:
# check if we have custom deleter, otherwise ignore
_DA(self, key)
except AttributeError:
pass
def __repr__(self): def __repr__(self):
"""Debug-friendly representation of this Trait.""" """Debug-friendly representation of this Trait."""
@ -1049,7 +1060,7 @@ class GaugeTrait(CounterTrait):
data_keys = { data_keys = {
"base": 0, "base": 0,
"mod": 0, "mod": 0,
"min": None, "min": 0,
} }
def _mod_base(self): def _mod_base(self):
@ -1076,28 +1087,36 @@ class GaugeTrait(CounterTrait):
@base.setter @base.setter
def base(self, value): def base(self, value):
"""Limit so base+mod can never go below min."""
if type(value) in (int, float): if type(value) in (int, float):
self._data["base"] = self._enforce_bounds(value) if value + self.mod < self.min:
value = self.min - self.mod
self._data["base"] = value
@property @property
def mod(self): def mod(self):
return self._data["mod"] return self._data["mod"]
@mod.setter @mod.setter
def mod(self, amount): def mod(self, value):
if type(amount) in (int, float): """Limit so base+mod can never go below min."""
self._data["mod"] = amount if type(value) in (int, float):
if value + self.base < self.min:
value = self.min - self.base
self._data["mod"] = value
@property @property
def min(self): def min(self):
return self._data["min"] val = self._data["min"]
return self.data_keys["min"] if val is None else val
@min.setter @min.setter
def min(self, value): def min(self, value):
"""Limit so min can never be greater than base+mod."""
if value is None: if value is None:
self._data["min"] = self.data_keys['min'] self._data["min"] = self.data_keys['min']
elif type(value) in (int, float): elif type(value) in (int, float):
self._data["min"] = min(self.value, self.base + self.mod) self._data["min"] = min(value, self.base + self.mod)
@property @property
def max(self): def max(self):
@ -1108,6 +1127,10 @@ class GaugeTrait(CounterTrait):
def max(self, value): def max(self, value):
raise TraitException("The .max property is not settable " raise TraitException("The .max property is not settable "
"on GaugeTraits. Set .base instead.") "on GaugeTraits. Set .base instead.")
@max.deleter
def max(self):
raise TraitException("The .max property cannot be reset "
"on GaugeTraits. Reset .mod and .base instead.")
@property @property
def current(self): def current(self):