funboost 45.3__py3-none-any.whl → 45.6__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 funboost might be problematic. Click here for more details.

funboost/__init__.py CHANGED
@@ -13,7 +13,7 @@ set_frame_config这个模块的 use_config_form_funboost_config_module() 是核
13
13
  这段注释说明和使用的用户无关,只和框架开发人员有关.
14
14
  '''
15
15
 
16
- __version__ = "45.3"
16
+ __version__ = "45.6"
17
17
 
18
18
  from funboost.set_frame_config import show_frame_config
19
19
 
@@ -65,5 +65,5 @@ from funboost.core.current_task import funboost_current_task
65
65
  # set_interrupt_signal_handler()
66
66
 
67
67
  # 有的包默认没加handlers,原始的日志不漂亮且不可跳转不知道哪里发生的。这里把warnning级别以上的日志默认加上handlers。
68
- # nb_log.get_logger(name='', log_level_int=30, _log_filename='pywarning.log')
68
+ # nb_log.get_logger(name='', log_level_int=30, log_filename='pywarning.log')
69
69
 
@@ -0,0 +1,18 @@
1
+ import asyncio
2
+
3
+ from faststream import FastStream
4
+ from faststream.rabbit import RabbitBroker
5
+ from funboost.funboost_config_deafult import BrokerConnConfig
6
+ from funboost.core.loggers import get_funboost_file_logger
7
+
8
+ get_funboost_file_logger('faststream.access.rabbit')
9
+
10
+ broker = RabbitBroker(BrokerConnConfig.RABBITMQ_URL, max_consumers=20)
11
+ app = FastStream(broker)
12
+
13
+ # asyncio.get_event_loop().run_until_complete(broker.connect())
14
+ #
15
+ # asyncio.get_event_loop().run_until_complete(broker.start())
16
+
17
+ def get_broker(max_consumers=None):
18
+ return RabbitBroker(BrokerConnConfig.RABBITMQ_URL, max_consumers=max_consumers)
@@ -0,0 +1 @@
1
+
@@ -35,9 +35,10 @@ from funboost.core.current_task import funboost_current_task, FctContext
35
35
  from funboost.core.loggers import develop_logger
36
36
 
37
37
  from funboost.core.func_params_model import BoosterParams, PublisherParams, BaseJsonAbleModel
38
+ from funboost.core.serialization import Serialization
38
39
  from funboost.core.task_id_logger import TaskIdLogger
39
40
  from funboost.constant import FunctionKind
40
- from funboost.utils.json_helper import JsonUtils
41
+
41
42
  from nb_libs.path_helper import PathHelper
42
43
  from nb_log import (get_logger, LoggerLevelSetterMixin, LogManager, is_main_process,
43
44
  nb_log_config_default)
@@ -139,7 +140,7 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
139
140
  if consumer_params.consuming_function is None:
140
141
  raise ValueError('必须传 consuming_function 参数')
141
142
 
142
- self._msg_schedule_time_intercal = 0 if consumer_params.qps is None else 1.0 / consumer_params.qps
143
+ self._msg_schedule_time_intercal = 0 if consumer_params.qps in (None, 0) else 1.0 / consumer_params.qps
143
144
 
144
145
  self._concurrent_mode_dispatcher = ConcurrentModeDispatcher(self)
145
146
  if consumer_params.concurrent_mode == ConcurrentModeEnum.ASYNC:
@@ -232,6 +233,10 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
232
233
  self.logger.info(f'{self.queue_name} consumer 的消费者配置:\n {self.consumer_params.json_str_value()}')
233
234
  atexit.register(self.join_shedual_task_thread)
234
235
 
236
+ if self.consumer_params.auto_start_consuming_message:
237
+ self.publisher_of_same_queue
238
+ self.start_consuming_message()
239
+
235
240
  def _build_logger(self):
236
241
  logger_prefix = self.consumer_params.logger_prefix
237
242
  if logger_prefix != '':
@@ -408,8 +413,7 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
408
413
  extra_params = {'task_id': task_id, 'publish_time': round(time.time(), 4),
409
414
  'publish_time_format': time.strftime('%Y-%m-%d %H:%M:%S')}
410
415
  """
411
- if isinstance(msg, str):
412
- msg = json.loads(msg)
416
+ msg = Serialization.to_dict(msg)
413
417
  # 以下是清洗补全字段.
414
418
  if 'extra' not in msg:
415
419
  msg['extra'] = {'is_auto_fill_extra': True}
@@ -536,7 +540,7 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
536
540
  # print(999)
537
541
  if self.consumer_params.is_show_message_get_from_broker:
538
542
  # self.logger.debug(f'从 {broker_name} 中间件 的 {self._queue_name} 中取出的消息是 {msg}')
539
- self.logger.debug(f'从 {broker_name or self.consumer_params.broker_kind} 中间件 的 {self._queue_name} 中取出的消息是 {JsonUtils.to_json_str(msg)}')
543
+ self.logger.debug(f'从 {broker_name or self.consumer_params.broker_kind} 中间件 的 {self._queue_name} 中取出的消息是 {Serialization.to_json_str(msg)}')
540
544
 
541
545
  def _get_priority_conf(self, kw: dict, broker_task_config_key: str):
542
546
  broker_task_config = kw['body'].get('extra', {}).get(broker_task_config_key, None)
@@ -585,10 +589,12 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
585
589
  # method_cls = getattr(sys.modules[self.consumer_params.consuming_function_class_module],
586
590
  # self.consumer_params.consuming_function_class_name)
587
591
  if self.publisher_params.consuming_function_kind == FunctionKind.CLASS_METHOD:
588
- method_cls = getattr(PathHelper(method_first_param_value[ConstStrForClassMethod.CLS_FILE]).import_as_module(),method_first_param_value[ConstStrForClassMethod.CLS_NAME])
592
+ method_cls = getattr(PathHelper.import_module(method_first_param_value[ConstStrForClassMethod.CLS_MODULE]),
593
+ method_first_param_value[ConstStrForClassMethod.CLS_NAME])
589
594
  real_function_only_params[method_first_param_name] = method_cls
590
595
  elif self.publisher_params.consuming_function_kind == FunctionKind.INSTANCE_METHOD:
591
- method_cls = getattr(PathHelper(method_first_param_value[ConstStrForClassMethod.CLS_FILE]).import_as_module(), method_first_param_value[ConstStrForClassMethod.CLS_NAME])
596
+ method_cls = getattr(PathHelper.import_module(method_first_param_value[ConstStrForClassMethod.CLS_MODULE]),
597
+ method_first_param_value[ConstStrForClassMethod.CLS_NAME])
592
598
  obj = method_cls(**method_first_param_value[ConstStrForClassMethod.OBJ_INIT_PARAMS])
593
599
  real_function_only_params[method_first_param_name] = obj
594
600
  # print(real_function_only_params)
@@ -642,7 +648,7 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
642
648
  # RedisMixin().redis_db_frame.expire(kw['body']['extra']['task_id'], 600)
643
649
  current_function_result_status.rpc_result_expire_seconds = self.consumer_params.rpc_result_expire_seconds
644
650
  p.lpush(kw['body']['extra']['task_id'],
645
- json.dumps(current_function_result_status.get_status_dict(without_datetime_obj=True)))
651
+ Serialization.to_json_str(current_function_result_status.get_status_dict(without_datetime_obj=True)))
646
652
  p.expire(kw['body']['extra']['task_id'], self.consumer_params.rpc_result_expire_seconds)
647
653
  p.execute()
648
654
 
@@ -812,7 +818,7 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
812
818
  with RedisMixin().redis_db_filter_and_rpc_result.pipeline() as p:
813
819
  current_function_result_status.rpc_result_expire_seconds = self.consumer_params.rpc_result_expire_seconds
814
820
  p.lpush(kw['body']['extra']['task_id'],
815
- json.dumps(current_function_result_status.get_status_dict(without_datetime_obj=True)))
821
+ Serialization.to_json_str(current_function_result_status.get_status_dict(without_datetime_obj=True)))
816
822
  p.expire(kw['body']['extra']['task_id'], self.consumer_params.rpc_result_expire_seconds)
817
823
  p.execute()
818
824
 
@@ -852,7 +858,6 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
852
858
  fct = funboost_current_task()
853
859
  fct.set_fct_context(None)
854
860
 
855
-
856
861
  # noinspection PyProtectedMember
857
862
  async def _async_run_consuming_function_with_confirm_and_retry(self, kw: dict, current_retry_times,
858
863
  function_result_status: FunctionResultStatus, ):
@@ -1161,14 +1166,14 @@ class DistributedConsumerStatistics(RedisMixin, FunboostFileLoggerMixin):
1161
1166
  results = self.redis_db_frame.smembers(redis_key)
1162
1167
  with self.redis_db_frame.pipeline() as p:
1163
1168
  for result in results:
1164
- result_dict = json.loads(result)
1169
+ result_dict = Serialization.to_dict(result)
1165
1170
  if self.timestamp() - result_dict['hearbeat_timestamp'] > 15 \
1166
1171
  or self._consumer_identification_map['consumer_uuid'] == result_dict['consumer_uuid']:
1167
1172
  # 因为这个是10秒钟运行一次,15秒还没更新,那肯定是掉线了。如果消费者本身是自己也先删除。
1168
1173
  p.srem(redis_key, result)
1169
1174
  self._consumer_identification_map['hearbeat_datetime_str'] = time_util.DatetimeConverter().datetime_str
1170
1175
  self._consumer_identification_map['hearbeat_timestamp'] = self.timestamp()
1171
- value = json.dumps(self._consumer_identification_map, sort_keys=True)
1176
+ value = Serialization.to_json_str(self._consumer_identification_map, )
1172
1177
  p.sadd(redis_key, value)
1173
1178
  p.execute()
1174
1179
 
@@ -5,7 +5,7 @@ import json
5
5
  import time
6
6
  from funboost.utils.redis_manager import RedisMixin
7
7
  from funboost.utils import decorators
8
-
8
+ from funboost.core.serialization import Serialization
9
9
  """
10
10
  此模块是依赖redis的确认消费,所以比较复杂。
11
11
  """
@@ -46,7 +46,7 @@ class ConsumerConfirmMixinWithTheHelpOfRedis(RedisMixin):
46
46
  time_max = time.time() - self.UNCONFIRMED_TIMEOUT
47
47
  for value in self.redis_db_frame.zrangebyscore(self._unack_zset_name, 0, time_max):
48
48
  self.logger.warning(f'向 {self._queue_name} 重新放入未消费确认的任务 {value}')
49
- self._requeue({'body': json.loads(value)})
49
+ self._requeue({'body': Serialization.to_dict(value)})
50
50
  self.redis_db_frame.zrem(self._unack_zset_name, value)
51
51
  self.logger.info(f'{self._unack_zset_name} 中有待确认消费任务的数量是'
52
52
  f' {self.redis_db_frame.zcard(self._unack_zset_name)}')
@@ -0,0 +1,54 @@
1
+ import asyncio
2
+ import json
3
+ import threading
4
+ import time
5
+
6
+ from funboost import EmptyConsumer
7
+ from funboost.assist.faststream_helper import broker,app,get_broker
8
+ from faststream import FastStream,Context
9
+ from faststream.annotations import Logger
10
+
11
+ from funboost.concurrent_pool.async_helper import simple_run_in_executor
12
+ from funboost.core.helper_funs import delete_keys_and_return_new_dict
13
+
14
+
15
+ class FastStreamConsumer(EmptyConsumer):
16
+ def custom_init(self):
17
+ self.broker = get_broker(max_consumers=self.consumer_params.concurrent_num)
18
+ subc = self.broker.subscriber(self.queue_name)
19
+ # @broker.subscriber(self.queue_name)
20
+ async def f(msg:str, logger: Logger,message=Context(),broker=Context(),context=Context(),):
21
+ self.logger.debug(f' 这条消息是 faststream 从 {self.queue_name} 队列中取出 ,是由 faststream 框架调度 {self.consuming_function.__name__} 函数处理,msg:{message} {context}')
22
+ # print(logger.name)
23
+ # return self.consuming_function(*args, **kwargs) # 如果没有声明 autoretry_for ,那么消费函数出错了就不会自动重试了。
24
+ # print(msg)
25
+ function_only_params = delete_keys_and_return_new_dict(json.loads(msg))
26
+ if self._consuming_function_is_asyncio:
27
+ result = await self.consuming_function(**function_only_params)
28
+ else:
29
+ result = await simple_run_in_executor(self.consuming_function,**function_only_params)
30
+ # print(result)
31
+ return result
32
+ subc(f)
33
+ self.faststream_subscriber = subc
34
+
35
+ def _shedual_task(self):
36
+ """ 完全由faststream框架接管控制消费,不使用funboost的AbstractConsumer的_run"""
37
+ while 1:
38
+ time.sleep(100)
39
+
40
+
41
+ def _confirm_consume(self, kw):
42
+ pass
43
+
44
+ def start_consuming_message(self):
45
+ def _f():
46
+ loop = asyncio.new_event_loop()
47
+ loop.run_until_complete(self.broker.connect())
48
+ loop.run_until_complete(self.faststream_subscriber.start())
49
+ loop.run_forever()
50
+ self.keep_circulating(10, block=False)(_f)()
51
+ # threading.Thread(target=_f).start()
52
+
53
+ def _requeue(self, kw):
54
+ pass
@@ -5,7 +5,7 @@ import json
5
5
  from queue import Queue,SimpleQueue
6
6
  from funboost.constant import BrokerEnum
7
7
  from funboost.consumers.base_consumer import AbstractConsumer
8
- from funboost.publishers import local_python_queue_publisher
8
+ from funboost.queues.memory_queues_map import PythonQueues
9
9
 
10
10
 
11
11
  class LocalPythonQueueConsumer(AbstractConsumer):
@@ -15,7 +15,7 @@ class LocalPythonQueueConsumer(AbstractConsumer):
15
15
 
16
16
  @property
17
17
  def local_python_queue(self) -> Queue:
18
- return local_python_queue_publisher.local_pyhton_queue_name__local_pyhton_queue_obj_map[self._queue_name]
18
+ return PythonQueues.get_queue(self._queue_name)
19
19
 
20
20
  def _shedual_task(self):
21
21
  while True:
@@ -10,7 +10,7 @@ import time
10
10
  from funboost.constant import BrokerEnum
11
11
  from funboost.consumers.base_consumer import AbstractConsumer
12
12
  from funboost.utils.redis_manager import RedisMixin
13
-
13
+ from funboost.core.serialization import Serialization
14
14
 
15
15
  class RedisConsumer(AbstractConsumer, RedisMixin):
16
16
  """
@@ -57,4 +57,4 @@ class RedisConsumer(AbstractConsumer, RedisMixin):
57
57
  pass # redis没有确认消费的功能。
58
58
 
59
59
  def _requeue(self, kw):
60
- self.redis_db_frame.rpush(self._queue_name, json.dumps(kw['body']))
60
+ self.redis_db_frame.rpush(self._queue_name,Serialization.to_json_str(kw['body']))
@@ -4,8 +4,8 @@
4
4
  import json
5
5
  import time
6
6
  from funboost.consumers.base_consumer import AbstractConsumer
7
+ from funboost.core.serialization import Serialization
7
8
  from funboost.utils.decorators import RedisDistributedLockContextManager
8
- from funboost.utils.json_helper import JsonUtils
9
9
  from funboost.utils.redis_manager import RedisMixin
10
10
 
11
11
 
@@ -40,7 +40,7 @@ class RedisConsumerAckUsingTimeout(AbstractConsumer, RedisMixin):
40
40
  self.redis_db_frame.zrem(self._unack_zset_name, kw['task_str'])
41
41
 
42
42
  def _requeue(self, kw):
43
- self.redis_db_frame.rpush(self._queue_name, JsonUtils.to_json_str(kw['body']))
43
+ self.redis_db_frame.rpush(self._queue_name, Serialization.to_json_str(kw['body']))
44
44
 
45
45
  def _shedual_task(self):
46
46
  lua = '''
@@ -4,6 +4,7 @@
4
4
  import json
5
5
  from funboost.constant import BrokerEnum
6
6
  from funboost.consumers.base_consumer import AbstractConsumer
7
+ from funboost.core.serialization import Serialization
7
8
  from funboost.utils.redis_manager import RedisMixin
8
9
 
9
10
 
@@ -24,6 +25,6 @@ class RedisConsumer(AbstractConsumer, RedisMixin):
24
25
  pass # redis没有确认消费的功能。
25
26
 
26
27
  def _requeue(self, kw):
27
- self.redis_db_frame.rpush(self._queue_name, json.dumps(kw['body']))
28
+ self.redis_db_frame.rpush(self._queue_name, Serialization.to_json_str(kw['body']))
28
29
 
29
30
 
@@ -11,6 +11,7 @@ import time
11
11
  from collections import OrderedDict
12
12
  import typing
13
13
 
14
+ from funboost.core.serialization import Serialization
14
15
  from funboost.utils import decorators
15
16
  from funboost.core.loggers import FunboostFileLoggerMixin
16
17
 
@@ -34,8 +35,7 @@ class RedisFilter(RedisMixin, FunboostFileLoggerMixin):
34
35
  @staticmethod
35
36
  def _get_ordered_str(value):
36
37
  """对json的键值对在redis中进行过滤,需要先把键值对排序,否则过滤会不准确如 {"a":1,"b":2} 和 {"b":2,"a":1}"""
37
- if isinstance(value, str):
38
- value = json.loads(value)
38
+ value = Serialization.to_dict(value)
39
39
  ordered_dict = OrderedDict()
40
40
  for k in sorted(value):
41
41
  ordered_dict[k] = value[k]
@@ -163,7 +163,7 @@ def get_current_taskid():
163
163
  # return fct.function_result_status.task_id
164
164
  try:
165
165
  fct = funboost_current_task()
166
- return fct.task_id # 不在funboost的消费函数里面就获取不到上下文了
166
+ return fct.task_id # 不在funboost的消费函数里面或者同个线程、协程就获取不到上下文了
167
167
  except (AttributeError, LookupError) as e:
168
168
  # print(e,type(e))
169
169
  return 'no_task_id'
@@ -177,6 +177,7 @@ class BoosterParams(BaseJsonAbleModel):
177
177
  do_not_run_by_specify_time: tuple = ('10:00:00', '22:00:00') # 不运行的时间段,在这个时间段自动不运行函数.
178
178
 
179
179
  schedule_tasks_on_main_thread: bool = False # 直接在主线程调度任务,意味着不能直接在当前主线程同时开启两个消费者。
180
+ auto_start_consuming_message: bool = False # 是否在定义后就自动启动消费,无需用户手动写 .consume() 来启动消息消费。
180
181
 
181
182
  consuming_function: typing.Callable = None # 消费函数,在@boost时候不用指定,因为装饰器知道下面的函数.
182
183
  consuming_function_raw: typing.Callable = None
@@ -195,8 +196,7 @@ class BoosterParams(BaseJsonAbleModel):
195
196
 
196
197
  auto_generate_info: dict = {} # 自动生成的信息,不需要用户主动传参.
197
198
 
198
- consuming_function_kind :typing.Optional[str]= None #自动生成的信息,不需要用户主动传参.
199
-
199
+ consuming_function_kind: typing.Optional[str] = None # 自动生成的信息,不需要用户主动传参,如果自动判断失误就传递。
200
200
 
201
201
  @root_validator(skip_on_failure=True)
202
202
  def check_values(cls, values: dict):
@@ -269,7 +269,6 @@ class PriorityConsumingControlConfig(BaseJsonAbleModel):
269
269
  misfire_grace_time: typing.Union[int, None] = None
270
270
  other_extra_params: dict = None # 其他参数, 例如消息优先级 , priority_control_config=PriorityConsumingControlConfig(other_extra_params={'priroty': priorityxx}),
271
271
 
272
-
273
272
  @root_validator(skip_on_failure=True)
274
273
  def cehck_values(cls, values: dict):
275
274
  if values['countdown'] and values['eta']:
@@ -297,7 +296,6 @@ class PublisherParams(BaseJsonAbleModel):
297
296
  consuming_function_kind: typing.Optional[str] = None # 自动生成的信息,不需要用户主动传参.
298
297
 
299
298
 
300
-
301
299
  if __name__ == '__main__':
302
300
  from funboost.concurrent_pool import FlexibleThreadPool
303
301
 
@@ -15,6 +15,7 @@ from pymongo import IndexModel, ReplaceOne
15
15
 
16
16
  from funboost.core.func_params_model import FunctionResultStatusPersistanceConfig
17
17
  from funboost.core.helper_funs import get_publish_time, delete_keys_and_return_new_dict
18
+ from funboost.core.serialization import Serialization
18
19
  from funboost.utils import time_util, decorators
19
20
  from funboost.utils.mongo_util import MongoMixin
20
21
  # from nb_log import LoggerMixin
@@ -46,7 +47,7 @@ class FunctionResultStatus():
46
47
  self.publish_time_str = time_util.DatetimeConverter(publish_time).datetime_str
47
48
  function_params = delete_keys_and_return_new_dict(msg_dict, )
48
49
  self.params = function_params
49
- self.params_str = json.dumps(function_params, ensure_ascii=False)
50
+ self.params_str = Serialization.to_json_str(function_params)
50
51
  self.result = None
51
52
  self.run_times = 0
52
53
  self.exception = None
@@ -78,7 +79,8 @@ class FunctionResultStatus():
78
79
  # item.pop('time_start')
79
80
  datetime_str = time_util.DatetimeConverter().datetime_str
80
81
  try:
81
- json.dumps(item['result']) # 不希望存不可json序列化的复杂类型。麻烦。存这种类型的结果是伪需求。
82
+ Serialization.to_json_str(item['result'])
83
+ # json.dumps(item['result']) # 不希望存不可json序列化的复杂类型。麻烦。存这种类型的结果是伪需求。
82
84
  except TypeError:
83
85
  item['result'] = str(item['result'])[:1000]
84
86
  item.update({'insert_time_str': datetime_str,
@@ -98,7 +100,7 @@ class FunctionResultStatus():
98
100
  return item
99
101
 
100
102
  def __str__(self):
101
- return f'''{self.__class__} {json.dumps(self.get_status_dict(), ensure_ascii=False)}'''
103
+ return f'''{self.__class__} {Serialization.to_json_str(self.get_status_dict())}'''
102
104
 
103
105
 
104
106
  class ResultPersistenceHelper(MongoMixin, FunboostFileLoggerMixin):
@@ -10,7 +10,7 @@ from funboost.concurrent_pool import CustomThreadPoolExecutor
10
10
  from funboost.concurrent_pool.flexible_thread_pool import FlexibleThreadPoolMinWorkers0
11
11
  from funboost.utils.redis_manager import RedisMixin
12
12
  from funboost.utils.redis_manager import AioRedisMixin
13
-
13
+ from funboost.core.serialization import Serialization
14
14
 
15
15
  class HasNotAsyncResult(Exception):
16
16
  pass
@@ -42,7 +42,7 @@ class AsyncResult(RedisMixin):
42
42
  self._has_pop = True
43
43
  if redis_value is not None:
44
44
  status_and_result_str = redis_value[1]
45
- self._status_and_result = json.loads(status_and_result_str)
45
+ self._status_and_result = Serialization.to_dict(status_and_result_str)
46
46
  self.redis_db_filter_and_rpc_result.lpush(self.task_id, status_and_result_str)
47
47
  self.redis_db_filter_and_rpc_result.expire(self.task_id, self._status_and_result['rpc_result_expire_seconds'])
48
48
  return self._status_and_result
@@ -147,7 +147,7 @@ if __name__ == '__main__':
147
147
  self._has_pop = True
148
148
  if redis_value is not None:
149
149
  status_and_result_str = redis_value[1]
150
- self._status_and_result = json.loads(status_and_result_str)
150
+ self._status_and_result = Serialization.to_dict(status_and_result_str)
151
151
  await self.aioredis_db_filter_and_rpc_result.lpush(self.task_id, status_and_result_str)
152
152
  await self.aioredis_db_filter_and_rpc_result.expire(self.task_id, self._status_and_result['rpc_result_expire_seconds'])
153
153
  return self._status_and_result
@@ -0,0 +1,16 @@
1
+ import typing
2
+
3
+ import orjson
4
+ class Serialization:
5
+ @staticmethod
6
+ def to_json_str(dic:typing.Union[dict,str]):
7
+ if isinstance(dic,str):
8
+ return dic
9
+ str1 =orjson.dumps(dic)
10
+ return str1.decode('utf8')
11
+
12
+ @staticmethod
13
+ def to_dict(strx:typing.Union[str,dict]):
14
+ if isinstance(strx,dict):
15
+ return strx
16
+ return orjson.loads(strx)
@@ -16,9 +16,9 @@ from funboost.publishers.local_python_queue_publisher import LocalPythonQueuePub
16
16
  from funboost.publishers.mongomq_publisher import MongoMqPublisher
17
17
 
18
18
  from funboost.publishers.persist_queue_publisher import PersistQueuePublisher
19
- from funboost.publishers.rabbitmq_amqpstorm_publisher import RabbitmqPublisherUsingAmqpStorm
19
+
20
20
  from funboost.publishers.rabbitmq_pika_publisher import RabbitmqPublisher
21
- from funboost.publishers.rabbitmq_rabbitpy_publisher import RabbitmqPublisherUsingRabbitpy
21
+
22
22
  from funboost.publishers.redis_publisher import RedisPublisher
23
23
  from funboost.publishers.rocketmq_publisher import RocketmqPublisher
24
24
  from funboost.publishers.redis_stream_publisher import RedisStreamPublisher
@@ -36,9 +36,8 @@ from funboost.consumers.nats_consumer import NatsConsumer
36
36
 
37
37
  from funboost.consumers.peewee_conusmer import PeeweeConsumer
38
38
  from funboost.consumers.persist_queue_consumer import PersistQueueConsumer
39
- from funboost.consumers.rabbitmq_amqpstorm_consumer import RabbitmqConsumerAmqpStorm
40
39
  from funboost.consumers.rabbitmq_pika_consumer import RabbitmqConsumer
41
- from funboost.consumers.rabbitmq_rabbitpy_consumer import RabbitmqConsumerRabbitpy
40
+
42
41
  from funboost.consumers.redis_brpoplpush_consumer import RedisBrpopLpushConsumer
43
42
  from funboost.consumers.redis_consumer import RedisConsumer
44
43
  from funboost.consumers.redis_consumer_ack_able import RedisConsumerAckAble
@@ -57,8 +56,8 @@ from funboost.consumers.base_consumer import AbstractConsumer
57
56
  from funboost.constant import BrokerEnum
58
57
 
59
58
  broker_kind__publsiher_consumer_type_map = {
60
- BrokerEnum.RABBITMQ_AMQPSTORM: (RabbitmqPublisherUsingAmqpStorm, RabbitmqConsumerAmqpStorm),
61
- BrokerEnum.RABBITMQ_RABBITPY: (RabbitmqPublisherUsingRabbitpy, RabbitmqConsumerRabbitpy),
59
+
60
+
62
61
  BrokerEnum.REDIS: (RedisPublisher, RedisConsumer),
63
62
  BrokerEnum.MEMORY_QUEUE: (LocalPythonQueuePublisher, LocalPythonQueueConsumer),
64
63
  BrokerEnum.RABBITMQ_PIKA: (RabbitmqPublisher, RabbitmqConsumer),
@@ -111,6 +110,15 @@ def regist_to_funboost(broker_kind: str):
111
110
  这样当用户需要使用某些三方包中间件作为消息队列时候,按照import报错信息,用户自己去pip安装好。或者 pip install funboost[all] 一次性安装所有中间件。
112
111
  建议按照 https://github.com/ydf0509/funboost/blob/master/setup.py 中的 extra_brokers 和 install_requires 里面的版本号来安装三方包版本.
113
112
  """
113
+ if broker_kind == BrokerEnum.RABBITMQ_AMQPSTORM:
114
+ from funboost.publishers.rabbitmq_amqpstorm_publisher import RabbitmqPublisherUsingAmqpStorm
115
+ from funboost.consumers.rabbitmq_amqpstorm_consumer import RabbitmqConsumerAmqpStorm
116
+ register_custom_broker(BrokerEnum.RABBITMQ_AMQPSTORM, RabbitmqPublisherUsingAmqpStorm, RabbitmqConsumerAmqpStorm)
117
+
118
+ if broker_kind == BrokerEnum.RABBITMQ_RABBITPY:
119
+ from funboost.publishers.rabbitmq_rabbitpy_publisher import RabbitmqPublisherUsingRabbitpy
120
+ from funboost.consumers.rabbitmq_rabbitpy_consumer import RabbitmqConsumerRabbitpy
121
+ register_custom_broker(BrokerEnum.RABBITMQ_RABBITPY, RabbitmqPublisherUsingRabbitpy, RabbitmqConsumerRabbitpy)
114
122
 
115
123
  if broker_kind == BrokerEnum.PULSAR:
116
124
  from funboost.consumers.pulsar_consumer import PulsarConsumer
@@ -16,7 +16,7 @@ import time
16
16
  import typing
17
17
  from functools import wraps
18
18
  from threading import Lock
19
- import amqpstorm
19
+
20
20
 
21
21
  import nb_log
22
22
  from funboost.constant import ConstStrForClassMethod, FunctionKind
@@ -24,14 +24,16 @@ from funboost.core.func_params_model import PublisherParams, PriorityConsumingCo
24
24
  from funboost.core.helper_funs import MsgGenerater
25
25
  from funboost.core.loggers import develop_logger
26
26
 
27
- from pikav1.exceptions import AMQPError as PikaAMQPError
27
+
28
28
 
29
29
  # from nb_log import LoggerLevelSetterMixin, LoggerMixin
30
30
  from funboost.core.loggers import LoggerLevelSetterMixin, FunboostFileLoggerMixin, get_logger
31
31
  from funboost.core.msg_result_getter import AsyncResult, AioAsyncResult
32
+ from funboost.core.serialization import Serialization
32
33
  from funboost.core.task_id_logger import TaskIdLogger
33
34
  from funboost.utils import decorators
34
35
  from funboost.funboost_config_deafult import BrokerConnConfig, FunboostCommonConfig
36
+ from nb_libs.path_helper import PathHelper
35
37
 
36
38
  RedisAsyncResult = AsyncResult # 别名
37
39
  RedisAioAsyncResult = AioAsyncResult # 别名
@@ -179,13 +181,13 @@ class AbstractPublisher(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
179
181
 
180
182
  @staticmethod
181
183
  def _get_from_other_extra_params(k: str, msg):
182
- msg_dict = json.loads(msg) if isinstance(msg, str) else msg
184
+ # msg_dict = json.loads(msg) if isinstance(msg, str) else msg
185
+ msg_dict = Serialization.to_dict(msg)
183
186
  return msg_dict['extra'].get('other_extra_params', {}).get(k, None)
184
187
 
185
188
  def _convert_msg(self, msg: typing.Union[str, dict], task_id=None,
186
- priority_control_config: PriorityConsumingControlConfig = None) -> (typing.Dict, typing.Dict, typing.Dict):
187
- if isinstance(msg, (str, bytes)):
188
- msg = json.loads(msg)
189
+ priority_control_config: PriorityConsumingControlConfig = None) -> (typing.Dict, typing.Dict, typing.Dict,str):
190
+ msg = Serialization.to_dict(msg)
189
191
  msg_function_kw = copy.deepcopy(msg)
190
192
  raw_extra = {}
191
193
  if 'extra' in msg:
@@ -214,7 +216,7 @@ class AbstractPublisher(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
214
216
  msg, msg_function_kw, extra_params, task_id = self._convert_msg(msg, task_id, priority_control_config)
215
217
  t_start = time.time()
216
218
  decorators.handle_exception(retry_times=10, is_throw_error=True, time_sleep=0.1)(
217
- self.concrete_realization_of_publish)(json.dumps(msg, ensure_ascii=False))
219
+ self.concrete_realization_of_publish)(Serialization.to_json_str(msg))
218
220
 
219
221
  self.logger.debug(f'向{self._queue_name} 队列,推送消息 耗时{round(time.time() - t_start, 4)}秒 {msg_function_kw}', extra={'task_id': task_id}) # 显示msg太长了。
220
222
  with self._lock_for_count:
@@ -228,10 +230,16 @@ class AbstractPublisher(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
228
230
 
229
231
  def send_msg(self, msg: typing.Union[dict, str]):
230
232
  """直接发送任意消息内容到消息队列,不生成辅助参数,无视函数入参名字,不校验入参个数和键名"""
231
- if isinstance(msg, dict):
232
- msg = json.dumps(msg, ensure_ascii=False)
233
233
  decorators.handle_exception(retry_times=10, is_throw_error=True, time_sleep=0.1)(
234
- self.concrete_realization_of_publish)(msg)
234
+ self.concrete_realization_of_publish)(Serialization.to_json_str(msg))
235
+
236
+ @staticmethod
237
+ def __get_cls_file(cls: type):
238
+ if cls.__module__ == '__main__':
239
+ cls_file = Path(sys.argv[0]).resolve().as_posix()
240
+ else:
241
+ cls_file = Path(sys.modules[cls.__module__].__file__).resolve().as_posix()
242
+ return cls_file
235
243
 
236
244
  def push(self, *func_args, **func_kwargs):
237
245
  """
@@ -260,9 +268,11 @@ class AbstractPublisher(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
260
268
  # )
261
269
  cls = func_args_list[0]
262
270
  # print(cls,cls.__name__, sys.modules[cls.__module__].__file__)
271
+
263
272
  func_args_list[0] = {ConstStrForClassMethod.FIRST_PARAM_NAME: self.publish_params_checker.all_arg_name[0],
264
273
  ConstStrForClassMethod.CLS_NAME: cls.__name__,
265
- ConstStrForClassMethod.CLS_FILE: Path(sys.modules[cls.__module__].__file__).resolve().as_posix(),
274
+ ConstStrForClassMethod.CLS_FILE: self.__get_cls_file(cls),
275
+ ConstStrForClassMethod.CLS_MODULE: PathHelper(self.__get_cls_file(cls)).get_module_name(),
266
276
  }
267
277
  elif self.publisher_params.consuming_function_kind == FunctionKind.INSTANCE_METHOD:
268
278
  obj = func_args[0]
@@ -271,7 +281,8 @@ class AbstractPublisher(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
271
281
  raise ValueError(f'消费函数 {self.publisher_params.consuming_function} 是实例方法,实例必须有 {ConstStrForClassMethod.OBJ_INIT_PARAMS} 属性')
272
282
  func_args_list[0] = {ConstStrForClassMethod.FIRST_PARAM_NAME: self.publish_params_checker.all_arg_name[0],
273
283
  ConstStrForClassMethod.CLS_NAME: cls.__name__,
274
- ConstStrForClassMethod.CLS_FILE: Path(sys.modules[cls.__module__].__file__).resolve().as_posix(),
284
+ ConstStrForClassMethod.CLS_FILE: self.__get_cls_file(cls),
285
+ ConstStrForClassMethod.CLS_MODULE: PathHelper(self.__get_cls_file(cls)).get_module_name(),
275
286
  ConstStrForClassMethod.OBJ_INIT_PARAMS: getattr(obj, ConstStrForClassMethod.OBJ_INIT_PARAMS),
276
287
 
277
288
  }
@@ -330,11 +341,14 @@ def deco_mq_conn_error(f):
330
341
  # noinspection PyBroadException
331
342
  try:
332
343
  return f(self, *args, **kwargs)
333
-
334
- except (PikaAMQPError, amqpstorm.AMQPError,) as e: # except BaseException as e: # 现在装饰器用到了绝大多出地方,单个异常类型不行。ex
335
- self.logger.error(f'中间件链接出错 ,方法 {f.__name__} 出错 ,{e}')
336
- self.init_broker()
337
- return f(self, *args, **kwargs)
344
+ except Exception as e:
345
+ import amqpstorm
346
+ from pikav1.exceptions import AMQPError as PikaAMQPError
347
+ if isinstance(e,(PikaAMQPError, amqpstorm.AMQPError)):
348
+ # except (PikaAMQPError, amqpstorm.AMQPError,) as e: # except BaseException as e: # 现在装饰器用到了绝大多出地方,单个异常类型不行。ex
349
+ self.logger.error(f'中间件链接出错 ,方法 {f.__name__} 出错 ,{e}')
350
+ self.init_broker()
351
+ return f(self, *args, **kwargs)
338
352
  except BaseException as e:
339
353
  self.logger.critical(e, exc_info=True)
340
354
 
@@ -0,0 +1,57 @@
1
+ # -*- coding: utf-8 -*-
2
+ # @Author : ydf
3
+ # @Time : 2023/8/8 0008 12:12
4
+
5
+ import abc
6
+ import asyncio
7
+ import json
8
+ import time
9
+ import typing
10
+
11
+ from funboost import PriorityConsumingControlConfig
12
+ from funboost.core.serialization import Serialization
13
+ from funboost.publishers.base_publisher import AbstractPublisher
14
+ from funboost.assist.faststream_helper import app,get_broker
15
+ from faststream import FastStream,Context
16
+ from faststream.annotations import Logger
17
+
18
+ class FastStreamPublisher(AbstractPublisher, metaclass=abc.ABCMeta):
19
+ """
20
+ 空的发布者,空的实现,需要搭配 boost入参的 consumer_override_cls 和 publisher_override_cls使用,或者被继承。
21
+ """
22
+ def custom_init(self):
23
+ pass
24
+ # asyncio.get_event_loop().run_until_complete(broker.start())
25
+ self.broker = get_broker()
26
+ asyncio.get_event_loop().run_until_complete(self.broker.connect())
27
+
28
+ def publish(self, msg: typing.Union[str, dict], task_id=None,
29
+ priority_control_config: PriorityConsumingControlConfig = None) :
30
+ msg, msg_function_kw, extra_params, task_id = self._convert_msg(msg, task_id, priority_control_config)
31
+ t_start = time.time()
32
+ faststream_result = asyncio.get_event_loop().run_until_complete(self.broker.publish(Serialization.to_json_str(msg), self.queue_name))
33
+ self.logger.debug(f'向{self._queue_name} 队列,推送消息 耗时{round(time.time() - t_start, 4)}秒 {msg_function_kw}') # 显示msg太长了。
34
+ with self._lock_for_count:
35
+ self.count_per_minute += 1
36
+ self.publish_msg_num_total += 1
37
+ if time.time() - self._current_time > 10:
38
+ self.logger.info(
39
+ f'10秒内推送了 {self.count_per_minute} 条消息,累计推送了 {self.publish_msg_num_total} 条消息到 {self._queue_name} 队列中')
40
+ self._init_count()
41
+ # return AsyncResult(task_id)
42
+ return faststream_result #
43
+
44
+ def concrete_realization_of_publish(self, msg):
45
+ pass
46
+
47
+
48
+ def clear(self):
49
+ pass
50
+
51
+
52
+ def get_message_count(self):
53
+ return -1
54
+
55
+
56
+ def close(self):
57
+ pass
@@ -5,6 +5,7 @@ from collections import deque
5
5
  from queue import Queue, SimpleQueue
6
6
 
7
7
  from funboost.publishers.base_publisher import AbstractPublisher
8
+ from funboost.queues.memory_queues_map import PythonQueues
8
9
 
9
10
  local_pyhton_queue_name__local_pyhton_queue_obj_map = dict() # 使local queue和其他中间件完全一样的使用方式,使用映射保存队列的名字,使消费和发布通过队列名字能找到队列对象。
10
11
 
@@ -15,22 +16,23 @@ class LocalPythonQueuePublisher(AbstractPublisher):
15
16
  """
16
17
 
17
18
  # noinspection PyAttributeOutsideInit
18
- def custom_init(self):
19
- if self._queue_name not in local_pyhton_queue_name__local_pyhton_queue_obj_map:
20
- local_pyhton_queue_name__local_pyhton_queue_obj_map[self._queue_name] = Queue(1000000)
21
- self.queue = local_pyhton_queue_name__local_pyhton_queue_obj_map[self._queue_name]
19
+
20
+ @property
21
+ def local_python_queue(self) -> Queue:
22
+ return PythonQueues.get_queue(self._queue_name)
22
23
 
23
24
  def concrete_realization_of_publish(self, msg):
24
25
  # noinspection PyTypeChecker
25
- self.queue.put(msg)
26
+ pass
27
+ self.local_python_queue.put(msg)
26
28
 
27
29
  def clear(self):
28
30
  # noinspection PyUnresolvedReferences
29
- self.queue.queue.clear()
31
+ self.local_python_queue.queue.clear()
30
32
  self.logger.warning(f'清除 本地队列中的消息成功')
31
33
 
32
34
  def get_message_count(self):
33
- return self.queue.qsize()
35
+ return self.local_python_queue.qsize()
34
36
 
35
37
  def close(self):
36
38
  pass
@@ -0,0 +1,11 @@
1
+ import queue
2
+
3
+
4
+ class PythonQueues:
5
+ local_pyhton_queue_name__local_pyhton_queue_obj_map = {}
6
+
7
+ @classmethod
8
+ def get_queue(cls,queue_name):
9
+ if queue_name not in cls.local_pyhton_queue_name__local_pyhton_queue_obj_map:
10
+ cls.local_pyhton_queue_name__local_pyhton_queue_obj_map[queue_name] = queue.Queue()
11
+ return cls.local_pyhton_queue_name__local_pyhton_queue_obj_map[queue_name]
@@ -45,20 +45,20 @@ def monkey_patch_json():
45
45
  json.dumps = _dumps
46
46
 
47
47
 
48
- class JsonUtils:
49
- @staticmethod
50
- def to_dict(obj:typing.Union[str,dict,list]):
51
- if isinstance(obj,str):
52
- return json.loads(obj)
53
- else:
54
- return obj
55
-
56
- @staticmethod
57
- def to_json_str(obj:typing.Union[str,dict,list]):
58
- if isinstance(obj,str):
59
- return obj
60
- else:
61
- return json.dumps(obj,ensure_ascii=False)
48
+ # class JsonUtils:
49
+ # @staticmethod
50
+ # def to_dict(obj:typing.Union[str,dict,list]):
51
+ # if isinstance(obj,str):
52
+ # return json.loads(obj)
53
+ # else:
54
+ # return obj
55
+ #
56
+ # @staticmethod
57
+ # def to_json_str(obj:typing.Union[str,dict,list]):
58
+ # if isinstance(obj,str):
59
+ # return obj
60
+ # else:
61
+ # return json.dumps(obj,ensure_ascii=False)
62
62
 
63
63
  if __name__ == '__main__':
64
64
  pass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: funboost
3
- Version: 45.3
3
+ Version: 45.6
4
4
  Summary: pip install funboost,python全功能分布式函数调度框架,。支持python所有类型的并发模式和一切知名消息队列中间件,支持如 celery dramatiq等框架整体作为funboost中间件,python函数加速器,框架包罗万象,用户能想到的控制功能全都有。一统编程思维,兼容50% python业务场景,适用范围广。只需要一行代码即可分布式执行python一切函数,99%用过funboost的pythoner 感受是 简易 方便 强劲 强大,相见恨晚
5
5
  Home-page: https://github.com/ydf0509/funboost
6
6
  Author: bfzs
@@ -27,7 +27,7 @@ Classifier: Topic :: Software Development :: Libraries
27
27
  Description-Content-Type: text/markdown
28
28
  License-File: LICENSE
29
29
  Requires-Dist: nb-log (>=12.6)
30
- Requires-Dist: nb-libs (>=1.6)
30
+ Requires-Dist: nb-libs (>=1.8)
31
31
  Requires-Dist: nb-time (>=1.8)
32
32
  Requires-Dist: pymongo (==4.3.3)
33
33
  Requires-Dist: AMQPStorm (==2.10.6)
@@ -52,6 +52,7 @@ Requires-Dist: auto-run-on-remote
52
52
  Requires-Dist: frozenlist
53
53
  Requires-Dist: fire
54
54
  Requires-Dist: pydantic
55
+ Requires-Dist: orjson
55
56
  Provides-Extra: all
56
57
  Requires-Dist: confluent-kafka (==1.7.0) ; extra == 'all'
57
58
  Requires-Dist: celery ; extra == 'all'
@@ -131,16 +132,25 @@ Requires-Dist: flask-login ; extra == 'flask'
131
132
 
132
133
 
133
134
  <pre style="color: greenyellow;background-color: #0c1119; font-size: medium;">
134
- pip install funboost ,python全功能分布式函数调度框架,。 用法例子见文档1.3
135
- 支持python所有类型的并发模式和全球一切知名消息队列中间件,
135
+ pip install funboost ,python全功能分布式函数调度框架。 demo用法例子见文档1.3
136
+
137
+ 只需要一行@boost代码即可分布式执行python一切任意函数,99%用过funboost的pythoner 感受是 方便 快速 强大。
138
+ 支持python所有类型的并发模式,消息队列方面支持全球一切知名消息队列中间件和模拟的实现消息队列,
136
139
  同时funboost支持celery整个框架作为核心来发布和消费消息,使用funboost的极简api方式来自动化配置和利用celery调度,
137
140
  也支持huey dramatiq rq等任务队列框架作为funboost的broker。
141
+
138
142
  python函数加速器,框架包罗万象,一统编程思维,兼容50% python编程业务场景,适用范围广。
139
- 只需要一行代码即可分布式执行python一切函数,99%用过funboost的pythoner 感受是 方便 快速 强大。
140
143
  python万能分布式函数调度框架,支持5种并发模式,30+种消息队列中间件(或任务队列框架),
141
144
  30种任务控制功能。给任意python函数赋能。
142
145
  用途概念就是常规经典的 生产者 + 消息队列中间件 + 消费者 编程思想。
146
+
143
147
  框架只需要学习@boost这一个装饰器的入参就可以,所有用法几乎和1.3例子一摸一样,非常简化简单。
148
+ 框架对代码没有入侵,可以加到任意已有项目而对项目python文件目录结构0要求,
149
+ 不像 celery django scrapy 这样的框架,要从一开始就开始规划好项目目录结构,如果不想用框架了,
150
+ 或者想改变使用其他框架框架,那么已经所写的代码组织形式就几乎成了废物,需要大改特改.
151
+ 但是funboost完全不会这样,加上或去掉@boost装饰器,对你的项目影响为0,用户照常使用,
152
+ 所以用户可以对任意项目,任意时候,引入使用funboost或者去掉使用funboost,代码组织形式不需要发生变化.
153
+
144
154
  </pre>
145
155
 
146
156
  ### 框架评价
@@ -157,6 +167,7 @@ python万能分布式函数调度框架,支持5种并发模式,30+种消息
157
167
  ,连celery命令行运行起来都要反复猜测尝试。
158
168
  正因为如此用户从心理已近十分惧怕学习一种叫python框架的东西了,用户顶多愿意学习一个python包或者模块,
159
169
  学习一个框架会非常害怕觉得难度高且耗时,所以非常反感尝试新的框架。
170
+ 用过的99%都说funboost比celery简单方便太多,看都不看的人第一秒就是开始质疑重复造轮子.
160
171
 
161
172
  funboost只有一个@boost装饰器,@boost入参能自动补全,更重要的是被@boost装饰的函数,
162
173
  有哪些方法,每个方法入参是什么都能自动补全。funboost的中间件配置文件自当生成在用户当前项目根目录,
@@ -295,10 +306,10 @@ if __name__ == '__main__':
295
306
  2)funboost 使用内存队列,设置10线程并发
296
307
  ```python
297
308
  import time
298
- from funboost import boost, BrokerEnum
309
+ from funboost import BoosterParams, BrokerEnum
299
310
 
300
311
 
301
- @boost("test_insteda_thread_queue", broker_kind=BrokerEnum.MEMORY_QUEUE, concurrent_num=10)
312
+ @BoosterParams(queue_name="test_insteda_thread_queue", broker_kind=BrokerEnum.MEMORY_QUEUE, concurrent_num=10, auto_start_consuming_message=True)
302
313
  def f(x):
303
314
  time.sleep(3)
304
315
  print(x)
@@ -307,7 +318,8 @@ def f(x):
307
318
  if __name__ == '__main__':
308
319
  for i in range(100):
309
320
  f.push(i)
310
- f.consume()
321
+
322
+
311
323
  ```
312
324
 
313
325
 
@@ -562,7 +574,7 @@ python比其他语言更需要分布式函数调度框架来执行函数,有
562
574
  funboost通过支持celery作为broker_kind,使celer框架变成了funboost的一个子集
563
575
  ```
564
576
 
565
- [查看分布式函数调度框架完整文档](https://funboost.readthedocs.io/zh-cn/latest/index.html)
577
+ [查看分布式函数调度框架完整文档](https://funboost.readthedocs.io/)
566
578
 
567
579
 
568
580
 
@@ -1,4 +1,4 @@
1
- funboost/__init__.py,sha256=pmnCpEqfGuA9KYCVdbiJ44Cpr7ZtO8vimcW3r5C6Ar4,3957
1
+ funboost/__init__.py,sha256=Gj0PweyuIxvJ8aa7Ai5WF0eFUdP06133pA_MQ7VLY5M,3956
2
2
  funboost/__init__old.py,sha256=9Kv3cPLnPkbzMRnuJFVkPsuDdx1CdcSIuITkpdncZSc,20382
3
3
  funboost/__main__.py,sha256=-6Nogi666Y0LN8fVm3JmHGTOk8xEGWvotW_GDbSaZME,1065
4
4
  funboost/constant.py,sha256=STzRDZbuCC5FFV-uD_0r2beGsD1Zni4kregzR11z3Ok,8148
@@ -7,10 +7,12 @@ funboost/set_frame_config.py,sha256=kxYo_x2pMihwfTgFWrLxNbgI0IbFYC_HpSd9YLQM1ig,
7
7
  funboost/assist/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  funboost/assist/celery_helper.py,sha256=EOxsl-y65JPwHrmL0LTGAj1nKLbS0qyRbuXcJ0xo9wc,5721
9
9
  funboost/assist/dramatiq_helper.py,sha256=9mUyfBMAJXzwvB8LwOmapn3rY30a6UXt3tNOfL6OXoM,2106
10
+ funboost/assist/faststream_helper.py,sha256=BmBaFsvsjQK2SO71ulHRsEwO28uYZm2uMMvXkHzLi1E,631
10
11
  funboost/assist/huey_helper.py,sha256=PuJHIzK5oRd5RzbuxLMHhWlkKO3J-ObRK0mrMs_iQWs,1770
11
12
  funboost/assist/rocketry_helper.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
13
  funboost/assist/rq_helper.py,sha256=HYvQ7VqIMx7dAVZZhtqQmGReCJavEuc1BQPYvfm1EvY,1549
13
14
  funboost/assist/rq_windows_worker.py,sha256=jQlGmU0FWPVoKVP8cyuwTuAca9VfSd75F5Qw_hR04y0,4831
15
+ funboost/assist/taskiq_helper.py,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
14
16
  funboost/beggar_version_implementation/beggar_redis_consumer.py,sha256=x5cH6Vc3_UooY2oPeC9MlpMewrGd9qXCx6gEWi1fGa0,3941
15
17
  funboost/concurrent_pool/__init__.py,sha256=C27xYXj7c1NGuVeG7K2LxKH7JKnGwfPfITyon6xFaoE,803
16
18
  funboost/concurrent_pool/async_helper.py,sha256=bK4SyTu_WpgxCvai2AaiI7EKQ-COiDbqu0WPnbaJ1jM,3421
@@ -33,11 +35,12 @@ funboost/concurrent_pool/backup/async_pool_executor0223.py,sha256=RVUZiylUvpTm6U
33
35
  funboost/concurrent_pool/backup/async_pool_executor_back.py,sha256=KL6zEQaa1KkZOlAO85mCC1gwLm-YC5Ghn21IUom0UKM,9598
34
36
  funboost/concurrent_pool/backup/async_pool_executor_janus.py,sha256=OHMWJ9l3EYTpPpcrPrGGKd4K0tmQ2PN8HiX0Dta0EOo,5728
35
37
  funboost/consumers/__init__.py,sha256=ZXY_6Kut1VYNQiF5aWEgIWobsW1ht9YUP0TdRZRWFqI,126
36
- funboost/consumers/base_consumer.py,sha256=EXauF0PD6lMb9J2S7q0uBDj0FXxmhX-xpMMyTUyfu9I,80091
38
+ funboost/consumers/base_consumer.py,sha256=prs8RnEHpNC_xh-3Uan3Xhcr4zyYqgFOl-5N4W8SHG8,80342
37
39
  funboost/consumers/celery_consumer.py,sha256=9gtz7nlZkmv3ErmaseT0_Q__ltSPx-fOcwi-TMPoaLA,9220
38
- funboost/consumers/confirm_mixin.py,sha256=eY6fNwx51Hn4bQSYRjyTRwOqfCGsikVnd2Ga_Ep31N4,6062
40
+ funboost/consumers/confirm_mixin.py,sha256=NPOhxYF0nmJR5J03nXD6ndLo33eAUI60PXvk3WFyoz0,6126
39
41
  funboost/consumers/dramatiq_consumer.py,sha256=ozmeAfeF0U-YNYHK4suQB0N264h5AZdfMH0O45Mh-8A,2229
40
42
  funboost/consumers/empty_consumer.py,sha256=pU5py60l-JDQFTJlaMpfuFyzRGqDUnOrpKhG9gOg39A,759
43
+ funboost/consumers/faststream_consumer.py,sha256=3xzq5fwzAcmhavdh06lLK-Wrjvm4woJIJYkUG3K8w9c,2295
41
44
  funboost/consumers/http_consumer.py,sha256=2MVqEzHW9qIKWvgIQS7VncGfvpmWhQ3BWWjfyHWC-as,2179
42
45
  funboost/consumers/http_consumer000.py,sha256=PiiSLSQB-hHgS1vAn8DoHD2siC3zO6_kKjVW0g6AFIc,4379
43
46
  funboost/consumers/httpsqs_consumer.py,sha256=kaqOcrKMLrSR27XqeiheRDmpF1KDVDghgbHcefTjqt8,1171
@@ -45,7 +48,7 @@ funboost/consumers/huey_consumer.py,sha256=cW10ZPxdZQzUuJwdqQpJIRPTj2vCbZS0uXFJ7
45
48
  funboost/consumers/kafka_consumer.py,sha256=OAq6fclhDBufsWTowAikGBYjptc28SIE-EDzFEz9J-I,4324
46
49
  funboost/consumers/kafka_consumer_manually_commit.py,sha256=kkHyTf4EQFEEoyiZ-pZQBR-g3M1rkFYnfKFXoeODCIE,9620
47
50
  funboost/consumers/kombu_consumer.py,sha256=hj2J-1r5hBbcTNXIpU6qe1MwYPD-8gEKlDTSRV3B5Js,10208
48
- funboost/consumers/local_python_queue_consumer.py,sha256=HyUFZXY6phF6eaL1qd5z7Kof3LZ0J6EO0zllBS-skqA,1220
51
+ funboost/consumers/local_python_queue_consumer.py,sha256=4Cel1WaNwbRpDux22USP8is5R9__A_-LlqyHjcw02Z4,1160
49
52
  funboost/consumers/memory_deque_consumer.py,sha256=tTwOkrB9GdySOQstVLnU4hnBnap6rafCeoXhmV0TI5c,1110
50
53
  funboost/consumers/mongomq_consumer.py,sha256=e1Cupe-Cb2LUuJlQhER6NecrvK7FyzKKZ2HxyfOI-OY,1119
51
54
  funboost/consumers/mqtt_consumer.py,sha256=iLWKxe0CjKHUYrE6YMWNJHto0tD3siUAvZl9ssNsz_s,2297
@@ -60,12 +63,12 @@ funboost/consumers/rabbitmq_pika_consumer.py,sha256=51IkRUSR0v2v7BUlA8oInx81VGeO
60
63
  funboost/consumers/rabbitmq_pika_consumerv0.py,sha256=rIQToBTBqGdjnzMhg0vyZMY87NtK_Uw8ggiTu39JQ_w,4777
61
64
  funboost/consumers/rabbitmq_rabbitpy_consumer.py,sha256=xxINY037pmgF3_lJA-zhf9qUIUx6DdqC7tsUOQL3dL4,1330
62
65
  funboost/consumers/redis_brpoplpush_consumer.py,sha256=HqjgjjEibq7D-5XFpJtyaPvtfXD5zbq9cjSHx4aM1gU,2952
63
- funboost/consumers/redis_consumer.py,sha256=z4opk78FHIes8LJVnDlQtmEQjuJEZo-g3NFN04lBStE,2668
66
+ funboost/consumers/redis_consumer.py,sha256=RdxJvWWQvQdjXWWdUebmgB7KRDWQBeF87Q9kd_DeUZk,2735
64
67
  funboost/consumers/redis_consumer_ack_able.py,sha256=7zP9BQNfONLBbYJFjgNtIPVqbHwklAdcPUIA8Y7M8go,7411
65
- funboost/consumers/redis_consumer_ack_using_timeout.py,sha256=PMSafTcj5cKrVZ10IkrjU3zIdmHevcuVRT4WxslrLSw,4204
68
+ funboost/consumers/redis_consumer_ack_using_timeout.py,sha256=XW3zUSqmS0xDR-eMN7ds7p2ThtOgS8qUDZ9fui4_z9o,4213
66
69
  funboost/consumers/redis_consumer_priority.py,sha256=C-ftnlGPPoB7YV3GvwLu9POVGDn_GKlBqO6NlsZ-hdY,5566
67
- funboost/consumers/redis_consumer_simple.py,sha256=3YrmxSRh8dpece_JR3Lwx8zsn-GBSP1S92lhUIQ3FoU,804
68
- funboost/consumers/redis_filter.py,sha256=rKNFz8mxVo4n4o8v3MP4UqdPAtoK7msQ-1xRgzFfts0,7290
70
+ funboost/consumers/redis_consumer_simple.py,sha256=trPrMHSVU1Y_fY-xz5tFFmt1zjV-9_joPYRHqvyZnL8,874
71
+ funboost/consumers/redis_filter.py,sha256=UlkTQSZQSwb98nqkAHkrEKVri_XxHF0T7HmpghZ597s,7316
69
72
  funboost/consumers/redis_pubsub_consumer.py,sha256=8V8EqobKpEXzpsQx_H3A-JBKLsWOsE0n16g62tUMMYY,1122
70
73
  funboost/consumers/redis_stream_consumer.py,sha256=LVQa19MDfSlC8SYSYFPwJnJi62UIzfPHAbBVysLmNw8,6538
71
74
  funboost/consumers/rocketmq_consumer.py,sha256=23KLRz8iO9e_x7asrceRJYhwJFMruUgmKw7m3pHVkw0,1692
@@ -84,27 +87,28 @@ funboost/contrib/save_result_status_to_sqldb.py,sha256=AxvD7nHs4sjr9U0kwEZzyPKrs
84
87
  funboost/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
85
88
  funboost/core/active_cousumer_info_getter.py,sha256=09fEc-BTEIRfDDfHmOvKnMjLjtOyp4edLsUlAXUR_Qs,4966
86
89
  funboost/core/booster.py,sha256=vztzE7KqfXsg_LODmW5cX1M8as6n2tPdqEjkOLa7SYM,18276
87
- funboost/core/current_task.py,sha256=cJw5ZDtsTJB_5LHN18F1XUFaT-YnAX60AOAi_CkvQoE,5772
90
+ funboost/core/current_task.py,sha256=Oils18_vAqGvV4pqM6yYwnhMaFn3nrpzo1PO48HHidQ,5799
88
91
  funboost/core/exceptions.py,sha256=pLF7BkRJAfDiWp2_xGZqencmwdPiSQI1NENbImExknY,1311
89
92
  funboost/core/fabric_deploy_helper.py,sha256=foieeqlNySuU9axJzNF6TavPjIUSYBx9UO3syVKUiyY,9999
90
93
  funboost/core/funboost_config_getter.py,sha256=TDccp5pQamkoJXkwyPwGsQGDJY8ej8ZT8L8RESSAD2w,382
91
94
  funboost/core/funboost_time.py,sha256=IbB4dFCpg3oGUe90ssAJ_x0eDPtAVfvsUr4esdoKaOk,1777
92
- funboost/core/func_params_model.py,sha256=CMePcxL8vNSWHAEVvQImJF5h7Qv-gIvieh6ejPYgjEU,20593
95
+ funboost/core/func_params_model.py,sha256=5cFPTPZxtnp9wOx7aie4fjEQv4u63tS7M8-UrokBupE,20776
93
96
  funboost/core/function_result_status_config.py,sha256=PyjqAQOiwsLt28sRtH-eYRjiI3edPFO4Nde0ILFRReE,1764
94
- funboost/core/function_result_status_saver.py,sha256=UdokGSwU630t70AZnT9Ecj7GpYXORBDivlc9kadoI2E,9172
97
+ funboost/core/function_result_status_saver.py,sha256=yHKZF9MjmhI-Q4Mkrka7DdweJ0wpgfLmgfAlsfkCeCk,9274
95
98
  funboost/core/helper_funs.py,sha256=Jzy6t4-cugI-tOztgt7DGSos3tkxk2R4IOUOK2OWJ-I,2365
96
99
  funboost/core/kill_remote_task.py,sha256=MZ5vWLGt6SxyN76h5Lf_id9tyVUzjR-qXNyJwXaGlZY,8129
97
100
  funboost/core/lazy_impoter.py,sha256=5yToRBbTB338heR787ap5i-UAGN0FanUCkYfK9Nsgnk,4957
98
101
  funboost/core/loggers.py,sha256=uy5mFLIUvKaVdJZLi6THyxqeuOmp9XEOKrH1Yci0zUM,2354
99
- funboost/core/msg_result_getter.py,sha256=oZDuLDR5XQNzzvgDTsA7wroICToPwrkU9-OAaXXUQSk,8031
102
+ funboost/core/msg_result_getter.py,sha256=wxJBTOsyPDptLbNYOIA7YwuDsDUKDpB5sFzj9_HPcPs,8106
100
103
  funboost/core/muliti_process_enhance.py,sha256=tI3178inc5sqPh-jQc0XaTuUD1diIZyHuukBRk1Gp6Y,3595
104
+ funboost/core/serialization.py,sha256=Q6cAfbaqBCKw0AQwisbMNjOOD0csq0xdes5BHn1Nelo,412
101
105
  funboost/core/task_id_logger.py,sha256=lR19HQcX6Pp8laURCD656xNpF_JP6nLB3zUKI69EWzE,864
102
106
  funboost/core/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
103
107
  funboost/core/cli/discovery_boosters.py,sha256=mbEyv0bUIGcmgkfXLI_Q1IK1QvVwKyro8XccuuMEA6o,3944
104
108
  funboost/core/cli/funboost_cli_user_templ.py,sha256=XUpKLxRKtYfebPUM8wii64kB0HW8L7j9LnRpT0xCfQI,2243
105
109
  funboost/core/cli/funboost_fire.py,sha256=OT0SNi9Zb0Tom2E0cWuArQs0obUowAA_rpCF7GdaKPs,5065
106
110
  funboost/factories/__init__.py,sha256=s7kKKjR1HU5eMjPD6r5b-SXTVMo1zBp2JjOAtkyt5Yo,178
107
- funboost/factories/broker_kind__publsiher_consumer_type_map.py,sha256=fra2cuBiFLtEaPKnkPTaC7YAoHahOfZ6HNvhUwt71Mc,9988
111
+ funboost/factories/broker_kind__publsiher_consumer_type_map.py,sha256=-kKhV65KnRf86b353PJuaEMXMolfV4B2CtTF1wD1kFQ,10189
108
112
  funboost/factories/consumer_factory.py,sha256=EaAw3OZJkGepkQxKkDvMshHjIVOQva_N6nUEhLO4JwU,1500
109
113
  funboost/factories/publisher_factotry.py,sha256=4651sxnbIAi6sFEUQdlUuv8UkbMQIE_Pbzm-1DimAs8,2535
110
114
  funboost/function_result_web/app.py,sha256=WFAsAoPqCVeTxKS31UvkAhuRqUurg-j8D3c9RtHZEyY,5059
@@ -125,18 +129,19 @@ funboost/function_result_web/static/js/jquery-1.11.0.min.js,sha256=ryQZ3RXgnqkTz
125
129
  funboost/function_result_web/templates/index.html,sha256=YM0582Q4t2da-xBf3Ga0McIfcsT9H98rjZck-irMkGo,20387
126
130
  funboost/function_result_web/templates/login.html,sha256=q37dj7O0LeyiV38Zd5P1Qn_qmhjdFomuYTRY1Yk48Bo,2007
127
131
  funboost/publishers/__init__.py,sha256=xqBHlvsJQVPfbdvP84G0LHmVB7-pFBS7vDnX1Uo9pVY,131
128
- funboost/publishers/base_publisher.py,sha256=0ctHAhhmZavFs5ssNjFo5dE-6XXSVo4rURF40q3UAu0,17046
132
+ funboost/publishers/base_publisher.py,sha256=K6ztxeCWCDakEFUXEtA-lmmcccY8fLEKFlOLIVzjz58,17718
129
133
  funboost/publishers/celery_publisher.py,sha256=uc9N1uLW74skUCw8dsnvxORM2O3cy4SiI7tUZRmvkHA,2336
130
134
  funboost/publishers/celery_publisher000.py,sha256=2XLOyU2__vlIUTi5L15uf0BJqAIjxbc3kCLIRDSOY9w,3966
131
135
  funboost/publishers/confluent_kafka_publisher.py,sha256=B4rF6gljixOMyN6L2eL1gzqTv97uoy7TTzgKUhHljEQ,4749
132
136
  funboost/publishers/dramatiq_publisher.py,sha256=IH9F-Ps1r94WDu2a7cZbJqWlBgblDbEcpjGj2rl-9WE,1413
133
137
  funboost/publishers/empty_publisher.py,sha256=Com5m-mkjXpcWxKZZneymhLNaRJNaGAtvwjhwHmECgg,870
138
+ funboost/publishers/faststream_publisher.py,sha256=ndGDHz53s_u5NsSi2vK7_Zll2m3lfqzQ8iaNPGHo6oE,2223
134
139
  funboost/publishers/http_publisher.py,sha256=pS3z_AVqH6h4PAgqB7usihvzLJP5ZzfPKQRMQfHrJHQ,753
135
140
  funboost/publishers/httpsqs_publisher.py,sha256=PS6h8-mn3wYFfMOsPt4tal8p0yZgYgrYYO9ZnIl9CwU,2737
136
141
  funboost/publishers/huey_publisher.py,sha256=9HBrsqTO61iPB1nI5fYOQNPuOaX4I4Wmb1BRNODAE_0,1118
137
142
  funboost/publishers/kafka_publisher.py,sha256=5qOkNl1SFh4TQaVg0hJSF2ms7T76bkpF6ZtjjLaW8Vg,2060
138
143
  funboost/publishers/kombu_publisher.py,sha256=Z0JKF_-xKJSTc21jqhIwphDUHUPO2X3wVojt-rHhDlM,5415
139
- funboost/publishers/local_python_queue_publisher.py,sha256=Do0eE2smI81jNxNiRcMP8lpZcekAfjgwMNMdEagQzVA,3555
144
+ funboost/publishers/local_python_queue_publisher.py,sha256=292NKW7X4MCMMPvfMDb-6_BdA997lda7-lOlODIAaOY,3477
140
145
  funboost/publishers/meomory_deque_publisher.py,sha256=0q6WKQ8ohnhlXDgXkxWGsImZCnwB12nFD6kUjldRQiw,1303
141
146
  funboost/publishers/mongomq_publisher.py,sha256=xQr3KMQEKksX4OEvzPlCl8v1VeBHaoZtYw2QujOUyGo,1874
142
147
  funboost/publishers/mqtt_publisher.py,sha256=nL-pweqL8lkoRUliNKQtdXgryG0wZO7iIvjFdr1it1s,3131
@@ -164,6 +169,7 @@ funboost/publishers/txt_file_publisher.py,sha256=dqrfBt1ejjwJbwFS3UqPo4VFDCa6NWb
164
169
  funboost/publishers/udp_publisher.py,sha256=TOiKrhmCMjx4teqIgdUwRic0lxyK2cupinafsz--wzY,1194
165
170
  funboost/publishers/zeromq_publisher.py,sha256=SHFzSLPLkoht9Ey-q894g59sCuu6IM603xDV0JD4CrY,1024
166
171
  funboost/queues/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
172
+ funboost/queues/memory_queues_map.py,sha256=e1S_cnjnCVI4DBsA_iupF51S_eX4OvCtlefQCqS1TYA,424
167
173
  funboost/queues/peewee_queue.py,sha256=FrwegrilkHZG6Y1cGdl5Bt_UtA25f7m5TJQJMZ9YnJI,5280
168
174
  funboost/queues/sqla_queue.py,sha256=b-2QPvvuMxklm41ibZhGKehaAV9trXBQFCOHzgThx_s,11080
169
175
  funboost/timing_job/__init__.py,sha256=uMKIiZOqr8aXGPxZhDcVVeeNTsDi-S7kOkLc1nHaLQw,9099
@@ -179,7 +185,7 @@ funboost/utils/custom_pysnooper.py,sha256=7yXLKEMY_JjPRRt0Y0N-wV2CFhILlYNh40Y6uR
179
185
  funboost/utils/decorators.py,sha256=lyi9TCBg__7xkoV17AZhRItTn95vU2XMtOxfXJVV5B4,26601
180
186
  funboost/utils/develop_log.py,sha256=Wsx0ongGjTit5xqgk1BztYlVEkC6d0-Y7GENXLedVqY,271
181
187
  funboost/utils/expire_lock.py,sha256=AOkd1KlvZeIwQaz8ZoKxLpGxWgqQ4mfNHcFphh04o8Q,4732
182
- funboost/utils/json_helper.py,sha256=GvPVJJ2f6MvOmABP2t006yBxXQIFJSJAeshiNoJ9Vsk,2259
188
+ funboost/utils/json_helper.py,sha256=kwN5O-0Z3_9KS3JdBx-BltS9LI3Rfoq329vgkxMPVw4,2286
183
189
  funboost/utils/mongo_util.py,sha256=g2AdsanKm2v9X-OaTCS6hx_0JvRw5WukXIttN3TD9sI,3069
184
190
  funboost/utils/monkey_color_log.py,sha256=QChhQMTB6phZ2eBaPq-9tFZF1n7pWeJgmJPIB_ugkvs,7367
185
191
  funboost/utils/monkey_patches.py,sha256=vGmtPuTwNLbS8T3gpufSC_cD8_Vnp85roZrCpJZUSE4,2890
@@ -271,9 +277,9 @@ funboost/utils/pysnooper_ydf/utils.py,sha256=evSmGi_Oul7vSP47AJ0DLjFwoCYCfunJZ1m
271
277
  funboost/utils/pysnooper_ydf/variables.py,sha256=QejRDESBA06KG9OH4sBT4J1M55eaU29EIHg8K_igaXo,3693
272
278
  funboost/utils/times/__init__.py,sha256=Y4bQD3SIA_E7W2YvHq2Qdi0dGM4H2DxyFNdDOuFOq1w,2417
273
279
  funboost/utils/times/version.py,sha256=11XfnZVVzOgIhXXdeN_mYfdXThfrsbQHpA0wCjz-hpg,17
274
- funboost-45.3.dist-info/LICENSE,sha256=9EPP2ktG_lAPB8PjmWV-c9BiaJHc_FP6pPLcUrUwx0E,11562
275
- funboost-45.3.dist-info/METADATA,sha256=j8GSigEDeKAcr4uZDTM-AGVW8fe6_9r5Zwd9AK8RxdM,31707
276
- funboost-45.3.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
277
- funboost-45.3.dist-info/entry_points.txt,sha256=BQMqRALuw-QT9x2d7puWaUHriXfy3wIzvfzF61AnSSI,97
278
- funboost-45.3.dist-info/top_level.txt,sha256=K8WuKnS6MRcEWxP1NvbmCeujJq6TEfbsB150YROlRw0,9
279
- funboost-45.3.dist-info/RECORD,,
280
+ funboost-45.6.dist-info/LICENSE,sha256=9EPP2ktG_lAPB8PjmWV-c9BiaJHc_FP6pPLcUrUwx0E,11562
281
+ funboost-45.6.dist-info/METADATA,sha256=d2uFQ75R_EbBoYH8UHkCQaFNVKkAXqS8Brg9PvXRhMg,32533
282
+ funboost-45.6.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
283
+ funboost-45.6.dist-info/entry_points.txt,sha256=BQMqRALuw-QT9x2d7puWaUHriXfy3wIzvfzF61AnSSI,97
284
+ funboost-45.6.dist-info/top_level.txt,sha256=K8WuKnS6MRcEWxP1NvbmCeujJq6TEfbsB150YROlRw0,9
285
+ funboost-45.6.dist-info/RECORD,,