yostlabs 2025.10.24__tar.gz → 2025.10.29__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {yostlabs-2025.10.24 → yostlabs-2025.10.29}/PKG-INFO +2 -1
- {yostlabs-2025.10.24 → yostlabs-2025.10.29}/pyproject.toml +3 -2
- {yostlabs-2025.10.24 → yostlabs-2025.10.29}/src/yostlabs/communication/ble.py +12 -11
- yostlabs-2025.10.29/src/yostlabs/communication/bluetooth.py +203 -0
- {yostlabs-2025.10.24 → yostlabs-2025.10.29}/src/yostlabs/communication/serial.py +3 -1
- yostlabs-2025.10.29/src/yostlabs/communication/socket.py +118 -0
- {yostlabs-2025.10.24 → yostlabs-2025.10.29}/.gitignore +0 -0
- {yostlabs-2025.10.24 → yostlabs-2025.10.29}/Examples/embedded_2024_dec_20.xml +0 -0
- {yostlabs-2025.10.24 → yostlabs-2025.10.29}/Examples/example_ble.py +0 -0
- {yostlabs-2025.10.24 → yostlabs-2025.10.29}/Examples/example_commands.py +0 -0
- {yostlabs-2025.10.24 → yostlabs-2025.10.29}/Examples/example_component_specific_settings_and_commands.py +0 -0
- {yostlabs-2025.10.24 → yostlabs-2025.10.29}/Examples/example_firmware_upload.py +0 -0
- {yostlabs-2025.10.24 → yostlabs-2025.10.29}/Examples/example_parsing_stored_binary.py +0 -0
- {yostlabs-2025.10.24 → yostlabs-2025.10.29}/Examples/example_read_settings.py +0 -0
- {yostlabs-2025.10.24 → yostlabs-2025.10.29}/Examples/example_streaming.py +0 -0
- {yostlabs-2025.10.24 → yostlabs-2025.10.29}/Examples/example_streaming_manager.py +0 -0
- {yostlabs-2025.10.24 → yostlabs-2025.10.29}/Examples/example_write_settings.py +0 -0
- {yostlabs-2025.10.24 → yostlabs-2025.10.29}/LICENSE +0 -0
- {yostlabs-2025.10.24 → yostlabs-2025.10.29}/README.md +0 -0
- {yostlabs-2025.10.24 → yostlabs-2025.10.29}/src/yostlabs/__init__.py +0 -0
- {yostlabs-2025.10.24 → yostlabs-2025.10.29}/src/yostlabs/communication/__init__.py +0 -0
- {yostlabs-2025.10.24 → yostlabs-2025.10.29}/src/yostlabs/communication/base.py +0 -0
- {yostlabs-2025.10.24 → yostlabs-2025.10.29}/src/yostlabs/math/__init__.py +0 -0
- {yostlabs-2025.10.24 → yostlabs-2025.10.29}/src/yostlabs/math/quaternion.py +0 -0
- {yostlabs-2025.10.24 → yostlabs-2025.10.29}/src/yostlabs/math/vector.py +0 -0
- {yostlabs-2025.10.24 → yostlabs-2025.10.29}/src/yostlabs/tss3/__init__.py +0 -0
- {yostlabs-2025.10.24 → yostlabs-2025.10.29}/src/yostlabs/tss3/api.py +0 -0
- {yostlabs-2025.10.24 → yostlabs-2025.10.29}/src/yostlabs/tss3/consts.py +0 -0
- {yostlabs-2025.10.24 → yostlabs-2025.10.29}/src/yostlabs/tss3/eepts.py +0 -0
- {yostlabs-2025.10.24 → yostlabs-2025.10.29}/src/yostlabs/tss3/utils/__init__.py +0 -0
- {yostlabs-2025.10.24 → yostlabs-2025.10.29}/src/yostlabs/tss3/utils/calibration.py +0 -0
- {yostlabs-2025.10.24 → yostlabs-2025.10.29}/src/yostlabs/tss3/utils/parser.py +0 -0
- {yostlabs-2025.10.24 → yostlabs-2025.10.29}/src/yostlabs/tss3/utils/streaming.py +0 -0
- {yostlabs-2025.10.24 → yostlabs-2025.10.29}/src/yostlabs/tss3/utils/version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: yostlabs
|
|
3
|
-
Version: 2025.10.
|
|
3
|
+
Version: 2025.10.29
|
|
4
4
|
Summary: Python resources and API for 3Space sensors from Yost Labs Inc.
|
|
5
5
|
Project-URL: Homepage, https://yostlabs.com/
|
|
6
6
|
Project-URL: Repository, https://github.com/YostLabs/3SpacePythonPackage/tree/main
|
|
@@ -16,6 +16,7 @@ Requires-Python: >=3.10
|
|
|
16
16
|
Requires-Dist: async-timeout
|
|
17
17
|
Requires-Dist: bleak
|
|
18
18
|
Requires-Dist: numpy
|
|
19
|
+
Requires-Dist: pybluez2
|
|
19
20
|
Requires-Dist: pyserial
|
|
20
21
|
Description-Content-Type: text/markdown
|
|
21
22
|
|
|
@@ -5,7 +5,7 @@ build-backend = "hatchling.build"
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "yostlabs"
|
|
7
7
|
#If uploading again on the same day, add a fourth number
|
|
8
|
-
version = "2025.10.
|
|
8
|
+
version = "2025.10.29"
|
|
9
9
|
authors = [
|
|
10
10
|
{ name="Yost Labs Inc.", email="techsupport@yostlabs.com" },
|
|
11
11
|
{ name="Andy Riedlinger", email="techsupport@yostlabs.com" },
|
|
@@ -24,7 +24,8 @@ dependencies = [
|
|
|
24
24
|
"pyserial",
|
|
25
25
|
"numpy",
|
|
26
26
|
"bleak",
|
|
27
|
-
"async-timeout"
|
|
27
|
+
"async-timeout",
|
|
28
|
+
"pybluez2"
|
|
28
29
|
]
|
|
29
30
|
|
|
30
31
|
[project.urls]
|
|
@@ -53,7 +53,7 @@ class ThreespaceBLEComClass(ThreespaceComClass):
|
|
|
53
53
|
cls.EVENT_LOOP_THREAD = threading.Thread(target=ylBleEventLoopThread, args=(cls.EVENT_LOOP,), daemon=True)
|
|
54
54
|
cls.EVENT_LOOP_THREAD.start()
|
|
55
55
|
|
|
56
|
-
def __init__(self, ble: BleakClient | BLEDevice | str, discover_name: bool = True, discovery_timeout=5, error_on_disconnect=True, adv: AdvertisementData = None):
|
|
56
|
+
def __init__(self, ble: BleakClient | BLEDevice | str, discover_name: bool = True, discovery_timeout=5, error_on_disconnect=True, adv: AdvertisementData = None, profile: ThreespaceBLENordicUartProfile=None):
|
|
57
57
|
"""
|
|
58
58
|
Parameters
|
|
59
59
|
----------
|
|
@@ -88,17 +88,18 @@ class ThreespaceBLEComClass(ThreespaceComClass):
|
|
|
88
88
|
raise TypeError("Invalid type for creating a ThreespaceBLEComClass:", type(ble), ble)
|
|
89
89
|
|
|
90
90
|
#Select the profile
|
|
91
|
-
self.profile =
|
|
92
|
-
if self.
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
91
|
+
self.profile = profile
|
|
92
|
+
if self.profile is None:
|
|
93
|
+
if self.adv is not None and len(self.adv.service_uuids) > 0:
|
|
94
|
+
for service_uuid in self.adv.service_uuids:
|
|
95
|
+
self.profile = self.get_profile(service_uuid)
|
|
96
|
+
if self.profile is not None:
|
|
97
|
+
break
|
|
98
|
+
if self.profile is None:
|
|
99
|
+
self.profile = ThreespaceBLEComClass.DEFAULT_PROFILE
|
|
100
|
+
raise Exception(f"Unknown Service UUIDS: {self.adv.service_uuids}")
|
|
101
|
+
else:
|
|
98
102
|
self.profile = ThreespaceBLEComClass.DEFAULT_PROFILE
|
|
99
|
-
raise Exception(f"Unknown Service UUIDS: {self.adv.service_uuids}")
|
|
100
|
-
else:
|
|
101
|
-
self.profile = ThreespaceBLEComClass.DEFAULT_PROFILE
|
|
102
103
|
|
|
103
104
|
self.__timeout = self.DEFAULT_TIMEOUT
|
|
104
105
|
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
from yostlabs.communication.socket import ThreespaceSocketComClass
|
|
2
|
+
import bluetooth
|
|
3
|
+
import time
|
|
4
|
+
import threading
|
|
5
|
+
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import Callable, Generator
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class COD:
|
|
11
|
+
raw: int
|
|
12
|
+
services: list[str]
|
|
13
|
+
major_class: str
|
|
14
|
+
minor_class: str
|
|
15
|
+
|
|
16
|
+
def decode_class_of_device(cod: int) -> dict:
|
|
17
|
+
"""Decode a Bluetooth Class of Device (CoD) integer into its components."""
|
|
18
|
+
|
|
19
|
+
# Service Class bit masks (11 bits total)
|
|
20
|
+
services = {
|
|
21
|
+
0x002000: "Limited Discoverable Mode",
|
|
22
|
+
0x004000: "Positioning (Location Identification)",
|
|
23
|
+
0x008000: "Networking",
|
|
24
|
+
0x010000: "Rendering",
|
|
25
|
+
0x020000: "Capturing",
|
|
26
|
+
0x040000: "Object Transfer",
|
|
27
|
+
0x080000: "Audio",
|
|
28
|
+
0x100000: "Telephony",
|
|
29
|
+
0x200000: "Information",
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# Major Device Classes (5 bits)
|
|
33
|
+
major_classes = {
|
|
34
|
+
0x00: "Miscellaneous",
|
|
35
|
+
0x01: "Computer",
|
|
36
|
+
0x02: "Phone",
|
|
37
|
+
0x03: "LAN/Network Access Point",
|
|
38
|
+
0x04: "Audio/Video",
|
|
39
|
+
0x05: "Peripheral",
|
|
40
|
+
0x06: "Imaging",
|
|
41
|
+
0x07: "Wearable",
|
|
42
|
+
0x08: "Toy",
|
|
43
|
+
0x09: "Health",
|
|
44
|
+
0x1F: "Uncategorized",
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
# Minor Class mappings for some major classes (for simplicity)
|
|
48
|
+
minor_classes = {
|
|
49
|
+
0x01: {
|
|
50
|
+
0x00: "Uncategorized",
|
|
51
|
+
0x01: "Desktop workstation",
|
|
52
|
+
0x02: "Server-class computer",
|
|
53
|
+
0x03: "Laptop",
|
|
54
|
+
0x04: "Handheld PC/PDA",
|
|
55
|
+
0x05: "Palm-size PC/PDA",
|
|
56
|
+
},
|
|
57
|
+
0x02: {
|
|
58
|
+
0x00: "Uncategorized",
|
|
59
|
+
0x01: "Cellular",
|
|
60
|
+
0x02: "Cordless",
|
|
61
|
+
0x03: "Smartphone",
|
|
62
|
+
0x04: "Wired modem or voice gateway",
|
|
63
|
+
0x05: "Common ISDN access",
|
|
64
|
+
},
|
|
65
|
+
0x05: {
|
|
66
|
+
0x00: "Uncategorized",
|
|
67
|
+
0x01: "Keyboard",
|
|
68
|
+
0x02: "Pointing device",
|
|
69
|
+
0x03: "Combo keyboard/pointing device",
|
|
70
|
+
},
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# Extract fields
|
|
74
|
+
service_bits = cod & 0xFFE000 # top 11 bits
|
|
75
|
+
major_class = (cod >> 8) & 0x1F
|
|
76
|
+
minor_class = (cod >> 2) & 0x3F
|
|
77
|
+
|
|
78
|
+
# Decode services
|
|
79
|
+
decoded_services = [name for bit, name in services.items() if service_bits & bit]
|
|
80
|
+
|
|
81
|
+
# Decode major class
|
|
82
|
+
major_name = major_classes.get(major_class, "Unknown")
|
|
83
|
+
|
|
84
|
+
# Decode minor class (context-dependent)
|
|
85
|
+
minor_name = minor_classes.get(major_class, {}).get(minor_class, f"Minor code {minor_class}")
|
|
86
|
+
|
|
87
|
+
return COD(cod, decoded_services or ["None"], major_name, minor_name)
|
|
88
|
+
|
|
89
|
+
@dataclass
|
|
90
|
+
class ScannerResult:
|
|
91
|
+
address: str
|
|
92
|
+
name: str
|
|
93
|
+
class_of_device: COD
|
|
94
|
+
|
|
95
|
+
class Scanner:
|
|
96
|
+
|
|
97
|
+
def __init__(self, interval=5):
|
|
98
|
+
self.enabled = False
|
|
99
|
+
self.done = True
|
|
100
|
+
|
|
101
|
+
self.nearby = None
|
|
102
|
+
self.thread = None
|
|
103
|
+
self.updated = False
|
|
104
|
+
self.duration = int(interval / 1.2)
|
|
105
|
+
|
|
106
|
+
def start(self):
|
|
107
|
+
if not self.done: return
|
|
108
|
+
self.thread = threading.Thread(target=self.process, daemon=True)
|
|
109
|
+
self.enabled = True
|
|
110
|
+
self.done = False
|
|
111
|
+
self.updated = False
|
|
112
|
+
self.thread.start()
|
|
113
|
+
|
|
114
|
+
def stop(self):
|
|
115
|
+
self.enabled = False
|
|
116
|
+
|
|
117
|
+
def get_most_recent(self):
|
|
118
|
+
if not self.updated: return None
|
|
119
|
+
self.updated = False
|
|
120
|
+
return self.nearby
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def is_running(self):
|
|
124
|
+
return self.done
|
|
125
|
+
|
|
126
|
+
def process(self):
|
|
127
|
+
while self.enabled:
|
|
128
|
+
nearby = bluetooth.discover_devices(duration=self.duration, lookup_names=True, lookup_class=True)
|
|
129
|
+
self.nearby = [ScannerResult(addr, name, decode_class_of_device(cod)) for addr, name, cod in nearby]
|
|
130
|
+
self.updated = True
|
|
131
|
+
self.done = True
|
|
132
|
+
|
|
133
|
+
class ThreespaceBluetoothComClass(ThreespaceSocketComClass):
|
|
134
|
+
|
|
135
|
+
SCANNER = None
|
|
136
|
+
|
|
137
|
+
def __init__(self, addr: str, name: str = None, connection_timeout=None):
|
|
138
|
+
super().__init__(bluetooth.BluetoothSocket(bluetooth.Protocols.RFCOMM), (addr, 1), connection_timeout=connection_timeout)
|
|
139
|
+
self.address = addr
|
|
140
|
+
self.__name = name or addr
|
|
141
|
+
|
|
142
|
+
@property
|
|
143
|
+
def name(self) -> str:
|
|
144
|
+
return self.__name
|
|
145
|
+
|
|
146
|
+
@classmethod
|
|
147
|
+
def __lazy_init_scanner(cls):
|
|
148
|
+
if cls.SCANNER is None:
|
|
149
|
+
cls.SCANNER = Scanner()
|
|
150
|
+
cls.SCANNER.start()
|
|
151
|
+
|
|
152
|
+
@staticmethod
|
|
153
|
+
def __default_filter(result: ScannerResult):
|
|
154
|
+
return result.class_of_device.major_class == "Wearable" and result.class_of_device.minor_class == "Minor code 0"
|
|
155
|
+
|
|
156
|
+
@staticmethod
|
|
157
|
+
def auto_detect(wait_for_update=True, filter: Callable[[ScannerResult],bool] = None) -> Generator["ThreespaceBluetoothComClass", None, None]:
|
|
158
|
+
"""
|
|
159
|
+
Returns a list of com classes of the same type called on nearby
|
|
160
|
+
"""
|
|
161
|
+
cls = ThreespaceBluetoothComClass
|
|
162
|
+
cls.__lazy_init_scanner()
|
|
163
|
+
if filter is None:
|
|
164
|
+
filter = cls.__default_filter
|
|
165
|
+
if wait_for_update:
|
|
166
|
+
cls.SCANNER.updated = False
|
|
167
|
+
while not cls.SCANNER.updated: time.sleep(0.1)
|
|
168
|
+
if cls.SCANNER.nearby is None: return
|
|
169
|
+
for device_info in cls.SCANNER.nearby:
|
|
170
|
+
if not filter(device_info): continue
|
|
171
|
+
name = device_info.name or None
|
|
172
|
+
yield ThreespaceBluetoothComClass(device_info.address, name)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
if __name__ == "__main__":
|
|
176
|
+
from yostlabs.tss3.api import ThreespaceSensor
|
|
177
|
+
|
|
178
|
+
com = None
|
|
179
|
+
for device in ThreespaceBluetoothComClass.auto_detect():
|
|
180
|
+
com = device
|
|
181
|
+
|
|
182
|
+
if com is None:
|
|
183
|
+
print("Failed to detect a bluetooth sensor")
|
|
184
|
+
exit()
|
|
185
|
+
print("Connecting to:", com.name)
|
|
186
|
+
|
|
187
|
+
sensor = ThreespaceSensor(com, verbose=True)
|
|
188
|
+
|
|
189
|
+
sensor.set_settings(stream_slots=0)
|
|
190
|
+
print(sensor.get_settings("stream_slots", "stream_hz"))
|
|
191
|
+
print(sensor.getTaredOrientation())
|
|
192
|
+
|
|
193
|
+
sensor.startStreaming()
|
|
194
|
+
|
|
195
|
+
start_time = time.perf_counter()
|
|
196
|
+
while time.perf_counter() - start_time < 5:
|
|
197
|
+
sensor.updateStreaming()
|
|
198
|
+
packet = sensor.getOldestStreamingPacket()
|
|
199
|
+
while packet is not None:
|
|
200
|
+
print(packet)
|
|
201
|
+
packet = sensor.getOldestStreamingPacket()
|
|
202
|
+
sensor.stopStreaming()
|
|
203
|
+
sensor.cleanup()
|
|
@@ -12,11 +12,13 @@ class ThreespaceSerialComClass(ThreespaceComClass):
|
|
|
12
12
|
PID_BOOTLOADER = 0x1000
|
|
13
13
|
PID_EMBED = 0x3040
|
|
14
14
|
PID_DL = 0x3050
|
|
15
|
+
PID_LX = 0x3090
|
|
15
16
|
|
|
16
17
|
PID_TO_STR_DICT = {
|
|
17
18
|
PID_EMBED: "EM",
|
|
18
19
|
PID_DL: "DL",
|
|
19
|
-
PID_BOOTLOADER: "BOOT"
|
|
20
|
+
PID_BOOTLOADER: "BOOT",
|
|
21
|
+
PID_LX: "LX"
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
from yostlabs.communication.base import *
|
|
2
|
+
import socket
|
|
3
|
+
import time
|
|
4
|
+
from typing import Callable
|
|
5
|
+
|
|
6
|
+
class ThreespaceSocketComClass(ThreespaceComClass):
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
Inheriting classes must implement auto_detect() functionality.
|
|
10
|
+
Should also implement 'name' functionality
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
def __init__(self, sock: socket.socket, *connection_params, connection_timeout=None):
|
|
14
|
+
self.socket = sock
|
|
15
|
+
self._opened = False
|
|
16
|
+
|
|
17
|
+
self.buffer = bytearray()
|
|
18
|
+
self.__timeout = 2
|
|
19
|
+
self.__connection_timeout = connection_timeout
|
|
20
|
+
self.__connection_params = connection_params
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def timeout(self) -> float:
|
|
24
|
+
return self.__timeout
|
|
25
|
+
|
|
26
|
+
@timeout.setter
|
|
27
|
+
def timeout(self, timeout: float):
|
|
28
|
+
self.__timeout = timeout
|
|
29
|
+
|
|
30
|
+
def open(self):
|
|
31
|
+
if self._opened: return True
|
|
32
|
+
try:
|
|
33
|
+
if self.__connection_timeout is None:
|
|
34
|
+
self.socket.setblocking(True)
|
|
35
|
+
else:
|
|
36
|
+
self.socket.settimeout(self.__connection_timeout)
|
|
37
|
+
self.socket.connect(*self.__connection_params)
|
|
38
|
+
except Exception as e:
|
|
39
|
+
try:
|
|
40
|
+
self.socket.close()
|
|
41
|
+
except: pass
|
|
42
|
+
print(e)
|
|
43
|
+
return False
|
|
44
|
+
self._opened = True
|
|
45
|
+
return True
|
|
46
|
+
|
|
47
|
+
def close(self):
|
|
48
|
+
if not self._opened: return
|
|
49
|
+
self.socket.close()
|
|
50
|
+
self._opened = False
|
|
51
|
+
|
|
52
|
+
def check_open(self):
|
|
53
|
+
return self._opened
|
|
54
|
+
|
|
55
|
+
def write(self, bytes: bytes):
|
|
56
|
+
self.socket.send(bytes)
|
|
57
|
+
|
|
58
|
+
def read(self, num_bytes: int):
|
|
59
|
+
self.__update_while(lambda: len(self.buffer) < num_bytes)
|
|
60
|
+
amount = min(len(self.buffer), num_bytes)
|
|
61
|
+
result = self.buffer[:amount]
|
|
62
|
+
del self.buffer[:amount]
|
|
63
|
+
return result
|
|
64
|
+
|
|
65
|
+
def peek(self, num_bytes: int):
|
|
66
|
+
self.__update_while(lambda: len(self.buffer) < num_bytes)
|
|
67
|
+
amount = min(len(self.buffer), num_bytes)
|
|
68
|
+
return self.buffer[:amount]
|
|
69
|
+
|
|
70
|
+
def read_until(self, expected: bytes):
|
|
71
|
+
self.__update_while(lambda: expected not in self.buffer)
|
|
72
|
+
if expected in self.buffer:
|
|
73
|
+
length = self.buffer.index(expected) + len(expected)
|
|
74
|
+
result = self.buffer[:length]
|
|
75
|
+
del self.buffer[:length]
|
|
76
|
+
else:
|
|
77
|
+
result = self.buffer.copy()
|
|
78
|
+
self.buffer.clear()
|
|
79
|
+
return result
|
|
80
|
+
|
|
81
|
+
def peek_until(self, expected: bytes, max_length: int = None):
|
|
82
|
+
self.__update_while(lambda: expected not in self.buffer and (max_length is None or len(self.buffer) < max_length))
|
|
83
|
+
if expected in self.buffer:
|
|
84
|
+
length = self.buffer.index(expected) + len(expected)
|
|
85
|
+
if max_length is not None:
|
|
86
|
+
length = min(length, max_length)
|
|
87
|
+
result = self.buffer[:length]
|
|
88
|
+
else:
|
|
89
|
+
length = len(self.buffer)
|
|
90
|
+
if max_length is not None:
|
|
91
|
+
length = min(length, max_length)
|
|
92
|
+
result = self.buffer[length]
|
|
93
|
+
return result
|
|
94
|
+
|
|
95
|
+
def __update_while(self, condition: Callable):
|
|
96
|
+
start_time = time.perf_counter()
|
|
97
|
+
while condition():
|
|
98
|
+
self.__update_buffer()
|
|
99
|
+
if time.perf_counter() - start_time >= self.timeout:
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
def __update_buffer(self, max_bytes: int = 1000):
|
|
103
|
+
self.socket.setblocking(False)
|
|
104
|
+
try:
|
|
105
|
+
self.buffer += self.socket.recv(max_bytes)
|
|
106
|
+
except BlockingIOError:
|
|
107
|
+
return False
|
|
108
|
+
self.socket.settimeout(self.timeout)
|
|
109
|
+
return True
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def length(self):
|
|
113
|
+
while self.__update_buffer(): pass
|
|
114
|
+
return len(self.buffer)
|
|
115
|
+
|
|
116
|
+
@property
|
|
117
|
+
def reenumerates(self) -> bool:
|
|
118
|
+
return False
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|