Skip to content

Commit ce741e1

Browse files
committed
Replace _IntFlag class with simple int constants
1 parent e45d3ab commit ce741e1

10 files changed

+59
-167
lines changed

.pylintrc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ ignore-patterns=
1919

2020
# Python code to execute, usually for sys.path manipulation such as
2121
# pygtk.require().
22-
init-hook='import sys;sys.path.insert(0,"./lib")'
22+
init-hook=
2323

2424
# Use multiple processes to speed up Pylint.
2525
jobs=1
@@ -159,7 +159,7 @@ ignored-classes=optparse.Values,thread._local,_thread._local
159159
# (useful for modules/projects where namespaces are manipulated during runtime
160160
# and thus existing member attributes cannot be deduced by static analysis. It
161161
# supports qualified module names, as well as Unix pattern matching.
162-
ignored-modules=board,socketpool
162+
ignored-modules=
163163

164164
# Show a hint with possible names when a member name was not found. The aspect
165165
# of finding the hint is based on edit distance.

adafruit_ntp.py

Lines changed: 16 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
except ImportError:
3838
pass
3939
try:
40-
from typing import Callable, Optional, Union, Tuple, Dict
40+
from typing import Callable, Optional, Tuple, Dict
4141
except ImportError:
4242
pass
4343
try:
@@ -73,79 +73,14 @@ def recv_into(
7373
PACKET_SIZE: int = 48
7474

7575

76-
# No enum module in CircuitPython, so implement simple version.
77-
class _IntFlag: # pylint:disable=too-few-public-methods
78-
"""Simple private implementation of an IntFlag enum.
79-
80-
This contains hard-coded limits specific to the EventType class."""
81-
82-
def __init__(self, value=0):
83-
if isinstance(value, _IntFlag):
84-
self.value = value.value
85-
else:
86-
self.value = value
87-
88-
def __and__(self, other):
89-
if isinstance(other, _IntFlag):
90-
return _IntFlag(self.value & other.value)
91-
return self.value & other
92-
93-
def __or__(self, other):
94-
if isinstance(other, _IntFlag):
95-
return _IntFlag(self.value | other.value)
96-
return self.value | other
97-
98-
def __xor__(self, other):
99-
if isinstance(other, _IntFlag):
100-
return _IntFlag(self.value ^ other.value)
101-
return self.value ^ other
102-
103-
def __add__(self, other):
104-
return self.__or__(other)
105-
106-
def __gt__(self, other):
107-
if isinstance(other, _IntFlag):
108-
return self.value > other.value
109-
return self.value > other
110-
111-
def __lt__(self, other):
112-
if isinstance(other, _IntFlag):
113-
return self.value < other.value
114-
return self.value < other
115-
116-
def __invert__(self):
117-
# This relies on ALL_EVENTS existing, and being set correctly.
118-
all_flags = EventType.ALL_EVENTS.value
119-
return _IntFlag(~self.value & all_flags)
120-
121-
def __repr__(self):
122-
return f"IntFlag({self.value})"
123-
124-
def __eq__(self, other):
125-
if isinstance(other, _IntFlag):
126-
return self.value == other.value
127-
return self.value == other
128-
129-
def __bool__(self):
130-
return bool(self.value)
131-
132-
@classmethod
133-
def from_value(cls, value):
134-
"""enum emulation"""
135-
return cls(value)
136-
137-
138-
class EventType(_IntFlag): # pylint:disable=too-few-public-methods
76+
class EventType: # pylint:disable=too-few-public-methods
13977
"""NTP callback notification Event Types."""
14078

141-
NO_EVENT = _IntFlag(0b000)
142-
SYNC_COMPLETE = _IntFlag(0b001)
143-
SYNC_FAILED = _IntFlag(0b010) # get packet failed
144-
LOOKUP_FAILED = _IntFlag(0b100) # getaddrinfo failed (not timedout?)
145-
# The custom _IntFlat class used uses the ALL_EVENTS attribute here to get a bit mask to use
146-
# when inverting flag values. Make sure to update this if changing how many event types are
147-
# being used.
148-
ALL_EVENTS = _IntFlag(0b111)
79+
NO_EVENT = const(0b000)
80+
SYNC_COMPLETE = const(0b001)
81+
SYNC_FAILED = const(0b010) # get packet failed
82+
LOOKUP_FAILED = const(0b100) # getaddrinfo failed (not timedout?)
83+
ALL_EVENTS = const(0b111)
14984

15085

15186
class NTPIncompleteError(TimeoutError):
@@ -247,7 +182,7 @@ def __init__( # pylint:disable=too-many-arguments
247182
self._next_sync: int = 0
248183
self._state: int = self.USING_CACHED_REFERENCE
249184
self._last_sync_time: int = 0 # Track the last successful sync time
250-
self._callbacks: Dict[Callable[[_IntFlag, int], None], _IntFlag] = {}
185+
self._callbacks: Dict[Callable[[int, int], None], int] = {}
251186
# The variables _next_rate_limit_end and _next_rate_limit_delay are intentionally
252187
# not initialized here because they are only used after calling
253188
# _initialize_backoff_timing(). Accessing them before initialization will raise an
@@ -317,7 +252,6 @@ def _update_time_sync(self) -> None:
317252
- GETTING_SOCKET: Perform DNS lookup for the NTP server.
318253
- GETTING_PACKET: Send NTP request and await response.
319254
"""
320-
self._notify_ntp_event_callbacks(EventType.NO_EVENT, -1)
321255
if self._state == self.USING_CACHED_REFERENCE:
322256
# Cached offset value expired, reinitialize backoff timing and proceed to DNS lookup.
323257
self._initialize_backoff_timing()
@@ -372,8 +306,8 @@ def _server_dns_lookup(self) -> None:
372306

373307
def register_ntp_event_callback(
374308
self,
375-
callback: Callable[[_IntFlag, int], None],
376-
event_types: Union[_IntFlag, int] = EventType.SYNC_COMPLETE,
309+
callback: Callable[[int, int], None],
310+
event_types: int = EventType.SYNC_COMPLETE,
377311
) -> None:
378312
"""
379313
Register a callback to be notified for specific NTP events.
@@ -424,23 +358,20 @@ def on_ntp_event(event_type: IntFlag, next_time: int) -> None:
424358
ntp.register_ntp_event_callback(on_ntp_event,
425359
EventType.SYNC_COMPLETE | EventType.SYNC_FAILED | EventType.LOOKUP_FAILED)
426360
"""
427-
if not isinstance(event_types, (_IntFlag, int)):
361+
if not isinstance(event_types, int):
428362
raise TypeError(f"{type(event_types)} is not compatible with event types")
429-
if not (
430-
(EventType.ALL_EVENTS | event_types) == EventType.ALL_EVENTS
431-
or event_types == EventType.NO_EVENT
363+
if (
364+
EventType.ALL_EVENTS | event_types != EventType.ALL_EVENTS
365+
or event_types == 0
432366
):
433-
events_value = (
434-
event_types if isinstance(event_types, int) else event_types.value
435-
)
436367
raise TypeError(
437-
f"Invalid event type mask 0b{events_value:b}. "
368+
f"Invalid event type mask 0b{event_types:b}. "
438369
"Only known events can receive notifications."
439370
)
440371
self._callbacks[callback] = event_types
441372

442373
def _notify_ntp_event_callbacks(
443-
self, event_type: _IntFlag, next_operation_time: int
374+
self, event_type: int, next_operation_time: int
444375
) -> None:
445376
"""
446377
Call all registered callbacks that are interested in the given event type.

examples/ntp_connection_manager.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
# determine which radio is available
1212
try:
13-
import wifi
13+
import wifi # type:ignore
1414
import os
1515

1616
# adjust method to get credentials as necessary...
@@ -22,7 +22,7 @@
2222
except ImportError:
2323
import board
2424
from digitalio import DigitalInOut
25-
from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K
25+
from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K # type:ignore
2626

2727
# adjust with busio.SPI() as necessary...
2828
spi = board.SPI()

examples/ntp_powersave.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
import os
77
import time
88

9-
import socketpool
10-
import wifi
9+
import socketpool # type:ignore
10+
import wifi # type:ignore
1111

1212
from adafruit_ntp import NTP, EventType, NTPIncompleteError
1313

examples/ntp_set_rtc.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
import os
77
import time
88

9-
import rtc
10-
import socketpool
11-
import wifi
9+
import rtc # type:ignore
10+
import socketpool # type:ignore
11+
import wifi # type:ignore
1212

1313
import adafruit_ntp
1414

examples/ntp_simpletest.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
import os
77
import time
88

9-
import socketpool
10-
import wifi
9+
import socketpool # type:ignore
10+
import wifi # type:ignore
1111

1212
import adafruit_ntp
1313

tests/mocks/mock_pool.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ def socket(
228228
return MockSocket(family, type, proto)
229229

230230
def set_getaddrinfo_responses(
231-
self, responses: List[Union[Exception, GetAddressInfoT]]
231+
self, responses: List[Union[Exception, GetAddressInfoT]] # type:ignore
232232
) -> None: # type:ignore
233233
"""Set a sequence of responses for the getaddrinfo method."""
234234
self.mock_getaddrinfo_attempts = responses
@@ -256,8 +256,7 @@ def __init__(self, context: str):
256256

257257
def mock_callback(self, event: int, delay: int) -> None:
258258
"""Log which callback was called, and the triggering event details"""
259-
event_num = event if isinstance(event, int) else event.value
260-
if event_num == 0:
259+
if event == 0:
261260
_logger.debug(MOCKED_CALLBACK_MSG % (self.context, event, delay))
262261
return
263262
_logger.info(MOCKED_CALLBACK_MSG % (self.context, event, delay))

tests/ntp_testing_support.py

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@
6666
from adafruit_ntp import ( # pylint:disable=wrong-import-position
6767
NTP,
6868
EventType,
69-
_IntFlag,
7069
NTPIncompleteError,
7170
) # pylint:disable=wrong-import-position
7271

@@ -120,7 +119,7 @@ def __init__(
120119
monotonic_start_ns: int = 0,
121120
next_sync: int = 0,
122121
last_sync_time: int = 0,
123-
callbacks: Dict[Callable[[_IntFlag, int], None], _IntFlag] = None,
122+
callbacks: Dict[Callable[[int, int], None], int] = None,
124123
state: int = 0,
125124
blocking: bool = True,
126125
monotonic_ns: int = 0, # mocked functionality internal state information
@@ -138,7 +137,7 @@ def __init__(
138137
self.monotonic_start_ns: int = monotonic_start_ns
139138
self.next_sync: int = next_sync
140139
self.last_sync_time: int = last_sync_time
141-
self.callbacks: Dict[Callable[[_IntFlag, int], None], _IntFlag] = (
140+
self.callbacks: Dict[Callable[[int, int], None], int] = (
142141
{} if callbacks is None else dict(callbacks)
143142
)
144143
self.state: int = state
@@ -865,19 +864,11 @@ def _changed_state_fields(
865864
# in memory.
866865
# All attempts to find a data structure and comparison code to match expected and actual
867866
# state dictionary callable instance have failed. An expected entry created from exactly
868-
# the same method as was registered still compares as not equal. The best can apparently
869-
# be done, is to make sure that the registered event masks are the same.
867+
# the same method as was registered still compares as not equal. The best that can
868+
# apparently be done, is to make sure that the registered event masks are the same.
870869
callback1 = getattr(state1, field)
871870
callback2 = getattr(state2, field)
872-
# Forcing all values to IntFlag instances seems easier than extracting the .value when the
873-
# dict value is an IntFlag. Need to get them to be consistent for the sorting (on
874-
# circuitpython) to work. Otherwise can get.
875-
# TypeError: unsupported types for __lt__: 'int', 'IntFlag'
876-
# Either cpython automatically reverse the comparison, or it happens to only compare IntFlag
877-
# to int, and not int to IntFlag.
878-
if sorted([_IntFlag(v) for v in callback1.values()]) != sorted(
879-
[_IntFlag(v) for v in callback2.values()]
880-
):
871+
if sorted(callback1.values()) != sorted(callback2.values()):
881872
changed_fields.add(field)
882873

883874
return changed_fields

tests/test_eventtype.py

Lines changed: 17 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,83 +9,63 @@
99

1010
import unittest
1111
from tests.shared_for_testing import mock_cleanup
12-
from adafruit_ntp import EventType, _IntFlag
12+
from adafruit_ntp import EventType
1313

1414

1515
class TestEventType(unittest.TestCase):
1616
"""Unittests for the EventType (emulated enum) class based on the custom IntFlag."""
1717

1818
def test_basic_event_types(self):
1919
"""Test basic event types and their values."""
20-
self.assertEqual(EventType.NO_EVENT.value, _IntFlag(0b000)) # 0
21-
self.assertEqual(EventType.SYNC_COMPLETE.value, _IntFlag(0b001)) # 1
22-
self.assertEqual(EventType.SYNC_FAILED.value, _IntFlag(0b010)) # 2
23-
self.assertEqual(EventType.LOOKUP_FAILED.value, _IntFlag(0b100)) # 4
24-
self.assertEqual(EventType.ALL_EVENTS.value, _IntFlag(0b111)) # 7
20+
self.assertEqual(EventType.NO_EVENT, 0b000) # 0
21+
self.assertEqual(EventType.SYNC_COMPLETE, 0b001) # 1
22+
self.assertEqual(EventType.SYNC_FAILED, 0b010) # 2
23+
self.assertEqual(EventType.LOOKUP_FAILED, 0b100) # 4
24+
self.assertEqual(EventType.ALL_EVENTS, 0b111) # 7
2525

2626
def test_event_combination(self):
2727
"""Test bitwise OR combination of event types."""
2828
combined_event = EventType.SYNC_COMPLETE | EventType.LOOKUP_FAILED
29-
self.assertEqual(combined_event, _IntFlag(0b101)) # 1 | 4 = 5
30-
self.assertEqual(combined_event.value, 0b101) # 1 | 4 = 5
29+
self.assertEqual(combined_event, 0b101) # 1 | 4 = 5
3130

3231
combined_event = EventType.SYNC_COMPLETE | EventType.SYNC_FAILED
33-
self.assertEqual(combined_event, _IntFlag(0b011)) # 1 | 2 = 3
34-
self.assertEqual(combined_event.value, 0b011) # 1 | 2 = 3
32+
self.assertEqual(combined_event, 0b011) # 1 | 2 = 3
3533

3634
combined_event = EventType.SYNC_FAILED | EventType.LOOKUP_FAILED
37-
self.assertEqual(combined_event, _IntFlag(0b110)) # 2 | 4 = 6
38-
self.assertEqual(combined_event.value, 0b110) # 2 | 4 = 6
35+
self.assertEqual(combined_event, 0b110) # 2 | 4 = 6
3936

4037
def test_event_intersection(self):
4138
"""Test bitwise AND intersection of event types."""
4239
combined_event = EventType.SYNC_COMPLETE | EventType.LOOKUP_FAILED
4340
intersect_event = combined_event & EventType.SYNC_COMPLETE
44-
self.assertEqual(intersect_event.value, 0b001) # 5 & 1 = 1
41+
self.assertEqual(intersect_event, 0b001) # 5 & 1 = 1
4542

4643
intersect_event = combined_event & EventType.SYNC_FAILED
47-
self.assertEqual(intersect_event.value, 0b000) # 5 & 2 = 0
44+
self.assertEqual(intersect_event, 0b000) # 5 & 2 = 0
4845

4946
def test_event_complement(self):
5047
"""Test bitwise NOT complement of event types."""
51-
complement_event = ~EventType.SYNC_COMPLETE
48+
complement_event = (~EventType.SYNC_COMPLETE) & 0b111
5249
# Expected to be the bitwise complement of SYNC_COMPLETE within the range of all flags
53-
expected_value = 2 | EventType.SYNC_FAILED.value | EventType.LOOKUP_FAILED.value
54-
self.assertEqual(complement_event.value, expected_value)
55-
self.assertNotEqual(complement_event.value, EventType.SYNC_COMPLETE.value)
50+
expected_value = 2 | EventType.SYNC_FAILED | EventType.LOOKUP_FAILED
51+
self.assertEqual(complement_event, expected_value)
52+
self.assertNotEqual(complement_event, EventType.SYNC_COMPLETE)
5653
self.assertEqual(complement_event & EventType.SYNC_COMPLETE, 0b000)
5754
self.assertEqual(complement_event & EventType.SYNC_COMPLETE, EventType.NO_EVENT)
58-
self.assertEqual(~EventType.NO_EVENT, 0b111)
59-
self.assertEqual(~EventType.NO_EVENT, EventType.ALL_EVENTS)
6055

6156
def test_event_equality(self):
6257
"""Test equality between event types."""
6358
event1 = EventType.SYNC_COMPLETE
6459
event2 = EventType.SYNC_COMPLETE
6560
self.assertEqual(event1, event2)
6661

67-
combined_event = EventType.SYNC_COMPLETE | EventType.LOOKUP_FAILED
68-
self.assertNotEqual(event1, combined_event)
69-
70-
self.assertEqual(EventType.SYNC_COMPLETE, EventType.SYNC_COMPLETE.value)
71-
self.assertEqual(EventType.SYNC_COMPLETE, _IntFlag(0b001))
72-
self.assertEqual(EventType.SYNC_COMPLETE, 0b001)
73-
self.assertNotEqual(EventType.SYNC_COMPLETE, EventType.SYNC_FAILED)
74-
self.assertNotEqual(EventType.SYNC_COMPLETE, EventType.NO_EVENT)
75-
self.assertNotEqual(EventType.SYNC_COMPLETE, EventType.LOOKUP_FAILED)
76-
self.assertNotEqual(EventType.SYNC_COMPLETE, EventType.ALL_EVENTS)
77-
self.assertNotEqual(EventType.SYNC_COMPLETE, _IntFlag(0b000))
78-
self.assertNotEqual(EventType.SYNC_COMPLETE, 0b000)
79-
self.assertNotEqual(EventType.SYNC_COMPLETE, _IntFlag(0b010))
80-
self.assertNotEqual(EventType.SYNC_COMPLETE, 0b010)
81-
8262
def test_event_xor(self):
8363
"""Test bitwise XOR operation of event types."""
8464
xor_event = EventType.SYNC_COMPLETE ^ EventType.SYNC_FAILED
85-
self.assertEqual(xor_event.value, 0b011) # 1 ^ 2 = 3
65+
self.assertEqual(xor_event, 0b011) # 1 ^ 2 = 3
8666

8767
xor_event = EventType.SYNC_COMPLETE ^ EventType.SYNC_COMPLETE
88-
self.assertEqual(xor_event.value, 0b000) # 1 ^ 1 = 0
68+
self.assertEqual(xor_event, 0b000) # 1 ^ 1 = 0
8969

9070
def test_event_to_bool(self):
9171
"""Test conversion of event types to bool."""

0 commit comments

Comments
 (0)