code: def test_property_lock(echo):
      # when this widget's value is set to 42, it sets itself to 2, and then back to 42 again (and then stops)
      class AnnoyingWidget(Widget):
          value = Float().tag(sync=True)
          stop = Bool(False)
  
          @observe("value")
          def _propagate_value(self, change):
              print("_propagate_value", change.new)
              if self.stop:
                  return
              if change.new == 42:
                  self.value = 2
              if change.new == 2:
                  self.stop = True
                  self.value = 42
  
      widget = AnnoyingWidget(value=1)
      assert widget.value == 1
  
      widget._send = mock.MagicMock()
      # this mimics a value coming from the front end
      widget.set_state({"value": 42})
      assert widget.value == 42
      assert widget.stop is True
  
      # we expect no new state to be sent
      calls = []
      widget._send.assert_has_calls(calls)
  
imports: import pytest
  from traitlets import Bool, Tuple, List, Instance, CFloat, CInt, Float, Int, TraitError, observe
  from .utils import setup, teardown
  import ipywidgets
  from ipywidgets import Widget
label_desc: Use image node_id 17 for calling a global function with example usage: test_property_lock(echo) without return types