pymobiledevice3 5.0.1__py3-none-any.whl → 5.0.3__py3-none-any.whl
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.
Potentially problematic release.
This version of pymobiledevice3 might be problematic. Click here for more details.
- misc/plist_sniffer.py +15 -15
- misc/remotexpc_sniffer.py +29 -28
- pymobiledevice3/__main__.py +128 -102
- pymobiledevice3/_version.py +2 -2
- pymobiledevice3/bonjour.py +36 -59
- pymobiledevice3/ca.py +32 -24
- pymobiledevice3/cli/activation.py +7 -7
- pymobiledevice3/cli/afc.py +19 -19
- pymobiledevice3/cli/amfi.py +4 -4
- pymobiledevice3/cli/apps.py +51 -39
- pymobiledevice3/cli/backup.py +58 -32
- pymobiledevice3/cli/bonjour.py +25 -18
- pymobiledevice3/cli/cli_common.py +112 -81
- pymobiledevice3/cli/companion_proxy.py +4 -4
- pymobiledevice3/cli/completions.py +10 -10
- pymobiledevice3/cli/crash.py +37 -31
- pymobiledevice3/cli/developer.py +602 -520
- pymobiledevice3/cli/diagnostics.py +38 -33
- pymobiledevice3/cli/lockdown.py +79 -74
- pymobiledevice3/cli/mounter.py +85 -68
- pymobiledevice3/cli/notification.py +10 -10
- pymobiledevice3/cli/pcap.py +19 -14
- pymobiledevice3/cli/power_assertion.py +12 -10
- pymobiledevice3/cli/processes.py +10 -10
- pymobiledevice3/cli/profile.py +88 -77
- pymobiledevice3/cli/provision.py +17 -17
- pymobiledevice3/cli/remote.py +186 -110
- pymobiledevice3/cli/restore.py +43 -40
- pymobiledevice3/cli/springboard.py +30 -28
- pymobiledevice3/cli/syslog.py +85 -58
- pymobiledevice3/cli/usbmux.py +21 -20
- pymobiledevice3/cli/version.py +3 -2
- pymobiledevice3/cli/webinspector.py +157 -79
- pymobiledevice3/common.py +1 -1
- pymobiledevice3/exceptions.py +154 -60
- pymobiledevice3/irecv.py +49 -53
- pymobiledevice3/irecv_devices.py +1489 -492
- pymobiledevice3/lockdown.py +396 -242
- pymobiledevice3/lockdown_service_provider.py +5 -7
- pymobiledevice3/osu/os_utils.py +18 -9
- pymobiledevice3/osu/posix_util.py +28 -15
- pymobiledevice3/osu/win_util.py +14 -8
- pymobiledevice3/pair_records.py +19 -19
- pymobiledevice3/remote/common.py +4 -4
- pymobiledevice3/remote/core_device/app_service.py +94 -67
- pymobiledevice3/remote/core_device/core_device_service.py +17 -14
- pymobiledevice3/remote/core_device/device_info.py +5 -5
- pymobiledevice3/remote/core_device/diagnostics_service.py +10 -8
- pymobiledevice3/remote/core_device/file_service.py +47 -33
- pymobiledevice3/remote/remote_service_discovery.py +53 -35
- pymobiledevice3/remote/remotexpc.py +64 -42
- pymobiledevice3/remote/tunnel_service.py +371 -293
- pymobiledevice3/remote/utils.py +12 -11
- pymobiledevice3/remote/xpc_message.py +145 -125
- pymobiledevice3/resources/dsc_uuid_map.py +19 -19
- pymobiledevice3/resources/firmware_notifications.py +16 -16
- pymobiledevice3/restore/asr.py +27 -27
- pymobiledevice3/restore/base_restore.py +90 -47
- pymobiledevice3/restore/consts.py +87 -66
- pymobiledevice3/restore/device.py +11 -11
- pymobiledevice3/restore/fdr.py +46 -46
- pymobiledevice3/restore/ftab.py +19 -19
- pymobiledevice3/restore/img4.py +130 -133
- pymobiledevice3/restore/mbn.py +35 -54
- pymobiledevice3/restore/recovery.py +125 -135
- pymobiledevice3/restore/restore.py +524 -523
- pymobiledevice3/restore/restore_options.py +122 -115
- pymobiledevice3/restore/restored_client.py +25 -22
- pymobiledevice3/restore/tss.py +378 -270
- pymobiledevice3/service_connection.py +50 -46
- pymobiledevice3/services/accessibilityaudit.py +137 -127
- pymobiledevice3/services/afc.py +350 -291
- pymobiledevice3/services/amfi.py +21 -18
- pymobiledevice3/services/companion.py +23 -19
- pymobiledevice3/services/crash_reports.py +60 -46
- pymobiledevice3/services/debugserver_applist.py +3 -3
- pymobiledevice3/services/device_arbitration.py +8 -8
- pymobiledevice3/services/device_link.py +56 -48
- pymobiledevice3/services/diagnostics.py +971 -968
- pymobiledevice3/services/dtfetchsymbols.py +8 -8
- pymobiledevice3/services/dvt/dvt_secure_socket_proxy.py +4 -4
- pymobiledevice3/services/dvt/dvt_testmanaged_proxy.py +4 -4
- pymobiledevice3/services/dvt/instruments/activity_trace_tap.py +85 -74
- pymobiledevice3/services/dvt/instruments/application_listing.py +2 -3
- pymobiledevice3/services/dvt/instruments/condition_inducer.py +7 -6
- pymobiledevice3/services/dvt/instruments/core_profile_session_tap.py +442 -421
- pymobiledevice3/services/dvt/instruments/device_info.py +11 -11
- pymobiledevice3/services/dvt/instruments/energy_monitor.py +1 -1
- pymobiledevice3/services/dvt/instruments/graphics.py +1 -1
- pymobiledevice3/services/dvt/instruments/location_simulation.py +1 -1
- pymobiledevice3/services/dvt/instruments/location_simulation_base.py +10 -10
- pymobiledevice3/services/dvt/instruments/network_monitor.py +17 -17
- pymobiledevice3/services/dvt/instruments/notifications.py +1 -1
- pymobiledevice3/services/dvt/instruments/process_control.py +25 -10
- pymobiledevice3/services/dvt/instruments/screenshot.py +2 -2
- pymobiledevice3/services/dvt/instruments/sysmontap.py +15 -15
- pymobiledevice3/services/dvt/testmanaged/xcuitest.py +42 -52
- pymobiledevice3/services/file_relay.py +10 -10
- pymobiledevice3/services/heartbeat.py +8 -7
- pymobiledevice3/services/house_arrest.py +12 -15
- pymobiledevice3/services/installation_proxy.py +119 -100
- pymobiledevice3/services/lockdown_service.py +12 -5
- pymobiledevice3/services/misagent.py +22 -19
- pymobiledevice3/services/mobile_activation.py +84 -72
- pymobiledevice3/services/mobile_config.py +330 -301
- pymobiledevice3/services/mobile_image_mounter.py +137 -116
- pymobiledevice3/services/mobilebackup2.py +188 -150
- pymobiledevice3/services/notification_proxy.py +11 -11
- pymobiledevice3/services/os_trace.py +69 -51
- pymobiledevice3/services/pcapd.py +306 -306
- pymobiledevice3/services/power_assertion.py +10 -9
- pymobiledevice3/services/preboard.py +4 -4
- pymobiledevice3/services/remote_fetch_symbols.py +16 -14
- pymobiledevice3/services/remote_server.py +176 -146
- pymobiledevice3/services/restore_service.py +16 -16
- pymobiledevice3/services/screenshot.py +13 -10
- pymobiledevice3/services/simulate_location.py +7 -7
- pymobiledevice3/services/springboard.py +15 -15
- pymobiledevice3/services/syslog.py +5 -5
- pymobiledevice3/services/web_protocol/alert.py +3 -3
- pymobiledevice3/services/web_protocol/automation_session.py +183 -179
- pymobiledevice3/services/web_protocol/cdp_screencast.py +44 -36
- pymobiledevice3/services/web_protocol/cdp_server.py +19 -19
- pymobiledevice3/services/web_protocol/cdp_target.py +411 -373
- pymobiledevice3/services/web_protocol/driver.py +47 -45
- pymobiledevice3/services/web_protocol/element.py +74 -63
- pymobiledevice3/services/web_protocol/inspector_session.py +106 -102
- pymobiledevice3/services/web_protocol/selenium_api.py +3 -3
- pymobiledevice3/services/web_protocol/session_protocol.py +15 -10
- pymobiledevice3/services/web_protocol/switch_to.py +11 -12
- pymobiledevice3/services/webinspector.py +129 -117
- pymobiledevice3/tcp_forwarder.py +35 -22
- pymobiledevice3/tunneld/api.py +20 -15
- pymobiledevice3/tunneld/server.py +212 -133
- pymobiledevice3/usbmux.py +183 -138
- pymobiledevice3/utils.py +14 -11
- {pymobiledevice3-5.0.1.dist-info → pymobiledevice3-5.0.3.dist-info}/METADATA +1 -1
- pymobiledevice3-5.0.3.dist-info/RECORD +173 -0
- pymobiledevice3-5.0.1.dist-info/RECORD +0 -173
- {pymobiledevice3-5.0.1.dist-info → pymobiledevice3-5.0.3.dist-info}/WHEEL +0 -0
- {pymobiledevice3-5.0.1.dist-info → pymobiledevice3-5.0.3.dist-info}/entry_points.txt +0 -0
- {pymobiledevice3-5.0.1.dist-info → pymobiledevice3-5.0.3.dist-info}/licenses/LICENSE +0 -0
- {pymobiledevice3-5.0.1.dist-info → pymobiledevice3-5.0.3.dist-info}/top_level.txt +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import contextlib
|
|
2
3
|
import logging
|
|
3
4
|
import plistlib
|
|
4
5
|
import socket
|
|
@@ -12,8 +13,12 @@ from typing import Any, Optional
|
|
|
12
13
|
import IPython
|
|
13
14
|
from pygments import formatters, highlight, lexers
|
|
14
15
|
|
|
15
|
-
from pymobiledevice3.exceptions import
|
|
16
|
-
|
|
16
|
+
from pymobiledevice3.exceptions import (
|
|
17
|
+
ConnectionTerminatedError,
|
|
18
|
+
DeviceNotFoundError,
|
|
19
|
+
NoDeviceConnectedError,
|
|
20
|
+
PyMobileDevice3Exception,
|
|
21
|
+
)
|
|
17
22
|
from pymobiledevice3.osu.os_utils import get_os_utils
|
|
18
23
|
from pymobiledevice3.usbmux import MuxDevice, select_device
|
|
19
24
|
|
|
@@ -39,7 +44,7 @@ print(client.recvall(20))
|
|
|
39
44
|
"""
|
|
40
45
|
|
|
41
46
|
|
|
42
|
-
def build_plist(d: dict, endianity: str =
|
|
47
|
+
def build_plist(d: dict, endianity: str = ">", fmt: Enum = plistlib.FMT_XML) -> bytes:
|
|
43
48
|
"""
|
|
44
49
|
Convert a dictionary to a plist-formatted byte string prefixed with a length field.
|
|
45
50
|
|
|
@@ -49,7 +54,7 @@ def build_plist(d: dict, endianity: str = '>', fmt: Enum = plistlib.FMT_XML) ->
|
|
|
49
54
|
:return: The plist-formatted byte string.
|
|
50
55
|
"""
|
|
51
56
|
payload = plistlib.dumps(d, fmt=fmt)
|
|
52
|
-
message = struct.pack(endianity +
|
|
57
|
+
message = struct.pack(endianity + "L", len(payload))
|
|
53
58
|
return message + payload
|
|
54
59
|
|
|
55
60
|
|
|
@@ -65,17 +70,17 @@ def parse_plist(payload: bytes) -> dict:
|
|
|
65
70
|
try:
|
|
66
71
|
return plistlib.loads(payload)
|
|
67
72
|
except xml.parsers.expat.ExpatError:
|
|
68
|
-
payload = bytes([b for b in payload if b >= 0x20 or b in (0x09,
|
|
73
|
+
payload = bytes([b for b in payload if b >= 0x20 or b in (0x09, 0x0A, 0x0D)])
|
|
69
74
|
try:
|
|
70
75
|
return plistlib.loads(payload)
|
|
71
|
-
except plistlib.InvalidFileException:
|
|
72
|
-
raise PyMobileDevice3Exception(f
|
|
73
|
-
except plistlib.InvalidFileException:
|
|
74
|
-
raise PyMobileDevice3Exception(f
|
|
76
|
+
except plistlib.InvalidFileException as e:
|
|
77
|
+
raise PyMobileDevice3Exception(f"parse_plist invalid data: {payload[:100].hex()}") from e
|
|
78
|
+
except plistlib.InvalidFileException as e:
|
|
79
|
+
raise PyMobileDevice3Exception(f"parse_plist invalid data: {payload[:100].hex()}") from e
|
|
75
80
|
|
|
76
81
|
|
|
77
82
|
class ServiceConnection:
|
|
78
|
-
"""
|
|
83
|
+
"""wrapper for tcp-relay connections"""
|
|
79
84
|
|
|
80
85
|
def __init__(self, sock: socket.socket, mux_device: MuxDevice = None):
|
|
81
86
|
"""
|
|
@@ -100,8 +105,9 @@ class ServiceConnection:
|
|
|
100
105
|
self.max_ssl_proto = ssl.TLSVersion.TLSv1_3
|
|
101
106
|
|
|
102
107
|
@staticmethod
|
|
103
|
-
def create_using_tcp(
|
|
104
|
-
|
|
108
|
+
def create_using_tcp(
|
|
109
|
+
hostname: str, port: int, keep_alive: bool = True, create_connection_timeout: int = DEFAULT_TIMEOUT
|
|
110
|
+
) -> "ServiceConnection":
|
|
105
111
|
"""
|
|
106
112
|
Create a ServiceConnection using a TCP connection.
|
|
107
113
|
|
|
@@ -118,8 +124,9 @@ class ServiceConnection:
|
|
|
118
124
|
return ServiceConnection(sock)
|
|
119
125
|
|
|
120
126
|
@staticmethod
|
|
121
|
-
def create_using_usbmux(
|
|
122
|
-
|
|
127
|
+
def create_using_usbmux(
|
|
128
|
+
udid: Optional[str], port: int, connection_type: Optional[str] = None, usbmux_address: Optional[str] = None
|
|
129
|
+
) -> "ServiceConnection":
|
|
123
130
|
"""
|
|
124
131
|
Create a ServiceConnection using a USBMux connection.
|
|
125
132
|
|
|
@@ -148,18 +155,16 @@ class ServiceConnection:
|
|
|
148
155
|
self.socket.setblocking(blocking)
|
|
149
156
|
|
|
150
157
|
def close(self) -> None:
|
|
151
|
-
"""
|
|
158
|
+
"""Close the connection."""
|
|
152
159
|
self.socket.close()
|
|
153
160
|
|
|
154
161
|
async def aio_close(self) -> None:
|
|
155
|
-
"""
|
|
162
|
+
"""Asynchronously close the connection."""
|
|
156
163
|
if self.writer is None:
|
|
157
164
|
return
|
|
158
165
|
self.writer.close()
|
|
159
|
-
|
|
166
|
+
with contextlib.suppress(ssl.SSLError):
|
|
160
167
|
await self.writer.wait_closed()
|
|
161
|
-
except ssl.SSLError:
|
|
162
|
-
pass
|
|
163
168
|
self.writer = None
|
|
164
169
|
self.reader = None
|
|
165
170
|
|
|
@@ -172,8 +177,8 @@ class ServiceConnection:
|
|
|
172
177
|
"""
|
|
173
178
|
try:
|
|
174
179
|
return self.socket.recv(length)
|
|
175
|
-
except ssl.SSLError:
|
|
176
|
-
raise ConnectionAbortedError()
|
|
180
|
+
except ssl.SSLError as e:
|
|
181
|
+
raise ConnectionAbortedError() from e
|
|
177
182
|
|
|
178
183
|
def sendall(self, data: bytes) -> None:
|
|
179
184
|
"""
|
|
@@ -187,7 +192,7 @@ class ServiceConnection:
|
|
|
187
192
|
except ssl.SSLEOFError as e:
|
|
188
193
|
raise ConnectionTerminatedError from e
|
|
189
194
|
|
|
190
|
-
def send_recv_plist(self, data: dict, endianity: str =
|
|
195
|
+
def send_recv_plist(self, data: dict, endianity: str = ">", fmt: Enum = plistlib.FMT_XML) -> Any:
|
|
191
196
|
"""
|
|
192
197
|
Send a plist to the socket and receive a plist response.
|
|
193
198
|
|
|
@@ -199,7 +204,7 @@ class ServiceConnection:
|
|
|
199
204
|
self.send_plist(data, endianity=endianity, fmt=fmt)
|
|
200
205
|
return self.recv_plist(endianity=endianity)
|
|
201
206
|
|
|
202
|
-
async def aio_send_recv_plist(self, data: dict, endianity: str =
|
|
207
|
+
async def aio_send_recv_plist(self, data: dict, endianity: str = ">", fmt: Enum = plistlib.FMT_XML) -> Any:
|
|
203
208
|
"""
|
|
204
209
|
Asynchronously send a plist to the socket and receive a plist response.
|
|
205
210
|
|
|
@@ -219,7 +224,7 @@ class ServiceConnection:
|
|
|
219
224
|
:return: The received data.
|
|
220
225
|
:raises ConnectionAbortedError: If the connection is aborted.
|
|
221
226
|
"""
|
|
222
|
-
data = b
|
|
227
|
+
data = b""
|
|
223
228
|
while len(data) < size:
|
|
224
229
|
chunk = self.recv(size - len(data))
|
|
225
230
|
if chunk is None or len(chunk) == 0:
|
|
@@ -227,7 +232,7 @@ class ServiceConnection:
|
|
|
227
232
|
data += chunk
|
|
228
233
|
return data
|
|
229
234
|
|
|
230
|
-
def recv_prefixed(self, endianity: str =
|
|
235
|
+
def recv_prefixed(self, endianity: str = ">") -> bytes:
|
|
231
236
|
"""
|
|
232
237
|
Receive a data block prefixed with a length field.
|
|
233
238
|
|
|
@@ -236,8 +241,8 @@ class ServiceConnection:
|
|
|
236
241
|
"""
|
|
237
242
|
size = self.recvall(4)
|
|
238
243
|
if not size or len(size) != 4:
|
|
239
|
-
return b
|
|
240
|
-
size = struct.unpack(endianity +
|
|
244
|
+
return b""
|
|
245
|
+
size = struct.unpack(endianity + "L", size)[0]
|
|
241
246
|
while True:
|
|
242
247
|
try:
|
|
243
248
|
return self.recvall(size)
|
|
@@ -254,7 +259,7 @@ class ServiceConnection:
|
|
|
254
259
|
"""
|
|
255
260
|
return await self.reader.readexactly(size)
|
|
256
261
|
|
|
257
|
-
async def aio_recv_prefixed(self, endianity: str =
|
|
262
|
+
async def aio_recv_prefixed(self, endianity: str = ">") -> bytes:
|
|
258
263
|
"""
|
|
259
264
|
Asynchronously receive a data block prefixed with a length field.
|
|
260
265
|
|
|
@@ -262,7 +267,7 @@ class ServiceConnection:
|
|
|
262
267
|
:return: The received data block.
|
|
263
268
|
"""
|
|
264
269
|
size = await self.aio_recvall(4)
|
|
265
|
-
size = struct.unpack(endianity +
|
|
270
|
+
size = struct.unpack(endianity + "L", size)[0]
|
|
266
271
|
return await self.aio_recvall(size)
|
|
267
272
|
|
|
268
273
|
def send_prefixed(self, data: bytes) -> None:
|
|
@@ -273,11 +278,11 @@ class ServiceConnection:
|
|
|
273
278
|
"""
|
|
274
279
|
if isinstance(data, str):
|
|
275
280
|
data = data.encode()
|
|
276
|
-
hdr = struct.pack(
|
|
277
|
-
msg = b
|
|
281
|
+
hdr = struct.pack(">L", len(data))
|
|
282
|
+
msg = b"".join([hdr, data])
|
|
278
283
|
return self.sendall(msg)
|
|
279
284
|
|
|
280
|
-
def recv_plist(self, endianity: str =
|
|
285
|
+
def recv_plist(self, endianity: str = ">") -> dict:
|
|
281
286
|
"""
|
|
282
287
|
Receive a plist from the socket and parse it into a dictionary.
|
|
283
288
|
|
|
@@ -286,7 +291,7 @@ class ServiceConnection:
|
|
|
286
291
|
"""
|
|
287
292
|
return parse_plist(self.recv_prefixed(endianity=endianity))
|
|
288
293
|
|
|
289
|
-
async def aio_recv_plist(self, endianity: str =
|
|
294
|
+
async def aio_recv_plist(self, endianity: str = ">") -> dict:
|
|
290
295
|
"""
|
|
291
296
|
Asynchronously receive a plist from the socket and parse it into a dictionary.
|
|
292
297
|
|
|
@@ -295,7 +300,7 @@ class ServiceConnection:
|
|
|
295
300
|
"""
|
|
296
301
|
return parse_plist(await self.aio_recv_prefixed(endianity))
|
|
297
302
|
|
|
298
|
-
def send_plist(self, d: dict, endianity: str =
|
|
303
|
+
def send_plist(self, d: dict, endianity: str = ">", fmt: Enum = plistlib.FMT_XML) -> None:
|
|
299
304
|
"""
|
|
300
305
|
Send a dictionary as a plist to the socket.
|
|
301
306
|
|
|
@@ -314,7 +319,7 @@ class ServiceConnection:
|
|
|
314
319
|
self.writer.write(payload)
|
|
315
320
|
await self.writer.drain()
|
|
316
321
|
|
|
317
|
-
async def aio_send_plist(self, d: dict, endianity: str =
|
|
322
|
+
async def aio_send_plist(self, d: dict, endianity: str = ">", fmt: Enum = plistlib.FMT_XML) -> None:
|
|
318
323
|
"""
|
|
319
324
|
Asynchronously send a dictionary as a plist to the socket.
|
|
320
325
|
|
|
@@ -335,10 +340,10 @@ class ServiceConnection:
|
|
|
335
340
|
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
|
336
341
|
context.minimum_version = self.min_ssl_proto
|
|
337
342
|
context.maximum_version = self.max_ssl_proto
|
|
338
|
-
if ssl.OPENSSL_VERSION.lower().startswith(
|
|
339
|
-
context.set_ciphers(
|
|
343
|
+
if ssl.OPENSSL_VERSION.lower().startswith("openssl"):
|
|
344
|
+
context.set_ciphers("ALL:!aNULL:!eNULL:@SECLEVEL=0")
|
|
340
345
|
else:
|
|
341
|
-
context.set_ciphers(
|
|
346
|
+
context.set_ciphers("ALL:!aNULL:!eNULL")
|
|
342
347
|
context.options |= 0x4 # OPENSSL OP_LEGACY_SERVER_CONNECT (required for legacy iOS devices)
|
|
343
348
|
context.check_hostname = False
|
|
344
349
|
context.verify_mode = ssl.CERT_NONE
|
|
@@ -362,22 +367,21 @@ class ServiceConnection:
|
|
|
362
367
|
:param keyfile: The path to the key file (optional).
|
|
363
368
|
"""
|
|
364
369
|
self.reader, self.writer = await asyncio.open_connection(
|
|
365
|
-
sock=self.socket,
|
|
366
|
-
ssl=self.create_ssl_context(certfile, keyfile=keyfile),
|
|
367
|
-
server_hostname=''
|
|
370
|
+
sock=self.socket, ssl=self.create_ssl_context(certfile, keyfile=keyfile), server_hostname=""
|
|
368
371
|
)
|
|
369
372
|
|
|
370
373
|
async def aio_start(self) -> None:
|
|
371
|
-
"""
|
|
374
|
+
"""Asynchronously start a connection."""
|
|
372
375
|
self.reader, self.writer = await asyncio.open_connection(sock=self.socket)
|
|
373
376
|
|
|
374
377
|
def shell(self) -> None:
|
|
375
|
-
"""
|
|
378
|
+
"""Start an interactive shell."""
|
|
376
379
|
IPython.embed(
|
|
377
|
-
header=highlight(SHELL_USAGE, lexers.PythonLexer(), formatters.Terminal256Formatter(style=
|
|
380
|
+
header=highlight(SHELL_USAGE, lexers.PythonLexer(), formatters.Terminal256Formatter(style="native")),
|
|
378
381
|
user_ns={
|
|
379
|
-
|
|
380
|
-
}
|
|
382
|
+
"client": self,
|
|
383
|
+
},
|
|
384
|
+
)
|
|
381
385
|
|
|
382
386
|
def read(self, size: int) -> bytes:
|
|
383
387
|
"""
|