mqttxx 2.0.2__py3-none-any.whl → 2.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.
- mqttxx/client.py +5 -7
- mqttxx/rpc.py +17 -24
- {mqttxx-2.0.2.dist-info → mqttxx-2.0.3.dist-info}/METADATA +1 -1
- mqttxx-2.0.3.dist-info/RECORD +12 -0
- mqttxx-2.0.2.dist-info/RECORD +0 -12
- {mqttxx-2.0.2.dist-info → mqttxx-2.0.3.dist-info}/LICENSE +0 -0
- {mqttxx-2.0.2.dist-info → mqttxx-2.0.3.dist-info}/WHEEL +0 -0
- {mqttxx-2.0.2.dist-info → mqttxx-2.0.3.dist-info}/top_level.txt +0 -0
mqttxx/client.py
CHANGED
|
@@ -284,6 +284,7 @@ class MQTTClient:
|
|
|
284
284
|
else:
|
|
285
285
|
# 修复 P0-3:RPC 请求无处理器时,立即返回错误响应(防止调用方超时)
|
|
286
286
|
from .protocol import RPCRequest, RPCResponse
|
|
287
|
+
|
|
287
288
|
if isinstance(msg, RPCRequest):
|
|
288
289
|
logger.warning(
|
|
289
290
|
f"收到 RPC 请求但无处理器 - topic: {topic_str}, method: {msg.method}"
|
|
@@ -291,9 +292,11 @@ class MQTTClient:
|
|
|
291
292
|
# 立即发送错误响应
|
|
292
293
|
error_response = RPCResponse(
|
|
293
294
|
request_id=msg.request_id,
|
|
294
|
-
error=f"No RPC handler registered for topic: {topic_str}"
|
|
295
|
+
error=f"No RPC handler registered for topic: {topic_str}",
|
|
296
|
+
)
|
|
297
|
+
await self.publish(
|
|
298
|
+
msg.reply_to, json.dumps(error_response.to_dict()), qos=1
|
|
295
299
|
)
|
|
296
|
-
await self.publish(msg.reply_to, json.dumps(error_response.to_dict()), qos=1)
|
|
297
300
|
else:
|
|
298
301
|
logger.debug(f"收到消息(无处理器)- topic: {topic_str}")
|
|
299
302
|
|
|
@@ -381,10 +384,6 @@ class MQTTClient:
|
|
|
381
384
|
|
|
382
385
|
async def publish(self, topic: str, payload: str, qos: int = 0):
|
|
383
386
|
"""发布消息
|
|
384
|
-
|
|
385
|
-
修复点:
|
|
386
|
-
- ✅ P1-2: 未连接时记录警告
|
|
387
|
-
|
|
388
387
|
Args:
|
|
389
388
|
topic: 目标主题
|
|
390
389
|
payload: 消息载荷(字符串)
|
|
@@ -399,7 +398,6 @@ class MQTTClient:
|
|
|
399
398
|
|
|
400
399
|
try:
|
|
401
400
|
await self._client.publish(topic, payload, qos=qos)
|
|
402
|
-
logger.debug(f"消息已发布 - topic: {topic}, qos: {qos}")
|
|
403
401
|
except aiomqtt.MqttError as e:
|
|
404
402
|
logger.error(f"发布失败 - topic: {topic}, error: {e}")
|
|
405
403
|
|
mqttxx/rpc.py
CHANGED
|
@@ -75,7 +75,7 @@ class RPCManager:
|
|
|
75
75
|
self,
|
|
76
76
|
client: MQTTClient,
|
|
77
77
|
config: Optional[RPCConfig] = None,
|
|
78
|
-
auth_callback: Optional[AuthCallback] = None
|
|
78
|
+
auth_callback: Optional[AuthCallback] = None,
|
|
79
79
|
):
|
|
80
80
|
"""初始化 RPC 管理器
|
|
81
81
|
|
|
@@ -106,7 +106,9 @@ class RPCManager:
|
|
|
106
106
|
# RPC 状态
|
|
107
107
|
self._pending_calls: dict[str, asyncio.Future] = {} # request_id → Future
|
|
108
108
|
self._handlers: dict[str, Callable] = {} # method_name → handler
|
|
109
|
-
self._pending_calls_lock =
|
|
109
|
+
self._pending_calls_lock = (
|
|
110
|
+
asyncio.Lock()
|
|
111
|
+
) # 修复 P0-1:保护 _pending_calls 并发访问
|
|
110
112
|
|
|
111
113
|
logger.info("RPCManager 已初始化")
|
|
112
114
|
|
|
@@ -129,9 +131,10 @@ class RPCManager:
|
|
|
129
131
|
# 同步方法也支持
|
|
130
132
|
return {"result": "ok"}
|
|
131
133
|
"""
|
|
134
|
+
|
|
132
135
|
def decorator(func: Callable):
|
|
133
136
|
self._handlers[method_name] = func
|
|
134
|
-
logger.
|
|
137
|
+
# logger.debug(f"RPC 方法已注册: {method_name}") 无需输出
|
|
135
138
|
return func
|
|
136
139
|
|
|
137
140
|
return decorator
|
|
@@ -171,12 +174,12 @@ class RPCManager:
|
|
|
171
174
|
if isinstance(message, RPCRequest):
|
|
172
175
|
asyncio.create_task(
|
|
173
176
|
self._handle_request(topic, message),
|
|
174
|
-
name=f"rpc_req_{message.request_id[:8]}"
|
|
177
|
+
name=f"rpc_req_{message.request_id[:8]}",
|
|
175
178
|
)
|
|
176
179
|
elif isinstance(message, RPCResponse):
|
|
177
180
|
asyncio.create_task(
|
|
178
181
|
self._handle_response(topic, message),
|
|
179
|
-
name=f"rpc_resp_{message.request_id[:8]}"
|
|
182
|
+
name=f"rpc_resp_{message.request_id[:8]}",
|
|
180
183
|
)
|
|
181
184
|
|
|
182
185
|
async def call(
|
|
@@ -316,8 +319,7 @@ class RPCManager:
|
|
|
316
319
|
f"RPC 权限拒绝 - caller: {message.caller_id}, method: {message.method}"
|
|
317
320
|
)
|
|
318
321
|
response = RPCResponse(
|
|
319
|
-
request_id=message.request_id,
|
|
320
|
-
error="Permission denied"
|
|
322
|
+
request_id=message.request_id, error="Permission denied"
|
|
321
323
|
)
|
|
322
324
|
await self._send_response(message.reply_to, response)
|
|
323
325
|
return
|
|
@@ -328,8 +330,7 @@ class RPCManager:
|
|
|
328
330
|
if not handler:
|
|
329
331
|
logger.warning(f"方法未找到 - method: {message.method}")
|
|
330
332
|
response = RPCResponse(
|
|
331
|
-
request_id=message.request_id,
|
|
332
|
-
error=f"方法未找到: {message.method}"
|
|
333
|
+
request_id=message.request_id, error=f"方法未找到: {message.method}"
|
|
333
334
|
)
|
|
334
335
|
else:
|
|
335
336
|
# 执行方法
|
|
@@ -353,22 +354,20 @@ class RPCManager:
|
|
|
353
354
|
try:
|
|
354
355
|
if asyncio.iscoroutinefunction(self._auth_callback):
|
|
355
356
|
allowed = await self._auth_callback(
|
|
356
|
-
message.caller_id,
|
|
357
|
-
message.method,
|
|
358
|
-
message
|
|
357
|
+
message.caller_id, message.method, message
|
|
359
358
|
)
|
|
360
359
|
else:
|
|
361
360
|
allowed = self._auth_callback(
|
|
362
|
-
message.caller_id,
|
|
363
|
-
message.method,
|
|
364
|
-
message
|
|
361
|
+
message.caller_id, message.method, message
|
|
365
362
|
)
|
|
366
363
|
return bool(allowed)
|
|
367
364
|
except Exception as e:
|
|
368
365
|
logger.exception(f"权限检查失败: {e}")
|
|
369
366
|
return False # 默认拒绝
|
|
370
367
|
|
|
371
|
-
async def _execute_handler(
|
|
368
|
+
async def _execute_handler(
|
|
369
|
+
self, handler: Callable, message: RPCRequest
|
|
370
|
+
) -> RPCResponse:
|
|
372
371
|
"""执行 RPC 方法处理器
|
|
373
372
|
|
|
374
373
|
Args:
|
|
@@ -390,10 +389,7 @@ class RPCManager:
|
|
|
390
389
|
result = handler(message.params)
|
|
391
390
|
|
|
392
391
|
logger.debug(f"RPC 方法执行成功 - method: {message.method}")
|
|
393
|
-
return RPCResponse(
|
|
394
|
-
request_id=message.request_id,
|
|
395
|
-
result=result
|
|
396
|
-
)
|
|
392
|
+
return RPCResponse(request_id=message.request_id, result=result)
|
|
397
393
|
|
|
398
394
|
except asyncio.CancelledError:
|
|
399
395
|
# 任务被取消,向上传播
|
|
@@ -401,10 +397,7 @@ class RPCManager:
|
|
|
401
397
|
|
|
402
398
|
except Exception as e:
|
|
403
399
|
logger.exception(f"RPC 方法执行失败 - method: {message.method}")
|
|
404
|
-
return RPCResponse(
|
|
405
|
-
request_id=message.request_id,
|
|
406
|
-
error=str(e)
|
|
407
|
-
)
|
|
400
|
+
return RPCResponse(request_id=message.request_id, error=str(e))
|
|
408
401
|
|
|
409
402
|
async def _send_response(self, topic: str, response: RPCResponse):
|
|
410
403
|
"""发送 RPC 响应
|
|
@@ -0,0 +1,12 @@
|
|
|
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,,
|
mqttxx-2.0.2.dist-info/RECORD
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
mqttxx/__init__.py,sha256=9jhV_niQTYU2D4aD4AfVvWAAfbYf4o2vjwbwSMA_Rv4,1722
|
|
2
|
-
mqttxx/client.py,sha256=mpJS4oTm9V9uqoU4kiRlNK-M8pBVb5mImvxvjyIi57E,16452
|
|
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=9klpfn3Qh2keq8Sk312n0psXa3BSI8ThlFXJZ0O1_wU,13749
|
|
8
|
-
mqttxx-2.0.2.dist-info/LICENSE,sha256=tfwCF8HPjAvvq_veljjH3_YL90X6ttkjTpRl3BGcXTg,1067
|
|
9
|
-
mqttxx-2.0.2.dist-info/METADATA,sha256=19jtxWnJVWu0vsr5qL5xQl8akMy_H-oktO-x5pPA-Gs,11545
|
|
10
|
-
mqttxx-2.0.2.dist-info/WHEEL,sha256=Z4pYXqR_rTB7OWNDYFOm1qRk0RX6GFP2o8LgvP453Hk,91
|
|
11
|
-
mqttxx-2.0.2.dist-info/top_level.txt,sha256=4SNRXgOpGCT6ThBzAb8zhUmTyZwnIracAIJWAdXXzw0,7
|
|
12
|
-
mqttxx-2.0.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|