funboost 45.1__py3-none-any.whl → 45.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 funboost might be problematic. Click here for more details.
- funboost/__init__.py +2 -2
- funboost/assist/faststream_helper.py +18 -0
- funboost/assist/taskiq_helper.py +1 -0
- funboost/consumers/base_consumer.py +14 -10
- funboost/consumers/confirm_mixin.py +2 -2
- funboost/consumers/faststream_consumer.py +54 -0
- funboost/consumers/redis_consumer.py +2 -2
- funboost/consumers/redis_consumer_ack_using_timeout.py +2 -2
- funboost/consumers/redis_consumer_simple.py +2 -1
- funboost/consumers/redis_filter.py +2 -2
- funboost/core/current_task.py +1 -1
- funboost/core/function_result_status_saver.py +5 -3
- funboost/core/msg_result_getter.py +3 -3
- funboost/core/serialization.py +16 -0
- funboost/factories/broker_kind__publsiher_consumer_type_map.py +14 -6
- funboost/publishers/base_publisher.py +28 -17
- funboost/publishers/faststream_publisher.py +57 -0
- funboost/publishers/local_python_queue_publisher.py +1 -0
- funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/StoppableThread.cpython-37.pyc +0 -0
- funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/dafunc.cpython-37.pyc +0 -0
- funboost/utils/json_helper.py +14 -14
- {funboost-45.1.dist-info → funboost-45.4.dist-info}/METADATA +3 -2
- {funboost-45.1.dist-info → funboost-45.4.dist-info}/RECORD +27 -23
- funboost/core/funboost_current_task_context_thread.py +0 -12
- {funboost-45.1.dist-info → funboost-45.4.dist-info}/LICENSE +0 -0
- {funboost-45.1.dist-info → funboost-45.4.dist-info}/WHEEL +0 -0
- {funboost-45.1.dist-info → funboost-45.4.dist-info}/entry_points.txt +0 -0
- {funboost-45.1.dist-info → funboost-45.4.dist-info}/top_level.txt +0 -0
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.
|
|
16
|
+
__version__ = "45.4"
|
|
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,
|
|
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
|
-
|
|
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
|
|
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:
|
|
@@ -408,8 +409,7 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
|
|
|
408
409
|
extra_params = {'task_id': task_id, 'publish_time': round(time.time(), 4),
|
|
409
410
|
'publish_time_format': time.strftime('%Y-%m-%d %H:%M:%S')}
|
|
410
411
|
"""
|
|
411
|
-
|
|
412
|
-
msg = json.loads(msg)
|
|
412
|
+
msg = Serialization.to_dict(msg)
|
|
413
413
|
# 以下是清洗补全字段.
|
|
414
414
|
if 'extra' not in msg:
|
|
415
415
|
msg['extra'] = {'is_auto_fill_extra': True}
|
|
@@ -536,7 +536,7 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
|
|
|
536
536
|
# print(999)
|
|
537
537
|
if self.consumer_params.is_show_message_get_from_broker:
|
|
538
538
|
# 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} 中取出的消息是 {
|
|
539
|
+
self.logger.debug(f'从 {broker_name or self.consumer_params.broker_kind} 中间件 的 {self._queue_name} 中取出的消息是 {Serialization.to_json_str(msg)}')
|
|
540
540
|
|
|
541
541
|
def _get_priority_conf(self, kw: dict, broker_task_config_key: str):
|
|
542
542
|
broker_task_config = kw['body'].get('extra', {}).get(broker_task_config_key, None)
|
|
@@ -585,7 +585,7 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
|
|
|
585
585
|
# method_cls = getattr(sys.modules[self.consumer_params.consuming_function_class_module],
|
|
586
586
|
# self.consumer_params.consuming_function_class_name)
|
|
587
587
|
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])
|
|
588
|
+
method_cls = getattr(PathHelper(method_first_param_value[ConstStrForClassMethod.CLS_FILE]).import_as_module(), method_first_param_value[ConstStrForClassMethod.CLS_NAME])
|
|
589
589
|
real_function_only_params[method_first_param_name] = method_cls
|
|
590
590
|
elif self.publisher_params.consuming_function_kind == FunctionKind.INSTANCE_METHOD:
|
|
591
591
|
method_cls = getattr(PathHelper(method_first_param_value[ConstStrForClassMethod.CLS_FILE]).import_as_module(), method_first_param_value[ConstStrForClassMethod.CLS_NAME])
|
|
@@ -642,7 +642,7 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
|
|
|
642
642
|
# RedisMixin().redis_db_frame.expire(kw['body']['extra']['task_id'], 600)
|
|
643
643
|
current_function_result_status.rpc_result_expire_seconds = self.consumer_params.rpc_result_expire_seconds
|
|
644
644
|
p.lpush(kw['body']['extra']['task_id'],
|
|
645
|
-
|
|
645
|
+
Serialization.to_json_str(current_function_result_status.get_status_dict(without_datetime_obj=True)))
|
|
646
646
|
p.expire(kw['body']['extra']['task_id'], self.consumer_params.rpc_result_expire_seconds)
|
|
647
647
|
p.execute()
|
|
648
648
|
|
|
@@ -675,6 +675,8 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
|
|
|
675
675
|
# self.logger.critical(msg=f'{log_msg} \n', exc_info=True)
|
|
676
676
|
# self.error_file_logger.critical(msg=f'{log_msg} \n', exc_info=True)
|
|
677
677
|
self.logger.critical(msg=log_msg, exc_info=True)
|
|
678
|
+
fct = funboost_current_task()
|
|
679
|
+
fct.set_fct_context(None)
|
|
678
680
|
|
|
679
681
|
# noinspection PyProtectedMember
|
|
680
682
|
def _run_consuming_function_with_confirm_and_retry(self, kw: dict, current_retry_times,
|
|
@@ -810,7 +812,7 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
|
|
|
810
812
|
with RedisMixin().redis_db_filter_and_rpc_result.pipeline() as p:
|
|
811
813
|
current_function_result_status.rpc_result_expire_seconds = self.consumer_params.rpc_result_expire_seconds
|
|
812
814
|
p.lpush(kw['body']['extra']['task_id'],
|
|
813
|
-
|
|
815
|
+
Serialization.to_json_str(current_function_result_status.get_status_dict(without_datetime_obj=True)))
|
|
814
816
|
p.expire(kw['body']['extra']['task_id'], self.consumer_params.rpc_result_expire_seconds)
|
|
815
817
|
p.execute()
|
|
816
818
|
|
|
@@ -847,6 +849,8 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
|
|
|
847
849
|
# self.logger.critical(msg=f'{log_msg} \n', exc_info=True)
|
|
848
850
|
# self.error_file_logger.critical(msg=f'{log_msg} \n', exc_info=True)
|
|
849
851
|
self.logger.critical(msg=log_msg, exc_info=True)
|
|
852
|
+
fct = funboost_current_task()
|
|
853
|
+
fct.set_fct_context(None)
|
|
850
854
|
|
|
851
855
|
# noinspection PyProtectedMember
|
|
852
856
|
async def _async_run_consuming_function_with_confirm_and_retry(self, kw: dict, current_retry_times,
|
|
@@ -1156,14 +1160,14 @@ class DistributedConsumerStatistics(RedisMixin, FunboostFileLoggerMixin):
|
|
|
1156
1160
|
results = self.redis_db_frame.smembers(redis_key)
|
|
1157
1161
|
with self.redis_db_frame.pipeline() as p:
|
|
1158
1162
|
for result in results:
|
|
1159
|
-
result_dict =
|
|
1163
|
+
result_dict = Serialization.to_dict(result)
|
|
1160
1164
|
if self.timestamp() - result_dict['hearbeat_timestamp'] > 15 \
|
|
1161
1165
|
or self._consumer_identification_map['consumer_uuid'] == result_dict['consumer_uuid']:
|
|
1162
1166
|
# 因为这个是10秒钟运行一次,15秒还没更新,那肯定是掉线了。如果消费者本身是自己也先删除。
|
|
1163
1167
|
p.srem(redis_key, result)
|
|
1164
1168
|
self._consumer_identification_map['hearbeat_datetime_str'] = time_util.DatetimeConverter().datetime_str
|
|
1165
1169
|
self._consumer_identification_map['hearbeat_timestamp'] = self.timestamp()
|
|
1166
|
-
value =
|
|
1170
|
+
value = Serialization.to_json_str(self._consumer_identification_map, )
|
|
1167
1171
|
p.sadd(redis_key, value)
|
|
1168
1172
|
p.execute()
|
|
1169
1173
|
|
|
@@ -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':
|
|
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
|
|
@@ -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,
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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]
|
funboost/core/current_task.py
CHANGED
|
@@ -150,7 +150,7 @@ def is_asyncio_environment():
|
|
|
150
150
|
|
|
151
151
|
def funboost_current_task():
|
|
152
152
|
if is_asyncio_environment():
|
|
153
|
-
if thread_current_task.get_fct_context().asyncio_use_thread_concurrent_mode is True:
|
|
153
|
+
if hasattr(thread_current_task._fct_local_data,'fct_context') and thread_current_task.get_fct_context().asyncio_use_thread_concurrent_mode is True:
|
|
154
154
|
# 如果用户使用的是默认的ConcurrentModeEnum.THREADING并发模式来运行async def 函数,那么也使用线程获取上下文
|
|
155
155
|
return thread_current_task
|
|
156
156
|
else:
|
|
@@ -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 =
|
|
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
|
-
|
|
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__} {
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
19
|
+
|
|
20
20
|
from funboost.publishers.rabbitmq_pika_publisher import RabbitmqPublisher
|
|
21
|
-
|
|
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
|
-
|
|
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
|
-
|
|
61
|
-
|
|
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
|
-
|
|
19
|
+
|
|
20
20
|
|
|
21
21
|
import nb_log
|
|
22
22
|
from funboost.constant import ConstStrForClassMethod, FunctionKind
|
|
@@ -24,11 +24,12 @@ 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
|
-
|
|
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
|
|
@@ -179,13 +180,13 @@ class AbstractPublisher(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
|
|
|
179
180
|
|
|
180
181
|
@staticmethod
|
|
181
182
|
def _get_from_other_extra_params(k: str, msg):
|
|
182
|
-
msg_dict = json.loads(msg) if isinstance(msg, str) else msg
|
|
183
|
+
# msg_dict = json.loads(msg) if isinstance(msg, str) else msg
|
|
184
|
+
msg_dict = Serialization.to_dict(msg)
|
|
183
185
|
return msg_dict['extra'].get('other_extra_params', {}).get(k, None)
|
|
184
186
|
|
|
185
187
|
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
|
-
|
|
188
|
-
msg = json.loads(msg)
|
|
188
|
+
priority_control_config: PriorityConsumingControlConfig = None) -> (typing.Dict, typing.Dict, typing.Dict,str):
|
|
189
|
+
msg = Serialization.to_dict(msg)
|
|
189
190
|
msg_function_kw = copy.deepcopy(msg)
|
|
190
191
|
raw_extra = {}
|
|
191
192
|
if 'extra' in msg:
|
|
@@ -214,7 +215,7 @@ class AbstractPublisher(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
|
|
|
214
215
|
msg, msg_function_kw, extra_params, task_id = self._convert_msg(msg, task_id, priority_control_config)
|
|
215
216
|
t_start = time.time()
|
|
216
217
|
decorators.handle_exception(retry_times=10, is_throw_error=True, time_sleep=0.1)(
|
|
217
|
-
self.concrete_realization_of_publish)(
|
|
218
|
+
self.concrete_realization_of_publish)(Serialization.to_json_str(msg))
|
|
218
219
|
|
|
219
220
|
self.logger.debug(f'向{self._queue_name} 队列,推送消息 耗时{round(time.time() - t_start, 4)}秒 {msg_function_kw}', extra={'task_id': task_id}) # 显示msg太长了。
|
|
220
221
|
with self._lock_for_count:
|
|
@@ -228,10 +229,16 @@ class AbstractPublisher(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
|
|
|
228
229
|
|
|
229
230
|
def send_msg(self, msg: typing.Union[dict, str]):
|
|
230
231
|
"""直接发送任意消息内容到消息队列,不生成辅助参数,无视函数入参名字,不校验入参个数和键名"""
|
|
231
|
-
if isinstance(msg, dict):
|
|
232
|
-
msg = json.dumps(msg, ensure_ascii=False)
|
|
233
232
|
decorators.handle_exception(retry_times=10, is_throw_error=True, time_sleep=0.1)(
|
|
234
|
-
self.concrete_realization_of_publish)(msg)
|
|
233
|
+
self.concrete_realization_of_publish)(Serialization.to_json_str(msg))
|
|
234
|
+
|
|
235
|
+
@staticmethod
|
|
236
|
+
def __get_cls_file(cls: type):
|
|
237
|
+
if cls.__module__ == '__main__':
|
|
238
|
+
cls_file = Path(sys.argv[0]).resolve().as_posix()
|
|
239
|
+
else:
|
|
240
|
+
cls_file = Path(sys.modules[cls.__module__].__file__).resolve().as_posix()
|
|
241
|
+
return cls_file
|
|
235
242
|
|
|
236
243
|
def push(self, *func_args, **func_kwargs):
|
|
237
244
|
"""
|
|
@@ -260,9 +267,10 @@ class AbstractPublisher(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
|
|
|
260
267
|
# )
|
|
261
268
|
cls = func_args_list[0]
|
|
262
269
|
# print(cls,cls.__name__, sys.modules[cls.__module__].__file__)
|
|
270
|
+
|
|
263
271
|
func_args_list[0] = {ConstStrForClassMethod.FIRST_PARAM_NAME: self.publish_params_checker.all_arg_name[0],
|
|
264
272
|
ConstStrForClassMethod.CLS_NAME: cls.__name__,
|
|
265
|
-
ConstStrForClassMethod.CLS_FILE:
|
|
273
|
+
ConstStrForClassMethod.CLS_FILE: self.__get_cls_file(cls),
|
|
266
274
|
}
|
|
267
275
|
elif self.publisher_params.consuming_function_kind == FunctionKind.INSTANCE_METHOD:
|
|
268
276
|
obj = func_args[0]
|
|
@@ -271,7 +279,7 @@ class AbstractPublisher(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
|
|
|
271
279
|
raise ValueError(f'消费函数 {self.publisher_params.consuming_function} 是实例方法,实例必须有 {ConstStrForClassMethod.OBJ_INIT_PARAMS} 属性')
|
|
272
280
|
func_args_list[0] = {ConstStrForClassMethod.FIRST_PARAM_NAME: self.publish_params_checker.all_arg_name[0],
|
|
273
281
|
ConstStrForClassMethod.CLS_NAME: cls.__name__,
|
|
274
|
-
ConstStrForClassMethod.CLS_FILE:
|
|
282
|
+
ConstStrForClassMethod.CLS_FILE: self.__get_cls_file(cls),
|
|
275
283
|
ConstStrForClassMethod.OBJ_INIT_PARAMS: getattr(obj, ConstStrForClassMethod.OBJ_INIT_PARAMS),
|
|
276
284
|
|
|
277
285
|
}
|
|
@@ -330,11 +338,14 @@ def deco_mq_conn_error(f):
|
|
|
330
338
|
# noinspection PyBroadException
|
|
331
339
|
try:
|
|
332
340
|
return f(self, *args, **kwargs)
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
341
|
+
except Exception as e:
|
|
342
|
+
import amqpstorm
|
|
343
|
+
from pikav1.exceptions import AMQPError as PikaAMQPError
|
|
344
|
+
if isinstance(e,(PikaAMQPError, amqpstorm.AMQPError)):
|
|
345
|
+
# except (PikaAMQPError, amqpstorm.AMQPError,) as e: # except BaseException as e: # 现在装饰器用到了绝大多出地方,单个异常类型不行。ex
|
|
346
|
+
self.logger.error(f'中间件链接出错 ,方法 {f.__name__} 出错 ,{e}')
|
|
347
|
+
self.init_broker()
|
|
348
|
+
return f(self, *args, **kwargs)
|
|
338
349
|
except BaseException as e:
|
|
339
350
|
self.logger.critical(e, exc_info=True)
|
|
340
351
|
|
|
@@ -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
|
|
Binary file
|
funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/dafunc.cpython-37.pyc
CHANGED
|
Binary file
|
funboost/utils/json_helper.py
CHANGED
|
@@ -45,20 +45,20 @@ def monkey_patch_json():
|
|
|
45
45
|
json.dumps = _dumps
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
class JsonUtils:
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
+
Version: 45.4
|
|
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.
|
|
30
|
+
Requires-Dist: nb-libs (>=1.6)
|
|
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'
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
funboost/__init__.py,sha256=
|
|
1
|
+
funboost/__init__.py,sha256=yWOlKCPbh9FzUmOWdmpZJ3G5kAs_eF3ZqbntyEezdAg,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=
|
|
38
|
+
funboost/consumers/base_consumer.py,sha256=1pYBXiNUUZDCAjp5vzKkQSyauN4WTAMlRb75EMHxQeE,80120
|
|
37
39
|
funboost/consumers/celery_consumer.py,sha256=9gtz7nlZkmv3ErmaseT0_Q__ltSPx-fOcwi-TMPoaLA,9220
|
|
38
|
-
funboost/consumers/confirm_mixin.py,sha256=
|
|
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
|
|
@@ -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=
|
|
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=
|
|
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=
|
|
68
|
-
funboost/consumers/redis_filter.py,sha256=
|
|
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,28 +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=
|
|
90
|
+
funboost/core/current_task.py,sha256=cJw5ZDtsTJB_5LHN18F1XUFaT-YnAX60AOAi_CkvQoE,5772
|
|
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
|
-
funboost/core/funboost_current_task_context_thread.py,sha256=RRhqmRl943jBccgTrsfra4lS20X0zB2TtLAUaZn_A18,441
|
|
92
94
|
funboost/core/funboost_time.py,sha256=IbB4dFCpg3oGUe90ssAJ_x0eDPtAVfvsUr4esdoKaOk,1777
|
|
93
95
|
funboost/core/func_params_model.py,sha256=CMePcxL8vNSWHAEVvQImJF5h7Qv-gIvieh6ejPYgjEU,20593
|
|
94
96
|
funboost/core/function_result_status_config.py,sha256=PyjqAQOiwsLt28sRtH-eYRjiI3edPFO4Nde0ILFRReE,1764
|
|
95
|
-
funboost/core/function_result_status_saver.py,sha256=
|
|
97
|
+
funboost/core/function_result_status_saver.py,sha256=yHKZF9MjmhI-Q4Mkrka7DdweJ0wpgfLmgfAlsfkCeCk,9274
|
|
96
98
|
funboost/core/helper_funs.py,sha256=Jzy6t4-cugI-tOztgt7DGSos3tkxk2R4IOUOK2OWJ-I,2365
|
|
97
99
|
funboost/core/kill_remote_task.py,sha256=MZ5vWLGt6SxyN76h5Lf_id9tyVUzjR-qXNyJwXaGlZY,8129
|
|
98
100
|
funboost/core/lazy_impoter.py,sha256=5yToRBbTB338heR787ap5i-UAGN0FanUCkYfK9Nsgnk,4957
|
|
99
101
|
funboost/core/loggers.py,sha256=uy5mFLIUvKaVdJZLi6THyxqeuOmp9XEOKrH1Yci0zUM,2354
|
|
100
|
-
funboost/core/msg_result_getter.py,sha256=
|
|
102
|
+
funboost/core/msg_result_getter.py,sha256=wxJBTOsyPDptLbNYOIA7YwuDsDUKDpB5sFzj9_HPcPs,8106
|
|
101
103
|
funboost/core/muliti_process_enhance.py,sha256=tI3178inc5sqPh-jQc0XaTuUD1diIZyHuukBRk1Gp6Y,3595
|
|
104
|
+
funboost/core/serialization.py,sha256=Q6cAfbaqBCKw0AQwisbMNjOOD0csq0xdes5BHn1Nelo,412
|
|
102
105
|
funboost/core/task_id_logger.py,sha256=lR19HQcX6Pp8laURCD656xNpF_JP6nLB3zUKI69EWzE,864
|
|
103
106
|
funboost/core/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
104
107
|
funboost/core/cli/discovery_boosters.py,sha256=mbEyv0bUIGcmgkfXLI_Q1IK1QvVwKyro8XccuuMEA6o,3944
|
|
105
108
|
funboost/core/cli/funboost_cli_user_templ.py,sha256=XUpKLxRKtYfebPUM8wii64kB0HW8L7j9LnRpT0xCfQI,2243
|
|
106
109
|
funboost/core/cli/funboost_fire.py,sha256=OT0SNi9Zb0Tom2E0cWuArQs0obUowAA_rpCF7GdaKPs,5065
|
|
107
110
|
funboost/factories/__init__.py,sha256=s7kKKjR1HU5eMjPD6r5b-SXTVMo1zBp2JjOAtkyt5Yo,178
|
|
108
|
-
funboost/factories/broker_kind__publsiher_consumer_type_map.py,sha256
|
|
111
|
+
funboost/factories/broker_kind__publsiher_consumer_type_map.py,sha256=-kKhV65KnRf86b353PJuaEMXMolfV4B2CtTF1wD1kFQ,10189
|
|
109
112
|
funboost/factories/consumer_factory.py,sha256=EaAw3OZJkGepkQxKkDvMshHjIVOQva_N6nUEhLO4JwU,1500
|
|
110
113
|
funboost/factories/publisher_factotry.py,sha256=4651sxnbIAi6sFEUQdlUuv8UkbMQIE_Pbzm-1DimAs8,2535
|
|
111
114
|
funboost/function_result_web/app.py,sha256=WFAsAoPqCVeTxKS31UvkAhuRqUurg-j8D3c9RtHZEyY,5059
|
|
@@ -126,18 +129,19 @@ funboost/function_result_web/static/js/jquery-1.11.0.min.js,sha256=ryQZ3RXgnqkTz
|
|
|
126
129
|
funboost/function_result_web/templates/index.html,sha256=YM0582Q4t2da-xBf3Ga0McIfcsT9H98rjZck-irMkGo,20387
|
|
127
130
|
funboost/function_result_web/templates/login.html,sha256=q37dj7O0LeyiV38Zd5P1Qn_qmhjdFomuYTRY1Yk48Bo,2007
|
|
128
131
|
funboost/publishers/__init__.py,sha256=xqBHlvsJQVPfbdvP84G0LHmVB7-pFBS7vDnX1Uo9pVY,131
|
|
129
|
-
funboost/publishers/base_publisher.py,sha256=
|
|
132
|
+
funboost/publishers/base_publisher.py,sha256=mkrXf6eUqVyYOtFmkE5Op1j1z2c-RP-BFqOkyEsQiWI,17424
|
|
130
133
|
funboost/publishers/celery_publisher.py,sha256=uc9N1uLW74skUCw8dsnvxORM2O3cy4SiI7tUZRmvkHA,2336
|
|
131
134
|
funboost/publishers/celery_publisher000.py,sha256=2XLOyU2__vlIUTi5L15uf0BJqAIjxbc3kCLIRDSOY9w,3966
|
|
132
135
|
funboost/publishers/confluent_kafka_publisher.py,sha256=B4rF6gljixOMyN6L2eL1gzqTv97uoy7TTzgKUhHljEQ,4749
|
|
133
136
|
funboost/publishers/dramatiq_publisher.py,sha256=IH9F-Ps1r94WDu2a7cZbJqWlBgblDbEcpjGj2rl-9WE,1413
|
|
134
137
|
funboost/publishers/empty_publisher.py,sha256=Com5m-mkjXpcWxKZZneymhLNaRJNaGAtvwjhwHmECgg,870
|
|
138
|
+
funboost/publishers/faststream_publisher.py,sha256=ndGDHz53s_u5NsSi2vK7_Zll2m3lfqzQ8iaNPGHo6oE,2223
|
|
135
139
|
funboost/publishers/http_publisher.py,sha256=pS3z_AVqH6h4PAgqB7usihvzLJP5ZzfPKQRMQfHrJHQ,753
|
|
136
140
|
funboost/publishers/httpsqs_publisher.py,sha256=PS6h8-mn3wYFfMOsPt4tal8p0yZgYgrYYO9ZnIl9CwU,2737
|
|
137
141
|
funboost/publishers/huey_publisher.py,sha256=9HBrsqTO61iPB1nI5fYOQNPuOaX4I4Wmb1BRNODAE_0,1118
|
|
138
142
|
funboost/publishers/kafka_publisher.py,sha256=5qOkNl1SFh4TQaVg0hJSF2ms7T76bkpF6ZtjjLaW8Vg,2060
|
|
139
143
|
funboost/publishers/kombu_publisher.py,sha256=Z0JKF_-xKJSTc21jqhIwphDUHUPO2X3wVojt-rHhDlM,5415
|
|
140
|
-
funboost/publishers/local_python_queue_publisher.py,sha256=
|
|
144
|
+
funboost/publishers/local_python_queue_publisher.py,sha256=SJFyQ_4fk1oEBVmtNqTPTsmMgkhnMbf5SA4v7RDE6Do,3569
|
|
141
145
|
funboost/publishers/meomory_deque_publisher.py,sha256=0q6WKQ8ohnhlXDgXkxWGsImZCnwB12nFD6kUjldRQiw,1303
|
|
142
146
|
funboost/publishers/mongomq_publisher.py,sha256=xQr3KMQEKksX4OEvzPlCl8v1VeBHaoZtYw2QujOUyGo,1874
|
|
143
147
|
funboost/publishers/mqtt_publisher.py,sha256=nL-pweqL8lkoRUliNKQtdXgryG0wZO7iIvjFdr1it1s,3131
|
|
@@ -180,7 +184,7 @@ funboost/utils/custom_pysnooper.py,sha256=7yXLKEMY_JjPRRt0Y0N-wV2CFhILlYNh40Y6uR
|
|
|
180
184
|
funboost/utils/decorators.py,sha256=lyi9TCBg__7xkoV17AZhRItTn95vU2XMtOxfXJVV5B4,26601
|
|
181
185
|
funboost/utils/develop_log.py,sha256=Wsx0ongGjTit5xqgk1BztYlVEkC6d0-Y7GENXLedVqY,271
|
|
182
186
|
funboost/utils/expire_lock.py,sha256=AOkd1KlvZeIwQaz8ZoKxLpGxWgqQ4mfNHcFphh04o8Q,4732
|
|
183
|
-
funboost/utils/json_helper.py,sha256=
|
|
187
|
+
funboost/utils/json_helper.py,sha256=kwN5O-0Z3_9KS3JdBx-BltS9LI3Rfoq329vgkxMPVw4,2286
|
|
184
188
|
funboost/utils/mongo_util.py,sha256=g2AdsanKm2v9X-OaTCS6hx_0JvRw5WukXIttN3TD9sI,3069
|
|
185
189
|
funboost/utils/monkey_color_log.py,sha256=QChhQMTB6phZ2eBaPq-9tFZF1n7pWeJgmJPIB_ugkvs,7367
|
|
186
190
|
funboost/utils/monkey_patches.py,sha256=vGmtPuTwNLbS8T3gpufSC_cD8_Vnp85roZrCpJZUSE4,2890
|
|
@@ -251,13 +255,13 @@ funboost/utils/dependency_packages_in_pythonpath/func_timeout/exceptions.py,sha2
|
|
|
251
255
|
funboost/utils/dependency_packages_in_pythonpath/func_timeout/py2_raise.py,sha256=9tpZLQ3-zIgU_ixazydwZmw8rFg7ybjI9alNYfSvwRk,169
|
|
252
256
|
funboost/utils/dependency_packages_in_pythonpath/func_timeout/py3_raise.py,sha256=Odvg1FtXTEC--Ru1EIfsHASamBpOm9hdXY7OnlEUObA,280
|
|
253
257
|
funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/StoppableThread.cpython-311.pyc,sha256=oFKhlv2kniXOCQ2KgP9B_I_RHt5hilYVdYlzWbPaypY,6484
|
|
254
|
-
funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/StoppableThread.cpython-37.pyc,sha256=
|
|
258
|
+
funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/StoppableThread.cpython-37.pyc,sha256=J2fgGZsSYL8VKtKcRsgRhkV4hGHcHnTtBWdBCYmGBCc,5121
|
|
255
259
|
funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/StoppableThread.cpython-39.pyc,sha256=FH5T5kZIWSS05PM-G7JhmItm_Y7yXCnFMPMIVsf204M,5141
|
|
256
260
|
funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/__init__.cpython-311.pyc,sha256=UBbUeoQg10pza2zo-5ELiOzE_Q-WclM1dafrFDRKZVs,857
|
|
257
261
|
funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/__init__.cpython-37.pyc,sha256=ACdP5kS3R-5Bt2Qk-sq6RHF3LaOyywwVyM9R7L8m1-Q,763
|
|
258
262
|
funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/__init__.cpython-39.pyc,sha256=5J3PkkFxVDKw4aFL59LwtJC5SDtteQT9ibIBR_1DkF8,767
|
|
259
263
|
funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/dafunc.cpython-311.pyc,sha256=lm1W6uTCo09YyK_P5WNpBr1dBMMBwl8Pt6ItwMyBNGA,11447
|
|
260
|
-
funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/dafunc.cpython-37.pyc,sha256=
|
|
264
|
+
funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/dafunc.cpython-37.pyc,sha256=AB8oSLB3lCqKJAGlz4rSTE4cCGU5bhcKHbhr1OV2lPw,8293
|
|
261
265
|
funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/dafunc.cpython-39.pyc,sha256=3szcbtA-GeP5VmaAgS9fG_nt3QppLPnuvaJ2i_raQqI,8086
|
|
262
266
|
funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/exceptions.cpython-311.pyc,sha256=9v_YQPA5cUlVs-3UHbVwfiIE8UBzPD1HyWzApfBSR7Y,4592
|
|
263
267
|
funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/exceptions.cpython-37.pyc,sha256=UXQJRMqqUOpudiCLxFYM2RWF8JXerjUttXl6Fiw55OE,3708
|
|
@@ -272,9 +276,9 @@ funboost/utils/pysnooper_ydf/utils.py,sha256=evSmGi_Oul7vSP47AJ0DLjFwoCYCfunJZ1m
|
|
|
272
276
|
funboost/utils/pysnooper_ydf/variables.py,sha256=QejRDESBA06KG9OH4sBT4J1M55eaU29EIHg8K_igaXo,3693
|
|
273
277
|
funboost/utils/times/__init__.py,sha256=Y4bQD3SIA_E7W2YvHq2Qdi0dGM4H2DxyFNdDOuFOq1w,2417
|
|
274
278
|
funboost/utils/times/version.py,sha256=11XfnZVVzOgIhXXdeN_mYfdXThfrsbQHpA0wCjz-hpg,17
|
|
275
|
-
funboost-45.
|
|
276
|
-
funboost-45.
|
|
277
|
-
funboost-45.
|
|
278
|
-
funboost-45.
|
|
279
|
-
funboost-45.
|
|
280
|
-
funboost-45.
|
|
279
|
+
funboost-45.4.dist-info/LICENSE,sha256=9EPP2ktG_lAPB8PjmWV-c9BiaJHc_FP6pPLcUrUwx0E,11562
|
|
280
|
+
funboost-45.4.dist-info/METADATA,sha256=OPw8J2SBtwnQV7VgLemWmfR2Xjo_aVyJ7abR8mihV1w,31729
|
|
281
|
+
funboost-45.4.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
|
|
282
|
+
funboost-45.4.dist-info/entry_points.txt,sha256=BQMqRALuw-QT9x2d7puWaUHriXfy3wIzvfzF61AnSSI,97
|
|
283
|
+
funboost-45.4.dist-info/top_level.txt,sha256=K8WuKnS6MRcEWxP1NvbmCeujJq6TEfbsB150YROlRw0,9
|
|
284
|
+
funboost-45.4.dist-info/RECORD,,
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
# import threading
|
|
2
|
-
#
|
|
3
|
-
# from funboost.core.current_task import thread_current_task
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
# class FctThread(threading.Thread):
|
|
7
|
-
# thread_current_task__dict_key = 'thread_current_task__dict'
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
# def run(self):
|
|
11
|
-
# thread_current_task._fct_local_data.__dict__.update(getattr(self,self.thread_current_task__dict_key)) # 把funboost的消费线程上下文需要传递到线程上下文里面来.
|
|
12
|
-
# super().run()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|