funboost 24.0__py3-none-any.whl → 24.2__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
@@ -6,6 +6,8 @@ from funboost.core import show_funboost_flag
6
6
 
7
7
  # noinspection PyUnresolvedReferences
8
8
  import nb_log
9
+ # noinspection PyUnresolvedReferences
10
+ from nb_log import get_logger,nb_print
9
11
  from funboost.set_frame_config import patch_frame_config, show_frame_config
10
12
  from funboost.funboost_config_deafult import BoostDecoratorDefaultParams
11
13
 
@@ -0,0 +1,140 @@
1
+ """
2
+ 比更简单的 ThreadPoolExecutorShrinkAble 的弹性线程池,因为 funboost的并发池永远不需要判断代码结束,所以不用 ThreadPoolExecutorShrinkAble 那么复杂来兼容判断并发池要随代码退出而结束循环
3
+ """
4
+
5
+ import asyncio
6
+ import inspect
7
+ import queue
8
+ import threading
9
+ from functools import wraps
10
+
11
+ import nb_log
12
+ from nb_log import LoggerMixin, LoggerLevelSetterMixin
13
+
14
+
15
+ class FlexibleThreadPool(LoggerMixin, LoggerLevelSetterMixin):
16
+ KEEP_ALIVE_TIME = 10
17
+ MIN_WORKERS = 0
18
+
19
+ def __init__(self, max_workers: int = None):
20
+ self.work_queue = queue.Queue(10)
21
+ self.max_workers = max_workers
22
+ self._threads_num = 0
23
+ self.threads_free_count = 0
24
+ self._lock_compute_start_thread = threading.Lock()
25
+ self._lock_compute_threads_free_count = threading.Lock()
26
+ self._lock_for_adjust_thread = threading.Lock()
27
+ self._lock_for_judge_threads_free_count = threading.Lock()
28
+ self.pool_ident = id(self)
29
+ self.asyncio_loop = asyncio.new_event_loop()
30
+
31
+ def _change_threads_free_count(self, change_num):
32
+ with self._lock_compute_threads_free_count:
33
+ self.threads_free_count += change_num
34
+
35
+ def _change_threads_start_count(self, change_num):
36
+ with self._lock_compute_start_thread:
37
+ self._threads_num += change_num
38
+
39
+ def submit(self, func, *args, **kwargs):
40
+ self.work_queue.put([func, args, kwargs])
41
+ with self._lock_for_adjust_thread:
42
+ if self.threads_free_count <= self.MIN_WORKERS and self._threads_num < self.max_workers:
43
+ _KeepAliveTimeThread(self).start()
44
+
45
+
46
+ def run_sync_or_async_fun(func, *args, **kwargs):
47
+ t1 =time.time()
48
+ fun_is_asyncio = inspect.iscoroutinefunction(func)
49
+
50
+ if fun_is_asyncio:
51
+ try:
52
+ loop = asyncio.get_event_loop()
53
+ except RuntimeError:
54
+ loop = asyncio.new_event_loop()
55
+ print(time.time() - t1)
56
+ try:
57
+ result = loop.run_until_complete(func(*args, **kwargs))
58
+
59
+ return result
60
+ finally:
61
+ pass
62
+ # loop.close()
63
+ else:
64
+ return func(*args, **kwargs)
65
+
66
+
67
+ def sync_or_async_fun_deco(func):
68
+ @wraps(func)
69
+ def _inner(*args, **kwargs):
70
+ return run_sync_or_async_fun(func, *args, **kwargs)
71
+
72
+ return _inner
73
+
74
+
75
+ # noinspection PyProtectedMember
76
+ class _KeepAliveTimeThread(threading.Thread):
77
+ logger = nb_log.get_logger('_KeepAliveTimeThread')
78
+
79
+ def __init__(self, thread_pool: FlexibleThreadPool):
80
+ super().__init__()
81
+ self.pool = thread_pool
82
+
83
+ def run(self) -> None:
84
+ self.logger.debug(f'新启动线程 {self.ident} ')
85
+ self.pool._change_threads_free_count(1)
86
+ self.pool._change_threads_start_count(1)
87
+ while 1:
88
+ try:
89
+ func, args, kwargs = self.pool.work_queue.get(block=True, timeout=self.pool.KEEP_ALIVE_TIME)
90
+ except queue.Empty:
91
+
92
+ with self.pool._lock_for_judge_threads_free_count:
93
+ # print(self.pool.threads_free_count)
94
+ if self.pool.threads_free_count > self.pool.MIN_WORKERS:
95
+ self.logger.debug(f'停止线程 {self._ident}, 触发条件是 {self.pool.pool_ident} 线程池中的 {self.ident} 线程 超过 {self.pool.KEEP_ALIVE_TIME} 秒没有任务,线程池中不在工作状态中的线程数量是 {self.pool.threads_free_count},超过了指定的最小核心数量 {self.pool.MIN_WORKERS}') # noqa
96
+ self.pool._change_threads_free_count(-1)
97
+ self.pool._change_threads_start_count(-1)
98
+ break # 退出while 1,即是结束。
99
+ else:
100
+ continue
101
+ self.pool._change_threads_free_count(-1)
102
+ try:
103
+ t1 = time.time()
104
+ fun = sync_or_async_fun_deco(func)
105
+ result = fun(*args, **kwargs)
106
+ print(time.time()-t1)
107
+ # print(result)
108
+ except BaseException as exc:
109
+ self.logger.exception(f'函数 {func.__name__} 中发生错误,错误原因是 {type(exc)} {exc} ')
110
+ self.pool._change_threads_free_count(1)
111
+
112
+
113
+ if __name__ == '__main__':
114
+ import time
115
+ from concurrent.futures import ThreadPoolExecutor
116
+ from custom_threadpool_executor import ThreadPoolExecutorShrinkAble
117
+
118
+
119
+ def testf(x):
120
+ # time.sleep(10)
121
+ if x % 10000 == 0:
122
+ print(x)
123
+
124
+
125
+ async def aiotestf(x):
126
+ # await asyncio.sleep(1)
127
+ # print(x)
128
+ return x * 2
129
+
130
+
131
+ pool = FlexibleThreadPool(100)
132
+ # pool = ThreadPoolExecutor(100)
133
+ # pool = ThreadPoolExecutorShrinkAble(100)
134
+
135
+ for i in range(2000):
136
+ # time.sleep(2)
137
+ pool.submit(aiotestf, i)
138
+
139
+ # for i in range(1000000):
140
+ # pool.submit(testf, i)
@@ -452,8 +452,6 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
452
452
  else:
453
453
  check_not_monkey()
454
454
 
455
- ''' 日志跳转代码行不正确,不用这种方式'''
456
-
457
455
  def _log_error(self, msg, exc_info=None):
458
456
  self.logger.error(msg=f'{msg} \n', exc_info=exc_info, extra={'sys_getframe_n': 3})
459
457
  self.error_file_logger.error(msg=f'{msg} \n', exc_info=exc_info, extra={'sys_getframe_n': 3})
@@ -767,7 +765,7 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
767
765
  avarage_function_spend_time = round(self._consuming_function_cost_time_total_every_unit_time / self._execute_task_times_every_unit_time, 4)
768
766
  msg = f'{self._unit_time_for_count} 秒内执行了 {self._execute_task_times_every_unit_time} 次函数 [ {self.consuming_function.__name__} ] ,' \
769
767
  f'函数平均运行耗时 {avarage_function_spend_time} 秒。 '
770
- self.logger.debug(msg)
768
+ self.logger.info(msg)
771
769
  if self._msg_num_in_broker != -1 and time.time() - self._last_show_remaining_execution_time > self._show_remaining_execution_time_interval: # 有的中间件无法统计或没实现统计队列剩余数量的,统一返回的是-1,不显示这句话。
772
770
  # msg += f''' ,预计还需要 {time_util.seconds_to_hour_minute_second(self._msg_num_in_broker * avarage_function_spend_time / active_consumer_num)} 时间 才能执行完成 {self._msg_num_in_broker}个剩余的任务'''
773
771
  need_time = time_util.seconds_to_hour_minute_second(self._msg_num_in_broker / (self._execute_task_times_every_unit_time / self._unit_time_for_count) /
@@ -915,7 +913,7 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
915
913
  avarage_function_spend_time = round(self._consuming_function_cost_time_total_every_unit_time / self._execute_task_times_every_unit_time, 4)
916
914
  msg = f'{self._unit_time_for_count} 秒内执行了 {self._execute_task_times_every_unit_time} 次函数 [ {self.consuming_function.__name__} ] ,' \
917
915
  f'函数平均运行耗时 {avarage_function_spend_time} 秒。 '
918
- self.logger.debug(msg)
916
+ self.logger.info(msg)
919
917
  if self._msg_num_in_broker != -1 and time.time() - self._last_show_remaining_execution_time > self._show_remaining_execution_time_interval: # 有的中间件无法统计或没实现统计队列剩余数量的,统一返回的是-1,不显示这句话。
920
918
  # msg += f''' ,预计还需要 {time_util.seconds_to_hour_minute_second(self._msg_num_in_broker * avarage_function_spend_time / active_consumer_num)} 时间 才能执行完成 {self._msg_num_in_broker}个剩余的任务'''
921
919
  need_time = time_util.seconds_to_hour_minute_second(self._msg_num_in_broker / (self._execute_task_times_every_unit_time / self._unit_time_for_count) /
@@ -14,7 +14,7 @@ class LocalPythonQueueConsumer(AbstractConsumer):
14
14
  """
15
15
 
16
16
  @property
17
- def local_python_queue(self) -> SimpleQueue:
17
+ def local_python_queue(self) -> Queue:
18
18
  return local_python_queue_publisher.local_pyhton_queue_name__local_pyhton_queue_obj_map[self._queue_name]
19
19
 
20
20
  def _shedual_task(self):
@@ -32,3 +32,4 @@ class LocalPythonQueueConsumer(AbstractConsumer):
32
32
 
33
33
  def _requeue(self, kw):
34
34
  self.local_python_queue.put(kw['body'])
35
+
funboost/core/booster.py CHANGED
@@ -21,6 +21,8 @@ class Booster:
21
21
  funboost极其重视代码能在pycharm下自动补全。元编程经常造成在pycharm下代码无法自动补全提示,要是实现代码补全难。
22
22
  这种__call__写法在pycahrm下 不仅能补全消费函数的 push consume等方法,也能补全函数本身的入参,一举两得。代码能自动补全很重要。
23
23
  一个函数fun被 boost装饰器装饰后, isinstance(fun,Booster) 为True.
24
+
25
+ Booster 是把Consumer 和 Publisher的方法集为一体。
24
26
  """
25
27
 
26
28
  def __init__(self, queue_name,
@@ -25,9 +25,9 @@ def get_boost_params_and_consuming_function(queue_name: str) -> (dict, typing.Ca
25
25
  return queue_name__boost_params_consuming_function_map[queue_name]
26
26
 
27
27
 
28
- def get_or_create_booster(queue_name, consuming_function, **boost_params, ):
28
+ def get_or_create_booster(queue_name, consuming_function, **boost_params, ) -> Booster:
29
29
  """
30
- 当前进程获得或者创建booster对象。方便有的人需要在函数内部临时动态根据队列名创建booster,不会无数次临时生成消费者 生产者创建消息队列连接。
30
+ 当前进程获得或者创建booster对象。方便有的人需要在函数内部临时动态根据队列名创建booster,不会无数次临时生成消费者、生产者、创建消息队列连接。
31
31
  :param boost_params: 就是 Booster的入参。
32
32
  :return:
33
33
  """
@@ -16,7 +16,7 @@ class LocalPythonQueuePublisher(AbstractPublisher):
16
16
  # noinspection PyAttributeOutsideInit
17
17
  def custom_init(self):
18
18
  if self._queue_name not in local_pyhton_queue_name__local_pyhton_queue_obj_map:
19
- local_pyhton_queue_name__local_pyhton_queue_obj_map[self._queue_name] = SimpleQueue()
19
+ local_pyhton_queue_name__local_pyhton_queue_obj_map[self._queue_name] = Queue(10000)
20
20
  self.queue = local_pyhton_queue_name__local_pyhton_queue_obj_map[self._queue_name]
21
21
 
22
22
  def concrete_realization_of_publish(self, msg):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: funboost
3
- Version: 24.0
3
+ Version: 24.2
4
4
  Summary: pip install funboost,python全功能分布式函数调度框架,。支持python所有类型的并发模式和一切知名消息队列中间件,支持celery框架整体作为funboost中间件,python函数加速器,框架包罗万象,一统编程思维,兼容50% python业务场景,适用范围广。只需要一行代码即可分布式执行python一切函数,99%用过funboost的pythoner 感受是 方便 快速 强大,相见恨晚
5
5
  Home-page: https://github.com/ydf0509/funboost
6
6
  Author: bfzs
@@ -1,4 +1,4 @@
1
- funboost/__init__.py,sha256=9Khpiu4k86MTbo_dltppDd1a6L-zH-HdBFhOizg2O7k,2465
1
+ funboost/__init__.py,sha256=JZZsKhrVUpcLzWOPkZA0SDdnKNLPXLv66WGLXSx8FbM,2544
2
2
  funboost/__init__old.py,sha256=cSzw-ZQqtAAp5_-7eONXQT5fwz_JbZlRjDQPASDLUHk,20378
3
3
  funboost/constant.py,sha256=tSdHQ8nrZH63jOKKZFtOfTo2melCsiMrk-O1lFcsIKA,6489
4
4
  funboost/funboost_config_deafult.py,sha256=mh9o91CgMC26glIWkw7489GxmJ7bibi4_oBMnv3SCNU,7430
@@ -20,13 +20,14 @@ funboost/concurrent_pool/custom_evenlet_pool_executor.py,sha256=f8ubB21J6TZr0qaW
20
20
  funboost/concurrent_pool/custom_gevent_pool_executor.py,sha256=qCkWB836UuBm6yVdI6hhWOaI5vFlog0N1y46ByzO0ro,4421
21
21
  funboost/concurrent_pool/custom_threadpool_executor.py,sha256=Ydqpx2bJ4mnbWCcQj247xepSvNN72ZfekaYA_vBMfyM,11745
22
22
  funboost/concurrent_pool/custom_threadpool_executor000.py,sha256=jJLXy3h-bELap6nZA6yLtdozzTWcvCtZ7IY6MTqLEAM,9317
23
+ funboost/concurrent_pool/flexible_thread_pool.py,sha256=kDSydmWI8M7qtnN-dNDzAqeVabx4ADsF-qO2_CHYDZA,5022
23
24
  funboost/concurrent_pool/single_thread_executor.py,sha256=NDWOegh8Nxpb-Bp-lUlj-DONWvepSmA9qepL1yNgdQI,373
24
25
  funboost/concurrent_pool/backup/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
26
  funboost/concurrent_pool/backup/async_pool_executor0223.py,sha256=iTxxJFk2lu1P9ZAIkBip3euq3oEQ4_qTODy3xUaOecY,9548
26
27
  funboost/concurrent_pool/backup/async_pool_executor_back.py,sha256=vIgUUyF4Zb0jIRPWgNPqyO09YEkQP32kkpGBldqm4qA,9568
27
28
  funboost/concurrent_pool/backup/async_pool_executor_janus.py,sha256=OHMWJ9l3EYTpPpcrPrGGKd4K0tmQ2PN8HiX0Dta0EOo,5728
28
29
  funboost/consumers/__init__.py,sha256=ZXY_6Kut1VYNQiF5aWEgIWobsW1ht9YUP0TdRZRWFqI,126
29
- funboost/consumers/base_consumer.py,sha256=CdBohmk2DIgT4oiyX-REuqhWh3GwBjyPV4SKmXm04xA,86845
30
+ funboost/consumers/base_consumer.py,sha256=1yt-Exk1x2vCUR8zm0UVPJtZpp5B8gQIXkRL0E-7hTc,86777
30
31
  funboost/consumers/celery_consumer.py,sha256=Jt7m7k53dXzdZFdbjl3ECwxlAq3ojdjhPqJLPG2jW4M,9088
31
32
  funboost/consumers/celery_consumer000.py,sha256=8SF8ppHIMH-BIAHO0NyJYnSQxe_PcT6hv05e7DySG54,4574
32
33
  funboost/consumers/confirm_mixin.py,sha256=oVBQ1RayIBFOzGNTJ69HdBR_kLd46xd0VfCOKfqDpLA,6033
@@ -38,7 +39,7 @@ funboost/consumers/huey_consumer.py,sha256=_Z2lpfAzvs1HqkSouncN7eH1VfyQJPYNZNgv1
38
39
  funboost/consumers/kafka_consumer.py,sha256=2BZT5UQIGqjS1emsX--V1J8gWFOhMjCMMiSnWy_Hto0,4217
39
40
  funboost/consumers/kafka_consumer_manually_commit.py,sha256=L-GYQrZ91TWCeI-GIDMXYg3LXFcq0mQ1ACu0gp4bkTI,6947
40
41
  funboost/consumers/kombu_consumer.py,sha256=VlU5LR69G0we6K5UCzWOiz-Ul_LJArWznMvTUCBwyR8,10186
41
- funboost/consumers/local_python_queue_consumer.py,sha256=Sc6uPDzpVZRRNCWWoNDHCOwVaixFoXWsLeNEOl2TKuU,1309
42
+ funboost/consumers/local_python_queue_consumer.py,sha256=jNYohp7IW1d8BT8weHE_6R11fvMFnMOFd8HE7kW8_yg,1305
42
43
  funboost/consumers/mongomq_consumer.py,sha256=W5d4QnYun1OnnCn8K41ZmXuGdpAXmnzAa4EI7H7KQO4,1090
43
44
  funboost/consumers/mqtt_consumer.py,sha256=StlfPUeQ6o0HiZBpt7TlC_r2DjzPHBZBF2xLSxQW13A,2228
44
45
  funboost/consumers/nameko_consumer.py,sha256=IVwUxBixUx2m3F_q8Xn-UsnJWXayMpnOIZjICCd6mcU,2183
@@ -71,12 +72,12 @@ funboost/contrib/queue2queue.py,sha256=I6e1d2s_TQ_29rbwSs3KuA7Tuj9B905EcJruT287C
71
72
  funboost/contrib/redis_consume_latest_msg_broker.py,sha256=V-8-pOyotOXNbs2olS7vig-yN1PxRdw_MeLOtpUufJc,1817
72
73
  funboost/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
73
74
  funboost/core/active_cousumer_info_getter.py,sha256=VwVRxsApdKqnoBdg1PsHEkyWiOFJMm0_qWIdCFbPOWg,4984
74
- funboost/core/booster.py,sha256=9jqNczG36-bEs9x708PHWqsugk5Y0gjOZcPO_zq3kmU,16688
75
+ funboost/core/booster.py,sha256=vc0mIztI2YeztvW2zt0sbbmGhCJk8bylamex6XgszaI,16756
75
76
  funboost/core/exit_signal.py,sha256=GDP3si_hNm7-iNZeeRaON62He47j_jX5weOOsDYW68Y,576
76
77
  funboost/core/fabric_deploy_helper.py,sha256=yIdWhlJAR1rCpzvcSme-KrU7RwKH8B7tdwHG-q6QH0U,8875
77
78
  funboost/core/function_result_status_config.py,sha256=CE7xaGoL7vT2VsLTHvv-EnJE7K83XVWivBimHVmVfnQ,1570
78
79
  funboost/core/function_result_status_saver.py,sha256=tNYRvUfb2T59mda9fLWDftbPbLvyKQg7Sph6BTBy_J4,8886
79
- funboost/core/get_booster.py,sha256=GcHmPyk0Rezw_fjVgj2bw7NW6_koXF_ttruX6dqTv30,1746
80
+ funboost/core/get_booster.py,sha256=G47Gukmy-2DTBng8ayTPfK-blQ7gxT83CJlryrhqpOo,1762
80
81
  funboost/core/global_boosters.py,sha256=CDrnKhxEEb-GxTL7JhrDTD1tyhLj7ciAtO1Cy2iyi1Q,340
81
82
  funboost/core/helper_funs.py,sha256=SKSMKytJTOFQJsuMKWJ-_mQg6e_Bgpq1ANDzjjBAcio,1192
82
83
  funboost/core/kill_remote_task.py,sha256=Rj4nrz13CKka8he1T8-k6CYN_yjvcaYZaZ7yurKcKzo,8159
@@ -115,7 +116,7 @@ funboost/publishers/httpsqs_publisher.py,sha256=7cf5ijwrbp4smq6ofndrKisruAqG0Wzf
115
116
  funboost/publishers/huey_publisher.py,sha256=z1CF18YZkQY6lqeoc6tiJkaYsRjRicbhYYeoHSvdD-w,1101
116
117
  funboost/publishers/kafka_publisher.py,sha256=cmlJ0mvwq0Ajlth4VQqwnoe6v_bZ4eIz49GgkiJr-ZU,2160
117
118
  funboost/publishers/kombu_publisher.py,sha256=TJt24M_Y4x8wjXHFl2hp6z3Dat6rjuA_xGiqA8RbRLg,5321
118
- funboost/publishers/local_python_queue_publisher.py,sha256=oETalt6ICcWbvvALG3dulgTQxOS4Ikq3BwcYl-tEX2Q,1383
119
+ funboost/publishers/local_python_queue_publisher.py,sha256=3Xl29I0ul2lWO-cIVyeDdtAYDGK2Jn_Q3qacHu2Ulpg,1382
119
120
  funboost/publishers/mongomq_publisher.py,sha256=xQr3KMQEKksX4OEvzPlCl8v1VeBHaoZtYw2QujOUyGo,1874
120
121
  funboost/publishers/mqtt_publisher.py,sha256=NKVDE5R12QL99IXgRjJtF4phyW8QaXKxHkqW5p_kXr4,3050
121
122
  funboost/publishers/nameko_publisher.py,sha256=2sqRe08mTjBlvnDe-mE55wFMxllzHCPKA4WtMABnKZ0,1670
@@ -242,8 +243,8 @@ funboost/utils/pysnooper_ydf/utils.py,sha256=evSmGi_Oul7vSP47AJ0DLjFwoCYCfunJZ1m
242
243
  funboost/utils/pysnooper_ydf/variables.py,sha256=QejRDESBA06KG9OH4sBT4J1M55eaU29EIHg8K_igaXo,3693
243
244
  funboost/utils/times/__init__.py,sha256=Y4bQD3SIA_E7W2YvHq2Qdi0dGM4H2DxyFNdDOuFOq1w,2417
244
245
  funboost/utils/times/version.py,sha256=11XfnZVVzOgIhXXdeN_mYfdXThfrsbQHpA0wCjz-hpg,17
245
- funboost-24.0.dist-info/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
246
- funboost-24.0.dist-info/METADATA,sha256=BS1P5aE66v6zQpatiFyJw1ORCVqC2t4DiFZQMmVWn5E,26979
247
- funboost-24.0.dist-info/WHEEL,sha256=D1Wh14kWDxPnrM-5t_6UCB-UuQNrEODtRa3vF4OsvQY,97
248
- funboost-24.0.dist-info/top_level.txt,sha256=K8WuKnS6MRcEWxP1NvbmCeujJq6TEfbsB150YROlRw0,9
249
- funboost-24.0.dist-info/RECORD,,
246
+ funboost-24.2.dist-info/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
247
+ funboost-24.2.dist-info/METADATA,sha256=0-Yhyjd8dSqlYiv80xxV7wdA_YyjLsYLW60bjmuSQrk,26979
248
+ funboost-24.2.dist-info/WHEEL,sha256=D1Wh14kWDxPnrM-5t_6UCB-UuQNrEODtRa3vF4OsvQY,97
249
+ funboost-24.2.dist-info/top_level.txt,sha256=K8WuKnS6MRcEWxP1NvbmCeujJq6TEfbsB150YROlRw0,9
250
+ funboost-24.2.dist-info/RECORD,,