mqttxx 2.0.3__py3-none-any.whl → 3.2.1__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,12 @@
1
+ mqttxx/__init__.py,sha256=_WoU5oZfMOJetRH1JkLuICp8PlqxpN3BWyXeZEjXeVo,1908
2
+ mqttxx/client.py,sha256=7umy-hJ-TUW9XD_rMdTh71q9Z7rlifnVPvHA80aIbcI,18794
3
+ mqttxx/config.py,sha256=A4AK54FL3eNyOCQ4E5IowIkC34zpT43d1bJzX0VOAHk,5986
4
+ mqttxx/events.py,sha256=bs7Khp6ygDK_aEOXftb74HO3SMqOY_Ln9Se_ro6ryiE,11747
5
+ mqttxx/exceptions.py,sha256=EC9NZmvZbfrK9iEdSyoYt4pUl0dp7yVup8mjOfgBaMw,3704
6
+ mqttxx/protocol.py,sha256=EwXJQ0Ic25oJx55coAfvntOxWh5FYYlBSMV93Cm6hHU,7445
7
+ mqttxx/rpc.py,sha256=KBu2006o9NukqbUk7ehvyIwgrdbO1CqY4rdo-OKr1KM,15831
8
+ mqttxx-3.2.1.dist-info/LICENSE,sha256=tfwCF8HPjAvvq_veljjH3_YL90X6ttkjTpRl3BGcXTg,1067
9
+ mqttxx-3.2.1.dist-info/METADATA,sha256=wFasvCdIOFRVZLnYEM84gxbRLE9uWd4CYa0VEtOFMGE,25544
10
+ mqttxx-3.2.1.dist-info/WHEEL,sha256=Z4pYXqR_rTB7OWNDYFOm1qRk0RX6GFP2o8LgvP453Hk,91
11
+ mqttxx-3.2.1.dist-info/top_level.txt,sha256=4SNRXgOpGCT6ThBzAb8zhUmTyZwnIracAIJWAdXXzw0,7
12
+ mqttxx-3.2.1.dist-info/RECORD,,
mqttxx/conventions.py DELETED
@@ -1,145 +0,0 @@
1
- # 约定式 RPC 管理器 - 去角色设计
2
-
3
- from typing import Any, Optional
4
- from loguru import logger
5
-
6
- from .client import MQTTClient
7
- from .config import RPCConfig
8
- from .rpc import RPCManager, AuthCallback
9
-
10
-
11
- class ConventionalRPCManager(RPCManager):
12
- """约定式 RPC 管理器
13
-
14
- 设计原则:
15
- - 约定优于配置
16
- - 自动订阅本地 topic
17
- - 自动注入 reply_to
18
-
19
- 核心功能:
20
- 1. 初始化时自动订阅 `my_topic`
21
- 2. 调用时自动将 `my_topic` 注入到 `reply_to`
22
-
23
- 适用场景:
24
- - 边缘设备 ↔ 云端(edge/xxx ↔ cloud/xxx)
25
- - 微服务之间(auth-service ↔ user-service)
26
- - IoT 网关 ↔ 设备(gateway/001 ↔ device/123)
27
- - 任何需要简化 RPC 调用的场景
28
-
29
- 示例:
30
- # 边缘设备
31
- rpc = ConventionalRPCManager(client, my_topic="edge/device_123")
32
-
33
- @rpc.register("get_status")
34
- async def get_status(params):
35
- return {"status": "online"}
36
-
37
- # 调用云端(自动注入 reply_to="edge/device_123")
38
- config = await rpc.call("cloud/config-service", "get_config")
39
-
40
- # 云端服务
41
- rpc = ConventionalRPCManager(client, my_topic="cloud/config-service")
42
-
43
- # 调用边缘设备(自动注入 reply_to="cloud/config-service")
44
- status = await rpc.call("edge/device_123", "execute_command")
45
- """
46
-
47
- def __init__(
48
- self,
49
- client: MQTTClient,
50
- my_topic: str,
51
- config: Optional[RPCConfig] = None,
52
- auth_callback: Optional[AuthCallback] = None,
53
- ):
54
- """初始化约定式 RPC 管理器
55
-
56
- Args:
57
- client: MQTTClient 实例
58
- my_topic: 本节点的 topic(自动订阅,自动注入到 reply_to)
59
- config: RPC 配置(可选)
60
- auth_callback: 权限检查回调(可选)
61
-
62
- 自动行为:
63
- - 自动订阅 my_topic
64
- - 自动绑定消息处理器
65
-
66
- 示例:
67
- # 边缘设备
68
- rpc = ConventionalRPCManager(client, my_topic="edge/device_123")
69
-
70
- # 云端服务
71
- rpc = ConventionalRPCManager(client, my_topic="cloud/server_001")
72
-
73
- # 微服务
74
- rpc = ConventionalRPCManager(client, my_topic="auth-service")
75
-
76
- # 多层级
77
- rpc = ConventionalRPCManager(client, my_topic="region/zone/device")
78
- """
79
- super().__init__(client, config, auth_callback)
80
-
81
- self._my_topic = my_topic
82
-
83
- # 自动订阅
84
- client.subscribe(my_topic, self.handle_rpc_message)
85
-
86
- logger.info(f"ConventionalRPCManager 已初始化 - my_topic: {my_topic}")
87
-
88
- async def call(
89
- self,
90
- topic: str,
91
- method: str,
92
- params: Any = None,
93
- timeout: Optional[float] = None,
94
- reply_to: Optional[str] = None,
95
- ) -> Any:
96
- """调用远程方法(自动注入 reply_to)
97
-
98
- Args:
99
- topic: 对方的 topic
100
- method: 方法名
101
- params: 参数(可选)
102
- timeout: 超时时间(可选)
103
- reply_to: 响应 topic(可选,默认使用 my_topic)
104
-
105
- Returns:
106
- 方法返回值
107
-
108
- Raises:
109
- MQTTXError: 客户端未连接
110
- RPCTimeoutError: 调用超时
111
- RPCRemoteError: 远程执行失败
112
-
113
- 示例:
114
- # 边缘设备调用云端
115
- rpc = ConventionalRPCManager(client, my_topic="edge/device_123")
116
- result = await rpc.call("cloud/server_001", "get_config")
117
- # 等价于:await super().call("cloud/server_001", "get_config", reply_to="edge/device_123")
118
-
119
- # 云端调用边缘设备
120
- rpc = ConventionalRPCManager(client, my_topic="cloud/server_001")
121
- result = await rpc.call("edge/device_123", "execute_command", params={"cmd": "restart"})
122
-
123
- # 微服务调用
124
- rpc = ConventionalRPCManager(client, my_topic="auth-service")
125
- user = await rpc.call("user-service", "get_user", params={"id": 123})
126
- """
127
- # 自动注入 reply_to
128
- reply_to = reply_to or self._my_topic
129
-
130
- return await super().call(
131
- topic=topic,
132
- method=method,
133
- params=params,
134
- timeout=timeout,
135
- reply_to=reply_to,
136
- )
137
-
138
- @property
139
- def my_topic(self) -> str:
140
- """获取当前节点的 topic
141
-
142
- Returns:
143
- 当前节点订阅的 topic
144
- """
145
- return self._my_topic
@@ -1,490 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: mqttxx
3
- Version: 2.0.3
4
- Summary: 基于 aiomqtt 的高级 MQTT 客户端和 RPC 框架
5
- Author: MQTTX Team
6
- License: MIT
7
- Keywords: mqtt,rpc,async,iot,messaging
8
- Description-Content-Type: text/markdown
9
- License-File: LICENSE
10
- Requires-Dist: aiomqtt <3.0.0,>=2.0.0
11
- Requires-Dist: loguru >=0.7.0
12
- Provides-Extra: dev
13
- Requires-Dist: pytest ; extra == 'dev'
14
- Requires-Dist: pytest-asyncio ; extra == 'dev'
15
- Requires-Dist: ruff ; extra == 'dev'
16
- Requires-Dist: build ; extra == 'dev'
17
- Requires-Dist: twine ; extra == 'dev'
18
-
19
- # MQTTX
20
-
21
- 基于 [aiomqtt](https://github.com/sbtinstruments/aiomqtt) 的高级 MQTT 客户端和 RPC 框架。
22
-
23
- **核心特性:**
24
- - ✅ 纯 async/await,无回调
25
- - ✅ 自动重连、订阅队列化
26
- - ✅ 双向对等 RPC(带权限控制)
27
- - ✅ TLS/SSL、认证支持
28
- - ✅ 约定式 RPC(零配置)
29
-
30
- ---
31
-
32
- ## 安装
33
-
34
- ```bash
35
- pip install mqttxx
36
- ```
37
-
38
- ---
39
-
40
- ## 快速开始
41
-
42
- ### 1. MQTT 基础用法
43
-
44
- ```python
45
- import asyncio
46
- from mqttxx import MQTTClient, MQTTConfig
47
-
48
- async def main():
49
- config = MQTTConfig(
50
- broker_host="localhost",
51
- broker_port=1883,
52
- client_id="device_123"
53
- )
54
-
55
- async with MQTTClient(config) as client:
56
- # 订阅主题
57
- def on_message(topic, message):
58
- print(f"{topic}: {message}")
59
-
60
- client.subscribe("sensors/#", on_message)
61
-
62
- # 发布消息
63
- await client.publish("sensors/temperature", "25.5", qos=1)
64
-
65
- await asyncio.sleep(60)
66
-
67
- asyncio.run(main())
68
- ```
69
-
70
- ---
71
-
72
- ### 2. 约定式 RPC(零配置)
73
-
74
- **推荐**:使用 `ConventionalRPCManager`,自动订阅 + 自动注入 reply_to。
75
-
76
- ```python
77
- from mqttxx import MQTTClient, MQTTConfig, ConventionalRPCManager
78
-
79
- # 边缘设备
80
- async def edge_device():
81
- client_id = "device_123"
82
- config = MQTTConfig(
83
- broker_host="localhost",
84
- client_id=client_id,
85
- )
86
-
87
- async with MQTTClient(config) as client:
88
- # 自动订阅 edge/device_123
89
- rpc = ConventionalRPCManager(client, my_topic=f"edge/{client_id}")
90
-
91
- @rpc.register("get_status")
92
- async def get_status(params):
93
- return {"status": "online"}
94
-
95
- # 调用云端(自动注入 reply_to="edge/device_123")
96
- config = await rpc.call("cloud/config-service", "get_device_config")
97
- print(config)
98
-
99
- await asyncio.sleep(60)
100
-
101
- # 云端服务
102
- async def cloud_service():
103
- client_id = "config-service"
104
- config = MQTTConfig(
105
- broker_host="localhost",
106
- client_id=client_id,
107
- )
108
-
109
- async with MQTTClient(config) as client:
110
- # 自动订阅 cloud/config-service
111
- rpc = ConventionalRPCManager(client, my_topic=f"cloud/{client_id}")
112
-
113
- @rpc.register("get_device_config")
114
- async def get_device_config(params):
115
- return {"update_interval": 60, "servers": ["s1", "s2"]}
116
-
117
- # 调用边缘设备(自动注入 reply_to="cloud/config-service")
118
- status = await rpc.call("edge/device_123", "execute_command", params={"cmd": "restart"})
119
- print(status)
120
-
121
- await asyncio.sleep(60)
122
-
123
- # 运行边缘设备或云端
124
- asyncio.run(edge_device()) # 或 asyncio.run(cloud_service())
125
- ```
126
-
127
- **对比传统 RPC:**
128
-
129
- | 场景 | 传统 RPC | 约定式 RPC |
130
- |-----|---------|-----------|
131
- | 初始化 | `rpc = RPCManager(client)`<br>`client.subscribe("edge/123", rpc.handle_rpc_message)` | `rpc = ConventionalRPCManager(client, my_topic="edge/123")`<br>→ 自动订阅 |
132
- | 调用 | `await rpc.call(topic="cloud/svc", method="get", reply_to="edge/123")` | `await rpc.call("cloud/svc", "get")` |
133
- | 代码量 | 100% | **60%** ↓ |
134
-
135
- ---
136
-
137
- ### 3. RPC 基础用法(传统模式)
138
-
139
- 需要手动订阅和传递 `reply_to`,适用于需要精细控制的场景。
140
-
141
- ```python
142
- from mqttxx import MQTTClient, MQTTConfig, RPCManager
143
-
144
- async def main():
145
- config = MQTTConfig(broker_host="localhost", client_id="device_001")
146
-
147
- async with MQTTClient(config) as client:
148
- rpc = RPCManager(client)
149
-
150
- # 注册本地方法
151
- @rpc.register("get_status")
152
- async def get_status(params):
153
- return {"status": "online", "cpu": 45.2}
154
-
155
- # 订阅 RPC 主题
156
- client.subscribe(
157
- "server/device_001",
158
- rpc.handle_rpc_message
159
- )
160
-
161
- # 调用远程方法
162
- result = await rpc.call(
163
- topic="bots/device_002",
164
- method="get_data",
165
- reply_to="server/device_001",
166
- timeout=5
167
- )
168
- print(result) # {"data": [1, 2, 3]}
169
-
170
- await asyncio.sleep(60)
171
-
172
- asyncio.run(main())
173
- ```
174
-
175
- ---
176
-
177
- ### 4. RPC 权限控制
178
-
179
- ```python
180
- from mqttxx import RPCManager, RPCRequest
181
-
182
- async def auth_check(caller_id: str, method: str, request: RPCRequest) -> bool:
183
- # 敏感方法只允许管理员
184
- if method in ["delete_user", "reset_system"]:
185
- return caller_id in ["admin_001", "admin_002"]
186
- return True
187
-
188
- rpc = RPCManager(client, auth_callback=auth_check)
189
-
190
- @rpc.register("delete_user")
191
- async def delete_user(params):
192
- return {"result": "user deleted"}
193
-
194
- # 未授权调用会返回 "Permission denied"
195
- ```
196
-
197
- ---
198
-
199
- ### 5. TLS/SSL 和认证
200
-
201
- ```python
202
- from mqttxx import MQTTConfig, TLSConfig, AuthConfig
203
- from pathlib import Path
204
-
205
- config = MQTTConfig(
206
- broker_host="secure.mqtt.example.com",
207
- broker_port=8883,
208
- tls=TLSConfig(
209
- enabled=True,
210
- ca_certs=Path("ca.crt"),
211
- certfile=Path("client.crt"),
212
- keyfile=Path("client.key"),
213
- ),
214
- auth=AuthConfig(
215
- username="mqtt_user",
216
- password="mqtt_password",
217
- ),
218
- )
219
-
220
- async with MQTTClient(config) as client:
221
- await client.publish("secure/topic", "encrypted message")
222
- ```
223
-
224
- ---
225
-
226
- ## API 速查
227
-
228
- ### MQTTClient
229
-
230
- ```python
231
- class MQTTClient:
232
- def __init__(self, config: MQTTConfig)
233
- async def connect(self) -> None
234
- async def disconnect(self) -> None
235
- def subscribe(self, topic: str, handler: Callable) -> None
236
- async def publish(self, topic: str, payload: str, qos: int = 0) -> None
237
-
238
- @property
239
- def is_connected(self) -> bool
240
- ```
241
-
242
- ---
243
-
244
- ### RPCManager(传统 RPC)
245
-
246
- ```python
247
- class RPCManager:
248
- def __init__(self, client: MQTTClient, config: RPCConfig = None, auth_callback: AuthCallback = None)
249
-
250
- def register(self, method_name: str) # 装饰器
251
- def unregister(self, method_name: str) -> None
252
- def handle_rpc_message(self, topic: str, message: RPCRequest | RPCResponse) -> None
253
-
254
- async def call(
255
- self,
256
- topic: str,
257
- method: str,
258
- params: Any = None,
259
- reply_to: str = None, # 必填
260
- timeout: float = None,
261
- ) -> Any
262
- ```
263
-
264
- ---
265
-
266
- ### ConventionalRPCManager(约定式 RPC)
267
-
268
- ```python
269
- class ConventionalRPCManager(RPCManager):
270
- def __init__(
271
- self,
272
- client: MQTTClient,
273
- my_topic: str, # 本节点 topic(自动订阅,自动注入到 reply_to)
274
- config: RPCConfig = None,
275
- auth_callback: AuthCallback = None,
276
- )
277
-
278
- async def call(
279
- self,
280
- topic: str, # 对方的 topic
281
- method: str,
282
- params: Any = None,
283
- timeout: float = None,
284
- reply_to: str = None, # 可选,默认使用 my_topic
285
- ) -> Any
286
-
287
- # 属性
288
- my_topic: str # 当前 topic(只读)
289
- ```
290
-
291
- **使用示例:**
292
-
293
- ```python
294
- # 边缘设备
295
- rpc = ConventionalRPCManager(client, my_topic="edge/device_123")
296
- config = await rpc.call("cloud/config-service", "get_config")
297
-
298
- # 云端服务
299
- rpc = ConventionalRPCManager(client, my_topic="cloud/config-service")
300
- status = await rpc.call("edge/device_123", "execute_command")
301
-
302
- # 微服务
303
- rpc = ConventionalRPCManager(client, my_topic="auth-service")
304
- user = await rpc.call("user-service", "get_user", params={"id": 123})
305
- ```
306
-
307
- ---
308
-
309
- ## 配置对象
310
-
311
- ### MQTTConfig
312
-
313
- ```python
314
- @dataclass
315
- class MQTTConfig:
316
- broker_host: str
317
- broker_port: int = 1883
318
- client_id: str = "" # 空字符串 = 自动生成
319
- keepalive: int = 60
320
- clean_session: bool = False
321
- tls: TLSConfig = field(default_factory=TLSConfig)
322
- auth: AuthConfig = field(default_factory=AuthConfig)
323
- reconnect: ReconnectConfig = field(default_factory=ReconnectConfig)
324
- max_queued_messages: int = 0 # 0 = 无限
325
- max_payload_size: int = 1024 * 1024 # 1MB
326
- ```
327
-
328
- ### TLSConfig
329
-
330
- ```python
331
- @dataclass
332
- class TLSConfig:
333
- enabled: bool = False
334
- ca_certs: Optional[Path] = None
335
- certfile: Optional[Path] = None
336
- keyfile: Optional[Path] = None
337
- verify_mode: str = "CERT_REQUIRED" # CERT_REQUIRED | CERT_OPTIONAL | CERT_NONE
338
- check_hostname: bool = True
339
- ```
340
-
341
- ### AuthConfig
342
-
343
- ```python
344
- @dataclass
345
- class AuthConfig:
346
- username: Optional[str] = None
347
- password: Optional[str] = None
348
- ```
349
-
350
- ### ReconnectConfig
351
-
352
- ```python
353
- @dataclass
354
- class ReconnectConfig:
355
- enabled: bool = True
356
- interval: int = 5 # 初始重连间隔(秒)
357
- max_attempts: int = 0 # 0 = 无限重试
358
- backoff_multiplier: float = 1.5 # 指数退避倍数
359
- max_interval: int = 60 # 最大重连间隔(秒)
360
- ```
361
-
362
- ### RPCConfig
363
-
364
- ```python
365
- @dataclass
366
- class RPCConfig:
367
- default_timeout: float = 30.0 # 默认超时时间(秒)
368
- max_concurrent_calls: int = 100 # 最大并发调用数
369
- ```
370
-
371
- ---
372
-
373
- ## 异常系统
374
-
375
- ```python
376
- # 基础异常
377
- class MQTTXError(Exception)
378
- class ConnectionError(MQTTXError)
379
- class MessageError(MQTTXError)
380
- class RPCError(MQTTXError)
381
-
382
- # RPC 异常
383
- class RPCTimeoutError(RPCError) # RPC 调用超时
384
- class RPCRemoteError(RPCError) # 远程方法执行失败
385
- class RPCMethodNotFoundError(RPCError) # 方法未找到
386
- class PermissionDeniedError(RPCError) # 权限拒绝
387
- class TooManyConcurrentCallsError(RPCError) # 并发调用超限
388
-
389
- # 错误码
390
- class ErrorCode(IntEnum):
391
- NOT_CONNECTED = 1001
392
- RPC_TIMEOUT = 3002
393
- PERMISSION_DENIED = 4001
394
- # ... 更多错误码见源码
395
- ```
396
-
397
- **使用示例:**
398
-
399
- ```python
400
- from mqttxx import RPCTimeoutError, RPCRemoteError
401
-
402
- try:
403
- result = await rpc.call_bot("456", "get_data", timeout=5)
404
- except RPCTimeoutError:
405
- print("调用超时")
406
- except RPCRemoteError as e:
407
- print(f"远程方法执行失败: {e}")
408
- ```
409
-
410
- ---
411
-
412
- ## RPC 消息协议
413
-
414
- ### 请求
415
-
416
- ```json
417
- {
418
- "type": "rpc_request",
419
- "request_id": "uuid-string",
420
- "method": "get_status",
421
- "params": {"id": 123},
422
- "reply_to": "server/device_001",
423
- "caller_id": "device_002"
424
- }
425
- ```
426
-
427
- ### 响应(成功)
428
-
429
- ```json
430
- {
431
- "type": "rpc_response",
432
- "request_id": "uuid-string",
433
- "result": {"status": "online"}
434
- }
435
- ```
436
-
437
- ### 响应(错误)
438
-
439
- ```json
440
- {
441
- "type": "rpc_response",
442
- "request_id": "uuid-string",
443
- "error": "Permission denied"
444
- }
445
- ```
446
-
447
- ---
448
-
449
- ## v2.0.0 重大变更
450
-
451
- 从 v2.0.0 开始,完全重写为基于 aiomqtt(纯 async/await),**不兼容** v0.x.x(gmqtt)。
452
-
453
- **主要变化:**
454
- - ✅ aiomqtt 替代 gmqtt
455
- - ✅ 原生 dataclass 替代 python-box(性能提升 6 倍)
456
- - ✅ 修复所有 P0 缺陷(并发竞态、连接状态、RPC 超时兜底)
457
- - ✅ 新增约定式 RPC(`ConventionalRPCManager`)
458
- - ✅ 新增权限控制(`auth_callback`)
459
- - ✅ 新增 TLS/SSL 支持
460
-
461
- **迁移关键点:**
462
- 1. 使用 `MQTTConfig` 配置对象
463
- 2. 使用 `async with` 上下文管理器
464
- 3. `publish_message()` → `publish()`
465
- 4. 移除 `EventEmitter`(改用 dict)
466
-
467
- ---
468
-
469
- ## 依赖
470
-
471
- - Python >= 3.10
472
- - aiomqtt >= 2.0.0, < 3.0.0
473
- - loguru >= 0.7.0
474
-
475
- ---
476
-
477
- ## 开发
478
-
479
- ```bash
480
- git clone https://github.com/yourusername/mqttx.git
481
- cd mqttx
482
- pip install -e ".[dev]"
483
- pytest tests/
484
- ```
485
-
486
- ---
487
-
488
- ## License
489
-
490
- MIT
@@ -1,12 +0,0 @@
1
- mqttxx/__init__.py,sha256=9jhV_niQTYU2D4aD4AfVvWAAfbYf4o2vjwbwSMA_Rv4,1722
2
- mqttxx/client.py,sha256=0wtw8fXW1buqnGbKOrg5sBJDORdSoHb3ErapiK3pkl4,16358
3
- mqttxx/config.py,sha256=MlhayABPcyHWAQR1kkWT5SNlVPIcJ5j8tBikvt1kMNE,5101
4
- mqttxx/conventions.py,sha256=d_OwOvLG4cdptZ2wmrsKe055LRVZXMO0pmt-OAgJhvI,4507
5
- mqttxx/exceptions.py,sha256=EC9NZmvZbfrK9iEdSyoYt4pUl0dp7yVup8mjOfgBaMw,3704
6
- mqttxx/protocol.py,sha256=mZfGY3naGGT_mDsv9enFPiAgUFjoqA5WbZhaT5xYCBQ,5313
7
- mqttxx/rpc.py,sha256=diXZg-H_MB6SiXll4Q9voh2ztC21_UAOgMKeLyBBJ4s,13597
8
- mqttxx-2.0.3.dist-info/LICENSE,sha256=tfwCF8HPjAvvq_veljjH3_YL90X6ttkjTpRl3BGcXTg,1067
9
- mqttxx-2.0.3.dist-info/METADATA,sha256=DuDs0jX9uoJD6dg_7r4uspORGjKsc_jN1WHRx27zZB8,11545
10
- mqttxx-2.0.3.dist-info/WHEEL,sha256=Z4pYXqR_rTB7OWNDYFOm1qRk0RX6GFP2o8LgvP453Hk,91
11
- mqttxx-2.0.3.dist-info/top_level.txt,sha256=4SNRXgOpGCT6ThBzAb8zhUmTyZwnIracAIJWAdXXzw0,7
12
- mqttxx-2.0.3.dist-info/RECORD,,
File without changes