diff --git a/oec/controller.py b/oec/controller.py index bf4bed2..4493053 100644 --- a/oec/controller.py +++ b/oec/controller.py @@ -5,7 +5,7 @@ oec.controller import time import logging -from select import select +import selectors from coax import poll, poll_ack, load_control_register, get_features, PollAction, \ KeystrokePollResponse, TerminalType, ReceiveTimeout, \ ReceiveError, ProtocolError @@ -29,6 +29,8 @@ class Controller: self.terminal = None self.session = None + self.session_selector = None + # Target time between POLL commands in seconds when a terminal is connected or # no terminal is connected. # @@ -45,11 +47,17 @@ class Controller: """Run the controller.""" self.running = True + self.session_selector = selectors.DefaultSelector() + while self.running: self._run_loop() self._terminate_session() + self.session_selector.close() + + self.session_selector = None + if self.terminal: self.terminal = None @@ -139,7 +147,7 @@ class Controller: def _handle_session_disconnected(self): self.logger.info('Session disconnected') - self.session = None + self._terminate_session() # Restart the session. self._start_session() @@ -149,10 +157,14 @@ class Controller: self.session.start() + self.session_selector.register(self.session, selectors.EVENT_READ) + def _terminate_session(self): if not self.session: return + self.session_selector.unregister(self.session) + self.session.terminate() self.session = None @@ -161,10 +173,15 @@ class Controller: while duration > 0: start_time = time.perf_counter() - if self.session not in select([self.session], [], [], duration)[0]: + selected = self.session_selector.select(duration) + + if not selected: break - self.session.handle_host() + for (key, events) in selected: + session = key.fileobj + + session.handle_host() duration -= (time.perf_counter() - start_time) diff --git a/requirements.txt b/requirements.txt index 0ca5fa2..d1e561c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ ptyprocess==0.7.0 pycoax==0.6.0 pyserial==3.5 pyte==0.8.0 -pytn3270==0.9.0 +pytn3270==0.9.1 sliplib==0.6.2 sortedcontainers==2.3.0 wcwidth==0.2.5 diff --git a/tests/test_controller.py b/tests/test_controller.py index 7080eb0..62fdf2d 100644 --- a/tests/test_controller.py +++ b/tests/test_controller.py @@ -1,3 +1,4 @@ +import selectors import unittest from unittest.mock import Mock, PropertyMock, patch from coax import PollAction, PowerOnResetCompletePollResponse, KeystrokePollResponse, ReceiveTimeout @@ -26,6 +27,8 @@ class RunLoopTestCase(unittest.TestCase): self.controller.connected_poll_period = 1 + self.controller.session_selector = Mock() + self.controller._update_session = Mock() patcher = patch('oec.controller.poll') @@ -257,14 +260,12 @@ class UpdateSessionTestCase(unittest.TestCase): self.controller.session = Mock() + self.controller.session_selector = Mock() + patcher = patch('oec.controller.time.perf_counter') self.perf_counter_mock = patcher.start() - patcher = patch('oec.controller.select') - - self.select_mock = patcher.start() - def test_zero_duration(self): # Act self.controller._update_session(0) @@ -272,11 +273,11 @@ class UpdateSessionTestCase(unittest.TestCase): # Assert self.controller.session.handle_host.assert_not_called() - self.select_mock.assert_not_called() + self.controller.session_selector.select.assert_not_called() def test_select_timeout(self): # Arrange - self.select_mock.return_value = ([], [], []) + self.controller.session_selector.select.return_value = [] # Act self.controller._update_session(1) @@ -284,13 +285,15 @@ class UpdateSessionTestCase(unittest.TestCase): # Assert self.controller.session.handle_host.assert_not_called() - self.select_mock.assert_called_once() + self.controller.session_selector.select.assert_called_once() def test_select_available(self): # Arrange self.perf_counter_mock.side_effect = [0, 0.75, 0.75] - self.select_mock.side_effect = [([self.controller.session], [], []), ([], [], [])] + selector_key = Mock(fileobj=self.controller.session) + + self.controller.session_selector.select.side_effect = [[(selector_key, selectors.EVENT_READ)], []] # Act self.controller._update_session(1) @@ -298,7 +301,9 @@ class UpdateSessionTestCase(unittest.TestCase): # Assert self.controller.session.handle_host.assert_called_once() - self.assertEqual(self.select_mock.call_count, 2) + self.assertEqual(self.controller.session_selector.select.call_count, 2) - self.assertEqual(self.select_mock.call_args_list[0][0][3], 1) - self.assertEqual(self.select_mock.call_args_list[1][0][3], 0.25) + call_args_list = self.controller.session_selector.select.call_args_list + + self.assertEqual(call_args_list[0][0][0], 1) + self.assertEqual(call_args_list[1][0][0], 0.25)