dotask 0.2.4__tar.gz → 0.3.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dotask
3
- Version: 0.2.4
3
+ Version: 0.3.0
4
4
  Summary: 发布订阅注解方式
5
5
  Home-page: https://gitee.com/d-yz/task-manager
6
6
  Author: dyz
@@ -35,6 +35,11 @@ Dynamic: license-file
35
35
  3. 添加本地调用shell工具函数->可以很好的适配@task消费者
36
36
  1. max_concurrent:控制并发执行的shell数量
37
37
  2. 引入dotask.Shell 调用Shell(max_concurrent=?).local_shell_execute(cmd,callback)来使用
38
+ ### 0.3.x
39
+ 1. 添加令牌桶,通过`from dotask.util import TokenBucket`使用,可以更好的限制生产消费速率
40
+ - 参数说明
41
+ - TokenBucket(capacity=1, rate=0),capacity:令牌桶容量,rate:每秒生成速率
42
+ - get_token(block=True),block:决定令牌桶无令牌时是阻塞还是返回false
38
43
  ## 快速开始
39
44
  ### 安装
40
45
  `pip install dotask`
@@ -16,6 +16,11 @@
16
16
  3. 添加本地调用shell工具函数->可以很好的适配@task消费者
17
17
  1. max_concurrent:控制并发执行的shell数量
18
18
  2. 引入dotask.Shell 调用Shell(max_concurrent=?).local_shell_execute(cmd,callback)来使用
19
+ ### 0.3.x
20
+ 1. 添加令牌桶,通过`from dotask.util import TokenBucket`使用,可以更好的限制生产消费速率
21
+ - 参数说明
22
+ - TokenBucket(capacity=1, rate=0),capacity:令牌桶容量,rate:每秒生成速率
23
+ - get_token(block=True),block:决定令牌桶无令牌时是阻塞还是返回false
19
24
  ## 快速开始
20
25
  ### 安装
21
26
  `pip install dotask`
@@ -33,7 +33,7 @@ class TopicManager:
33
33
  q = self.get_topic_queue(topic)
34
34
  with self.get_topic_lock(topic):
35
35
  q.put(data)
36
- logger.info(f"发布主题-[{topic}]:{data}")
36
+ logger.info(f"publish topic-[{topic}]:{data}")
37
37
 
38
38
  def consume(self, subscribe_topics: List[str], handler: Callable) -> None:
39
39
  subscribe_set: Set[str] = set(subscribe_topics)
@@ -51,7 +51,7 @@ class TopicManager:
51
51
  except queue.Empty:
52
52
  continue
53
53
  except Exception as e:
54
- logger.error(f"消费[{topic}]出错:{e}")
54
+ logger.error(f"Topic[{topic}] consumption error:{e}")
55
55
  time.sleep(0.01)
56
56
 
57
57
  def stop_all(self):
@@ -59,7 +59,7 @@ class TopicManager:
59
59
  with self.global_lock:
60
60
  for topic, q in self.topic_queues.items():
61
61
  q.join()
62
- logger.warn("\n 所有主题的生产/消费已停止")
62
+ logger.warning("\n all task is stopped!")
63
63
 
64
64
  # 初始化全局主题管理器
65
65
  topic_manager = TopicManager()
@@ -102,7 +102,7 @@ def task(
102
102
  # ------------------------------
103
103
  if role == "producer":
104
104
  if not topic:
105
- raise ValueError("生产者必须指定topic参数(如topic='num'")
105
+ raise ValueError("The producer must specify the 'topic' parameter (e.g., topic='num')")
106
106
  topics = [t.strip() for t in topic.split(",")]
107
107
 
108
108
  def producer_func():
@@ -111,9 +111,9 @@ def task(
111
111
  produce_data = func(*fixed_args, **fixed_kwargs)
112
112
  for t in topics:
113
113
  topic_manager.publish(t, produce_data)
114
- time.sleep(0.5)
114
+ time.sleep(0.1)
115
115
  except Exception as e:
116
- print(f" 生产者[{thread_name}]出错:{e}")
116
+ logger.error(f"producer[{thread_name}] error {e}")
117
117
 
118
118
  thread = threading.Thread(
119
119
  target=producer_func,
@@ -128,7 +128,7 @@ def task(
128
128
  # ------------------------------
129
129
  elif role == "consumer":
130
130
  if not subscribe:
131
- raise ValueError("消费者必须指定subscribe参数(如subscribe='num'")
131
+ raise ValueError("The consumer must specify the 'subscribe' parameter (e.g., subscribe='num')")
132
132
  subscribe_topics = [t.strip() for t in subscribe.split(",")]
133
133
  # 解析消费后要发布的主题(多个用,分隔)
134
134
  publish_topics = [t.strip() for t in publish_after.split(",")] if publish_after else []
@@ -150,7 +150,7 @@ def task(
150
150
  # logger.info(f"消费后发布[{t}]:{consume_result}")
151
151
 
152
152
  except Exception as e:
153
- logger.error(f"消费者[{thread_name}]处理出错:{e}")
153
+ logger.error(f"Consumer [{thread_name}] failed to process message: {e}")
154
154
 
155
155
  # 开始消费
156
156
  topic_manager.consume(subscribe_topics, handler)
@@ -164,7 +164,7 @@ def task(
164
164
  return thread
165
165
 
166
166
  else:
167
- raise ValueError(f"不支持的角色:{role},可选:producer/consumer")
167
+ raise ValueError(f"Unsupported role: {role}, available options: producer/consumer")
168
168
 
169
169
  wrapper_result = wrapper()
170
170
  return wrapper_result
@@ -0,0 +1,3 @@
1
+ from .logger import logger
2
+ from .shell import Shell
3
+ from .limit import TokenBucket
@@ -0,0 +1,69 @@
1
+ import time
2
+ from threading import Lock
3
+
4
+
5
+ class TokenBucket:
6
+ """
7
+ 令牌桶限流
8
+ - capacity: 令牌桶最大容量
9
+ - rate: 令牌生成速率 (每秒生成多少个令牌)
10
+ """
11
+ def __init__(self,capacity:int,rate:float):
12
+ self.capacity = max(1,capacity)
13
+ self.rate = max(0.0001,rate)
14
+ # 当前令牌数量
15
+ self.tokens = capacity
16
+ # 上次填充令牌的时间
17
+ self.last_refill_time = time.time()
18
+ self.lock = Lock()
19
+
20
+ def get_token(self,block:bool=False) -> bool:
21
+ """
22
+ 获取令牌
23
+ :param block: 是否阻塞等待令牌(True:阻塞直到获取到令牌,Flase:非阻塞,获取不到直接返回False)
24
+ :return: 是否成功获取令牌
25
+ """
26
+ with self.lock:
27
+ # 1.填充令牌(根据上次填充时间和速率计算应该补充的令牌数)
28
+ now = time.time()
29
+ time_elapsed = now - self.last_refill_time
30
+ # 新生成令牌数=时间间隔*令牌生成速率
31
+ new_tokens = time_elapsed * self.rate
32
+ # 取最小,防止令牌数超过桶的最大容量(防止超发)
33
+ self.tokens = min(self.capacity,self.tokens+new_tokens)
34
+ self.last_refill_time = now
35
+
36
+ # 2.尝试获取令牌
37
+ if self.tokens >= 1:
38
+ self.tokens -= 1
39
+ return True
40
+ elif block:
41
+ # 计算需要等待的时间
42
+ # 需要等待的时间=(需要的令牌数-当前令牌数)/令牌生成速率
43
+ wait_time = (1-self.tokens)/self.rate
44
+ time.sleep(wait_time)
45
+ # 安全消耗1个令牌,避免令牌数为负
46
+ self.tokens = max(0,self.tokens-1)
47
+ return True
48
+ else:
49
+ return False
50
+
51
+
52
+
53
+
54
+
55
+
56
+
57
+
58
+
59
+
60
+
61
+
62
+
63
+
64
+
65
+
66
+
67
+
68
+
69
+
@@ -4,34 +4,20 @@ import subprocess
4
4
  from typing import Optional,Callable
5
5
 
6
6
  class Shell:
7
- _shell_semaphore:threading.Semaphore = None
8
- _semaphore_lock = threading.Lock()
7
+ # _shell_semaphore:threading.Semaphore = None
8
+ # _semaphore_lock = threading.Lock()
9
9
 
10
- def __init__(self,max_concurrent:int,global_sem=False):
11
- self.global_sem = global_sem
12
- if global_sem:
13
- if max_concurrent <= 0:
14
- raise ValueError("最大并发数必须大于0")
15
- self._shell_semaphore = threading.Semaphore(max_concurrent)
16
- self._max_concurrent = max_concurrent
17
- else:
18
- with Shell._semaphore_lock:
19
- if Shell._shell_semaphore is None:
20
- if max_concurrent <= 0:
21
- raise ValueError("最大并发数必须大于0")
22
- Shell._shell_semaphore = threading.Semaphore(max_concurrent)
23
- self._max_concurrent = max_concurrent
24
- else:
25
- if Shell._shell_semaphore._value != max_concurrent:
26
- logger.warning(f"全局信号量已存在(并发数:{Shell._shell_semaphore._value}),忽略传入的max_concurrent={max_concurrent}")
27
- self._max_concurrent = Shell._shell_semaphore._value
10
+ def __init__(self,max_concurrent:int=1):
11
+ if max_concurrent <= 0:
12
+ raise ValueError("Maximum concurrency must be greater than 0")
13
+ self._shell_semaphore = threading.Semaphore(max_concurrent)
14
+ self._max_concurrent = max_concurrent
28
15
 
29
16
  def local_shell_execute(self,cmd:str,callback: Optional[Callable[[bool, str], None]] = None) -> bool:
30
17
  def execute_and_wait():
31
- semaphore = self._shell_semaphore if self.global_sem else Shell._shell_semaphore
32
-
33
- with semaphore:
34
- logger.debug(f"实例:{id(self)}-剩余并发名额: {semaphore._value} | 开始执行命令: {cmd[:50]}")
18
+ with self._shell_semaphore:
19
+ truncated_cmd = cmd[:50] + "..." if len(cmd) > 50 else cmd
20
+ logger.debug(f"Instance {id(self)}: Remaining concurrency quota {self._shell_semaphore._value} | Executing command: {truncated_cmd}")
35
21
  proc = None
36
22
  try:
37
23
  # 创建并执行命令
@@ -48,15 +34,15 @@ class Shell:
48
34
 
49
35
  # 处理结果
50
36
  if proc.returncode == 0:
51
- logger.debug(f"命令执行成功: {cmd[:50]}")
37
+ logger.debug(f"Command Succeeded: {truncated_cmd}")
52
38
  self._callback(callback,success=True, result=stdout.strip())
53
39
  else:
54
- error_msg = f"返回码:{proc.returncode},错误信息:{stderr.strip()[:200]}"
55
- logger.error(f"命令执行失败: {cmd[:50]} → {error_msg}")
40
+ error_msg = f"Code:{proc.returncode},Error_msg:{stderr.strip()}"
41
+ logger.error(f"Command Failed: {truncated_cmd} → {error_msg}")
56
42
  self._callback(callback,success=False, result=error_msg)
57
43
 
58
44
  except Exception as ex:
59
- logger.error(f"命令执行失败: {cmd[:50]} → {str(ex)}")
45
+ logger.error(f"Command Failed: {truncated_cmd} → {str(ex)}")
60
46
  self._callback(callback,success=False, result=str(ex))
61
47
  finally:
62
48
  if proc is not None:
@@ -69,17 +55,17 @@ class Shell:
69
55
 
70
56
  try:
71
57
  # 启动线程执行命令(守护线程,不阻塞主线程)
72
- safe_cmd_name = cmd[:20].replace(' ', '_').replace('/', '_').replace('\\', '_').replace('|', '_')
58
+ safe_cmd_name = cmd[:20].replace(' ', '_').replace('/', '_').replace('\\', '_').replace('|', '_')+ "..." if len(cmd) > 20 else cmd
73
59
  exec_thread = threading.Thread(
74
60
  target=execute_and_wait,
75
61
  daemon=True,
76
62
  name=f"CmdExecutor-{safe_cmd_name}"
77
63
  )
78
64
  exec_thread.start()
79
- logger.debug(f"任务已提交: {cmd[:50]}")
65
+ logger.debug(f"Task submitted: {safe_cmd_name}")
80
66
  return True
81
67
  except Exception as e:
82
- logger.error(f"任务提交失败: {cmd[:50]} → {str(e)}")
68
+ logger.error(f"Task submission failed: {cmd} → {str(e)}")
83
69
  self._callback(callback,success=False,result=str(e))
84
70
  return False
85
71
 
@@ -89,5 +75,5 @@ class Shell:
89
75
  try:
90
76
  callback(success, result)
91
77
  except Exception as ex:
92
- logger.error(f"回调函数执行失败: {str(ex)}")
78
+ logger.error(f"Callback function execution failed: {str(ex)}")
93
79
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dotask
3
- Version: 0.2.4
3
+ Version: 0.3.0
4
4
  Summary: 发布订阅注解方式
5
5
  Home-page: https://gitee.com/d-yz/task-manager
6
6
  Author: dyz
@@ -35,6 +35,11 @@ Dynamic: license-file
35
35
  3. 添加本地调用shell工具函数->可以很好的适配@task消费者
36
36
  1. max_concurrent:控制并发执行的shell数量
37
37
  2. 引入dotask.Shell 调用Shell(max_concurrent=?).local_shell_execute(cmd,callback)来使用
38
+ ### 0.3.x
39
+ 1. 添加令牌桶,通过`from dotask.util import TokenBucket`使用,可以更好的限制生产消费速率
40
+ - 参数说明
41
+ - TokenBucket(capacity=1, rate=0),capacity:令牌桶容量,rate:每秒生成速率
42
+ - get_token(block=True),block:决定令牌桶无令牌时是阻塞还是返回false
38
43
  ## 快速开始
39
44
  ### 安装
40
45
  `pip install dotask`
@@ -9,5 +9,6 @@ dotask.egg-info/SOURCES.txt
9
9
  dotask.egg-info/dependency_links.txt
10
10
  dotask.egg-info/top_level.txt
11
11
  dotask/util/__init__.py
12
+ dotask/util/limit.py
12
13
  dotask/util/logger.py
13
14
  dotask/util/shell.py
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = dotask
3
- version = 0.2.4
3
+ version = 0.3.0
4
4
  author = dyz
5
5
  author_email = 837701454@qq.com
6
6
  description = 发布订阅注解方式
@@ -1,2 +0,0 @@
1
- from .logger import logger
2
- from .shell import Shell
File without changes
File without changes
File without changes
File without changes