Skip to content

Commit

Permalink
Defer resize handling when when threaded
Browse files Browse the repository at this point in the history
  • Loading branch information
avylove committed Nov 26, 2020
1 parent d555721 commit 82a4ec2
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 13 deletions.
1 change: 1 addition & 0 deletions doc/spelling_wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ iterable
iterables
natively
programmatically
resize
resizing
stdout
stderr
Expand Down
23 changes: 17 additions & 6 deletions enlighten/_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from collections import OrderedDict
import signal
import sys
import threading
import time

from enlighten._counter import Counter
Expand All @@ -36,6 +37,8 @@ class Manager(object):
below. (Default: :py:data:`None`)
enabled(bool): Status (Default: True)
no_resize(bool): Disable resizing support
threaded(bool): When True resize handling is deferred until next write (Default: False
unless multiple threads are detected)
kwargs(Dict[str, Any]): Any additional :py:term:`keyword arguments<keyword argument>`
will be used as default values when :py:meth:`counter` is called.
Expand Down Expand Up @@ -70,6 +73,7 @@ def __init__(self, stream=None, counter_class=Counter, **kwargs):
self.enabled = kwargs.get('enabled', True) # Double duty for counters
self.no_resize = kwargs.pop('no_resize', False)
self.set_scroll = kwargs.pop('set_scroll', True)
self.threaded = kwargs.pop('threaded', threading.active_count() > 1)
self.term = Terminal(stream=self.stream)

# Set up companion stream
Expand Down Expand Up @@ -264,9 +268,14 @@ def _stage_resize(self, *args, **kwarg): # pylint: disable=unused-argument
# Set semaphore to trigger resize on next write
self._resize = True

# Reset update time to avoid any delay in resize
for counter in self.counters:
counter.last_update = 0
if self.threaded:
# Reset update time to avoid any delay in resize
for counter in self.counters:
counter.last_update = 0

else:
# If not threaded, handle resize now
self._resize_handler()

def _resize_handler(self):
"""
Expand All @@ -290,10 +299,12 @@ def _resize_handler(self):
if newHeight < self.height:
term.move_to(0, max(0, newHeight - self.scroll_offset))
self.stream.write(u'\n' * (2 * max(self.counters.values())))
elif newHeight > self.height and self.threaded:
term.move_to(0, newHeight)
self.stream.write(u'\n' * (self.scroll_offset - 1))

if newWidth < self.width:
term.move_to(0, max(0, newHeight - self.scroll_offset))
self.stream.write(term.clear_eos)
term.move_to(0, max(0, newHeight - self.scroll_offset))
self.stream.write(term.clear_eos)

self.width = newWidth
self._set_scroll_area(force=True)
Expand Down
41 changes: 34 additions & 7 deletions tests/test_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -583,7 +583,7 @@ def test_stop_position_1(self):
manager.stop()
self.assertTrue(termfeed.called)

def test_resize_handler(self):
def test_resize(self):
"""
Resize lock must be False for handler to run
Terminal size is cached unless resize handler runs
Expand All @@ -600,7 +600,7 @@ def test_resize_handler(self):

manager.resize_lock = True
with mock.patch('enlighten._manager.Manager._set_scroll_area') as ssa:
manager._resize_handler()
manager._stage_resize()
self.assertFalse(ssa.called)

self.assertEqual(manager.width, 80)
Expand All @@ -624,11 +624,11 @@ def test_resize_handler(self):

self.assertEqual(counter3.calls, ['refresh(flush=False, elapsed=None)'])

def test_resize(self):
def test_resize_threaded(self):
"""
Test a resize event
Test a resize event threading behavior
"""
manager = _manager.Manager(stream=self.tty.stdout, counter_class=MockCounter)
manager = _manager.Manager(stream=self.tty.stdout, counter_class=MockCounter, threaded=True)
counter3 = MockCounter(manager=manager)
counter3.last_update = time.time()
manager.counters[counter3] = 3
Expand Down Expand Up @@ -658,10 +658,10 @@ def test_resize(self):
self.assertFalse(manager._resize)
self.assertEqual(counter3.calls, ['refresh(flush=False, elapsed=None)'])

def test_resize_handler_height_only(self):
def test_resize_handler_height_less(self):

with mock.patch('%s.height' % TERMINAL, new_callable=mock.PropertyMock) as mockheight:
mockheight.side_effect = [25, 23, 28, 20, 20]
mockheight.side_effect = [25, 23]

manager = _manager.Manager(stream=self.tty.stdout, counter_class=MockCounter)
counter3 = MockCounter(manager=manager)
Expand All @@ -681,6 +681,33 @@ def test_resize_handler_height_only(self):

self.assertEqual(counter3.calls, ['refresh(flush=False, elapsed=None)'])

def test_resize_handler_height_greater_threaded(self):

with mock.patch('%s.height' % TERMINAL, new_callable=mock.PropertyMock) as mockheight:
mockheight.side_effect = [25, 27]

manager = _manager.Manager(stream=self.tty.stdout, counter_class=MockCounter,
threaded=True)
counter3 = MockCounter(manager=manager)
manager.counters[counter3] = 3
manager.scroll_offset = 4
term = manager.term

with mock.patch('enlighten._manager.Manager._set_scroll_area') as ssa:
manager._resize_handler()
self.assertEqual(ssa.call_count, 1)

# Height is set in _set_scroll_area which is mocked
self.assertEqual(manager.height, 25)

self.tty.stdout.write(u'X\n')
self.assertEqual(self.tty.stdread.readline(), term.move(27, 0) + '\n')
self.assertEqual(self.tty.stdread.readline(), '\n')
self.assertEqual(self.tty.stdread.readline(), '\n')
self.assertEqual(self.tty.stdread.readline(), term.move(23, 0) + term.clear_eos + 'X\n')

self.assertEqual(counter3.calls, ['refresh(flush=False, elapsed=None)'])

def test_disable(self):
mgr = _manager.Manager(stream=self.tty.stdout, enabled=False)
self.assertFalse(mgr.enabled)
Expand Down

0 comments on commit 82a4ec2

Please sign in to comment.