-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathmisc.py
More file actions
177 lines (136 loc) · 4.63 KB
/
misc.py
File metadata and controls
177 lines (136 loc) · 4.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# Handy functions that don't really fit anywhere else
import time
_unique_time = 0
def unique_time():
"""
Return a unique timestamp
"""
global _unique_time
t = int(time.time())
if t <= _unique_time:
t = _unique_time + 1
_unique_time = t
return t
def pathstr(path, flood=False):
"""
Convenience function to convert a path bytearray/bytes object to a printable string
None = Flood
[] = Direct, or 0-hop if flood=True
[ ... ] = hex path, comma separated
"""
if path is None:
return "Flood"
if len(path) == 0:
return "0-hop" if flood else "Direct"
return ",".join( (f"{p:02x}" for p in path) )
def pad(data, length):
"""
Zero-pad the data to the length specified
data should be a bytes-like object or str, which will be converted to bytes
"""
if isinstance(data, str):
b = data.encode()
else:
b = data
l = len(b)
if l>=length:
return b
return b + bytes(length - l)
# Break a string up into chunks of max_size bytes, trying to split on word boundaries, and not
# splitting in the middle of a multi-byte UTF-8 character
def split_unicode_string(s, max_size):
"""
Split a unicode string into utf-8 byte strings, each no longer than max_size.
Splits on word boundaries where possible.
"""
if not isinstance(s, str):
raise ValueError("Input must be a unicode string")
utf8_bytes = s.encode('utf-8')
chunks = []
start = 0
while start < len(utf8_bytes):
end = start + max_size
if end >= len(utf8_bytes):
chunks.append(utf8_bytes[start:])
break
# Adjust the boundary to avoid splitting in the middle of a character
boundary = end
while boundary > start and (utf8_bytes[boundary] & 0xC0) == 0x80:
boundary -= 1
# Find the last space within the valid boundary
space_boundary = utf8_bytes[start:boundary].rfind(b' ')
if space_boundary != -1:
boundary = start + space_boundary
chunks.append(utf8_bytes[start:boundary])
start = boundary + 1 # Move past the space
return chunks
# Validate a latitude and longitude
# Must be a number between -180 and 180 for lon, and -90 to 90 for lat
# Returns a tuple of (lat, lon) as floats, or raises ValueError
def validate_latlon(lat, lon):
try:
lat = float(lat)
lon = float(lon)
except ValueError:
raise ValueError("Latitude and longitude must be numbers")
if lat < -90 or lat > 90:
raise ValueError("Latitude must be between -90 and 90")
if lon < -180 or lon > 180:
raise ValueError("Longitude must be between -180 and 180")
return (lat, lon)
# Subclass of list which calls a callback when modified
class CallbackList(list):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._callback = None
self._callback_args = ()
def set_callback(self, callback, *args):
"""
Set the callback function to be called when the list is modified
The callback function will be called with the list as the first argument,
followed by any additional args specified here
"""
self._callback = callback
self._callback_args = args
def _trigger_callback(self):
if self._callback:
self._callback(self, *self._callback_args)
def append(self, item):
super().append(item)
self._trigger_callback()
def extend(self, iterable):
super().extend(iterable)
self._trigger_callback()
def insert(self, index, item):
super().insert(index, item)
self._trigger_callback()
def remove(self, item):
super().remove(item)
self._trigger_callback()
def pop(self, index=-1):
item = super().pop(index)
self._trigger_callback()
return item
def clear(self):
super().clear()
self._trigger_callback()
def __iadd__(self, value):
newlist = super().__iadd__(value)
self._trigger_callback()
return newlist
def __imul__(self, value):
newlist = super().__imul__(value)
self._trigger_callback()
return newlist
def __setitem__(self, index, value):
super().__setitem__(index, value)
self._trigger_callback()
def __delitem__(self, index):
super().__delitem__(index)
self._trigger_callback()
def sort(self, *args, **kwargs):
super().sort(*args, **kwargs)
self._trigger_callback()
def reverse(self):
super().reverse()
self._trigger_callback()