onestep 0.4.3__py3-none-any.whl → 0.4.4__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.

Potentially problematic release.


This version of onestep might be problematic. Click here for more details.

onestep/__init__.py CHANGED
@@ -1,4 +1,5 @@
1
1
  from .onestep import step
2
+ from .cron import Cron
2
3
  from .retry import (
3
4
  BaseRetry, BaseErrorCallback, NackErrorCallBack,
4
5
  NeverRetry, AlwaysRetry, TimesRetry, RetryIfException, AdvancedRetry
@@ -19,7 +20,7 @@ from .exception import (
19
20
  )
20
21
 
21
22
  __all__ = [
22
- 'step',
23
+ 'step', 'Cron',
23
24
 
24
25
  # broker
25
26
  'BaseBroker',
@@ -62,4 +63,4 @@ __all__ = [
62
63
  '__version__'
63
64
  ]
64
65
 
65
- __version__ = '0.4.3'
66
+ __version__ = '0.4.4'
@@ -6,3 +6,13 @@ from .webhook import WebHookBroker
6
6
  from .rabbitmq import RabbitMQBroker
7
7
  from .redis import RedisStreamBroker, RedisPubSubBroker
8
8
  from .cron import CronBroker
9
+
10
+
11
+ __all__ = [
12
+ "BaseBroker", "BaseConsumer",
13
+ "MemoryBroker", "MemoryConsumer",
14
+ "WebHookBroker",
15
+ "RabbitMQBroker",
16
+ "RedisStreamBroker", "RedisPubSubBroker",
17
+ "CronBroker"
18
+ ]
onestep/broker/base.py CHANGED
@@ -119,9 +119,17 @@ class BaseConsumer:
119
119
 
120
120
  def __next__(self):
121
121
  try:
122
- data = self.queue.get(timeout=self.timeout / 1000)
122
+ q = self.queue
123
+ if q is None:
124
+ return None
125
+ timeout_ms = self.timeout
126
+ if isinstance(timeout_ms, (int, float)) and timeout_ms > 0:
127
+ data = q.get(timeout=timeout_ms / 1000.0)
128
+ else:
129
+ # 当超时为0、负数或非数字时,使用非阻塞获取以避免ValueError
130
+ data = q.get_nowait()
123
131
  return self.message_cls.from_broker(broker_message=data)
124
- except Empty:
132
+ except (Empty, ValueError):
125
133
  return None
126
134
 
127
135
  def __iter__(self):
onestep/broker/cron.py CHANGED
@@ -1,5 +1,9 @@
1
1
  """
2
- 使用CRON表达式触发任务执行
2
+ 使用 CRON 表达式触发任务执行
3
+
4
+ 支持人性化 DSL 与宏:
5
+ - DSL:Cron.every(minutes=5)、Cron.daily(at="09:00")、Cron.weekly(on=["mon","fri"], at="10:30") 等
6
+ - 宏:@hourly/@daily/@weekly/@monthly/@yearly 以及扩展 @workdays/@weekends/@every 5m/2h/3d/1mo
3
7
  """
4
8
  import logging
5
9
  import threading
@@ -7,6 +11,7 @@ from datetime import datetime
7
11
  from typing import Any
8
12
 
9
13
  from croniter import croniter
14
+ from ..cron import resolve_cron
10
15
 
11
16
  from .memory import MemoryBroker, MemoryConsumer
12
17
 
@@ -17,9 +22,10 @@ class CronBroker(MemoryBroker):
17
22
 
18
23
  def __init__(self, cron, name=None, middlewares=None, body: Any = None, start_time=None, *args, **kwargs):
19
24
  super().__init__(name=name, middlewares=middlewares, *args, **kwargs)
20
- self.cron = cron
25
+ # 支持 DSL/宏/原始字符串:统一解析为标准表达式
26
+ self.cron = resolve_cron(cron)
21
27
  self.start_time = start_time or datetime.now()
22
- self.itr = croniter(cron, self.start_time)
28
+ self.itr = croniter(self.cron, self.start_time)
23
29
  self.next_fire_time = self.itr.get_next(datetime)
24
30
  self.body = body
25
31
  self._thread = None
onestep/broker/memory.py CHANGED
@@ -26,7 +26,10 @@ class MemoryMessage(Message):
26
26
  if "body" not in message:
27
27
  # 来自 外部的消息 可能没有 body, 故直接认为都是 message.body
28
28
  message = {"body": message}
29
- return cls(body=message.get("body"), extra=message.get("extra"), message=broker_message)
29
+ extra_val = message.get("extra")
30
+ if not isinstance(extra_val, dict):
31
+ extra_val = None
32
+ return cls(body=message.get("body"), extra=extra_val, message=broker_message)
30
33
 
31
34
 
32
35
  class MemoryBroker(BaseBroker):
@@ -86,7 +86,7 @@ class RabbitMQBroker(BaseBroker):
86
86
 
87
87
  def consume(self, *args, **kwargs):
88
88
  daemon = kwargs.pop('daemon', True)
89
- thread = threading.Thread(target=self._consume, *args, **kwargs)
89
+ thread = threading.Thread(target=self._consume, args=args, kwargs=kwargs)
90
90
  thread.daemon = daemon
91
91
  thread.start()
92
92
  self.threads.append(thread)
@@ -98,11 +98,21 @@ class RabbitMQBroker(BaseBroker):
98
98
 
99
99
  def confirm(self, message: Message):
100
100
  """确认消息"""
101
- message.message.ack()
101
+ broker_msg = getattr(message, "message", None)
102
+ if broker_msg is not None and hasattr(broker_msg, "ack"):
103
+ broker_msg.ack()
104
+ else:
105
+ # 无可确认的原始消息,忽略确认
106
+ return
102
107
 
103
108
  def reject(self, message: Message):
104
109
  """拒绝消息"""
105
- message.message.reject(requeue=False)
110
+ broker_msg = getattr(message, "message", None)
111
+ if broker_msg is not None and hasattr(broker_msg, "reject"):
112
+ broker_msg.reject(requeue=False)
113
+ else:
114
+ # 无法拒绝(无原始消息对象),忽略
115
+ return
106
116
 
107
117
  def requeue(self, message: Message, is_source=False):
108
118
  """
@@ -111,11 +121,20 @@ class RabbitMQBroker(BaseBroker):
111
121
  :param message: 消息
112
122
  :param is_source: 是否是原始消息,True: 使用原始消息重入当前队列,False: 使用消息的最新数据重入当前队列
113
123
  """
114
- if is_source:
115
- message.message.reject(requeue=True)
124
+ broker_msg = getattr(message, "message", None)
125
+ if broker_msg is not None and hasattr(broker_msg, "reject"):
126
+ if is_source:
127
+ broker_msg.reject(requeue=True)
128
+ else:
129
+ broker_msg.reject(requeue=False)
130
+ self.send(message)
116
131
  else:
117
- message.message.reject(requeue=False)
118
- self.send(message)
132
+ # 没有原始消息控制能力,回退为直接发送当前消息状态
133
+ if is_source and broker_msg is not None and hasattr(broker_msg, "body"):
134
+ # 尝试使用原始消息体重入队列
135
+ self.client.send(self.queue_name, broker_msg.body)
136
+ else:
137
+ self.send(message)
119
138
 
120
139
  def shutdown(self):
121
140
  self.client.shutdown()
@@ -69,7 +69,7 @@ class RedisStreamBroker(BaseBroker):
69
69
 
70
70
  def consume(self, *args, **kwargs):
71
71
  daemon = kwargs.pop('daemon', True)
72
- thread = threading.Thread(target=self._consume, *args, **kwargs)
72
+ thread = threading.Thread(target=self._consume, args=args, kwargs=kwargs)
73
73
  thread.daemon = daemon
74
74
  thread.start()
75
75
  self.threads.append(thread)
@@ -85,10 +85,14 @@ class RedisStreamBroker(BaseBroker):
85
85
  publish = send
86
86
 
87
87
  def confirm(self, message: Message):
88
- self.client.ack(message.message)
88
+ broker_msg = getattr(message, "message", None)
89
+ if broker_msg is not None:
90
+ self.client.ack(broker_msg)
89
91
 
90
92
  def reject(self, message: Message):
91
- self.client.reject(message.message)
93
+ broker_msg = getattr(message, "message", None)
94
+ if broker_msg is not None:
95
+ self.client.reject(broker_msg)
92
96
 
93
97
  def requeue(self, message: Message, is_source=False):
94
98
  """
@@ -99,8 +103,9 @@ class RedisStreamBroker(BaseBroker):
99
103
  """
100
104
  self.reject(message)
101
105
 
102
- if is_source:
103
- self.client.send(message.message.body)
106
+ broker_msg = getattr(message, "message", None)
107
+ if is_source and broker_msg is not None and hasattr(broker_msg, "body"):
108
+ self.client.send(broker_msg.body)
104
109
  else:
105
110
  self.send(message)
106
111
 
onestep/cron.py ADDED
@@ -0,0 +1,211 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Cron DSL/Builder 与宏解析
4
+
5
+ 提供人性化的 API 来生成 cron 表达式,并支持常用别名/宏:
6
+ - Cron.every(minutes=5) / Cron.every(seconds=10) / Cron.every(hours=2) / Cron.every(days=3) / Cron.every(months=1)
7
+ - Cron.daily(at="09:00")
8
+ - Cron.weekly(on="mon" or ["mon","fri"], at="10:30")
9
+ - Cron.monthly(on_day=1, at="00:00")
10
+ - Cron.yearly(on="01-01", at="00:00")
11
+ - 宏别名:@hourly/@daily/@weekly/@monthly/@yearly
12
+ - 扩展宏:@workdays(1-5)/@weekends(0,6)/@every <n><unit>(unit: s/m/h/d/mo)
13
+
14
+ 注意:
15
+ - 标准 5 字段:minute hour day month day_of_week
16
+ - 当包含 seconds(如 `at` 使用 HH:MM:SS 或 `every(seconds=...)`)时输出 6 字段:second minute hour day month day_of_week
17
+ - `@every <n>d` 等按“日/月字段取模”的语义生成表达式,并非滚动间隔。
18
+ """
19
+ from __future__ import annotations
20
+
21
+ from typing import List, Optional, Tuple, Union
22
+
23
+ DOW_MAP = {
24
+ "sun": 0, "mon": 1, "tue": 2, "wed": 3, "thu": 4, "fri": 5, "sat": 6,
25
+ }
26
+
27
+ ALIAS_MAP = {
28
+ "@hourly": "0 * * * *",
29
+ "@daily": "0 0 * * *",
30
+ "@weekly": "0 0 * * 0",
31
+ "@monthly": "0 0 1 * *",
32
+ "@yearly": "0 0 1 1 *",
33
+ }
34
+
35
+
36
+ def _parse_at(at: Optional[str]) -> Tuple[Optional[int], int, int]:
37
+ """解析 at 参数,支持 HH:MM 或 HH:MM:SS。
38
+ 返回 (second, minute, hour)。如果不传 at,默认 minute=0, hour=0。
39
+ """
40
+ if not at:
41
+ return None, 0, 0
42
+ parts = at.strip().split(":")
43
+ if len(parts) == 2:
44
+ h, m = parts
45
+ return None, int(m), int(h)
46
+ elif len(parts) == 3:
47
+ h, m, s = parts
48
+ return int(s), int(m), int(h)
49
+ raise ValueError(f"Invalid time format for at='{at}', expected HH:MM or HH:MM:SS")
50
+
51
+
52
+ def _to_dow_expr(on: Union[str, int, List[Union[str, int]]]) -> str:
53
+ """将星期输入转换为 day_of_week 字段表达式。
54
+ 支持:字符串缩写(mon...sun)、数字 0-6、列表。
55
+ """
56
+ def _to_num(v: Union[str, int]) -> int:
57
+ if isinstance(v, int):
58
+ if v < 0 or v > 6:
59
+ raise ValueError("day_of_week must be in 0-6")
60
+ return v
61
+ v = str(v).lower()
62
+ if v.isdigit():
63
+ num = int(v)
64
+ if num < 0 or num > 6:
65
+ raise ValueError("day_of_week must be in 0-6")
66
+ return num
67
+ if v not in DOW_MAP:
68
+ raise ValueError(f"Unknown day_of_week '{v}'")
69
+ return DOW_MAP[v]
70
+
71
+ if isinstance(on, (list, tuple, set)):
72
+ vals = sorted({_to_num(v) for v in on})
73
+ return ",".join(str(x) for x in vals)
74
+ else:
75
+ return str(_to_num(on))
76
+
77
+
78
+ def _join_fields(second: Optional[int], minute: Union[int, str], hour: Union[int, str], day: Union[int, str], month: Union[int, str], dow: Union[int, str]) -> str:
79
+ """组合 5/6 字段。若 second 为 None,输出 5 字段,否则输出 6 字段(秒在最后)。"""
80
+ if second is None:
81
+ return f"{minute} {hour} {day} {month} {dow}"
82
+ return f"{minute} {hour} {day} {month} {dow} {second}"
83
+
84
+
85
+ class Cron:
86
+ @staticmethod
87
+ def every(*, seconds: Optional[int] = None, minutes: Optional[int] = None, hours: Optional[int] = None, days: Optional[int] = None, months: Optional[int] = None) -> str:
88
+ """每隔指定单位执行。
89
+ 只能指定一个非 None 的单位。
90
+ - seconds: 6 字段:*/s * * * * *
91
+ - minutes: 5 字段:*/m * * * *
92
+ - hours: 5 字段:0 */h * * *
93
+ - days: 5 字段:0 0 */d * *
94
+ - months: 5 字段:0 0 1 */mo *
95
+ """
96
+ units = [("seconds", seconds), ("minutes", minutes), ("hours", hours), ("days", days), ("months", months)]
97
+ chosen = [(k, v) for k, v in units if v is not None]
98
+ if len(chosen) != 1:
99
+ raise ValueError("Cron.every() must specify exactly one unit")
100
+ k, v = chosen[0]
101
+ if not isinstance(v, int) or v <= 0:
102
+ raise ValueError("Interval must be a positive integer")
103
+ if k == "seconds":
104
+ return f"* * * * * */{v}"
105
+ if k == "minutes":
106
+ return "*/%d * * * *" % v
107
+ if k == "hours":
108
+ return "0 */%d * * *" % v
109
+ if k == "days":
110
+ return "0 0 */%d * *" % v
111
+ if k == "months":
112
+ return "0 0 1 */%d *" % v
113
+ raise RuntimeError("unreachable")
114
+
115
+ @staticmethod
116
+ def daily(*, at: Optional[str] = None) -> str:
117
+ s, m, h = _parse_at(at)
118
+ day, month, dow = "*", "*", "*"
119
+ return _join_fields(s, m, h, day, month, dow)
120
+
121
+ @staticmethod
122
+ def weekly(*, on: Union[str, int, List[Union[str, int]]] = "mon", at: Optional[str] = None) -> str:
123
+ s, m, h = _parse_at(at)
124
+ dow = _to_dow_expr(on)
125
+ day, month = "*", "*"
126
+ return _join_fields(s, m, h, day, month, dow)
127
+
128
+ @staticmethod
129
+ def monthly(*, on_day: Union[int, List[int]] = 1, at: Optional[str] = None) -> str:
130
+ s, m, h = _parse_at(at)
131
+ month, dow = "*", "*"
132
+ def _to_day(v: Union[int, str]) -> int:
133
+ iv = int(v)
134
+ if iv < 1 or iv > 31:
135
+ raise ValueError("day_of_month must be in 1-31")
136
+ return iv
137
+ if isinstance(on_day, (list, tuple, set)):
138
+ day = ",".join(str(_to_day(v)) for v in sorted({int(x) for x in on_day}))
139
+ else:
140
+ day = str(_to_day(on_day))
141
+ return _join_fields(s, m, h, day, month, dow)
142
+
143
+ @staticmethod
144
+ def yearly(*, on: str = "01-01", at: Optional[str] = None) -> str:
145
+ s, m, h = _parse_at(at)
146
+ dow = "*"
147
+ try:
148
+ mon_str, day_str = on.split("-")
149
+ month = str(int(mon_str))
150
+ day = str(int(day_str))
151
+ except Exception:
152
+ raise ValueError("yearly(on=...) expects 'MM-DD', e.g., '01-01'")
153
+ return _join_fields(s, m, h, day, month, dow)
154
+
155
+ @staticmethod
156
+ def alias(name: str) -> str:
157
+ """返回内置宏别名对应的表达式(如果已知)。未知别名原样返回,以便 croniter 自行处理。"""
158
+ return ALIAS_MAP.get(name.strip().lower(), name)
159
+
160
+
161
+ def resolve_cron(cron_like: str) -> str:
162
+ """将类似 cron 的输入(字符串/宏)解析为标准 cron 表达式。
163
+ - 内置关键字直接映射或保留(croniter 也支持);
164
+ - 扩展宏:@workdays/@weekends/@every <n><unit>(unit: s/m/h/d/mo)。
165
+ """
166
+ s = str(cron_like).strip()
167
+ if not s.startswith("@"):
168
+ return s
169
+ lower = s.lower()
170
+ # 内置别名
171
+ if lower in ALIAS_MAP:
172
+ return ALIAS_MAP[lower]
173
+ # 扩展:工作日/周末(默认 00:00 执行)
174
+ if lower == "@workdays":
175
+ return "0 0 * * 1-5"
176
+ if lower == "@weekends":
177
+ return "0 0 * * 0,6"
178
+ # 扩展:@every <n><unit>
179
+ if lower.startswith("@every"):
180
+ tail = lower.replace("@every", "", 1).strip()
181
+ if not tail:
182
+ raise ValueError("@every requires an interval, e.g., '@every 5m'")
183
+ # 支持类似 "5m" / "10 h" / "2d" / "3mo" / "30s"
184
+ tail = tail.replace(" ", "")
185
+ # 处理 'mo' 与单位优先级
186
+ if tail.endswith("mo"):
187
+ num = int(tail[:-2])
188
+ if num <= 0:
189
+ raise ValueError("@every months must be > 0")
190
+ return "0 0 1 */%d *" % num
191
+ unit = tail[-1]
192
+ try:
193
+ num = int(tail[:-1])
194
+ except Exception:
195
+ raise ValueError("@every expects '<n><unit>', units: s/m/h/d/mo")
196
+ if num <= 0:
197
+ raise ValueError("@every interval must be > 0")
198
+ if unit == "s":
199
+ return "* * * * * */%d" % num
200
+ if unit == "m":
201
+ return "*/%d * * * *" % num
202
+ if unit == "h":
203
+ return "0 */%d * * *" % num
204
+ if unit == "d":
205
+ return "0 0 */%d * *" % num
206
+ if unit == "w":
207
+ # Cron 不支持“每 N 周”,仅建议直接使用 weekly(on=..., at=...)
208
+ raise ValueError("@every <n>w is not supported; use weekly(on=..., at=...) instead")
209
+ raise ValueError("Unknown unit for @every; use s/m/h/d/mo")
210
+ # 未知宏直接返回,交由 croniter 处理(可能抛错)
211
+ return s
onestep/message.py CHANGED
@@ -5,20 +5,21 @@ import uuid
5
5
  from dataclasses import dataclass
6
6
  from traceback import TracebackException
7
7
  from typing import Optional, Any, Union
8
+ from types import TracebackType
8
9
 
9
10
  from onestep._utils import catch_error
10
11
 
11
12
 
12
13
  class MessageTracebackException(TracebackException):
13
- def __init__(self, exc_type, exc_value, exc_traceback, **kwargs):
14
+ def __init__(self, exc_type: type[BaseException], exc_value: BaseException, exc_traceback: Optional[TracebackType], **kwargs):
14
15
  super().__init__(exc_type, exc_value, exc_traceback, **kwargs)
15
16
  self.exc_value = exc_value
16
17
 
17
18
 
18
19
  @dataclass
19
20
  class Extra:
20
- task_id: str = None
21
- publish_time: float = None
21
+ task_id: Optional[str] = None
22
+ publish_time: Optional[float] = None
22
23
  failure_count: int = 0
23
24
 
24
25
  def __post_init__(self):
@@ -70,7 +71,11 @@ class Message:
70
71
 
71
72
  def set_exception(self):
72
73
  """设置异常信息,会自动获取"""
73
- self._exception = MessageTracebackException(*sys.exc_info())
74
+ exc_type, exc_value, exc_tb = sys.exc_info()
75
+ if exc_type is None or exc_value is None:
76
+ exc_type = Exception
77
+ exc_value = Exception("No exception info")
78
+ self._exception = MessageTracebackException(exc_type, exc_value, exc_tb)
74
79
  self.failure_count = self.failure_count + 1
75
80
 
76
81
  @property
@@ -112,12 +117,16 @@ class Message:
112
117
  @catch_error()
113
118
  def confirm(self):
114
119
  """确认消息"""
115
- self.broker.confirm(self)
120
+ broker = getattr(self, 'broker', None)
121
+ if broker and hasattr(broker, 'confirm'):
122
+ broker.confirm(self)
116
123
 
117
124
  @catch_error()
118
125
  def reject(self):
119
126
  """拒绝消息"""
120
- self.broker.reject(self)
127
+ broker = getattr(self, 'broker', None)
128
+ if broker and hasattr(broker, 'reject'):
129
+ broker.reject(self)
121
130
 
122
131
  @catch_error()
123
132
  def requeue(self, is_source=False):
@@ -126,7 +135,9 @@ class Message:
126
135
 
127
136
  :param is_source: 是否是源消息,True: 使用消息的最新数据重入当前队列,False: 使用消息的最新数据重入当前队列
128
137
  """
129
- self.broker.requeue(self, is_source=is_source)
138
+ broker = getattr(self, 'broker', None)
139
+ if broker and hasattr(broker, 'requeue'):
140
+ broker.requeue(self, is_source=is_source)
130
141
 
131
142
  def __getattr__(self, item):
132
143
  return None
onestep/onestep.py CHANGED
@@ -28,7 +28,7 @@ class BaseOneStep:
28
28
 
29
29
  def __init__(self, fn,
30
30
  group: str = "OneStep",
31
- name: str = None,
31
+ name: Optional[str] = None,
32
32
  from_broker: Union[BaseBroker, List[BaseBroker], None] = None,
33
33
  to_broker: Union[BaseBroker, List[BaseBroker], None] = None,
34
34
  workers: Optional[int] = None,
@@ -220,7 +220,7 @@ class step:
220
220
  def __init__(self,
221
221
  *,
222
222
  group: str = "OneStep",
223
- name: str = None,
223
+ name: Optional[str] = None,
224
224
  from_broker: Union[BaseBroker, List[BaseBroker], None] = None,
225
225
  to_broker: Union[BaseBroker, List[BaseBroker], None] = None,
226
226
  workers: Optional[int] = None,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: onestep
3
- Version: 0.4.3
3
+ Version: 0.4.4
4
4
  Summary: 仅需一步,轻松实现分布式异步任务。
5
5
  Author: miclon
6
6
  Author-email: jcnd@163.com
@@ -1,27 +1,28 @@
1
- onestep/__init__.py,sha256=G-MOCgma1x23C6hAW6UIpsyCRFna-41SPyGX0CSaH3E,1517
1
+ onestep/__init__.py,sha256=t-3MoodNTribclqLNgFF_JHTZdVTZ9E7ybI0pWOQLKk,1548
2
2
  onestep/_utils.py,sha256=ySYmy-o2o3BurvLfuLWKGXR8TLwug6aTDNMvA3_QjKM,414
3
- onestep/broker/__init__.py,sha256=L-roNwPZVKsjytOlKCOlyAyvfejI28FyOj9uzfyXcrk,257
4
- onestep/broker/base.py,sha256=G_MzKIbpDSPDx2rx-Q2rwourbcI_faBXQdQZRxjgx8s,4192
5
- onestep/broker/cron.py,sha256=3CZGHftQKrNwzdKGtVG8uiPuDb3U5W1T6WI-fdY5ea4,1246
6
- onestep/broker/memory.py,sha256=ycOWWHhWhiuNPMDqsbxKbMh8rpt84FlRsJkINLJNX1U,2070
7
- onestep/broker/rabbitmq.py,sha256=c5rIkUprFlBIGZOdTI5_2tp6DmsdvD9u40Yp6-COQ-o,4301
3
+ onestep/broker/__init__.py,sha256=XJx5XV7mgmG9zzGGllu2WP0N4P2qIpzqBmwbKJ_75Q4,451
4
+ onestep/broker/base.py,sha256=xwYih4K2qRcxvqTP_HiteignNxHoSqh4g_i1syk14UU,4547
5
+ onestep/broker/cron.py,sha256=p85pgtRpV12Md-HW56j6NSX42gGcBH9AHnbajPkZpM0,1608
6
+ onestep/broker/memory.py,sha256=GdmYFbFwemlShrUVHJrI-hbMVkgSKRmj_rl8VmnQdp8,2173
7
+ onestep/broker/rabbitmq.py,sha256=O5S8HcFG8SEjamFB1WSRbgU96ByBb3pd05jEQE7oLGQ,5237
8
8
  onestep/broker/redis/__init__.py,sha256=lqjvBKpMGf6I34nyJTEAq7XU2a8aQemhMGCbb5aByFI,236
9
9
  onestep/broker/redis/pubsub.py,sha256=m0QzBT--rftfTXSc6D3wTzfQrwUgv5mEEDnnER7nB9A,2708
10
- onestep/broker/redis/stream.py,sha256=yUOD-Z0gzU5zNjRW1ZxNwl35i2pbJKTw33xIj0ga3vY,3504
10
+ onestep/broker/redis/stream.py,sha256=A134jbMZckrlElkWZAfSpKxI8XmJsT_u_Y4U_lVuvsE,3800
11
11
  onestep/broker/webhook.py,sha256=x8h6zGsgjGFQdzr-UVXW8vGGUM_XCllglSCBqXh4rtw,2501
12
12
  onestep/cli.py,sha256=vXrMAg7Nl0Ra79DraBrCtQq8xJC443AUEqFXe7rVNYo,2133
13
+ onestep/cron.py,sha256=9SqA_eeOx4lfmssOhqg6YEJsKpZ7r8qxF5HMmapb9uk,8138
13
14
  onestep/exception.py,sha256=T-tqfRrJZT9Y85qe-1l-1PcHZzV2jxv-61HAhOc4AKE,753
14
- onestep/message.py,sha256=W37HKnIDxpd5UmtDbPTw2DTb3lfxu9Vs2KYIiCr5b2s,4057
15
+ onestep/message.py,sha256=8MGAYHT4cNpzUbwnYonn_vP7g_5uBaE6G-uPmW5dDUI,4660
15
16
  onestep/middleware/__init__.py,sha256=MP_45lqr4pecmbzQBnn2-AODQ0N_Fss8nl2SA7Zzljo,347
16
17
  onestep/middleware/base.py,sha256=adWQ_Lx2Nkaw4ySojIaa3lwUIUu97pHR_l5YMr0lrbw,975
17
18
  onestep/middleware/config.py,sha256=WZIvGXhpdSQRAFTbEWXcZdnhFcbvhGkLdkFIj-_QuZM,2438
18
19
  onestep/middleware/unique.py,sha256=L4F4NnpDO6hReHa21tUiiPH5GRMeRNgGgC0o6zgu5v0,1355
19
- onestep/onestep.py,sha256=PWAmjrrqXxnsh_CzA_pXET3Xe7jkKteWiIsfs5_gVaY,9373
20
+ onestep/onestep.py,sha256=6zL_OC8XGb8k9xz-Kkyl1IYwcbLTWt_IyyikPPfSxyc,9393
20
21
  onestep/retry.py,sha256=fPO139hQrcMjXZuQ4QfFaQR57VEqv_EKS2WSz92Ayvc,3706
21
22
  onestep/signal.py,sha256=tz9bGzTFZeBviG_iaGLfy4OyFKlWat6szEI6kWEGfTA,337
22
23
  onestep/state.py,sha256=UVG91CXCabU64X6qgcO0S7RzbBP8ut0ID7aTMww94us,618
23
24
  onestep/worker.py,sha256=SLLaAjUA65i5PEHRbRzHaWLNokvrpPz1-BzSiZdbhqg,7029
24
- onestep-0.4.3.dist-info/METADATA,sha256=fGTZznrO5qUZ-lD_3Gh_3hARAVLRxYW0k-KmcIZKshU,2675
25
- onestep-0.4.3.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
26
- onestep-0.4.3.dist-info/entry_points.txt,sha256=ZfWnNQqiGujz2PPLjSlKPocOFRryL7Ot0vQ41TU1xw0,44
27
- onestep-0.4.3.dist-info/RECORD,,
25
+ onestep-0.4.4.dist-info/METADATA,sha256=bSGOEl6mZw4BcTfRbDMx__eOSgCd8oX-bnJw5pIXItM,2675
26
+ onestep-0.4.4.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
27
+ onestep-0.4.4.dist-info/entry_points.txt,sha256=ZfWnNQqiGujz2PPLjSlKPocOFRryL7Ot0vQ41TU1xw0,44
28
+ onestep-0.4.4.dist-info/RECORD,,