codosdk 1.0.41__tar.gz → 1.0.43__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.
Files changed (52) hide show
  1. {codosdk-1.0.41 → codosdk-1.0.43}/PKG-INFO +2 -2
  2. {codosdk-1.0.41 → codosdk-1.0.43}/codosdk.egg-info/PKG-INFO +2 -2
  3. {codosdk-1.0.41 → codosdk-1.0.43}/codosdk.egg-info/requires.txt +1 -1
  4. {codosdk-1.0.41 → codosdk-1.0.43}/setup.py +2 -2
  5. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/mqhelper.py +132 -24
  6. {codosdk-1.0.41 → codosdk-1.0.43}/LICENSE +0 -0
  7. {codosdk-1.0.41 → codosdk-1.0.43}/README.md +0 -0
  8. {codosdk-1.0.41 → codosdk-1.0.43}/codosdk.egg-info/SOURCES.txt +0 -0
  9. {codosdk-1.0.41 → codosdk-1.0.43}/codosdk.egg-info/dependency_links.txt +0 -0
  10. {codosdk-1.0.41 → codosdk-1.0.43}/codosdk.egg-info/top_level.txt +0 -0
  11. {codosdk-1.0.41 → codosdk-1.0.43}/opssdk/__init__.py +0 -0
  12. {codosdk-1.0.41 → codosdk-1.0.43}/opssdk/utils/__init__.py +0 -0
  13. {codosdk-1.0.41 → codosdk-1.0.43}/pyproject.toml +0 -0
  14. {codosdk-1.0.41 → codosdk-1.0.43}/setup.cfg +0 -0
  15. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/__init__.py +0 -0
  16. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/api_set.py +0 -0
  17. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/apis/__init__.py +0 -0
  18. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/apis/admin_apis.py +0 -0
  19. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/apis/agent_apis.py +0 -0
  20. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/apis/cmdb_apis.py +0 -0
  21. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/apis/kerrigan_apis.py +0 -0
  22. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/apis/mgv4_apis.py +0 -0
  23. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/apis/notice_apis.py +0 -0
  24. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/apis/task_apis.py +0 -0
  25. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/application.py +0 -0
  26. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/base_handler.py +0 -0
  27. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/cache.py +0 -0
  28. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/cache_context.py +0 -0
  29. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/client.py +0 -0
  30. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/cloud/__init__.py +0 -0
  31. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/cloud/qcloud_api.py +0 -0
  32. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/cloud/ucloud_api.py +0 -0
  33. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/cloud_utils.py +0 -0
  34. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/configs.py +0 -0
  35. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/consts.py +0 -0
  36. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/crud_utils.py +0 -0
  37. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/db_context.py +0 -0
  38. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/error.py +0 -0
  39. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/fetch_coroutine.py +0 -0
  40. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/jwt_token.py +0 -0
  41. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/ldap.py +0 -0
  42. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/logger.py +0 -0
  43. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/model_utils.py +0 -0
  44. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/program.py +0 -0
  45. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/salt_api.py +0 -0
  46. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/sqlalchemy_pagination.py +0 -0
  47. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/tools.py +0 -0
  48. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/utils/__init__.py +0 -0
  49. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/utils/cc_crypto.py +0 -0
  50. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/utils/date_format.py +0 -0
  51. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/utils/pydantic_utils.py +0 -0
  52. {codosdk-1.0.41 → codosdk-1.0.43}/websdk2/web_logs.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codosdk
3
- Version: 1.0.41
3
+ Version: 1.0.43
4
4
  Summary: CODO项目的Python SDK
5
5
  Home-page: https://github.com/ss1917/codo_sdk/
6
6
  Author: shenshuo
@@ -23,7 +23,7 @@ Requires-Dist: fire==0.5.0
23
23
  Requires-Dist: shortuuid==1.0.11
24
24
  Requires-Dist: pymysql==0.9.3
25
25
  Requires-Dist: sqlalchemy==1.3.23
26
- Requires-Dist: pika==1.3.1
26
+ Requires-Dist: pika==1.3.2
27
27
  Requires-Dist: PyJWT==2.0.1
28
28
  Requires-Dist: requests==2.28.2
29
29
  Requires-Dist: redis==4.5.1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codosdk
3
- Version: 1.0.41
3
+ Version: 1.0.43
4
4
  Summary: CODO项目的Python SDK
5
5
  Home-page: https://github.com/ss1917/codo_sdk/
6
6
  Author: shenshuo
@@ -23,7 +23,7 @@ Requires-Dist: fire==0.5.0
23
23
  Requires-Dist: shortuuid==1.0.11
24
24
  Requires-Dist: pymysql==0.9.3
25
25
  Requires-Dist: sqlalchemy==1.3.23
26
- Requires-Dist: pika==1.3.1
26
+ Requires-Dist: pika==1.3.2
27
27
  Requires-Dist: PyJWT==2.0.1
28
28
  Requires-Dist: requests==2.28.2
29
29
  Requires-Dist: redis==4.5.1
@@ -2,7 +2,7 @@ fire==0.5.0
2
2
  shortuuid==1.0.11
3
3
  pymysql==0.9.3
4
4
  sqlalchemy==1.3.23
5
- pika==1.3.1
5
+ pika==1.3.2
6
6
  PyJWT==2.0.1
7
7
  requests==2.28.2
8
8
  redis==4.5.1
@@ -9,7 +9,7 @@ desc : CODO SDK
9
9
  import sys
10
10
  from setuptools import setup, find_packages
11
11
 
12
- VERSION = '1.0.41'
12
+ VERSION = '1.0.43'
13
13
 
14
14
  if sys.version_info < (2, 7) or (3, 0) <= sys.version_info < (3, 6):
15
15
  print('This program requires at least Python 2.7 or 3.6 to run.')
@@ -24,7 +24,7 @@ def get_data_files():
24
24
 
25
25
 
26
26
  def get_install_requires():
27
- requires = ['fire==0.5.0', 'shortuuid==1.0.11', 'pymysql==0.9.3', 'sqlalchemy==1.3.23', 'pika==1.3.1',
27
+ requires = ['fire==0.5.0', 'shortuuid==1.0.11', 'pymysql==0.9.3', 'sqlalchemy==1.3.23', 'pika==1.3.2',
28
28
  'PyJWT==2.0.1', 'requests==2.28.2', 'redis==4.5.1', 'tornado>=6.0', 'loguru>=0.6.0',
29
29
  'cryptography==42.0.4', 'ldap3==2.9', 'pydantic>=1.10.5']
30
30
  return requires
@@ -148,22 +148,31 @@ class RabbitMQConnectionPool:
148
148
  mq_config = configs[const.MQ_CONFIG_ITEM][mq_key]
149
149
  cls._validate_config(mq_config)
150
150
 
151
- # 创建连接
151
+ # 创建连接参数,优化超时和重试设置
152
152
  credentials = pika.PlainCredentials(
153
153
  mq_config[const.MQ_USER],
154
154
  mq_config[const.MQ_PWD]
155
155
  )
156
- connection = pika.BlockingConnection(
157
- pika.ConnectionParameters(
158
- host=mq_config[const.MQ_ADDR],
159
- port=int(mq_config[const.MQ_PORT]),
160
- virtual_host=mq_config[const.MQ_VHOST],
161
- credentials=credentials,
162
- heartbeat=600,
163
- blocked_connection_timeout=300
164
- )
156
+
157
+ connection_params = pika.ConnectionParameters(
158
+ host=mq_config[const.MQ_ADDR],
159
+ port=int(mq_config[const.MQ_PORT]),
160
+ virtual_host=mq_config[const.MQ_VHOST],
161
+ credentials=credentials,
162
+ heartbeat=300, # 减少心跳间隔
163
+ blocked_connection_timeout=60, # 减少阻塞超时
164
+ socket_timeout=30, # 添加socket超时
165
+ connection_attempts=3, # 连接重试次数
166
+ retry_delay=1, # 重试延迟
167
+ stack_timeout=30 # 栈超时
165
168
  )
166
169
 
170
+ connection = pika.BlockingConnection(connection_params)
171
+
172
+ # 验证连接状态
173
+ if not connection.is_open:
174
+ raise Exception("Connection failed to open properly")
175
+
167
176
  cls._connections[mq_key] = connection
168
177
  logging.info(f"Created new RabbitMQ connection for key: {mq_key}")
169
178
 
@@ -255,12 +264,86 @@ class MessageQueueBase:
255
264
  )
256
265
 
257
266
  def _get_channel(self) -> Any:
258
- """获取新的通道"""
259
- connection = RabbitMQConnectionPool.get_connection(self._mq_key)
260
- return connection.channel()
267
+ """获取新的通道 - 使用更安全的方式避免pika并发Bug"""
268
+ max_attempts = 2 # 减少重试次数避免过度重试
269
+
270
+ for attempt in range(max_attempts):
271
+ try:
272
+ # 每次都创建新连接,避免连接复用导致的并发问题
273
+ if attempt > 0:
274
+ # 强制清理并等待
275
+ with RabbitMQConnectionPool._connection_lock:
276
+ try:
277
+ if self._mq_key in RabbitMQConnectionPool._connections:
278
+ RabbitMQConnectionPool._cleanup_connection(self._mq_key)
279
+ RabbitMQConnectionPool._connections[self._mq_key] = None
280
+ except:
281
+ pass
282
+
283
+ # 等待稍长时间让连接完全清理
284
+ time.sleep(0.5)
285
+
286
+ connection = RabbitMQConnectionPool.get_connection(self._mq_key)
287
+ if not connection:
288
+ raise Exception("Failed to get connection")
289
+
290
+ # 简化状态检查,避免触发pika内部Bug
291
+ if hasattr(connection, 'is_open') and not connection.is_open:
292
+ raise Exception("Connection is not open")
293
+
294
+ # 直接创建channel,不做额外的心跳检查
295
+ channel = connection.channel()
296
+ if not channel:
297
+ raise Exception("Failed to create channel")
298
+
299
+ if hasattr(channel, 'is_open') and not channel.is_open:
300
+ raise Exception("Channel is not open")
301
+
302
+ return channel
303
+
304
+ except Exception as e:
305
+ self.logger.warning(f"Failed to get channel (attempt {attempt + 1}/{max_attempts}): {e}")
306
+ if attempt == max_attempts - 1:
307
+ # 最后一次失败,创建一个临时连接
308
+ try:
309
+ return self._create_emergency_channel()
310
+ except Exception as emergency_e:
311
+ raise Exception(f"All channel attempts failed. Last error: {e}, Emergency error: {emergency_e}")
312
+
313
+ raise Exception("Unexpected error in channel creation")
314
+
315
+ def _create_emergency_channel(self) -> Any:
316
+ """创建紧急通道 - 直接连接,不使用连接池"""
317
+ try:
318
+ mq_config = configs[const.MQ_CONFIG_ITEM][self._mq_key]
319
+
320
+ credentials = pika.PlainCredentials(
321
+ mq_config[const.MQ_USER],
322
+ mq_config[const.MQ_PWD]
323
+ )
324
+
325
+ # 使用最简单的连接参数
326
+ connection_params = pika.ConnectionParameters(
327
+ host=mq_config[const.MQ_ADDR],
328
+ port=int(mq_config[const.MQ_PORT]),
329
+ virtual_host=mq_config[const.MQ_VHOST],
330
+ credentials=credentials,
331
+ heartbeat=0, # 禁用心跳
332
+ socket_timeout=10
333
+ )
334
+
335
+ connection = pika.BlockingConnection(connection_params)
336
+ channel = connection.channel()
337
+
338
+ self.logger.info("Created emergency channel")
339
+ return channel
340
+
341
+ except Exception as e:
342
+ self.logger.error(f"Failed to create emergency channel: {e}")
343
+ raise
261
344
 
262
345
  def _with_retry(self, operation: Callable, *args, **kwargs) -> Any:
263
- """带重试机制执行操作"""
346
+ """带重试机制执行操作 - 优化版本"""
264
347
  last_exception = None
265
348
 
266
349
  for attempt in range(self._max_retries + 1):
@@ -273,8 +356,24 @@ class MessageQueueBase:
273
356
  break
274
357
  else:
275
358
  self.logger.warning(f"Attempt {attempt + 1} failed: {e}. Retrying...")
276
- # 触发连接重建
277
- RabbitMQConnectionPool._connections[self._mq_key] = None
359
+
360
+ # 只在特定错误类型时清理连接
361
+ error_str = str(e).lower()
362
+ if any(keyword in error_str for keyword in [
363
+ 'connection', 'channel', 'socket', 'transport',
364
+ 'callback', 'deque', 'abort', 'closed', 'asynctransport'
365
+ ]):
366
+ # 安全地清理连接
367
+ with RabbitMQConnectionPool._connection_lock:
368
+ try:
369
+ if self._mq_key in RabbitMQConnectionPool._connections:
370
+ RabbitMQConnectionPool._cleanup_connection(self._mq_key)
371
+ RabbitMQConnectionPool._connections[self._mq_key] = None
372
+ except Exception as cleanup_e:
373
+ self.logger.debug(f"Error during connection cleanup: {cleanup_e}")
374
+
375
+ # 递增延迟重试,但不要太长
376
+ time.sleep(min(0.1 * (2 ** attempt), 2.0))
278
377
 
279
378
  raise last_exception
280
379
 
@@ -302,7 +401,7 @@ class MessageQueueBase:
302
401
 
303
402
  # 如果有队列名,声明并绑定队列
304
403
  if self._queue_name:
305
- channel.queue_declare(queue=self._queue_name)
404
+ channel.queue_declare(queue=self._queue_name, durable=True)
306
405
  channel.queue_bind(
307
406
  exchange=self._exchange,
308
407
  queue=self._queue_name,
@@ -322,8 +421,11 @@ class MessageQueueBase:
322
421
  f'Published message to exchange:{self._exchange}, routing_key:{actual_routing_key}')
323
422
 
324
423
  finally:
325
- if channel and channel.is_open:
326
- channel.close()
424
+ if channel and hasattr(channel, 'is_open') and channel.is_open:
425
+ try:
426
+ channel.close()
427
+ except:
428
+ pass # 忽略关闭时的错误
327
429
 
328
430
  self._with_retry(_publish_operation)
329
431
 
@@ -373,11 +475,17 @@ class MessageQueueBase:
373
475
 
374
476
  except KeyboardInterrupt:
375
477
  self.logger.info("Received interrupt signal, stopping consumption")
376
- if channel and channel.is_open:
377
- channel.stop_consuming()
478
+ if channel and hasattr(channel, 'is_open') and channel.is_open:
479
+ try:
480
+ channel.stop_consuming()
481
+ except:
482
+ pass
378
483
  finally:
379
- if channel and channel.is_open:
380
- channel.close()
484
+ if channel and hasattr(channel, 'is_open') and channel.is_open:
485
+ try:
486
+ channel.close()
487
+ except:
488
+ pass
381
489
 
382
490
  self._with_retry(_consume_operation)
383
491
 
@@ -498,5 +606,5 @@ if __name__ == "__main__":
498
606
 
499
607
  # 示例3:向后兼容的使用方式
500
608
  print("=== 向后兼容使用 ===")
501
- with MessageQueueBase('test_exchange', 'direct', routing_key='test.key') as old_mq:
609
+ with MessageQueueBase('test_exchange', 'direct', routing_key='test.key', queue_name='task_test_queue') as old_mq:
502
610
  old_mq.publish_message("Hello from old interface!")
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