8
8
9
9
import struct
10
10
11
+ try :
12
+ from typing import Dict , Any , Union , List , Optional , Type , Literal , TypeVar
13
+ from _bleio import ScanEntry
11
14
12
- def to_hex (seq ):
15
+ LazyObjectField_GivenClass = TypeVar ("LazyObjectField_GivenClass" )
16
+
17
+ except ImportError :
18
+ pass
19
+
20
+
21
+ def to_hex (seq : bytes ) -> str :
13
22
"""Pretty prints a byte sequence as hex values."""
14
23
return " " .join ("{:02x}" .format (v ) for v in seq )
15
24
16
25
17
- def to_bytes_literal (seq ) :
26
+ def to_bytes_literal (seq : bytes ) -> str :
18
27
"""Prints a byte sequence as a Python bytes literal that only uses hex encoding."""
19
28
return 'b"' + "" .join ("\\ x{:02x}" .format (v ) for v in seq ) + '"'
20
29
21
30
22
- def decode_data (data , * , key_encoding = "B" ):
31
+ def decode_data (data : bytes , * , key_encoding : str = "B" ) -> Dict [ Any , Union [ bytes , List [ bytes ]]] :
23
32
"""Helper which decodes length encoded structures into a dictionary with the given key
24
33
encoding."""
25
34
i = 0
@@ -42,7 +51,7 @@ def decode_data(data, *, key_encoding="B"):
42
51
return data_dict
43
52
44
53
45
- def compute_length (data_dict , * , key_encoding = "B" ):
54
+ def compute_length (data_dict : Dict [ Any , Union [ bytes , List [ bytes ]]], * , key_encoding : str = "B" ) -> int :
46
55
"""Computes the length of the encoded data dictionary."""
47
56
value_size = 0
48
57
for value in data_dict .values ():
@@ -54,7 +63,7 @@ def compute_length(data_dict, *, key_encoding="B"):
54
63
return len (data_dict ) + len (data_dict ) * struct .calcsize (key_encoding ) + value_size
55
64
56
65
57
- def encode_data (data_dict , * , key_encoding = "B" ):
66
+ def encode_data (data_dict : Dict [ Any , Union [ bytes , List [ bytes ]]], * , key_encoding : str = "B" ) -> bytes :
58
67
"""Helper which encodes dictionaries into length encoded structures with the given key
59
68
encoding."""
60
69
length = compute_length (data_dict , key_encoding = key_encoding )
@@ -72,25 +81,23 @@ def encode_data(data_dict, *, key_encoding="B"):
72
81
return bytes (data )
73
82
74
83
84
+ # pylint: disable=too-few-public-methods
75
85
class AdvertisingDataField :
76
86
"""Top level class for any descriptor classes that live in Advertisement or its subclasses."""
77
87
78
- # pylint: disable=too-few-public-methods,unnecessary-pass
79
- pass
80
-
81
88
82
89
class AdvertisingFlag :
83
90
"""A single bit flag within an AdvertisingFlags object."""
84
91
85
- def __init__ (self , bit_position ) :
92
+ def __init__ (self , bit_position : int ) -> None :
86
93
self ._bitmask = 1 << bit_position
87
94
88
- def __get__ (self , obj , cls ) :
95
+ def __get__ (self , obj : Optional [ "AdvertisingFlags" ] , cls : Type [ "AdvertisingFlags" ]) -> Union [ bool , "AdvertisingFlag" ] :
89
96
if obj is None :
90
97
return self
91
98
return (obj .flags & self ._bitmask ) != 0
92
99
93
- def __set__ (self , obj , value ) :
100
+ def __set__ (self , obj : "AdvertisingFlags" , value : bool ) -> None :
94
101
if value :
95
102
obj .flags |= self ._bitmask
96
103
else :
@@ -108,20 +115,20 @@ class AdvertisingFlags(AdvertisingDataField):
108
115
"""BR/EDR not supported."""
109
116
# BR/EDR flags not included here, since we don't support BR/EDR.
110
117
111
- def __init__ (self , advertisement , advertising_data_type ) :
118
+ def __init__ (self , advertisement : "Advertisement" , advertising_data_type : int ) -> None :
112
119
self ._advertisement = advertisement
113
120
self ._adt = advertising_data_type
114
121
self .flags = 0
115
122
if self ._adt in self ._advertisement .data_dict :
116
123
self .flags = self ._advertisement .data_dict [self ._adt ][0 ]
117
124
118
- def __len__ (self ):
125
+ def __len__ (self ) -> Literal [ 1 ] :
119
126
return 1
120
127
121
- def __bytes__ (self ):
128
+ def __bytes__ (self ) -> bytes :
122
129
return bytes ([self .flags ])
123
130
124
- def __str__ (self ):
131
+ def __str__ (self ) -> str :
125
132
parts = []
126
133
for attr in dir (self .__class__ ):
127
134
attribute_instance = getattr (self .__class__ , attr )
@@ -136,48 +143,48 @@ class String(AdvertisingDataField):
136
143
137
144
Not null terminated once encoded because length is always transmitted."""
138
145
139
- def __init__ (self , * , advertising_data_type ) :
146
+ def __init__ (self , * , advertising_data_type : int ) -> None :
140
147
self ._adt = advertising_data_type
141
148
142
- def __get__ (self , obj , cls ) :
149
+ def __get__ (self , obj : Optional [ "Advertisement" ] , cls : Type [ "Advertisement" ]) -> Optional [ Union [ str , "String" ]] :
143
150
if obj is None :
144
151
return self
145
152
if self ._adt not in obj .data_dict :
146
153
return None
147
154
return str (obj .data_dict [self ._adt ], "utf-8" )
148
155
149
- def __set__ (self , obj , value ) :
156
+ def __set__ (self , obj : "Advertisement" , value : str ) -> None :
150
157
obj .data_dict [self ._adt ] = value .encode ("utf-8" )
151
158
152
159
153
160
class Struct (AdvertisingDataField ):
154
161
"""`struct` encoded data in an Advertisement."""
155
162
156
- def __init__ (self , struct_format , * , advertising_data_type ) :
163
+ def __init__ (self , struct_format : str , * , advertising_data_type : int ) -> None :
157
164
self ._format = struct_format
158
165
self ._adt = advertising_data_type
159
166
160
- def __get__ (self , obj , cls ) :
167
+ def __get__ (self , obj : Optional [ "Advertisement" ] , cls : Type [ "Advertisement" ]) -> Optional [ Union [ Any , "Struct" ]] :
161
168
if obj is None :
162
169
return self
163
170
if self ._adt not in obj .data_dict :
164
171
return None
165
172
return struct .unpack (self ._format , obj .data_dict [self ._adt ])[0 ]
166
173
167
- def __set__ (self , obj , value ) :
174
+ def __set__ (self , obj : "Advertisement" , value : Any ) -> None :
168
175
obj .data_dict [self ._adt ] = struct .pack (self ._format , value )
169
176
170
177
171
178
class LazyObjectField (AdvertisingDataField ):
172
179
"""Non-data descriptor useful for lazily binding a complex object to an advertisement object."""
173
180
174
- def __init__ (self , cls , attribute_name , * , advertising_data_type , ** kwargs ):
181
+ def __init__ (self , cls : Any , attribute_name : str , * , advertising_data_type : int , ** kwargs ) -> None :
175
182
self ._cls = cls
176
183
self ._attribute_name = attribute_name
177
184
self ._adt = advertising_data_type
178
185
self ._kwargs = kwargs
179
186
180
- def __get__ (self , obj , cls ) :
187
+ def __get__ (self , obj : Optional [ "Advertisement" ] , cls : Type [ "Advertisement" ]) -> Any :
181
188
if obj is None :
182
189
return self
183
190
# Return None if our object is immutable and the data is not present.
@@ -190,7 +197,7 @@ def __get__(self, obj, cls):
190
197
return bound_obj
191
198
192
199
@property
193
- def advertising_data_type (self ):
200
+ def advertising_data_type (self ) -> int :
194
201
"""Return the data type value used to indicate this field."""
195
202
return self ._adt
196
203
@@ -237,7 +244,7 @@ class Advertisement:
237
244
# MAX_LEGACY_DATA_SIZE = 31
238
245
# """Data size in a regular BLE packet."""
239
246
240
- def __init__ (self , * , entry = None ):
247
+ def __init__ (self , * , entry : Optional [ ScanEntry ] = None ) -> None :
241
248
"""Create an empty advertising packet or one from a ScanEntry."""
242
249
if entry :
243
250
self .data_dict = decode_data (entry .advertisement_bytes )
@@ -255,13 +262,13 @@ def __init__(self, *, entry=None):
255
262
self .scan_response = False
256
263
257
264
@property
258
- def rssi (self ):
265
+ def rssi (self ) -> Optional [ int ] :
259
266
"""Signal strength of the scanned advertisement. Only available on Advertisements returned
260
267
from `BLERadio.start_scan()`. (read-only)"""
261
268
return self ._rssi
262
269
263
270
@classmethod
264
- def get_prefix_bytes (cls ):
271
+ def get_prefix_bytes (cls ) -> Optional [ bytes ] :
265
272
"""Return a merged version of match_prefixes as a single bytes object,
266
273
with length headers.
267
274
"""
@@ -281,15 +288,15 @@ def get_prefix_bytes(cls):
281
288
return cls ._prefix_bytes
282
289
283
290
@classmethod
284
- def matches (cls , entry ) :
291
+ def matches (cls , entry : ScanEntry ) -> bool :
285
292
"""Returns ``True`` if the given `_bleio.ScanEntry` advertisement fields
286
293
matches all of the given prefixes in the `match_prefixes` tuple attribute.
287
294
Subclasses may override this to match any instead of all.
288
295
"""
289
296
return cls .matches_prefixes (entry , all_ = True )
290
297
291
298
@classmethod
292
- def matches_prefixes (cls , entry , * , all_ ) :
299
+ def matches_prefixes (cls , entry : ScanEntry , * , all_ : bool ) -> bool :
293
300
"""Returns ``True`` if the given `_bleio.ScanEntry` advertisement fields
294
301
match any or all of the given prefixes in the `match_prefixes` tuple attribute.
295
302
If ``all_`` is ``True``, all the prefixes must match. If ``all_`` is ``False``,
@@ -298,16 +305,16 @@ def matches_prefixes(cls, entry, *, all_):
298
305
# Returns True if cls.get_prefix_bytes() is empty.
299
306
return entry .matches (cls .get_prefix_bytes (), all = all_ )
300
307
301
- def __bytes__ (self ):
308
+ def __bytes__ (self ) -> bytes :
302
309
"""The raw packet bytes."""
303
310
return encode_data (self .data_dict )
304
311
305
- def __eq__ (self , other ):
312
+ def __eq__ (self , other ) -> bool :
306
313
if isinstance (other , Advertisement ):
307
314
return self .data_dict == other .data_dict
308
315
return False
309
316
310
- def __str__ (self ):
317
+ def __str__ (self ) -> str :
311
318
parts = []
312
319
for attr in dir (self .__class__ ):
313
320
attribute_instance = getattr (self .__class__ , attr )
@@ -324,10 +331,10 @@ def __str__(self):
324
331
parts .append ("{}={}" .format (attr , str (value )))
325
332
return "<{} {} >" .format (self .__class__ .__name__ , " " .join (parts ))
326
333
327
- def __len__ (self ):
334
+ def __len__ (self ) -> int :
328
335
return compute_length (self .data_dict )
329
336
330
- def __repr__ (self ):
337
+ def __repr__ (self ) -> str :
331
338
return "Advertisement(data={})" .format (
332
339
to_bytes_literal (encode_data (self .data_dict ))
333
340
)
0 commit comments