Skip to content

Commit

Permalink
Factor out json and buffer list compare.
Browse files Browse the repository at this point in the history
Compare deterministic json strings instead of rehydrating them into objects.
  • Loading branch information
jasongrout committed Aug 9, 2017
1 parent 7d56205 commit b9eb8c8
Showing 1 changed file with 32 additions and 27 deletions.
59 changes: 32 additions & 27 deletions ipywidgets/widgets/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
Undefined)
from ipython_genutils.py3compat import string_types, PY3
from IPython.display import display
from json import loads as jsonloads, dumps as jsondumps
from json import dumps as jsondumps

from base64 import standard_b64decode, standard_b64encode

Expand Down Expand Up @@ -127,6 +127,35 @@ def _remove_buffers(state):
state = _separate_buffers(state, [], buffer_paths, buffers)
return state, buffer_paths, buffers

def _buffer_list_equal(a, b):
"""Compare two lists of buffers for equality.
Used to decide whether two sequences of buffers (memoryviews,
bytearrays, or python 3 bytes) differ, such that a sync is needed.
Returns True if equal, False if unequal
"""
if len(a) != len(b):
return False
if a == b:
return True
for ia, ib in zip(a, b):
# Check byte equality:
# NOTE: Simple ia != ib does not always work as intended, as
# e.g. memoryview(np.frombuffer(ia, dtype='float32')) !=
# memoryview(np.frombuffer(b)), since the format info differs.
# However, since we only transfer bytes, we use `tobytes()`.
ia_bytes = ia.tobytes() if isinstance(ia, memoryview) else ia
ib_bytes = ib.tobytes() if isinstance(ib, memoryview) else ib
if ia_bytes != ib_bytes:
return False
return True

def _json_equal(a, b):
"""Compare the JSON strings generated by two objects."""
return (jsondumps(a, separators=(',',':'), sort_keys=True, allow_nan=False)
== jsondumps(b, separators=(',',':'), sort_keys=True, allow_nan=False))

class LoggingHasTraits(HasTraits):
"""A parent class for HasTraits that log.
Subclasses have a log trait, and the default behavior
Expand Down Expand Up @@ -485,30 +514,6 @@ def _compare(self, a, b):
else:
return a == b

def _buffer_list_equal(self, a, b):
"""Compare two lists of buffers for equality.
Used to decide whether two sequences of buffers (memoryviews,
bytearrays, or python 3 bytes) differ, such that a sync is needed.
Returns True if equal, False if unequal
"""
if len(a) != len(b):
return False
if a == b:
return True
for ia, ib in zip(a, b):
# Check byte equality:
# NOTE: Simple ia != ib does not always work as intended, as
# e.g. memoryview(np.frombuffer(ia, dtype='float32')) !=
# memoryview(np.frombuffer(b)), since the format info differs.
# However, since we only transfer bytes, we use `tobytes()`.
ia_bytes = ia.tobytes() if isinstance(ia, memoryview) else ia
ib_bytes = ib.tobytes() if isinstance(ib, memoryview) else ib
if ia_bytes != ib_bytes:
return False
return True

def set_state(self, sync_data):
"""Called when a state is received from the front-end."""
# The order of these context managers is important. Properties must
Expand Down Expand Up @@ -628,9 +633,9 @@ def _should_send_property(self, key, value):
split_value = _remove_buffers({ key: to_json(value, self)})
split_lock = _remove_buffers({ key: self._property_lock[key]})
# Compare state and buffer_paths
if (jsonloads(jsondumps(split_value[0])) == jsonloads(jsondumps(split_lock[0]))
if (_json_equal(split_value[0], split_lock[0])
and split_value[1] == split_lock[1]
and self._buffer_list_equal(split_value[2], split_lock[2])):
and _buffer_list_equal(split_value[2], split_lock[2])):
return False
if self._holding_sync:
self._states_to_send.add(key)
Expand Down

0 comments on commit b9eb8c8

Please sign in to comment.