Skip to content

Commit cbcc4dc

Browse files
committed
Include initial and remaining time in deadline string representation
Add initial timeout and remaining time when converting a deadline to an string. Also, simplify deadline state adding a pointer to the timeout that was used to create it.
1 parent 4f74eba commit cbcc4dc

File tree

5 files changed

+43
-48
lines changed

5 files changed

+43
-48
lines changed

maxcube/commander.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def get_unsolicited_messages(self) -> List[Message]:
4545
return result
4646

4747
def update(self) -> List[Message]:
48-
deadline = UPDATE_TIMEOUT.deadline()
48+
deadline = Deadline(UPDATE_TIMEOUT)
4949
if self.__is_connected():
5050
try:
5151
response = self.__call(L_MSG, deadline)
@@ -60,7 +60,7 @@ def update(self) -> List[Message]:
6060
return self.get_unsolicited_messages()
6161

6262
def send_radio_msg(self, hex_radio_msg: str) -> bool:
63-
deadline = SEND_RADIO_MSG_TIMEOUT.deadline()
63+
deadline = Deadline(SEND_RADIO_MSG_TIMEOUT)
6464
request = Message(
6565
"s", base64.b64encode(bytearray.fromhex(hex_radio_msg)).decode("utf-8")
6666
)

maxcube/deadline.py

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,35 +8,32 @@ class Timeout:
88
name: str
99
duration: float
1010

11-
def deadline(self):
12-
return Deadline(self.name, time() + self.duration)
13-
1411

1512
class Deadline:
16-
def __init__(self, name: str, deadline: float, *, parent=None):
17-
self.__name = name
18-
self.__deadline = deadline
13+
def __init__(self, timeout: Timeout, *, parent=None):
14+
if parent is None:
15+
self.__deadline = time() + timeout.duration
16+
else:
17+
self.__deadline = min(time() + timeout.duration, parent.__deadline)
18+
self.__timeout = timeout
1919
self.__parent = parent
2020

21+
def name(self) -> str:
22+
return f"{self.__timeout.name}[{self.remaining():.3g}/{self.__timeout.duration:.3g}]"
23+
2124
def fullname(self) -> str:
22-
if not self.__parent:
23-
return self.__name
24-
return self.__parent.fullname() + ":" + self.__name
25+
if self.__parent is None:
26+
return self.name()
27+
return self.__parent.fullname() + ":" + self.name()
2528

2629
def remaining(self, *, lower_bound: float = 0, upper_bound: float = inf) -> float:
2730
return min(max(lower_bound, self.__deadline - time()), upper_bound)
2831

2932
def is_expired(self) -> bool:
3033
return self.remaining() <= 0
3134

32-
def subdeadline(self, name, deadline: float) -> "Deadline":
33-
return Deadline(name, min(self.__deadline, deadline), parent=self)
34-
3535
def subtimeout(self, timeout: Timeout) -> "Deadline":
36-
return self.subdeadline(timeout.name, time() + timeout.duration)
36+
return Deadline(timeout, parent=self)
3737

3838
def __str__(self) -> str:
39-
rem = self.remaining(lower_bound=-inf)
40-
if rem < 0:
41-
return "Deadline %s expired %.03f seconds ago" % (self.fullname(), -rem)
42-
return "Deadline %s will expire in %.03f seconds" % (self.fullname(), rem)
39+
return "Deadline " + self.fullname()

tests/test_commander.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ def testSendRadioMsgClosesConnectionOnErrorAndRetriesIfReusingConnection(
139139
newConnection.close.assert_not_called()
140140

141141
def __send_radio_msg(self, msg: Message, deadline: Deadline):
142-
with patch("maxcube.commander.Timeout.deadline") as deadlineMock:
142+
with patch("maxcube.commander.Deadline") as deadlineMock:
143143
deadlineMock.return_value = deadline
144144
return self.commander.send_radio_msg(msg)
145145

@@ -150,7 +150,7 @@ def testSendRadioMsgShouldNotRetryOnErrorWhenConnectionIsNew(self, ClassMock):
150150

151151
deadline = MagicMock(Deadline)
152152
deadline.is_expired.side_effect = [False, True]
153-
deadline.subtimeout.return_value = TEST_TIMEOUT.deadline()
153+
deadline.subtimeout.return_value = Deadline(TEST_TIMEOUT)
154154

155155
self.assertFalse(self.__send_radio_msg(S_CMD_HEX, deadline))
156156

@@ -164,7 +164,7 @@ def testSendRadioMsgFailsOnLogicalError(self, ClassMock):
164164

165165
deadline: Deadline = MagicMock(Deadline)
166166
deadline.is_expired.side_effect = [False, True]
167-
deadline.subtimeout.return_value = TEST_TIMEOUT.deadline()
167+
deadline.subtimeout.return_value = Deadline(TEST_TIMEOUT)
168168
self.assertFalse(self.__send_radio_msg(S_CMD_HEX, deadline))
169169

170170
self.connection.send.assert_called_once_with(S_CMD)
@@ -179,7 +179,7 @@ def testSendRadioMsgRetriesOnThrottlingError(self, ClassMock):
179179
deadline: Deadline = MagicMock(Deadline)
180180
deadline.is_expired.side_effect = [False, True]
181181
deadline.remaining.return_value = 0.1
182-
deadline.subtimeout.return_value = TEST_TIMEOUT.deadline()
182+
deadline.subtimeout.return_value = Deadline(TEST_TIMEOUT)
183183
self.assertFalse(self.__send_radio_msg(S_CMD_HEX, deadline))
184184

185185
self.connection.send.assert_called_once_with(S_CMD)

tests/test_connection.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from unittest.mock import patch
44

55
from maxcube.connection import Connection
6-
from maxcube.deadline import Timeout
6+
from maxcube.deadline import Deadline, Timeout
77
from maxcube.message import Message
88

99
TEST_TIMEOUT = Timeout("test", 1000.0)
@@ -24,7 +24,7 @@ def testReadAMessage(self, socketMock):
2424
self.socket.recv.return_value = b"A:B\r\n"
2525

2626
self.assertEqual(
27-
Message("A", "B"), self.connection.recv(TEST_TIMEOUT.deadline())
27+
Message("A", "B"), self.connection.recv(Deadline(TEST_TIMEOUT))
2828
)
2929
self.socket.close.assert_not_called()
3030

@@ -33,33 +33,31 @@ def testReadPartialLine(self, socketMock):
3333
self.socket.recv.side_effect = [b"A:", b"B\r\n"]
3434

3535
self.assertEqual(
36-
Message("A", "B"), self.connection.recv(TEST_TIMEOUT.deadline())
36+
Message("A", "B"), self.connection.recv(Deadline(TEST_TIMEOUT))
3737
)
3838

3939
def testReadMultipleLines(self, socketMock):
4040
self.connect(socketMock)
4141
self.socket.recv.return_value = b"A:B\r\nC\r\n"
4242

4343
self.assertEqual(
44-
Message("A", "B"), self.connection.recv(TEST_TIMEOUT.deadline())
44+
Message("A", "B"), self.connection.recv(Deadline(TEST_TIMEOUT))
4545
)
4646
self.socket.recv.reset_mock()
47-
self.assertEqual(
48-
Message("C", ""), self.connection.recv(TEST_TIMEOUT.deadline())
49-
)
47+
self.assertEqual(Message("C", ""), self.connection.recv(Deadline(TEST_TIMEOUT)))
5048

5149
def testReadAtConnectionClosing(self, socketMock):
5250
self.connect(socketMock)
5351
self.socket.recv.return_value = b""
5452

55-
self.assertIsNone(self.connection.recv(TEST_TIMEOUT.deadline()))
53+
self.assertIsNone(self.connection.recv(Deadline(TEST_TIMEOUT)))
5654
self.socket.close.assert_called_once()
5755

5856
def testReadTimeout(self, socketMock):
5957
self.connect(socketMock)
6058
self.socket.recv.side_effect = [socket.timeout]
6159

62-
self.assertIsNone(self.connection.recv(TEST_TIMEOUT.deadline()))
60+
self.assertIsNone(self.connection.recv(Deadline(TEST_TIMEOUT)))
6361
self.socket.close.assert_not_called()
6462

6563
def testSendMessage(self, socketMock):

tests/test_deadline.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from unittest import TestCase
22
from unittest.mock import patch
33

4-
from maxcube.deadline import Timeout
4+
from maxcube.deadline import Deadline, Timeout
55

66
ZERO_TIMEOUT = Timeout("zero", 0)
77
ONE_TIMEOUT = Timeout("one", 1)
@@ -14,48 +14,48 @@ class TestDeadline(TestCase):
1414

1515
def testDeadlineLowerBoundForRemainingTime(self, timeMock):
1616
timeMock.side_effect = [0, 0.4, 0.6]
17-
deadline = ONE_TIMEOUT.deadline()
17+
deadline = Deadline(ONE_TIMEOUT)
1818
self.assertAlmostEqual(0.6, deadline.remaining(lower_bound=0.50))
1919
self.assertAlmostEqual(0.5, deadline.remaining(lower_bound=0.50))
2020

2121
def testDeadlineUpperBoundForRemainingTime(self, timeMock):
2222
timeMock.side_effect = [0, 0.4, 0.6]
23-
deadline = ONE_TIMEOUT.deadline()
23+
deadline = Deadline(ONE_TIMEOUT)
2424
self.assertAlmostEqual(0.5, deadline.remaining(upper_bound=0.50))
2525
self.assertAlmostEqual(0.4, deadline.remaining(upper_bound=0.50))
2626

2727
def testDeadlineIsExpired(self, timeMock):
2828
timeMock.side_effect = [0, 0.4, 1.0, 1.000001]
29-
deadline = ONE_TIMEOUT.deadline()
29+
deadline = Deadline(ONE_TIMEOUT)
3030
self.assertFalse(deadline.is_expired())
3131
self.assertTrue(deadline.is_expired())
3232
self.assertTrue(deadline.is_expired())
3333

3434
def testZeroDeadlineIsAlreadyExpired(self, timeMock):
35-
timeMock.side_effect = [0.0, 0.0]
36-
deadline = ZERO_TIMEOUT.deadline()
37-
self.assertEqual("zero", deadline.fullname())
35+
timeMock.return_value = 0.0
36+
deadline = Deadline(ZERO_TIMEOUT)
37+
self.assertEqual("zero[0/0]", deadline.fullname())
3838
self.assertTrue(deadline.is_expired())
3939

4040
def testSubtimeoutHandling(self, timeMock):
41-
timeMock.side_effect = [0.0, 0.1, 0.2]
42-
deadline = ROOT_TIMEOUT.deadline()
41+
timeMock.side_effect = [0.0, 0.1, 0.2, 0.2, 0.3]
42+
deadline = Deadline(ROOT_TIMEOUT)
4343
subdeadline = deadline.subtimeout(ONE_TIMEOUT)
44-
self.assertEqual("root:one", subdeadline.fullname())
45-
self.assertAlmostEqual(0.9, subdeadline.remaining())
44+
self.assertEqual("root[9.8/10]:one[0.9/1]", subdeadline.fullname())
45+
self.assertAlmostEqual(0.8, subdeadline.remaining())
4646

4747
def testSubtimeoutHandlingWhenLargerThanTimeout(self, timeMock):
4848
timeMock.side_effect = [0.0, 9.1, 9.2]
49-
deadline = ROOT_TIMEOUT.deadline()
49+
deadline = Deadline(ROOT_TIMEOUT)
5050
subdeadline = deadline.subtimeout(ONE_TIMEOUT)
5151
self.assertAlmostEqual(0.8, subdeadline.remaining())
5252

5353
def testToString(self, timeMock):
5454
timeMock.side_effect = [0.0, 0.1]
55-
deadline = ONE_TIMEOUT.deadline()
56-
self.assertEqual("Deadline one will expire in 0.900 seconds", str(deadline))
55+
deadline = Deadline(ONE_TIMEOUT)
56+
self.assertEqual("Deadline one[0.9/1]", str(deadline))
5757

5858
def testToStringForExpiredDeadline(self, timeMock):
5959
timeMock.side_effect = [0.0, 1.1]
60-
deadline = ONE_TIMEOUT.deadline()
61-
self.assertEqual("Deadline one expired 0.100 seconds ago", str(deadline))
60+
deadline = Deadline(ONE_TIMEOUT)
61+
self.assertEqual("Deadline one[0/1]", str(deadline))

0 commit comments

Comments
 (0)