Repository: bokeh/bokeh

Issue:
`Property.__delete__` does not send a change notification
We support "revert to default" with `del model.prop` but it won't change-notify I don't thinK:
https://github.com/bokeh/bokeh/blob/0c5bb0039b2505d09f9d5360eff861ba466db85c/bokeh/core/properties.py#L464-L466

The needed code would be similar to the stuff in `apply_theme` I think, plus the fix should come with tests.
https://github.com/bokeh/bokeh/blob/0c5bb0039b2505d09f9d5360eff861ba466db85c/bokeh/core/properties.py#L874-L877



Discussion:
FYI new code location is here:

https://github.com/bokeh/bokeh/blob/master/bokeh/core/property/descriptors.py#L497-L508



Resolving patch:
diff --git a/bokeh/core/property/descriptors.py b/bokeh/core/property/descriptors.py
--- a/bokeh/core/property/descriptors.py
+++ b/bokeh/core/property/descriptors.py
@@ -501,8 +501,11 @@ def __delete__(self, obj):
             obj (HasProps) : An instance to delete this property from
 
         '''
+
         if self.name in obj._property_values:
+            old_value = obj._property_values[self.name]
             del obj._property_values[self.name]
+            self.trigger_if_changed(obj, old_value)
 
         if self.name in obj._unstable_default_values:
             del obj._unstable_default_values[self.name]


Test patch:
diff --git a/bokeh/core/property/tests/test_descriptors.py b/bokeh/core/property/tests/test_descriptors.py
--- a/bokeh/core/property/tests/test_descriptors.py
+++ b/bokeh/core/property/tests/test_descriptors.py
@@ -3,6 +3,9 @@
 
 import bokeh.core.property.descriptors as pd
 
+from bokeh.model import Model
+from bokeh.core.properties import Int, List
+
 def test_PropertyDescriptor__init__():
     d = pd.PropertyDescriptor("foo")
     assert d.name == "foo"
@@ -107,22 +110,62 @@ class Foo(object): pass
     assert str(e).endswith("Cannot set a property value 'foo' on a str instance before HasProps.__init__")
 
 def test_BasicPropertyDescriptor__delete__():
-    class Foo(object):
-        _property_values = dict(foo=0)
-        _unstable_default_values = dict(bar=[10])
+    class Foo(Model):
+        foo = Int()
+        bar = List(Int, default=[10])
+        baz = Int(default=20)
+        quux = List(Int, default=[30])
     f = Foo()
-    d1 = pd.BasicPropertyDescriptor("foo", f)
-    d2 = pd.BasicPropertyDescriptor("bar", f)
-    d3 = pd.BasicPropertyDescriptor("baz", f)
-    d1.__delete__(f)
+    f.foo
+    f.bar
+    f.baz
+    f.quux
+
+    calls = []
+
+    def cb(attr, old, new):
+        calls.append(attr)
+
+    for name in ['foo', 'bar', 'baz', 'quux']:
+        f.on_change(name, cb)
+
     assert f._property_values == {}
-    assert f._unstable_default_values == dict(bar=[10])
-    d2.__delete__(f)
+    assert f._unstable_default_values == dict(bar=[10], quux=[30])
+
+    del f.foo
     assert f._property_values == {}
-    assert f._unstable_default_values == {}
-    d3.__delete__(f)
+    assert f._unstable_default_values == dict(bar=[10], quux=[30])
+    assert calls == []
+
+    f.baz = 50
+
+    assert f.baz == 50
+    assert f._unstable_default_values == dict(bar=[10], quux=[30])
+    assert calls == ['baz']
+
+    del f.baz
+    assert f.baz == 20
+    assert f._unstable_default_values == dict(bar=[10], quux=[30])
+    assert calls == ['baz', 'baz']
+
+    del f.bar
     assert f._property_values == {}
-    assert f._unstable_default_values == {}
+    assert f._unstable_default_values == dict(quux=[30])
+    assert calls == ['baz', 'baz']
+
+    f.bar = [60]
+    assert f.bar == [60]
+    assert f._unstable_default_values == dict(quux=[30])
+    assert calls == ['baz', 'baz', 'bar']
+
+    del f.bar
+    assert f.bar == [10]
+    assert f._unstable_default_values == dict(bar=[10], quux=[30])
+    assert calls == ['baz', 'baz', 'bar', 'bar']
+
+    del f.quux
+    assert f._unstable_default_values == dict(bar=[10])
+    assert calls == ['baz', 'baz', 'bar', 'bar']
 
 def test_BasicPropertyDescriptor_class_default():
     result = {}