addons.View: better order reversal

Deal with some subtleties in order reversal, add a toggle method that emits
refresh.
This commit is contained in:
Aldo Cortesi 2016-10-29 10:55:57 +13:00
parent 12a70d03ad
commit 90e7142b5c
2 changed files with 55 additions and 16 deletions

View File

@ -34,7 +34,7 @@ class View(collections.Sequence):
self._store = {} self._store = {}
self.filter = matchall self.filter = matchall
self.order_key = key_request_start self.order_key = key_request_start
self.order_reverse = False self.order_reversed = False
self._view = sortedcontainers.SortedListWithKey(key = self.order_key) self._view = sortedcontainers.SortedListWithKey(key = self.order_key)
# These signals broadcast events that affect the view. That is, an # These signals broadcast events that affect the view. That is, an
@ -46,13 +46,42 @@ class View(collections.Sequence):
# Signals that the view should be refreshed completely # Signals that the view should be refreshed completely
self.sig_refresh = blinker.Signal() self.sig_refresh = blinker.Signal()
def _rev(self, idx: int) -> int:
"""
Reverses an index, if needed
"""
if self.order_reversed:
if idx < 0:
idx = -idx - 1
else:
idx = len(self._view) - idx - 1
if idx < 0:
raise IndexError
return idx
def __len__(self): def __len__(self):
return len(self._view) return len(self._view)
def __getitem__(self, offset) -> flow.Flow: def __getitem__(self, offset) -> flow.Flow:
if self.order_reverse: return self._view[self._rev(offset)]
offset = -offset - 1
return self._view[offset] # Reflect some methods to the efficient underlying implementation
def bisect(self, f: flow.Flow) -> int:
v = self._view.bisect(f)
# Bisect returns an item to the RIGHT of the existing entries.
if v == 0:
return v
return self._rev(v - 1) + 1
def index(self, f: flow.Flow) -> int:
return self._rev(self._view.index(f))
# API
def toggle_reversed(self):
self.order_reversed = not self.order_reversed
self.sig_refresh.send(self)
def set_order(self, order_key: typing.Callable): def set_order(self, order_key: typing.Callable):
""" """
@ -122,14 +151,8 @@ class View(collections.Sequence):
# The value was not in the view # The value was not in the view
pass pass
# Reflect some methods to the efficient underlying implementation
def bisect(self, f: flow.Flow) -> int:
return self._view.bisect(f)
def index(self, f: flow.Flow) -> int:
return self._view.index(f)
# Event handlers # Event handlers
def request(self, f): def request(self, f):
self.add(f) self.add(f)
@ -186,8 +209,7 @@ class Focus:
def _sig_refresh(self, view): def _sig_refresh(self, view):
if len(view) == 0: if len(view) == 0:
self.focusflow = None self.focusflow = None
else: elif self.focusflow not in view:
if self.focusflow not in view:
self.focusflow = view[0] self.focusflow = view[0]
def _sig_add(self, view, flow): def _sig_add(self, view, flow):

View File

@ -62,16 +62,33 @@ def test_order():
v.set_order(view.key_request_method) v.set_order(view.key_request_method)
assert [i.request.method for i in v] == ["GET", "GET", "PUT", "PUT"] assert [i.request.method for i in v] == ["GET", "GET", "PUT", "PUT"]
v.order_reverse = True v.toggle_reversed()
assert [i.request.method for i in v] == ["PUT", "PUT", "GET", "GET"] assert [i.request.method for i in v] == ["PUT", "PUT", "GET", "GET"]
v.set_order(view.key_request_start) v.set_order(view.key_request_start)
assert [i.request.timestamp_start for i in v] == [4, 3, 2, 1] assert [i.request.timestamp_start for i in v] == [4, 3, 2, 1]
v.order_reverse = False v.toggle_reversed()
assert [i.request.timestamp_start for i in v] == [1, 2, 3, 4] assert [i.request.timestamp_start for i in v] == [1, 2, 3, 4]
def test_reversed():
v = view.View()
v.request(tft(start=1))
v.request(tft(start=2))
v.request(tft(start=3))
v.toggle_reversed()
assert v[0].request.timestamp_start == 3
assert v[-1].request.timestamp_start == 1
assert v[2].request.timestamp_start == 1
tutils.raises(IndexError, v.__getitem__, 5)
tutils.raises(IndexError, v.__getitem__, -5)
assert v.bisect(v[0]) == 1
assert v.bisect(v[2]) == 3
def test_update(): def test_update():
v = view.View() v = view.View()
flt = flowfilter.parse("~m get") flt = flowfilter.parse("~m get")