pymammotion 0.4.0b7__py3-none-any.whl → 0.4.0b8__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.
@@ -0,0 +1,5 @@
1
+ """Package for linkkit."""
2
+
3
+ from .linkkit import LinkKit
4
+
5
+ __all__ = ["LinkKit"]
@@ -0,0 +1,585 @@
1
+ #
2
+ # Copyright (c) 2014-2018 Alibaba Group. All rights reserved.
3
+ # License-Identifier: Apache-2.0
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ #
17
+ #
18
+
19
+ import concurrent.futures
20
+ import hashlib
21
+ import hmac
22
+ import logging
23
+ import os
24
+ import ssl
25
+ import threading
26
+ import time
27
+ from types import TracebackType
28
+
29
+ import crcmod
30
+ import hyper
31
+
32
+
33
+ def _assert_value(condition, error_msg):
34
+ if not condition:
35
+ raise ValueError(error_msg)
36
+
37
+
38
+ _H2_OPT_HEART_BEAT_TIME_DEFAULT = 25
39
+ _H2_OPT_PORT_DEFAULT = 443
40
+ _H2_MAX_FILE_SIZE = 1024 * 1024 * 1024
41
+
42
+
43
+ def h2_set_option(opt, value) -> None:
44
+ if opt == "heart_beat_interval":
45
+ global _H2_OPT_HEART_BEAT_TIME_DEFAULT
46
+ _H2_OPT_HEART_BEAT_TIME_DEFAULT = value
47
+ elif opt == "port":
48
+ global _H2_OPT_PORT_DEFAULT
49
+ _H2_OPT_PORT_DEFAULT = value
50
+ elif opt == "max_file_size":
51
+ global _H2_MAX_FILE_SIZE
52
+ _H2_MAX_FILE_SIZE = value
53
+
54
+
55
+ class StreamHandler:
56
+ def __init__(self) -> None:
57
+ pass
58
+
59
+ def __enter__(self) -> None:
60
+ pass
61
+
62
+ def __exit__(
63
+ self, type: type[BaseException] | None, value: BaseException | None, trace: TracebackType | None
64
+ ) -> None:
65
+ pass
66
+
67
+ def get_content_length(self):
68
+ return None
69
+
70
+ def next(self):
71
+ return None
72
+
73
+ def has_next(self) -> bool:
74
+ return False
75
+
76
+
77
+ class FileStreamHandler(StreamHandler):
78
+ def __init__(self, filename, block_size=512 * 1024, opt_crc64=False) -> None:
79
+ self.__filename = filename
80
+ self.__block_size = block_size
81
+ self.__size = os.stat(filename).st_size
82
+ self.__opt_crc64 = opt_crc64
83
+ self.__last_crc = 0
84
+ self.__read_size = 0
85
+
86
+ def get_content_length(self):
87
+ return self.__size
88
+
89
+ def __enter__(self) -> None:
90
+ logging.debug("open the file, filename:%s" % self.__filename)
91
+ self.__f = open(self.__filename, "rb")
92
+ self.__read_size = 0
93
+
94
+ def __exit__(
95
+ self, type: type[BaseException] | None, value: BaseException | None, trace: TracebackType | None
96
+ ) -> None:
97
+ if self.__f:
98
+ self.__f.close()
99
+ self.__f = None
100
+
101
+ def next(self):
102
+ if not self.__f or self.__read_size >= self.__size:
103
+ return None
104
+ data = self.__f.read(self.__block_size)
105
+ if data:
106
+ self.__read_size += len(data)
107
+ if self.__opt_crc64:
108
+ do_crc64 = crcmod.mkCrcFun(
109
+ 0x142F0E1EBA9EA3693, initCrc=self.__last_crc, xorOut=0xFFFFFFFFFFFFFFFF, rev=True
110
+ )
111
+ self.__last_crc = do_crc64(data)
112
+ return data
113
+
114
+ def has_next(self):
115
+ return self.__f.tell() < self.__size
116
+
117
+ def get_crc64(self):
118
+ return self.__last_crc
119
+
120
+ def get_read_size(self):
121
+ return self.__read_size
122
+
123
+
124
+ class H2Exception(Exception):
125
+ def __init__(self, code, msg) -> None:
126
+ Exception.__init__(self, msg)
127
+ self.__code = code
128
+ self.__msg = msg
129
+
130
+ def get_code(self):
131
+ return self.__code
132
+
133
+ def get_msg(self):
134
+ return self.__msg
135
+
136
+ def __name__(self) -> str:
137
+ return "H2Exception"
138
+
139
+
140
+ class UploadFileInfo:
141
+ def __init__(self, local_filename, remote_filename=None, overwrite=True) -> None:
142
+ self.local_filename = local_filename
143
+ self.opt_overwrite = overwrite
144
+ if not remote_filename:
145
+ self.remote_filename = os.path.basename(local_filename)
146
+ else:
147
+ self.remote_filename = remote_filename
148
+
149
+ def __name__(self) -> str:
150
+ return "UploadFileInfo"
151
+
152
+
153
+ class UploadFileResult:
154
+ def __init__(self, code=None, exception=None, upload_size=None, total_size=None, file_store_id=None) -> None:
155
+ self.upload_size = upload_size
156
+ self.total_size = total_size
157
+ self.file_store_id = file_store_id
158
+ self.code = code
159
+ self.exception = exception
160
+
161
+ def __name__(self) -> str:
162
+ return "UploadFileResult"
163
+
164
+
165
+ class H2FileUploadSink:
166
+ def on_file_upload_start(self, id, upload_file_info, user_data) -> None:
167
+ pass
168
+
169
+ def on_file_upload_end(self, id, upload_file_info, upload_file_result, user_data) -> None:
170
+ pass
171
+
172
+ def on_file_upload_progress(self, id, upload_file_info, upload_file_result, user_data) -> None:
173
+ pass
174
+
175
+
176
+ class H2FileTask:
177
+ def __init__(self, id, file_info, future_result) -> None:
178
+ self.__file_info = file_info
179
+ self.__future_result = future_result
180
+ self.__id = id
181
+
182
+ def get_file_info(self):
183
+ return self.__file_info
184
+
185
+ def get_future_result(self):
186
+ return self.__future_result
187
+
188
+ def result(self, timeout=None):
189
+ return self.__future_result.result(timeout)
190
+
191
+ def cancel(self) -> None:
192
+ self.__future_result.call()
193
+
194
+ def get_id(self):
195
+ return self.__id
196
+
197
+ def __name__(self) -> str:
198
+ return "H2FileTask"
199
+
200
+
201
+ class H2Stream:
202
+ def __init__(self, client, id) -> None:
203
+ self.__client = client
204
+ self.__conn = None
205
+ # self.__length = None
206
+ self.__total_sent_size = 0
207
+ self.__path = None
208
+ self.__id = id
209
+ self.__stream_id = None
210
+ self.__x_request_id = None
211
+ self.__x_data_stream_id = None
212
+
213
+ def __name__(self) -> str:
214
+ return "H2Stream"
215
+
216
+ def get_id(self):
217
+ return self.__id
218
+
219
+ def open(self, path, header):
220
+ _assert_value(path, "path is required")
221
+
222
+ with self.__client._get_auth_lock():
223
+ url = "/stream/open" + path
224
+ self.__conn = self.__client.get_connect()
225
+
226
+ # self.__length = length
227
+ self.__total_sent_size = 0
228
+ self.__path = path
229
+
230
+ logging.debug("request url: %s" % url)
231
+
232
+ # open the stream
233
+ conn_header = self.__client.get_default_header()
234
+ if header:
235
+ conn_header.update(header)
236
+
237
+ req_id = self.__conn.request("GET", url, None, conn_header)
238
+ response = self.__conn.get_response(req_id)
239
+
240
+ self.__check_response(response)
241
+ self.__x_request_id = response.headers["x-request-id"][0]
242
+ self.__x_data_stream_id = response.headers["x-data-stream-id"][0]
243
+
244
+ logging.debug("x_request_id: %s" % self.__x_request_id)
245
+ logging.debug("x_data_stream_id: %s" % self.__x_data_stream_id)
246
+
247
+ return response
248
+
249
+ def close(self, header):
250
+ logging.debug("close the stream")
251
+ final_header = {"x-request-id": self.__x_request_id, "x-data-stream-id": self.__x_data_stream_id}
252
+ final_header.update(header)
253
+ req_id = self.__conn.request("GET", "/stream/close/" + self.__path, None, final_header)
254
+ response = self.__conn.get_response(req_id)
255
+ self.__check_response(response)
256
+ return response
257
+
258
+ def send(self, headers, data_handler):
259
+ # prepare for sending
260
+ with self.__client._get_auth_lock():
261
+ url = "/stream/send" + self.__path
262
+ logging.debug("request url: %s" % url)
263
+ self.__stream_id = self.__conn.putrequest("GET", url)
264
+ self.__conn.putheader("x-request-id", self.__x_request_id, stream_id=self.__stream_id)
265
+ self.__conn.putheader("x-data-stream-id", self.__x_data_stream_id, stream_id=self.__stream_id)
266
+ content_length = data_handler.get_content_length()
267
+ if content_length:
268
+ self.__conn.putheader("content-length", "%s" % (content_length), self.__stream_id)
269
+ for k, v in headers.items():
270
+ self.__conn.putheader(k, v, self.__stream_id)
271
+ self.__conn.endheaders(stream_id=self.__stream_id)
272
+
273
+ with data_handler:
274
+ final = False
275
+ while not final:
276
+ data = data_handler.next()
277
+ if data == None or len(data) == 0:
278
+ break
279
+ final = not data_handler.has_next()
280
+ self.__conn.send(data, final, stream_id=self.__stream_id)
281
+
282
+ response = self.__conn.get_response(self.__stream_id)
283
+ # response.read()
284
+ self.__check_response(response)
285
+ return response
286
+
287
+ def __check_response(self, response, msg=None):
288
+ if response.status != 200:
289
+ raise H2Exception(response.status, msg if msg else "fail to request http/2, code:%d" % (response.status))
290
+
291
+ def __str__(self) -> str:
292
+ return "H2Stream(id=%s,stream_x_id=%s,x_request_id=%s,x_data_stream_id:%s" % (
293
+ self.__id,
294
+ self.__stream_id,
295
+ self.__x_request_id,
296
+ self.__x_data_stream_id,
297
+ )
298
+
299
+
300
+ class H2Client:
301
+ def __init__(
302
+ self, region, product_key, device_name, device_secret, client_id=None, opt_max_thread_num=4, endpoint=None
303
+ ) -> None:
304
+ _assert_value(region, "region is not empty")
305
+ _assert_value(product_key, "product_key is not empty")
306
+ _assert_value(device_name, "device_name is not empty")
307
+
308
+ self.__product_key = product_key
309
+ self.__device_name = device_name
310
+ self.__client_id = client_id
311
+ self.__device_secret = device_secret
312
+ self.__region = region
313
+ self.__endpoint = endpoint
314
+ self.__opt_free_idle_connect = False
315
+ self.__connected = False
316
+ self.__port = _H2_OPT_PORT_DEFAULT
317
+ self.__conn = None
318
+ self.__opt_heart_beat_time = _H2_OPT_HEART_BEAT_TIME_DEFAULT
319
+ self.__conn_lock = threading.RLock()
320
+ self.__lock = threading.RLock()
321
+ self.__stream_list = []
322
+ self.__stream_list_lock = threading.RLock()
323
+ self.__thread_executor = concurrent.futures.ThreadPoolExecutor(max_workers=opt_max_thread_num)
324
+ self.__auth_lock = threading.RLock()
325
+ self.__id = 0
326
+ self.__heart_beat_lock = threading.RLock()
327
+ self.__timer = None
328
+
329
+ def get_endpoint(self):
330
+ return self.__endpoint
331
+
332
+ def get_actual_endpoint(self):
333
+ return self.__generate_endpoint()
334
+
335
+ def __generate_endpoint(self):
336
+ if self.__endpoint:
337
+ return self.__endpoint
338
+ else:
339
+ return self.__product_key + ".iot-as-http2.%s.aliyuncs.com" % (self.__region)
340
+
341
+ def open(self):
342
+ with self.__conn_lock:
343
+ if self.__conn:
344
+ logging.info("the client is opened")
345
+ return -1
346
+ return self.__connect()
347
+
348
+ def close(self):
349
+ with self.__conn_lock:
350
+ return self.__close_connect()
351
+ self.__close_all_streams()
352
+
353
+ def upload_file_async(
354
+ self, local_filename, remote_filename=None, over_write=True, upload_file_sink=None, upload_sink_user_data=None
355
+ ):
356
+ _assert_value(local_filename, "local_filename is required")
357
+ self.__check_file(local_filename)
358
+
359
+ file_info = UploadFileInfo(local_filename, remote_filename, over_write)
360
+
361
+ future_result = self.__thread_executor.submit(
362
+ self.__post_file_task, file_info, upload_file_sink, upload_sink_user_data
363
+ )
364
+ return H2FileTask(id, file_info, future_result)
365
+
366
+ def upload_file_sync(
367
+ self,
368
+ local_filename,
369
+ remote_filename=None,
370
+ over_write=True,
371
+ timeout=None,
372
+ upload_file_sink=None,
373
+ upload_sink_user_data=None,
374
+ ):
375
+ self.__check_file(local_filename)
376
+ f = self.upload_file_async(local_filename, remote_filename, over_write, upload_file_sink, upload_sink_user_data)
377
+ return f.result(timeout)
378
+
379
+ def __create_stream_id(self):
380
+ with self.__lock:
381
+ self.__id += 1
382
+ return self.__id
383
+
384
+ def new_stream(self):
385
+ return H2Stream(self, self.__create_stream_id())
386
+
387
+ def _get_auth_lock(self):
388
+ return self.__auth_lock
389
+
390
+ def __crc_equal(self, value1, value2):
391
+ if value1 == value2:
392
+ return True
393
+ return self.__to_unsign(value1) == self.__to_unsign(value2)
394
+
395
+ def __to_unsign(self, value):
396
+ return value if value > 0 else (0xFFFFFFFFFFFFFFFF + 1 + value)
397
+
398
+ def __check_file(self, path):
399
+ stat_info = os.stat(path)
400
+ if stat_info.st_size >= _H2_MAX_FILE_SIZE:
401
+ raise ValueError("maximum file size exceeded")
402
+
403
+ def __post_file_task(self, file_info, sink=None, user_data=None):
404
+ local_filename = file_info.local_filename
405
+ remote_filename = file_info.remote_filename
406
+ over_write = file_info.opt_overwrite
407
+ fs = None
408
+ file_store_id = None
409
+ exception = None
410
+ code = 0
411
+ x_file_upload_id = None
412
+
413
+ stream = self.new_stream()
414
+ self.__on_new_stream(stream)
415
+ try:
416
+ logging.info(
417
+ "start to post file, local_filename:%s, remote:%s, over_write:%d"
418
+ % (local_filename, remote_filename, over_write)
419
+ )
420
+
421
+ # callback
422
+ if sink:
423
+ sink.on_file_upload_start(stream.get_id(), file_info, user_data)
424
+
425
+ # open stream
426
+ header = {"x-file-name": remote_filename, "x-file-overwrite": "1" if over_write else "0"}
427
+ response = stream.open("/c/iot/sys/thing/file/upload", header)
428
+ x_file_upload_id = response.headers["x-file-upload-id"][0]
429
+
430
+ # send stream
431
+ header = {"x-file-upload-id": x_file_upload_id}
432
+ fs = FileStreamHandler(local_filename, opt_crc64=True)
433
+ stream.send(header, fs)
434
+
435
+ # close stream
436
+ response = stream.close(header)
437
+ remote_crc64 = int(response.headers["x-file-crc64ecma"][0])
438
+ logging.info("crc64, local:%ld, remote:%ld" % (fs.get_crc64(), remote_crc64))
439
+ if not self.__crc_equal(fs.get_crc64(), remote_crc64):
440
+ raise Exception("fail to check crc64, local:%ld, remote:%ld" % (fs.get_crc64(), remote_crc64))
441
+ file_store_id = response.headers["x-file-store-id"][0]
442
+ logging.info(
443
+ "finish uploading file, local_filename:%s, remote:%s, over_write:%d, file_store_id:%s"
444
+ % (local_filename, remote_filename, over_write, file_store_id)
445
+ )
446
+
447
+ return UploadFileResult(code, exception, fs.get_read_size(), fs.get_content_length, file_store_id)
448
+ except H2Exception as e:
449
+ logging.error(
450
+ "fail to upload the file, local_filename:%s, remote:%s, over_write:%d, x_file_upload_id:%s, stream:%s, code:%s, error:%s"
451
+ % (local_filename, remote_filename, over_write, x_file_upload_id, stream, e.get_code(), e)
452
+ )
453
+ return UploadFileResult(
454
+ e.get_code(),
455
+ exception,
456
+ (fs.get_read_size() if fs else -1),
457
+ (fs.get_content_length() if fs else -1),
458
+ file_store_id,
459
+ )
460
+ except Exception as e:
461
+ logging.error(
462
+ "fail to upload the file, local_filename:%s, remote:%s, over_write:%d, x_file_upload_id:%s, stream:%s, error:%s"
463
+ % (local_filename, remote_filename, over_write, x_file_upload_id, stream, e)
464
+ )
465
+ return UploadFileResult(
466
+ -1,
467
+ exception,
468
+ (fs.get_read_size() if fs else -1),
469
+ (fs.get_content_length() if fs else -1),
470
+ file_store_id,
471
+ )
472
+ # raise e
473
+ finally:
474
+ self.__on_free_stream(stream)
475
+ if sink:
476
+ result = UploadFileResult(
477
+ code,
478
+ exception,
479
+ (fs.get_read_size() if fs else -1),
480
+ (fs.get_content_length() if fs else -1),
481
+ file_store_id,
482
+ )
483
+ sink.on_file_upload_end(stream.get_id(), file_info, result, user_data)
484
+
485
+ def __connect(self) -> int:
486
+ with self.__conn_lock:
487
+ ctx = ssl.SSLContext(ssl.PROTOCOL_TLS)
488
+ h2_endpoint = self.__generate_endpoint()
489
+ logging.debug("http/2 endpoint:%s" % (h2_endpoint))
490
+ self.__conn = hyper.HTTP20Connection(
491
+ h2_endpoint, port=self.__port, force_proto=hyper.tls.NPN_PROTOCOL, ssl_context=ctx
492
+ )
493
+ return 0
494
+
495
+ def get_connect(self):
496
+ with self.__conn_lock:
497
+ if self.__conn:
498
+ return self.__conn
499
+ return self.__connect()
500
+
501
+ def __fill_auth_header(self, header):
502
+ client_id = self.__client_id or self.__device_name
503
+ timestamp = str(int(time.time() * 1000))
504
+ sign_content = (
505
+ "clientId"
506
+ + client_id
507
+ + "deviceName"
508
+ + self.__device_name
509
+ + "productKey"
510
+ + self.__product_key
511
+ + "timestamp"
512
+ + timestamp
513
+ )
514
+ sign = hmac.new(self.__device_secret.encode("utf-8"), sign_content.encode("utf-8"), hashlib.sha256).hexdigest()
515
+ header["x-auth-param-timestamp"] = timestamp
516
+ header["x-auth-param-signmethod"] = "hmacsha256"
517
+ header["x-auth-param-sign"] = sign
518
+ header["x-auth-param-product-key"] = self.__product_key
519
+ header["x-auth-param-device-name"] = self.__device_name
520
+ header["x-auth-param-client-id"] = client_id
521
+ header["x-auth-name"] = "devicename"
522
+ return header
523
+
524
+ def __fill_sdk_header(self, header):
525
+ header["x-sdk-version"] = "1.2.0"
526
+ header["x-sdk-version-name"] = "1.2.0"
527
+ header["x-sdk-platform"] = "python"
528
+ return header
529
+
530
+ def get_default_header(self):
531
+ header = {}
532
+ self.__fill_auth_header(header)
533
+ self.__fill_sdk_header(header)
534
+ return header
535
+
536
+ def __close_connect(self) -> int:
537
+ with self.__conn_lock:
538
+ if self.__conn:
539
+ self.__conn.close(0)
540
+ return 0
541
+
542
+ def __close_all_streams(self) -> None:
543
+ with self.__stream_list_lock:
544
+ self.__stream_list.clear()
545
+ self.__stream_list = None
546
+ self.__stop_heart_beat()
547
+
548
+ def __on_new_stream(self, stream) -> None:
549
+ with self.__stream_list_lock:
550
+ self.__stream_list.append(stream)
551
+
552
+ if len(self.__stream_list) == 1:
553
+ self.__start_heart_beat()
554
+
555
+ def __on_free_stream(self, stream) -> None:
556
+ with self.__stream_list_lock:
557
+ self.__stream_list.remove(stream)
558
+
559
+ if len(self.__stream_list) == 0:
560
+ self.__stop_heart_beat()
561
+
562
+ def __start_heart_beat(self) -> None:
563
+ logging.debug("start heart_beat")
564
+ self.__schedule_heart_beat()
565
+
566
+ def __handle_heart_beat(self) -> None:
567
+ logging.debug("heart...")
568
+ self.__conn.ping(b"PINGPONG")
569
+ self.__schedule_heart_beat()
570
+
571
+ def __stop_heart_beat(self) -> None:
572
+ logging.debug("stop heart")
573
+ self.__cancel_heart_beat()
574
+
575
+ def __schedule_heart_beat(self) -> None:
576
+ with self.__heart_beat_lock:
577
+ if self.__opt_heart_beat_time and self.__opt_heart_beat_time > 0:
578
+ self.__timer = threading.Timer(self.__opt_heart_beat_time, self.__handle_heart_beat)
579
+ self.__timer.start()
580
+
581
+ def __cancel_heart_beat(self) -> None:
582
+ with self.__heart_beat_lock:
583
+ if self.__timer:
584
+ self.__timer.cancel()
585
+ self.__timer = None