funboost 44.7__py3-none-any.whl → 44.9__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 +1 -1
- funboost/constant.py +14 -4
- funboost/consumers/base_consumer.py +54 -20
- funboost/core/booster.py +15 -0
- funboost/core/current_task.py +88 -69
- funboost/core/func_params_model.py +23 -0
- funboost/core/lazy_impoter.py +8 -2
- funboost/publishers/base_publisher.py +21 -2
- funboost/set_frame_config.py +2 -1
- funboost/utils/class_utils.py +101 -51
- funboost/utils/dependency_packages_in_pythonpath/func_timeout/StoppableThread.py +3 -2
- funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/StoppableThread.cpython-39.pyc +0 -0
- funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/dafunc.cpython-39.pyc +0 -0
- funboost/utils/dependency_packages_in_pythonpath/func_timeout/dafunc.py +4 -4
- funboost/utils/dependency_packages_in_pythonpath/func_timeout/dafunc2222.py +244 -0
- {funboost-44.7.dist-info → funboost-44.9.dist-info}/METADATA +1 -1
- {funboost-44.7.dist-info → funboost-44.9.dist-info}/RECORD +21 -20
- {funboost-44.7.dist-info → funboost-44.9.dist-info}/LICENSE +0 -0
- {funboost-44.7.dist-info → funboost-44.9.dist-info}/WHEEL +0 -0
- {funboost-44.7.dist-info → funboost-44.9.dist-info}/entry_points.txt +0 -0
- {funboost-44.7.dist-info → funboost-44.9.dist-info}/top_level.txt +0 -0
funboost/__init__.py
CHANGED
funboost/constant.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# coding= utf-8
|
|
2
2
|
class BrokerEnum:
|
|
3
|
-
EMPTY = 'empty'
|
|
3
|
+
EMPTY = 'empty' # 空的实现,需要搭配 boost入参的 consumer_override_cls 和 publisher_override_cls使用,或者被继承。
|
|
4
4
|
|
|
5
5
|
RABBITMQ_AMQPSTORM = 'RABBITMQ_AMQPSTORM' # 使用 amqpstorm 包操作rabbitmq 作为 分布式消息队列,支持消费确认.强烈推荐这个作为funboost中间件。
|
|
6
6
|
RABBITMQ = RABBITMQ_AMQPSTORM
|
|
@@ -41,8 +41,6 @@ class BrokerEnum:
|
|
|
41
41
|
|
|
42
42
|
ZEROMQ = 'ZEROMQ' # 基于zeromq作为分布式消息队列,不需要安装中间件,可以支持跨机器但不支持持久化。
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
44
|
"""
|
|
47
45
|
操作 kombu 包,这个包也是celery的中间件依赖包,这个包可以操作10种中间件(例如rabbitmq redis),但没包括分布式函数调度框架的kafka nsq zeromq 等。
|
|
48
46
|
同时 kombu 包的性能非常差,可以用原生redis的lpush和kombu的publish测试发布,使用brpop 和 kombu 的 drain_events测试消费,对比差距相差了5到10倍。
|
|
@@ -71,7 +69,6 @@ class BrokerEnum:
|
|
|
71
69
|
|
|
72
70
|
PEEWEE = 'PEEWEE' # peewee包操作mysql,使用表模拟消息队列
|
|
73
71
|
|
|
74
|
-
|
|
75
72
|
CELERY = 'CELERY' # funboost支持celery框架来发布和消费任务,由celery框架来调度执行任务,但是写法简单远远暴击用户亲自使用celery的麻烦程度,
|
|
76
73
|
# 用户永无无需关心和操作Celery对象实例,无需关心celery的task_routes和include配置,funboost来自动化设置这些celery配置。
|
|
77
74
|
|
|
@@ -92,4 +89,17 @@ class ConcurrentModeEnum:
|
|
|
92
89
|
SINGLE_THREAD = 'single_thread' # 如果你不想并发,不想预先从消息队列中间件拉取消息到python程序的内存queue队列缓冲中,那么就适合使用此并发模式。
|
|
93
90
|
SOLO = SINGLE_THREAD
|
|
94
91
|
|
|
92
|
+
|
|
95
93
|
# is_fsdf_remote_run = 0
|
|
94
|
+
|
|
95
|
+
class FunctionKind:
|
|
96
|
+
CLASS_METHOD = 'CLASS_METHOD'
|
|
97
|
+
INSTANCE_METHOD = 'INSTANCE_METHOD'
|
|
98
|
+
STATIC_METHOD = 'STATIC_METHOD'
|
|
99
|
+
COMMON_FUNCTION = 'COMMON_FUNCTION'
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class ConstStrForClassMethod:
|
|
103
|
+
FIRST_PARAM_NAME = 'first_param_name'
|
|
104
|
+
CLS_NAME = 'cls_name'
|
|
105
|
+
OBJ_INIT_PARAMS = 'obj_init_params'
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
框架做主要的功能都是在这个文件里面实现的.
|
|
9
9
|
"""
|
|
10
10
|
import functools
|
|
11
|
+
import sys
|
|
11
12
|
import typing
|
|
12
13
|
import abc
|
|
13
14
|
import copy
|
|
@@ -30,13 +31,14 @@ from threading import Lock
|
|
|
30
31
|
import asyncio
|
|
31
32
|
|
|
32
33
|
import nb_log
|
|
33
|
-
from funboost.core.current_task import funboost_current_task
|
|
34
|
+
from funboost.core.current_task import funboost_current_task, FctContext
|
|
34
35
|
from funboost.core.loggers import develop_logger
|
|
35
36
|
|
|
36
37
|
from funboost.core.func_params_model import BoosterParams, PublisherParams, BaseJsonAbleModel
|
|
37
38
|
from funboost.core.task_id_logger import TaskIdLogger
|
|
39
|
+
from funboost.constant import FunctionKind
|
|
38
40
|
from funboost.utils.json_helper import JsonUtils
|
|
39
|
-
from nb_log import (get_logger, LoggerLevelSetterMixin, LogManager,
|
|
41
|
+
from nb_log import (get_logger, LoggerLevelSetterMixin, LogManager, is_main_process,
|
|
40
42
|
nb_log_config_default)
|
|
41
43
|
from funboost.core.loggers import FunboostFileLoggerMixin, logger_prompt
|
|
42
44
|
|
|
@@ -66,7 +68,7 @@ from funboost.consumers.redis_filter import RedisFilter, RedisImpermanencyFilter
|
|
|
66
68
|
from funboost.factories.publisher_factotry import get_publisher
|
|
67
69
|
|
|
68
70
|
from funboost.utils import decorators, time_util, redis_manager
|
|
69
|
-
from funboost.constant import ConcurrentModeEnum, BrokerEnum
|
|
71
|
+
from funboost.constant import ConcurrentModeEnum, BrokerEnum, ConstStrForClassMethod
|
|
70
72
|
from funboost.core import kill_remote_task
|
|
71
73
|
from funboost.core.exceptions import ExceptionForRequeue, ExceptionForPushToDlxqueue
|
|
72
74
|
|
|
@@ -562,9 +564,35 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
|
|
|
562
564
|
"""
|
|
563
565
|
self._do_not_delete_extra_from_msg = True
|
|
564
566
|
|
|
565
|
-
def user_custom_record_process_info_func(self,current_function_result_status:FunctionResultStatus): # 这个可以继承
|
|
567
|
+
def user_custom_record_process_info_func(self, current_function_result_status: FunctionResultStatus): # 这个可以继承
|
|
566
568
|
pass
|
|
567
569
|
|
|
570
|
+
async def aio_user_custom_record_process_info_func(self, current_function_result_status: FunctionResultStatus): # 这个可以继承
|
|
571
|
+
pass
|
|
572
|
+
|
|
573
|
+
def _convert_real_function_only_params_by_conusuming_function_kind(self, function_only_params: dict):
|
|
574
|
+
"""对于实例方法和classmethod 方法, 从消息队列的消息恢复第一个入参, self 和 cls"""
|
|
575
|
+
if self.consumer_params.consuming_function_kind in [FunctionKind.CLASS_METHOD, FunctionKind.INSTANCE_METHOD]:
|
|
576
|
+
real_function_only_params = copy.copy(function_only_params)
|
|
577
|
+
method_first_param_name = None
|
|
578
|
+
method_first_param_value = None
|
|
579
|
+
for k, v in function_only_params.items():
|
|
580
|
+
if isinstance(v, dict) and ConstStrForClassMethod.FIRST_PARAM_NAME in v:
|
|
581
|
+
method_first_param_name = k
|
|
582
|
+
method_first_param_value = v
|
|
583
|
+
break
|
|
584
|
+
method_cls = getattr(sys.modules[self.consumer_params.consuming_function_class_module],
|
|
585
|
+
self.consumer_params.consuming_function_class_name)
|
|
586
|
+
if self.publisher_params.consuming_function_kind == FunctionKind.CLASS_METHOD:
|
|
587
|
+
real_function_only_params[method_first_param_name] = method_cls
|
|
588
|
+
elif self.publisher_params.consuming_function_kind == FunctionKind.INSTANCE_METHOD:
|
|
589
|
+
obj = method_cls(**method_first_param_value[ConstStrForClassMethod.OBJ_INIT_PARAMS])
|
|
590
|
+
real_function_only_params[method_first_param_name] = obj
|
|
591
|
+
# print(real_function_only_params)
|
|
592
|
+
return real_function_only_params
|
|
593
|
+
else:
|
|
594
|
+
return function_only_params
|
|
595
|
+
|
|
568
596
|
# noinspection PyProtectedMember
|
|
569
597
|
def _run(self, kw: dict, ):
|
|
570
598
|
# print(kw)
|
|
@@ -624,9 +652,9 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
|
|
|
624
652
|
msg = f'{self._unit_time_for_count} 秒内执行了 {self._execute_task_times_every_unit_time} 次函数 [ {self.consuming_function.__name__} ] ,' \
|
|
625
653
|
f'函数平均运行耗时 {avarage_function_spend_time} 秒。 '
|
|
626
654
|
self.logger.info(msg)
|
|
627
|
-
if
|
|
655
|
+
if time.time() - self._last_show_remaining_execution_time > self._show_remaining_execution_time_interval:
|
|
628
656
|
self._msg_num_in_broker = self.publisher_of_same_queue.get_message_count()
|
|
629
|
-
if self._msg_num_in_broker != -1
|
|
657
|
+
if self._msg_num_in_broker != -1: # 有的中间件无法统计或没实现统计队列剩余数量的,统一返回的是-1,不显示这句话。
|
|
630
658
|
# 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}个剩余的任务'''
|
|
631
659
|
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) /
|
|
632
660
|
self._distributed_consumer_statistics.active_consumer_num)
|
|
@@ -636,7 +664,7 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
|
|
|
636
664
|
self._current_time_for_execute_task_times_every_unit_time = time.time()
|
|
637
665
|
self._consuming_function_cost_time_total_every_unit_time = 0
|
|
638
666
|
self._execute_task_times_every_unit_time = 0
|
|
639
|
-
self.user_custom_record_process_info_func(current_function_result_status)
|
|
667
|
+
self.user_custom_record_process_info_func(current_function_result_status) # 两种方式都可以自定义,记录结果,建议继承方式,不使用boost中指定 user_custom_record_process_info_func
|
|
640
668
|
if self.consumer_params.user_custom_record_process_info_func:
|
|
641
669
|
self.consumer_params.user_custom_record_process_info_func(current_function_result_status)
|
|
642
670
|
except BaseException as e:
|
|
@@ -653,17 +681,19 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
|
|
|
653
681
|
t_start = time.time()
|
|
654
682
|
# function_result_status.run_times = current_retry_times + 1
|
|
655
683
|
fct = funboost_current_task()
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
684
|
+
fct_context = FctContext(function_params=function_only_params,
|
|
685
|
+
full_msg=kw['body'],
|
|
686
|
+
function_result_status=function_result_status,
|
|
687
|
+
logger=self.logger, )
|
|
688
|
+
|
|
660
689
|
try:
|
|
661
690
|
function_run = self.consuming_function
|
|
662
691
|
if self._consuming_function_is_asyncio:
|
|
663
|
-
|
|
692
|
+
fct_context.asyncio_use_thread_concurrent_mode = True
|
|
664
693
|
function_run = sync_or_async_fun_deco(function_run)
|
|
665
694
|
else:
|
|
666
|
-
|
|
695
|
+
fct_context.asynco_use_thread_concurrent_mode = False
|
|
696
|
+
fct.set_fct_context(fct_context)
|
|
667
697
|
function_timeout = self._get_priority_conf(kw, 'function_timeout')
|
|
668
698
|
function_run = function_run if self.consumer_params.consumin_function_decorator is None else self.consumer_params.consumin_function_decorator(function_run)
|
|
669
699
|
function_run = function_run if not function_timeout else self._concurrent_mode_dispatcher.timeout_deco(
|
|
@@ -675,7 +705,7 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
|
|
|
675
705
|
self.logger.warning(f'取消运行 {task_id} {function_only_params}')
|
|
676
706
|
return function_result_status
|
|
677
707
|
function_run = kill_remote_task.kill_fun_deco(task_id)(function_run) # 用杀死装饰器包装起来在另一个线程运行函数,以便等待远程杀死。
|
|
678
|
-
function_result_status.result = function_run(**function_only_params)
|
|
708
|
+
function_result_status.result = function_run(**self._convert_real_function_only_params_by_conusuming_function_kind(function_only_params))
|
|
679
709
|
# if asyncio.iscoroutine(function_result_status.result):
|
|
680
710
|
# log_msg = f'''异步的协程消费函数必须使用 async 并发模式并发,请设置消费函数 {self.consuming_function.__name__} 的concurrent_mode 为 ConcurrentModeEnum.ASYNC 或 4'''
|
|
681
711
|
# # self.logger.critical(msg=f'{log_msg} \n')
|
|
@@ -804,8 +834,11 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
|
|
|
804
834
|
self._consuming_function_cost_time_total_every_unit_time = 0
|
|
805
835
|
self._execute_task_times_every_unit_time = 0
|
|
806
836
|
|
|
837
|
+
self.user_custom_record_process_info_func(current_function_result_status) # 两种方式都可以自定义,记录结果.建议使用文档4.21.b的方式继承来重写
|
|
838
|
+
await self.aio_user_custom_record_process_info_func(current_function_result_status)
|
|
807
839
|
if self.consumer_params.user_custom_record_process_info_func:
|
|
808
|
-
|
|
840
|
+
self.consumer_params.user_custom_record_process_info_func(current_function_result_status)
|
|
841
|
+
|
|
809
842
|
except BaseException as e:
|
|
810
843
|
log_msg = f' error 严重错误 {type(e)} {e} '
|
|
811
844
|
# self.logger.critical(msg=f'{log_msg} \n', exc_info=True)
|
|
@@ -822,12 +855,13 @@ class AbstractConsumer(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
|
|
|
822
855
|
# noinspection PyBroadException
|
|
823
856
|
t_start = time.time()
|
|
824
857
|
fct = funboost_current_task()
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
858
|
+
fct_context = FctContext(function_params=function_only_params,
|
|
859
|
+
full_msg=kw['body'],
|
|
860
|
+
function_result_status=function_result_status,
|
|
861
|
+
logger=self.logger, )
|
|
862
|
+
fct.set_fct_context(fct_context)
|
|
829
863
|
try:
|
|
830
|
-
corotinue_obj = self.consuming_function(**function_only_params)
|
|
864
|
+
corotinue_obj = self.consuming_function(**self._convert_real_function_only_params_by_conusuming_function_kind(function_only_params))
|
|
831
865
|
if not asyncio.iscoroutine(corotinue_obj):
|
|
832
866
|
log_msg = f'''当前设置的并发模式为 async 并发模式,但消费函数不是异步协程函数,请不要把消费函数 {self.consuming_function.__name__} 的 concurrent_mode 设置错误'''
|
|
833
867
|
# self.logger.critical(msg=f'{log_msg} \n')
|
funboost/core/booster.py
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
import copy
|
|
3
|
+
import inspect
|
|
3
4
|
import os
|
|
5
|
+
import sys
|
|
4
6
|
import types
|
|
5
7
|
import typing
|
|
6
8
|
|
|
7
9
|
from funboost.concurrent_pool import FlexibleThreadPool
|
|
8
10
|
from funboost.concurrent_pool.async_helper import simple_run_in_executor
|
|
11
|
+
from funboost.constant import FunctionKind
|
|
12
|
+
from funboost.utils.class_utils import ClsHelper
|
|
13
|
+
|
|
9
14
|
from funboost.utils.ctrl_c_end import ctrl_c_recv
|
|
10
15
|
from funboost.core.loggers import flogger, develop_logger, logger_prompt
|
|
11
16
|
|
|
@@ -85,6 +90,16 @@ class Booster:
|
|
|
85
90
|
if len(kwargs) == 0 and len(args) == 1 and isinstance(args[0], typing.Callable):
|
|
86
91
|
consuming_function = args[0]
|
|
87
92
|
self.boost_params.consuming_function = consuming_function
|
|
93
|
+
# print(consuming_function)
|
|
94
|
+
# print(ClsHelper.get_method_kind(consuming_function))
|
|
95
|
+
# print(inspect.getsourcelines(consuming_function))
|
|
96
|
+
if self.boost_params.consuming_function_kind is None:
|
|
97
|
+
self.boost_params.consuming_function_kind = ClsHelper.get_method_kind(consuming_function)
|
|
98
|
+
if self.boost_params.consuming_function_kind in [FunctionKind.CLASS_METHOD,FunctionKind.INSTANCE_METHOD]:
|
|
99
|
+
if self.boost_params.consuming_function_class_module is None:
|
|
100
|
+
self.boost_params.consuming_function_class_module = consuming_function.__module__
|
|
101
|
+
if self.boost_params.consuming_function_class_name is None:
|
|
102
|
+
self.boost_params.consuming_function_class_name = consuming_function.__qualname__.split('.')[0]
|
|
88
103
|
logger_prompt.debug(f''' {self.boost_params.queue_name} booster 配置是 {self.boost_params.json_str_value()}''')
|
|
89
104
|
self.consuming_function = consuming_function
|
|
90
105
|
self.is_decorated_as_consume_function = True
|
funboost/core/current_task.py
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import abc
|
|
1
2
|
import contextvars
|
|
3
|
+
from dataclasses import dataclass
|
|
2
4
|
import logging
|
|
3
5
|
import threading
|
|
4
6
|
import asyncio
|
|
7
|
+
|
|
5
8
|
from funboost.core.function_result_status_saver import FunctionResultStatus
|
|
6
9
|
|
|
7
10
|
""" 用法例子
|
|
@@ -47,38 +50,55 @@ if __name__ == '__main__':
|
|
|
47
50
|
"""
|
|
48
51
|
|
|
49
52
|
|
|
50
|
-
|
|
53
|
+
@dataclass
|
|
54
|
+
class FctContext:
|
|
51
55
|
"""
|
|
52
|
-
|
|
56
|
+
fct 是 funboost current task 的简写
|
|
53
57
|
"""
|
|
54
58
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
59
|
+
function_params: dict
|
|
60
|
+
full_msg: dict
|
|
61
|
+
function_result_status: FunctionResultStatus
|
|
62
|
+
logger: logging.Logger
|
|
63
|
+
asyncio_use_thread_concurrent_mode: bool = False
|
|
64
|
+
|
|
65
|
+
# class FctContext:
|
|
66
|
+
# """
|
|
67
|
+
# fct 是 funboost current task 的简写
|
|
68
|
+
# """
|
|
69
|
+
#
|
|
70
|
+
# def __init__(self, function_params: dict,
|
|
71
|
+
# full_msg: dict,
|
|
72
|
+
# function_result_status: FunctionResultStatus,
|
|
73
|
+
# logger: logging.Logger,
|
|
74
|
+
# asyncio_use_thread_concurrent_mode: bool = False):
|
|
75
|
+
# self.function_params = function_params
|
|
76
|
+
# self.full_msg = full_msg
|
|
77
|
+
# self.function_result_status = function_result_status
|
|
78
|
+
# self.logger = logger
|
|
79
|
+
# self.asyncio_use_thread_concurrent_mode = asyncio_use_thread_concurrent_mode
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class _BaseCurrentTask(metaclass=abc.ABCMeta):
|
|
83
|
+
@abc.abstractmethod
|
|
84
|
+
def set_fct_context(self, fct_context: FctContext):
|
|
85
|
+
raise NotImplemented
|
|
86
|
+
|
|
87
|
+
@abc.abstractmethod
|
|
88
|
+
def get_fct_context(self) -> FctContext:
|
|
89
|
+
raise NotImplemented
|
|
58
90
|
|
|
59
91
|
@property
|
|
60
92
|
def function_params(self):
|
|
61
|
-
return self.
|
|
62
|
-
|
|
63
|
-
@function_params.setter
|
|
64
|
-
def function_params(self, function_params: dict):
|
|
65
|
-
self._fct_local_data.function_params = function_params
|
|
93
|
+
return self.get_fct_context().function_params
|
|
66
94
|
|
|
67
95
|
@property
|
|
68
96
|
def full_msg(self) -> dict:
|
|
69
|
-
return self.
|
|
70
|
-
|
|
71
|
-
@full_msg.setter
|
|
72
|
-
def full_msg(self, full_msg: dict):
|
|
73
|
-
self._fct_local_data.full_msg = full_msg
|
|
97
|
+
return self.get_fct_context().full_msg
|
|
74
98
|
|
|
75
99
|
@property
|
|
76
100
|
def function_result_status(self) -> FunctionResultStatus:
|
|
77
|
-
return self.
|
|
78
|
-
|
|
79
|
-
@function_result_status.setter
|
|
80
|
-
def function_result_status(self, function_result_status: FunctionResultStatus):
|
|
81
|
-
self._fct_local_data.function_result_status = function_result_status
|
|
101
|
+
return self.get_fct_context().function_result_status
|
|
82
102
|
|
|
83
103
|
@property
|
|
84
104
|
def task_id(self) -> FunctionResultStatus:
|
|
@@ -86,69 +106,52 @@ class __ThreadCurrentTask:
|
|
|
86
106
|
|
|
87
107
|
@property
|
|
88
108
|
def logger(self) -> logging.Logger:
|
|
89
|
-
return self.
|
|
109
|
+
return self.get_fct_context().logger
|
|
90
110
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
self._fct_local_data.logger = logger
|
|
111
|
+
def __str__(self):
|
|
112
|
+
return f'<{self.__class__.__name__} [{self.function_result_status.get_status_dict()}]>'
|
|
94
113
|
|
|
95
|
-
thread_current_task = __ThreadCurrentTask()
|
|
96
|
-
def is_asyncio_environment():
|
|
97
|
-
try:
|
|
98
|
-
loop = asyncio.get_running_loop()
|
|
99
|
-
return True
|
|
100
|
-
except RuntimeError as e:
|
|
101
|
-
return False
|
|
102
114
|
|
|
115
|
+
class __ThreadCurrentTask(_BaseCurrentTask):
|
|
116
|
+
"""
|
|
117
|
+
用于在用户自己函数内部去获取 消息的完整体,当前重试次数等.
|
|
118
|
+
"""
|
|
103
119
|
|
|
104
|
-
|
|
105
|
-
_function_params = contextvars.ContextVar("function_params")
|
|
106
|
-
_full_msg = contextvars.ContextVar("full_msg")
|
|
107
|
-
_function_result_status = contextvars.ContextVar("function_result_status")
|
|
108
|
-
_logger = contextvars.ContextVar('logger')
|
|
120
|
+
_fct_local_data = threading.local()
|
|
109
121
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
return self._function_params.get()
|
|
122
|
+
def set_fct_context(self, fct_context: FctContext):
|
|
123
|
+
self._fct_local_data.fct_context = fct_context
|
|
113
124
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
self._function_params.set(function_params)
|
|
125
|
+
def get_fct_context(self) -> FctContext:
|
|
126
|
+
return self._fct_local_data.fct_context
|
|
117
127
|
|
|
118
|
-
@property
|
|
119
|
-
def full_msg(self) -> dict:
|
|
120
|
-
return self._full_msg.get()
|
|
121
128
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
self._full_msg.set(full_msg)
|
|
129
|
+
class __AsyncioCurrentTask(_BaseCurrentTask):
|
|
130
|
+
_fct_context = contextvars.ContextVar('fct_context')
|
|
125
131
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
return self._function_result_status.get()
|
|
132
|
+
def set_fct_context(self, fct_context: FctContext):
|
|
133
|
+
self._fct_context.set(fct_context)
|
|
129
134
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
self._function_result_status.set(function_result_status)
|
|
135
|
+
def get_fct_context(self) -> FctContext:
|
|
136
|
+
return self._fct_context.get()
|
|
133
137
|
|
|
134
|
-
@property
|
|
135
|
-
def task_id(self) :
|
|
136
|
-
return self.function_result_status.task_id
|
|
137
138
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
return self._logger.get()
|
|
139
|
+
thread_current_task = __ThreadCurrentTask()
|
|
140
|
+
asyncio_current_task = __AsyncioCurrentTask()
|
|
141
141
|
|
|
142
|
-
@logger.setter
|
|
143
|
-
def logger(self, logger: logging.Logger):
|
|
144
|
-
self._logger.set(logger)
|
|
145
142
|
|
|
146
|
-
|
|
143
|
+
def is_asyncio_environment():
|
|
144
|
+
try:
|
|
145
|
+
asyncio.get_running_loop()
|
|
146
|
+
return True
|
|
147
|
+
except RuntimeError:
|
|
148
|
+
return False
|
|
149
|
+
|
|
147
150
|
|
|
148
151
|
def funboost_current_task():
|
|
149
152
|
if is_asyncio_environment():
|
|
150
|
-
if
|
|
151
|
-
# 如果用户使用的是默认的ConcurrentModeEnum.THREADING
|
|
153
|
+
if thread_current_task.get_fct_context().asyncio_use_thread_concurrent_mode is True:
|
|
154
|
+
# 如果用户使用的是默认的ConcurrentModeEnum.THREADING并发模式来运行async def 函数,那么也使用线程获取上下文
|
|
152
155
|
return thread_current_task
|
|
153
156
|
else:
|
|
154
157
|
return asyncio_current_task
|
|
@@ -161,14 +164,30 @@ def get_current_taskid():
|
|
|
161
164
|
# return fct.function_result_status.task_id
|
|
162
165
|
try:
|
|
163
166
|
return fct.task_id # 不在funboost的消费函数里面就获取不到上下文了
|
|
164
|
-
except (AttributeError,LookupError) as e:
|
|
167
|
+
except (AttributeError, LookupError) as e:
|
|
165
168
|
# print(e,type(e))
|
|
166
169
|
return 'no_task_id'
|
|
167
170
|
|
|
171
|
+
|
|
172
|
+
class FctContextThread(threading.Thread):
|
|
173
|
+
"""
|
|
174
|
+
这个类自动把当前线程的 线程上下文 自动传递给新开的线程。
|
|
175
|
+
"""
|
|
176
|
+
def __init__(self, group=None, target=None, name=None,
|
|
177
|
+
args=(), kwargs=None, *, daemon=None,
|
|
178
|
+
):
|
|
179
|
+
threading.Thread.__init__(**locals())
|
|
180
|
+
self.fct_context = thread_current_task.get_fct_context()
|
|
181
|
+
|
|
182
|
+
def run(self):
|
|
183
|
+
thread_current_task.set_fct_context(self.fct_context)
|
|
184
|
+
super().run()
|
|
185
|
+
|
|
186
|
+
|
|
168
187
|
if __name__ == '__main__':
|
|
169
188
|
print(is_asyncio_environment())
|
|
170
189
|
print()
|
|
171
|
-
for i in range(
|
|
190
|
+
for i in range(2):
|
|
172
191
|
funboost_current_task()
|
|
173
|
-
get_current_taskid()
|
|
192
|
+
print(get_current_taskid())
|
|
174
193
|
print()
|
|
@@ -10,6 +10,8 @@ from funboost.concurrent_pool import FunboostBaseConcurrentPool, FlexibleThreadP
|
|
|
10
10
|
from funboost.constant import ConcurrentModeEnum, BrokerEnum
|
|
11
11
|
from pydantic import BaseModel, validator, root_validator, BaseConfig, Field
|
|
12
12
|
|
|
13
|
+
from funboost.core.lazy_impoter import funboost_lazy_impoter
|
|
14
|
+
|
|
13
15
|
|
|
14
16
|
def _patch_for_pydantic_field_deepcopy():
|
|
15
17
|
from concurrent.futures import ThreadPoolExecutor
|
|
@@ -191,6 +193,10 @@ class BoosterParams(BaseJsonAbleModel):
|
|
|
191
193
|
# func_params_is_pydantic_model: bool = False # funboost 兼容支持 函数娼还是 pydantic model类型,funboost在发布之前和取出来时候自己转化。
|
|
192
194
|
|
|
193
195
|
auto_generate_info: dict = {} # 自动生成的信息,不需要用户主动传参.
|
|
196
|
+
consuming_function_class:typing.Optional[typing.Type] = None
|
|
197
|
+
consuming_function_kind :typing.Optional[str]= None #自动生成的信息,不需要用户主动传参.
|
|
198
|
+
consuming_function_class_module:typing.Optional[str] = None #自动生成的信息,不需要用户主动传参.
|
|
199
|
+
consuming_function_class_name: typing.Optional[str] = None #自动生成的信息,不需要用户主动传参.
|
|
194
200
|
|
|
195
201
|
@root_validator(skip_on_failure=True)
|
|
196
202
|
def check_values(cls, values: dict):
|
|
@@ -213,6 +219,19 @@ class BoosterParams(BaseJsonAbleModel):
|
|
|
213
219
|
raise ValueError(f'{cls.__name__} 的字段新增了父类 BoosterParams 不存在的字段 "{k}"') # 使 BoosterParams的子类,不能增加字段,只能覆盖字段.
|
|
214
220
|
return values
|
|
215
221
|
|
|
222
|
+
def __call__(self, func):
|
|
223
|
+
"""
|
|
224
|
+
新增加一种语法
|
|
225
|
+
@BoosterParams(queue_name='q1',qps=2) 这个和 @boost(BoosterParams(queue_name='q1',qps=2)) 写法等效
|
|
226
|
+
|
|
227
|
+
@BoosterParams(queue_name='q1',qps=2)
|
|
228
|
+
def f(a,b):
|
|
229
|
+
print(a,b)
|
|
230
|
+
:param func:
|
|
231
|
+
:return:
|
|
232
|
+
"""
|
|
233
|
+
return funboost_lazy_impoter.boost(self)(func)
|
|
234
|
+
|
|
216
235
|
|
|
217
236
|
class BoosterParamsComplete(BoosterParams):
|
|
218
237
|
"""
|
|
@@ -275,6 +294,10 @@ class PublisherParams(BaseJsonAbleModel):
|
|
|
275
294
|
publisher_override_cls: typing.Optional[typing.Type] = None
|
|
276
295
|
# func_params_is_pydantic_model: bool = False # funboost 兼容支持 函数娼还是 pydantic model类型,funboost在发布之前和取出来时候自己转化。
|
|
277
296
|
|
|
297
|
+
consuming_function_kind: typing.Optional[str] = None # 自动生成的信息,不需要用户主动传参.
|
|
298
|
+
consuming_function_class_module: typing.Optional[str] = None # 自动生成的信息,不需要用户主动传参.
|
|
299
|
+
consuming_function_class_name: typing.Optional[str] = None # 自动生成的信息,不需要用户主动传参.
|
|
300
|
+
|
|
278
301
|
|
|
279
302
|
if __name__ == '__main__':
|
|
280
303
|
from funboost.concurrent_pool import FlexibleThreadPool
|
funboost/core/lazy_impoter.py
CHANGED
|
@@ -16,8 +16,14 @@ class FunboostLazyImpoter(SingletonBaseNew):
|
|
|
16
16
|
@property
|
|
17
17
|
@cached_method_result
|
|
18
18
|
def BoostersManager(self):
|
|
19
|
-
from funboost.core
|
|
20
|
-
return BoostersManager
|
|
19
|
+
from funboost.core import booster
|
|
20
|
+
return booster.BoostersManager
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
@cached_method_result
|
|
24
|
+
def boost(self):
|
|
25
|
+
from funboost.core import booster
|
|
26
|
+
return booster.boost
|
|
21
27
|
|
|
22
28
|
# @property
|
|
23
29
|
# @cached_method_result
|
|
@@ -16,6 +16,7 @@ from threading import Lock
|
|
|
16
16
|
import amqpstorm
|
|
17
17
|
|
|
18
18
|
import nb_log
|
|
19
|
+
from funboost.constant import ConstStrForClassMethod, FunctionKind
|
|
19
20
|
from funboost.core.func_params_model import PublisherParams, PriorityConsumingControlConfig
|
|
20
21
|
from funboost.core.helper_funs import MsgGenerater
|
|
21
22
|
from funboost.core.loggers import develop_logger
|
|
@@ -29,6 +30,8 @@ from funboost.core.task_id_logger import TaskIdLogger
|
|
|
29
30
|
from funboost.utils import decorators
|
|
30
31
|
from funboost.funboost_config_deafult import BrokerConnConfig, FunboostCommonConfig
|
|
31
32
|
|
|
33
|
+
|
|
34
|
+
|
|
32
35
|
RedisAsyncResult = AsyncResult # 别名
|
|
33
36
|
RedisAioAsyncResult = AioAsyncResult # 别名
|
|
34
37
|
|
|
@@ -243,15 +246,31 @@ class AbstractPublisher(LoggerLevelSetterMixin, metaclass=abc.ABCMeta, ):
|
|
|
243
246
|
:param func_kwargs:
|
|
244
247
|
:return:
|
|
245
248
|
"""
|
|
246
|
-
# print(func_args,func_kwargs,self.publish_params_checker.all_arg_name)
|
|
249
|
+
# print(func_args, func_kwargs, self.publish_params_checker.all_arg_name)
|
|
247
250
|
msg_dict = func_kwargs
|
|
248
251
|
# print(msg_dict)
|
|
249
252
|
# print(self.publish_params_checker.position_arg_name_list)
|
|
250
253
|
# print(func_args)
|
|
251
|
-
|
|
254
|
+
func_args_list = list(func_args)
|
|
255
|
+
if self.publisher_params.consuming_function_kind == FunctionKind.CLASS_METHOD:
|
|
256
|
+
# print(self.publish_params_checker.all_arg_name[0])
|
|
257
|
+
# func_args_list.insert(0, {'first_param_name': self.publish_params_checker.all_arg_name[0],
|
|
258
|
+
# 'cls_type': ClsHelper.get_classs_method_cls(self.publisher_params.consuming_function).__name__},
|
|
259
|
+
# )
|
|
260
|
+
func_args_list.insert(0, {ConstStrForClassMethod.FIRST_PARAM_NAME: self.publish_params_checker.all_arg_name[0],
|
|
261
|
+
ConstStrForClassMethod.CLS_NAME: self.publisher_params.consuming_function_class_name,})
|
|
262
|
+
elif self.publisher_params.consuming_function_kind == FunctionKind.INSTANCE_METHOD:
|
|
263
|
+
if not hasattr(func_args[0],ConstStrForClassMethod.OBJ_INIT_PARAMS):
|
|
264
|
+
raise ValueError(f'消费函数 {self.publisher_params.consuming_function} 是实例方法,实例必须有 {ConstStrForClassMethod.OBJ_INIT_PARAMS} 属性')
|
|
265
|
+
func_args_list[0] = {ConstStrForClassMethod.FIRST_PARAM_NAME: self.publish_params_checker.all_arg_name[0],
|
|
266
|
+
ConstStrForClassMethod.OBJ_INIT_PARAMS: getattr(func_args[0],ConstStrForClassMethod.OBJ_INIT_PARAMS),
|
|
267
|
+
ConstStrForClassMethod.CLS_NAME: self.publisher_params.consuming_function_class_name}
|
|
268
|
+
|
|
269
|
+
for index, arg in enumerate(func_args_list):
|
|
252
270
|
# print(index,arg,self.publish_params_checker.position_arg_name_list)
|
|
253
271
|
# msg_dict[self.publish_params_checker.position_arg_name_list[index]] = arg
|
|
254
272
|
msg_dict[self.publish_params_checker.all_arg_name[index]] = arg
|
|
273
|
+
|
|
255
274
|
# print(msg_dict)
|
|
256
275
|
return self.publish(msg_dict)
|
|
257
276
|
|
funboost/set_frame_config.py
CHANGED
|
@@ -100,7 +100,7 @@ def show_frame_config():
|
|
|
100
100
|
# only_print_on_main_process(f'{var_name}: {var_value}')
|
|
101
101
|
logger_prompt.debug(f'''读取的 BrokerConnConfig 配置是:\n {funboost_config_deafult.BrokerConnConfig().get_pwd_enc_json(indent=4)} ''')
|
|
102
102
|
|
|
103
|
-
logger_prompt.debug(f'''读取的 FunboostCommonConfig 配置是:\n {funboost_config_deafult.FunboostCommonConfig().get_json(indent=
|
|
103
|
+
logger_prompt.debug(f'''读取的 FunboostCommonConfig 配置是:\n {funboost_config_deafult.FunboostCommonConfig().get_json(indent=4)} ''')
|
|
104
104
|
|
|
105
105
|
# only_print_on_main_process(f'读取的 BoostDecoratorDefaultParams 默认 @boost 装饰器入参的默认全局配置是: \n '
|
|
106
106
|
# f'{funboost_config_deafult.BoostDecoratorDefaultParams().get_json()}')
|
|
@@ -118,6 +118,7 @@ def use_config_form_funboost_config_module():
|
|
|
118
118
|
当第一次运行脚本时候,函数调度框架会在你的python当前项目的根目录下 {project_root_path} 下,创建一个名为 funboost_config.py 的文件。
|
|
119
119
|
自动读取配置,会优先读取启动脚本的所在目录 {current_script_path} 的funboost_config.py文件,
|
|
120
120
|
如果没有 {current_script_path}/funboost_config.py 文件,则读取项目根目录 {project_root_path} 下的funboost_config.py做配置。
|
|
121
|
+
只要 funboost_config.py 在任意 PYTHONPATH 的文件夹下,就能自动读取到。
|
|
121
122
|
在 "{project_root_path}/funboost_config.py:1" 文件中,需要按需重新设置要使用到的中间件的键和值,例如没有使用rabbitmq而是使用redis做中间件,则不需要配置rabbitmq。
|
|
122
123
|
"""
|
|
123
124
|
# sys.stdout.write(f'\033[0;33m{time.strftime("%H:%M:%S")}\033[0m "{__file__}:{sys._getframe().f_lineno}" \033[0;30;43m{inspect_msg}\033[0m\n')
|
funboost/utils/class_utils.py
CHANGED
|
@@ -1,51 +1,101 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
#
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
#
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
1
|
+
import copy
|
|
2
|
+
import gc
|
|
3
|
+
import inspect
|
|
4
|
+
import re
|
|
5
|
+
import sys
|
|
6
|
+
import typing
|
|
7
|
+
|
|
8
|
+
import nb_log
|
|
9
|
+
from types import MethodType, FunctionType
|
|
10
|
+
|
|
11
|
+
from funboost.constant import FunctionKind
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ClsHelper:
|
|
15
|
+
@staticmethod
|
|
16
|
+
def get_instncae_method_cls(instncae_method):
|
|
17
|
+
print(instncae_method)
|
|
18
|
+
print(instncae_method.__qualname__)
|
|
19
|
+
print(instncae_method.__module__)
|
|
20
|
+
return getattr(sys.modules[instncae_method.__module__],instncae_method.__qualname__.split('.')[0])
|
|
21
|
+
# return instncae_method.__self__.__class__
|
|
22
|
+
|
|
23
|
+
@staticmethod
|
|
24
|
+
def get_classs_method_cls(class_method):
|
|
25
|
+
print(class_method)
|
|
26
|
+
print(class_method.__qualname__)
|
|
27
|
+
print(class_method.__module__)
|
|
28
|
+
return getattr(sys.modules[class_method.__module__],class_method.__qualname__.split('.')[0])
|
|
29
|
+
|
|
30
|
+
@staticmethod
|
|
31
|
+
def is_class_method(method):
|
|
32
|
+
# if inspect.ismethod(method):
|
|
33
|
+
# if hasattr(method, '__self__') and inspect.isclass(method.__self__):
|
|
34
|
+
# return True
|
|
35
|
+
# return False
|
|
36
|
+
|
|
37
|
+
sourcelines = inspect.getsourcelines(method)
|
|
38
|
+
# print(sourcelines)
|
|
39
|
+
line0: str = sourcelines[0][0]
|
|
40
|
+
if line0.replace(' ', '').startswith('@classmethod'):
|
|
41
|
+
return True
|
|
42
|
+
|
|
43
|
+
@staticmethod
|
|
44
|
+
def is_static_method(method):
|
|
45
|
+
sourcelines = inspect.getsourcelines(method)
|
|
46
|
+
line0: str = sourcelines[0][0]
|
|
47
|
+
if line0.replace(' ', '').startswith('@staticmethod'):
|
|
48
|
+
return True
|
|
49
|
+
|
|
50
|
+
@classmethod
|
|
51
|
+
def is_instance_method(cls, method):
|
|
52
|
+
if cls.is_class_method(method):
|
|
53
|
+
return False
|
|
54
|
+
if cls.is_static_method(method):
|
|
55
|
+
return False
|
|
56
|
+
if isinstance(method, FunctionType):
|
|
57
|
+
sourcelines = inspect.getsourcelines(method)
|
|
58
|
+
for line in sourcelines[0][:50]:
|
|
59
|
+
if not line.replace( ' ','').startswith('#'):
|
|
60
|
+
if not line.startswith('def') and re.search('\(\s*?self\s*?,',line):
|
|
61
|
+
return True
|
|
62
|
+
# method_class = getattr(method, '__qualname__', '').rsplit('.', 1)[0]
|
|
63
|
+
# if method_class: # 如果能找到类名,说明是类的成员
|
|
64
|
+
# print( f"{method.__name__} 属于类 {method_class} 的成员")
|
|
65
|
+
#
|
|
66
|
+
# return True
|
|
67
|
+
|
|
68
|
+
@classmethod
|
|
69
|
+
def is_common_function(cls, method):
|
|
70
|
+
if cls.is_static_method(method):
|
|
71
|
+
return False
|
|
72
|
+
if cls.is_class_method(method):
|
|
73
|
+
return False
|
|
74
|
+
if cls.is_instance_method(method):
|
|
75
|
+
return False
|
|
76
|
+
if isinstance(method, FunctionType):
|
|
77
|
+
sourcelines = inspect.getsourcelines(method)
|
|
78
|
+
for line in sourcelines[0][:50]:
|
|
79
|
+
if not line.replace(' ', '').startswith('#'):
|
|
80
|
+
if not re.search('\(\s*?self\s*?,', line):
|
|
81
|
+
return True
|
|
82
|
+
|
|
83
|
+
@classmethod
|
|
84
|
+
def get_method_kind(cls, method: typing.Callable) -> str:
|
|
85
|
+
if cls.is_class_method(method):
|
|
86
|
+
return FunctionKind.CLASS_METHOD
|
|
87
|
+
elif cls.is_static_method(method):
|
|
88
|
+
return FunctionKind.STATIC_METHOD
|
|
89
|
+
elif cls.is_instance_method(method):
|
|
90
|
+
return FunctionKind.INSTANCE_METHOD
|
|
91
|
+
elif cls.is_common_function(method):
|
|
92
|
+
return FunctionKind.COMMON_FUNCTION
|
|
93
|
+
|
|
94
|
+
@staticmethod
|
|
95
|
+
def get_obj_init_params_for_funboost(obj_init_params: dict):
|
|
96
|
+
obj_init_params.pop('self')
|
|
97
|
+
return copy.deepcopy(obj_init_params)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
if __name__ == '__main__':
|
|
101
|
+
pass
|
|
@@ -9,9 +9,10 @@ import os
|
|
|
9
9
|
import ctypes
|
|
10
10
|
import threading
|
|
11
11
|
|
|
12
|
+
from funboost.core.current_task import FctContextThread
|
|
12
13
|
__all__ = ('StoppableThread', 'JoinThread')
|
|
13
14
|
|
|
14
|
-
class StoppableThread(threading.Thread
|
|
15
|
+
class StoppableThread(FctContextThread): # 这里重要,继承的是FctContextThread,而不是原生 threading.Thread
|
|
15
16
|
'''
|
|
16
17
|
StoppableThread - A thread that can be stopped by forcing an exception in the execution context.
|
|
17
18
|
|
|
@@ -80,7 +81,7 @@ class StoppableThread(threading.Thread):
|
|
|
80
81
|
return self._stopThread(exception, raiseEvery)
|
|
81
82
|
|
|
82
83
|
|
|
83
|
-
class JoinThread(
|
|
84
|
+
class JoinThread(FctContextThread):
|
|
84
85
|
'''
|
|
85
86
|
JoinThread - The workhouse that stops the StoppableThread.
|
|
86
87
|
|
|
Binary file
|
funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/dafunc.cpython-39.pyc
CHANGED
|
Binary file
|
|
@@ -66,9 +66,9 @@ def func_timeout(timeout, func, args=(), kwargs=None):
|
|
|
66
66
|
|
|
67
67
|
from funboost.core.current_task import thread_current_task
|
|
68
68
|
|
|
69
|
-
def funcwrap(args2, kwargs2,
|
|
70
|
-
fct = thread_current_task
|
|
71
|
-
fct.
|
|
69
|
+
def funcwrap(args2, kwargs2,):
|
|
70
|
+
# fct = thread_current_task
|
|
71
|
+
# fct.set_fct_context(fct_context) # 把funboost的消费线程上下文需要传递到超时线程上下文里面来.
|
|
72
72
|
try:
|
|
73
73
|
ret.append( func(*args2, **kwargs2) )
|
|
74
74
|
except FunctionTimedOut:
|
|
@@ -86,7 +86,7 @@ def func_timeout(timeout, func, args=(), kwargs=None):
|
|
|
86
86
|
|
|
87
87
|
|
|
88
88
|
# fct = funboost_current_task()
|
|
89
|
-
thread = StoppableThread(target=funcwrap, args=(args, kwargs,
|
|
89
|
+
thread = StoppableThread(target=funcwrap, args=(args, kwargs,))
|
|
90
90
|
thread.daemon = True
|
|
91
91
|
|
|
92
92
|
thread.start()
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
|
|
2
|
+
# vim: set ts=4 sw=4 expandtab :
|
|
3
|
+
|
|
4
|
+
'''
|
|
5
|
+
Copyright (c) 2016, 2017 Tim Savannah All Rights Reserved.
|
|
6
|
+
|
|
7
|
+
Licensed under the Lesser GNU Public License Version 3, LGPLv3. You should have recieved a copy of this with the source distribution as
|
|
8
|
+
LICENSE, otherwise it is available at https://github.com/kata198/func_timeout/LICENSE
|
|
9
|
+
'''
|
|
10
|
+
|
|
11
|
+
import copy
|
|
12
|
+
import functools
|
|
13
|
+
import inspect
|
|
14
|
+
import threading
|
|
15
|
+
import time
|
|
16
|
+
import types
|
|
17
|
+
import sys
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
from .exceptions import FunctionTimedOut
|
|
21
|
+
from .StoppableThread import StoppableThread
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
from .py3_raise import raise_exception
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
from functools import wraps
|
|
28
|
+
|
|
29
|
+
__all__ = ('func_timeout', 'func_set_timeout')
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def func_timeout(timeout, func, args=(), kwargs=None):
|
|
35
|
+
'''
|
|
36
|
+
func_timeout - Runs the given function for up to #timeout# seconds.
|
|
37
|
+
|
|
38
|
+
Raises any exceptions #func# would raise, returns what #func# would return (unless timeout is exceeded), in which case it raises FunctionTimedOut
|
|
39
|
+
|
|
40
|
+
@param timeout <float> - Maximum number of seconds to run #func# before terminating
|
|
41
|
+
|
|
42
|
+
@param func <function> - The function to call
|
|
43
|
+
|
|
44
|
+
@param args <tuple> - Any ordered arguments to pass to the function
|
|
45
|
+
|
|
46
|
+
@param kwargs <dict/None> - Keyword arguments to pass to the function.
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@raises - FunctionTimedOut if #timeout# is exceeded, otherwise anything #func# could raise will be raised
|
|
50
|
+
|
|
51
|
+
If the timeout is exceeded, FunctionTimedOut will be raised within the context of the called function every two seconds until it terminates,
|
|
52
|
+
but will not block the calling thread (a new thread will be created to perform the join). If possible, you should try/except FunctionTimedOut
|
|
53
|
+
to return cleanly, but in most cases it will 'just work'.
|
|
54
|
+
|
|
55
|
+
@return - The return value that #func# gives
|
|
56
|
+
'''
|
|
57
|
+
|
|
58
|
+
if not kwargs:
|
|
59
|
+
kwargs = {}
|
|
60
|
+
if not args:
|
|
61
|
+
args = ()
|
|
62
|
+
|
|
63
|
+
ret = []
|
|
64
|
+
exception = []
|
|
65
|
+
isStopped = False
|
|
66
|
+
|
|
67
|
+
from funboost.core.current_task import thread_current_task
|
|
68
|
+
|
|
69
|
+
def funcwrap(args2, kwargs2,fct_context):
|
|
70
|
+
fct = thread_current_task
|
|
71
|
+
fct.set_fct_context(fct_context) # 把funboost的消费线程上下文需要传递到超时线程上下文里面来.
|
|
72
|
+
try:
|
|
73
|
+
ret.append( func(*args2, **kwargs2) )
|
|
74
|
+
except FunctionTimedOut:
|
|
75
|
+
# Don't print traceback to stderr if we time out
|
|
76
|
+
pass
|
|
77
|
+
except BaseException as e:
|
|
78
|
+
exc_info = sys.exc_info()
|
|
79
|
+
if isStopped is False:
|
|
80
|
+
# Assemble the alternate traceback, excluding this function
|
|
81
|
+
# from the trace (by going to next frame)
|
|
82
|
+
# Pytohn3 reads native from __traceback__,
|
|
83
|
+
# python2 has a different form for "raise"
|
|
84
|
+
e.__traceback__ = exc_info[2].tb_next
|
|
85
|
+
exception.append( e )
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
# fct = funboost_current_task()
|
|
89
|
+
thread = StoppableThread(target=funcwrap, args=(args, kwargs,thread_current_task.get_fct_context()))
|
|
90
|
+
thread.daemon = True
|
|
91
|
+
|
|
92
|
+
thread.start()
|
|
93
|
+
thread.join(timeout)
|
|
94
|
+
|
|
95
|
+
stopException = None
|
|
96
|
+
if thread.is_alive():
|
|
97
|
+
isStopped = True
|
|
98
|
+
|
|
99
|
+
class FunctionTimedOutTempType(FunctionTimedOut):
|
|
100
|
+
def __init__(self):
|
|
101
|
+
return FunctionTimedOut.__init__(self, '', timeout, func, args, kwargs)
|
|
102
|
+
|
|
103
|
+
FunctionTimedOutTemp = type('FunctionTimedOut' + str( hash( "%d_%d_%d_%d" %(id(timeout), id(func), id(args), id(kwargs))) ), FunctionTimedOutTempType.__bases__, dict(FunctionTimedOutTempType.__dict__))
|
|
104
|
+
|
|
105
|
+
stopException = FunctionTimedOutTemp
|
|
106
|
+
# raise FunctionTimedOut('', timeout, func, args, kwargs)
|
|
107
|
+
thread._stopThread(stopException)
|
|
108
|
+
thread.join(min(.1, timeout / 50.0))
|
|
109
|
+
raise FunctionTimedOut('', timeout, func, args, kwargs)
|
|
110
|
+
else:
|
|
111
|
+
# We can still cleanup the thread here..
|
|
112
|
+
# Still give a timeout... just... cuz..
|
|
113
|
+
thread.join(.5)
|
|
114
|
+
|
|
115
|
+
if exception:
|
|
116
|
+
raise_exception(exception)
|
|
117
|
+
|
|
118
|
+
if ret:
|
|
119
|
+
return ret[0]
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def func_set_timeout(timeout, allowOverride=False):
|
|
123
|
+
'''
|
|
124
|
+
func_set_timeout - Decorator to run a function with a given/calculated timeout (max execution time).
|
|
125
|
+
Optionally (if #allowOverride is True), adds a paramater, "forceTimeout", to the
|
|
126
|
+
function which, if provided, will override the default timeout for that invocation.
|
|
127
|
+
|
|
128
|
+
If #timeout is provided as a lambda/function, it will be called
|
|
129
|
+
prior to each invocation of the decorated function to calculate the timeout to be used
|
|
130
|
+
for that call, based on the arguments passed to the decorated function.
|
|
131
|
+
|
|
132
|
+
For example, you may have a "processData" function whose execution time
|
|
133
|
+
depends on the number of "data" elements, so you may want a million elements to have a
|
|
134
|
+
much higher timeout than seven elements.)
|
|
135
|
+
|
|
136
|
+
If #allowOverride is True AND a kwarg of "forceTimeout" is passed to the wrapped function, that timeout
|
|
137
|
+
will be used for that single call.
|
|
138
|
+
|
|
139
|
+
@param timeout <float OR lambda/function> -
|
|
140
|
+
|
|
141
|
+
**If float:**
|
|
142
|
+
Default number of seconds max to allow function to execute
|
|
143
|
+
before throwing FunctionTimedOut
|
|
144
|
+
|
|
145
|
+
**If lambda/function:
|
|
146
|
+
|
|
147
|
+
If a function/lambda is provided, it will be called for every
|
|
148
|
+
invocation of the decorated function (unless #allowOverride=True and "forceTimeout" was passed)
|
|
149
|
+
to determine the timeout to use based on the arguments to the decorated function.
|
|
150
|
+
|
|
151
|
+
The arguments as passed into the decorated function will be passed to this function.
|
|
152
|
+
They either must match exactly to what the decorated function has, OR
|
|
153
|
+
if you prefer to get the *args (list of ordered args) and **kwargs ( key : value keyword args form),
|
|
154
|
+
define your calculate function like:
|
|
155
|
+
|
|
156
|
+
def calculateTimeout(*args, **kwargs):
|
|
157
|
+
...
|
|
158
|
+
|
|
159
|
+
or lambda like:
|
|
160
|
+
|
|
161
|
+
calculateTimeout = lambda *args, **kwargs : ...
|
|
162
|
+
|
|
163
|
+
otherwise the args to your calculate function should match exactly the decorated function.
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@param allowOverride <bool> Default False, if True adds a keyword argument to the decorated function,
|
|
167
|
+
"forceTimeout" which, if provided, will override the #timeout. If #timeout was provided as a lambda / function, it
|
|
168
|
+
will not be called.
|
|
169
|
+
|
|
170
|
+
@throws FunctionTimedOut If time alloted passes without function returning naturally
|
|
171
|
+
|
|
172
|
+
@see func_timeout
|
|
173
|
+
'''
|
|
174
|
+
# Try to be as efficent as possible... don't compare the args more than once
|
|
175
|
+
|
|
176
|
+
# Helps closure issue on some versions of python
|
|
177
|
+
defaultTimeout = copy.copy(timeout)
|
|
178
|
+
|
|
179
|
+
isTimeoutAFunction = bool( issubclass(timeout.__class__, (types.FunctionType, types.MethodType, types.LambdaType, types.BuiltinFunctionType, types.BuiltinMethodType) ) )
|
|
180
|
+
|
|
181
|
+
if not isTimeoutAFunction:
|
|
182
|
+
if not issubclass(timeout.__class__, (float, int)):
|
|
183
|
+
try:
|
|
184
|
+
timeout = float(timeout)
|
|
185
|
+
except:
|
|
186
|
+
raise ValueError('timeout argument must be a float/int for number of seconds, or a function/lambda which gets passed the function arguments and returns a calculated timeout (as float or int). Passed type: < %s > is not of any of these, and cannot be converted to a float.' %( timeout.__class__.__name__, ))
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
if not allowOverride and not isTimeoutAFunction:
|
|
190
|
+
# Only defaultTimeout provided. Simple function wrapper
|
|
191
|
+
def _function_decorator(func):
|
|
192
|
+
|
|
193
|
+
return wraps(func)(lambda *args, **kwargs : func_timeout(defaultTimeout, func, args=args, kwargs=kwargs))
|
|
194
|
+
|
|
195
|
+
# def _function_wrapper(*args, **kwargs):
|
|
196
|
+
# return func_timeout(defaultTimeout, func, args=args, kwargs=kwargs)
|
|
197
|
+
# return _function_wrapper
|
|
198
|
+
return _function_decorator
|
|
199
|
+
|
|
200
|
+
if not isTimeoutAFunction:
|
|
201
|
+
# allowOverride is True and timeout is not a function. Simple conditional on every call
|
|
202
|
+
def _function_decorator(func):
|
|
203
|
+
def _function_wrapper(*args, **kwargs):
|
|
204
|
+
if 'forceTimeout' in kwargs:
|
|
205
|
+
useTimeout = kwargs.pop('forceTimeout')
|
|
206
|
+
else:
|
|
207
|
+
useTimeout = defaultTimeout
|
|
208
|
+
|
|
209
|
+
return func_timeout(useTimeout, func, args=args, kwargs=kwargs)
|
|
210
|
+
|
|
211
|
+
return wraps(func)(_function_wrapper)
|
|
212
|
+
return _function_decorator
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
# At this point, timeout IS known to be a function.
|
|
216
|
+
timeoutFunction = timeout
|
|
217
|
+
|
|
218
|
+
if allowOverride:
|
|
219
|
+
# Could use a lambda here... but want traceback to highlight the calculate function,
|
|
220
|
+
# and not the invoked function
|
|
221
|
+
def _function_decorator(func):
|
|
222
|
+
def _function_wrapper(*args, **kwargs):
|
|
223
|
+
if 'forceTimeout' in kwargs:
|
|
224
|
+
useTimeout = kwargs.pop('forceTimeout')
|
|
225
|
+
else:
|
|
226
|
+
useTimeout = timeoutFunction(*args, **kwargs)
|
|
227
|
+
|
|
228
|
+
return func_timeout(useTimeout, func, args=args, kwargs=kwargs)
|
|
229
|
+
|
|
230
|
+
return wraps(func)(_function_wrapper)
|
|
231
|
+
return _function_decorator
|
|
232
|
+
|
|
233
|
+
# Cannot override, and calculate timeout function
|
|
234
|
+
def _function_decorator(func):
|
|
235
|
+
def _function_wrapper(*args, **kwargs):
|
|
236
|
+
useTimeout = timeoutFunction(*args, **kwargs)
|
|
237
|
+
|
|
238
|
+
return func_timeout(useTimeout, func, args=args, kwargs=kwargs)
|
|
239
|
+
|
|
240
|
+
return wraps(func)(_function_wrapper)
|
|
241
|
+
return _function_decorator
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
# vim: set ts=4 sw=4 expandtab :
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: funboost
|
|
3
|
-
Version: 44.
|
|
3
|
+
Version: 44.9
|
|
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
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
funboost/__init__.py,sha256=
|
|
1
|
+
funboost/__init__.py,sha256=Ki3on2Rd5a9xM8t0KMXYipvSBeJcqjW3aHco_92bGtA,3957
|
|
2
2
|
funboost/__init__old.py,sha256=07A1MLsxLtuRQQOIfDyphddOwNBrGe34enoHWAnjV14,20379
|
|
3
3
|
funboost/__main__.py,sha256=-6Nogi666Y0LN8fVm3JmHGTOk8xEGWvotW_GDbSaZME,1065
|
|
4
|
-
funboost/constant.py,sha256
|
|
4
|
+
funboost/constant.py,sha256=PqD6qx0IahvvpVXCiaTOm14GeH5rCRWVdHC45gYQsXc,8088
|
|
5
5
|
funboost/funboost_config_deafult.py,sha256=K-kCFGEjD107wHWFspNrIWsPNSVteP2Xww1yRbXd-Wk,6651
|
|
6
|
-
funboost/set_frame_config.py,sha256=
|
|
6
|
+
funboost/set_frame_config.py,sha256=YOTvzW1pIXg4HAhrOT0usPOHmx95rfx95B65CMrNNMo,14436
|
|
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
|
|
@@ -33,7 +33,7 @@ funboost/concurrent_pool/backup/async_pool_executor0223.py,sha256=RVUZiylUvpTm6U
|
|
|
33
33
|
funboost/concurrent_pool/backup/async_pool_executor_back.py,sha256=KL6zEQaa1KkZOlAO85mCC1gwLm-YC5Ghn21IUom0UKM,9598
|
|
34
34
|
funboost/concurrent_pool/backup/async_pool_executor_janus.py,sha256=OHMWJ9l3EYTpPpcrPrGGKd4K0tmQ2PN8HiX0Dta0EOo,5728
|
|
35
35
|
funboost/consumers/__init__.py,sha256=ZXY_6Kut1VYNQiF5aWEgIWobsW1ht9YUP0TdRZRWFqI,126
|
|
36
|
-
funboost/consumers/base_consumer.py,sha256=
|
|
36
|
+
funboost/consumers/base_consumer.py,sha256=WrZuKfp77m1NHo5LbxlZUgEfQ3foB0OEUPJWk-V4zaE,79520
|
|
37
37
|
funboost/consumers/celery_consumer.py,sha256=9gtz7nlZkmv3ErmaseT0_Q__ltSPx-fOcwi-TMPoaLA,9220
|
|
38
38
|
funboost/consumers/confirm_mixin.py,sha256=eY6fNwx51Hn4bQSYRjyTRwOqfCGsikVnd2Ga_Ep31N4,6062
|
|
39
39
|
funboost/consumers/dramatiq_consumer.py,sha256=ozmeAfeF0U-YNYHK4suQB0N264h5AZdfMH0O45Mh-8A,2229
|
|
@@ -83,19 +83,19 @@ funboost/contrib/redis_consume_latest_msg_broker.py,sha256=ESortBZ2qu_4PBCa3e3Fe
|
|
|
83
83
|
funboost/contrib/save_result_status_to_sqldb.py,sha256=AxvD7nHs4sjr9U0kwEZzyPKrsGdU_JzEgzzhh_V1_4w,4071
|
|
84
84
|
funboost/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
85
85
|
funboost/core/active_cousumer_info_getter.py,sha256=09fEc-BTEIRfDDfHmOvKnMjLjtOyp4edLsUlAXUR_Qs,4966
|
|
86
|
-
funboost/core/booster.py,sha256=
|
|
87
|
-
funboost/core/current_task.py,sha256=
|
|
86
|
+
funboost/core/booster.py,sha256=toTSkXhKE7dqukZAbtOEr6Jz4nPz4LlmValCMreujzo,18191
|
|
87
|
+
funboost/core/current_task.py,sha256=DTfiVbrrz8p8qX0VjErEaXXaScFMspKRrdmwIdbLZOM,5705
|
|
88
88
|
funboost/core/exceptions.py,sha256=pLF7BkRJAfDiWp2_xGZqencmwdPiSQI1NENbImExknY,1311
|
|
89
89
|
funboost/core/fabric_deploy_helper.py,sha256=foieeqlNySuU9axJzNF6TavPjIUSYBx9UO3syVKUiyY,9999
|
|
90
90
|
funboost/core/funboost_config_getter.py,sha256=TDccp5pQamkoJXkwyPwGsQGDJY8ej8ZT8L8RESSAD2w,382
|
|
91
91
|
funboost/core/funboost_current_task_context_thread.py,sha256=RRhqmRl943jBccgTrsfra4lS20X0zB2TtLAUaZn_A18,441
|
|
92
92
|
funboost/core/funboost_time.py,sha256=IbB4dFCpg3oGUe90ssAJ_x0eDPtAVfvsUr4esdoKaOk,1777
|
|
93
|
-
funboost/core/func_params_model.py,sha256=
|
|
93
|
+
funboost/core/func_params_model.py,sha256=QjBLS6AK6l5lNrb0swGwG69nEwKoMpCds6J99jCHEBY,21069
|
|
94
94
|
funboost/core/function_result_status_config.py,sha256=PyjqAQOiwsLt28sRtH-eYRjiI3edPFO4Nde0ILFRReE,1764
|
|
95
95
|
funboost/core/function_result_status_saver.py,sha256=UdokGSwU630t70AZnT9Ecj7GpYXORBDivlc9kadoI2E,9172
|
|
96
96
|
funboost/core/helper_funs.py,sha256=1MZjedV6TGdaAjmj9q-ykgoTI_BtG9ZQm58PLOMVdDM,2362
|
|
97
97
|
funboost/core/kill_remote_task.py,sha256=MZ5vWLGt6SxyN76h5Lf_id9tyVUzjR-qXNyJwXaGlZY,8129
|
|
98
|
-
funboost/core/lazy_impoter.py,sha256=
|
|
98
|
+
funboost/core/lazy_impoter.py,sha256=5yToRBbTB338heR787ap5i-UAGN0FanUCkYfK9Nsgnk,4957
|
|
99
99
|
funboost/core/loggers.py,sha256=uy5mFLIUvKaVdJZLi6THyxqeuOmp9XEOKrH1Yci0zUM,2354
|
|
100
100
|
funboost/core/msg_result_getter.py,sha256=oZDuLDR5XQNzzvgDTsA7wroICToPwrkU9-OAaXXUQSk,8031
|
|
101
101
|
funboost/core/muliti_process_enhance.py,sha256=tI3178inc5sqPh-jQc0XaTuUD1diIZyHuukBRk1Gp6Y,3595
|
|
@@ -125,7 +125,7 @@ funboost/function_result_web/static/js/jquery-1.11.0.min.js,sha256=ryQZ3RXgnqkTz
|
|
|
125
125
|
funboost/function_result_web/templates/index.html,sha256=YM0582Q4t2da-xBf3Ga0McIfcsT9H98rjZck-irMkGo,20387
|
|
126
126
|
funboost/function_result_web/templates/login.html,sha256=q37dj7O0LeyiV38Zd5P1Qn_qmhjdFomuYTRY1Yk48Bo,2007
|
|
127
127
|
funboost/publishers/__init__.py,sha256=xqBHlvsJQVPfbdvP84G0LHmVB7-pFBS7vDnX1Uo9pVY,131
|
|
128
|
-
funboost/publishers/base_publisher.py,sha256=
|
|
128
|
+
funboost/publishers/base_publisher.py,sha256=rwqfCQatpGzxluEVjsXwqxbuWnjUC_erIuEg9mKR1-o,16598
|
|
129
129
|
funboost/publishers/celery_publisher.py,sha256=uc9N1uLW74skUCw8dsnvxORM2O3cy4SiI7tUZRmvkHA,2336
|
|
130
130
|
funboost/publishers/celery_publisher000.py,sha256=2XLOyU2__vlIUTi5L15uf0BJqAIjxbc3kCLIRDSOY9w,3966
|
|
131
131
|
funboost/publishers/confluent_kafka_publisher.py,sha256=B4rF6gljixOMyN6L2eL1gzqTv97uoy7TTzgKUhHljEQ,4749
|
|
@@ -173,7 +173,7 @@ funboost/utils/__init__.py,sha256=rAyXE7lgCo_3VdMvGrIJiqsTHv2nZPTJDTj1f6s_KgE,58
|
|
|
173
173
|
funboost/utils/apscheduler_monkey.py,sha256=CcUISbqX6nMWSxr_QjZ26IvvhUk_ojYZWRaKenpsKfE,3124
|
|
174
174
|
funboost/utils/block_exit.py,sha256=BnfxNYo3lnmhk686RAEoc4u3D4RU_iEMMMgu5L8gIuI,96
|
|
175
175
|
funboost/utils/bulk_operation.py,sha256=B4FBxlz5f4oqlKDWqer7axn4gnDSfsYoMW2zSUCnGcQ,10101
|
|
176
|
-
funboost/utils/class_utils.py,sha256=
|
|
176
|
+
funboost/utils/class_utils.py,sha256=yDsGF4-SaO7k4gtkpYLlACSeejS5FfdIHn087BE_4Cw,3579
|
|
177
177
|
funboost/utils/ctrl_c_end.py,sha256=FgT9An-qsUA5gW-V-UKWqOh5shC7C_uvTFn0fS7i8GI,439
|
|
178
178
|
funboost/utils/custom_pysnooper.py,sha256=7yXLKEMY_JjPRRt0Y0N-wV2CFhILlYNh40Y6uRBUaj8,5923
|
|
179
179
|
funboost/utils/decorators.py,sha256=lyi9TCBg__7xkoV17AZhRItTn95vU2XMtOxfXJVV5B4,26601
|
|
@@ -224,15 +224,16 @@ funboost/utils/dependency_packages_in_pythonpath/aioredis/__pycache__/connection
|
|
|
224
224
|
funboost/utils/dependency_packages_in_pythonpath/aioredis/__pycache__/exceptions.cpython-39.pyc,sha256=EL9HO-DGiUifKhsYJb1IhBXTkiPKYh_6tAOPgCrlzFc,3135
|
|
225
225
|
funboost/utils/dependency_packages_in_pythonpath/aioredis/__pycache__/lock.cpython-39.pyc,sha256=8toq_bQ96ifLGuoKqZAW4fG06lTBt31y7m3ZYB-ayXU,10198
|
|
226
226
|
funboost/utils/dependency_packages_in_pythonpath/aioredis/__pycache__/utils.cpython-39.pyc,sha256=1Qi5z-nd1F1LsWV6KVi2upaJ8NvfIhtYT5GVpjtgGlA,2017
|
|
227
|
-
funboost/utils/dependency_packages_in_pythonpath/func_timeout/StoppableThread.py,sha256=
|
|
227
|
+
funboost/utils/dependency_packages_in_pythonpath/func_timeout/StoppableThread.py,sha256=Hz8SKOO1Jg4sQWnsVMQ9N1agTWyBdml_BpGnUN9wfwY,5517
|
|
228
228
|
funboost/utils/dependency_packages_in_pythonpath/func_timeout/__init__.py,sha256=P3gPbNKNWi0P9ASJcFBnxsxiyII-3neM-FsVjwT_IeU,603
|
|
229
|
-
funboost/utils/dependency_packages_in_pythonpath/func_timeout/dafunc.py,sha256
|
|
229
|
+
funboost/utils/dependency_packages_in_pythonpath/func_timeout/dafunc.py,sha256=Yy98BzsmgWi07ja5zM4ElLwb1h8NXahxtRG5YC4_R7Q,9828
|
|
230
|
+
funboost/utils/dependency_packages_in_pythonpath/func_timeout/dafunc2222.py,sha256=s7hkiBrLWHSWy78KiCRqHxynT6QdBmxnhm2N7US6elY,9872
|
|
230
231
|
funboost/utils/dependency_packages_in_pythonpath/func_timeout/exceptions.py,sha256=ilz6xOeq-H0qspAraXfScQTBEQ8TME3i9FItDscm3H0,4072
|
|
231
232
|
funboost/utils/dependency_packages_in_pythonpath/func_timeout/py2_raise.py,sha256=HS-jk-HqLm4jHK82bw8P_FmrrifAG8DAol_gEmIW6nE,176
|
|
232
233
|
funboost/utils/dependency_packages_in_pythonpath/func_timeout/py3_raise.py,sha256=lrJ3yWuym8-rcQ_s___E5w7bzJ1dFGpXtmWo3RPpJoE,287
|
|
233
|
-
funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/StoppableThread.cpython-39.pyc,sha256=
|
|
234
|
+
funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/StoppableThread.cpython-39.pyc,sha256=k_DjgnG9gFv3D4k_pdOjiw5MFVvEX-405opzLxUCan8,5141
|
|
234
235
|
funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/__init__.cpython-39.pyc,sha256=m4vNDlsjMFuAOjmudVIWvyp_7Td0NlpjA_nRGZvPbQk,767
|
|
235
|
-
funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/dafunc.cpython-39.pyc,sha256=
|
|
236
|
+
funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/dafunc.cpython-39.pyc,sha256=gIsUIfM8jptQSf535IcCKL-tgRpdnIfCEvdrRpSNx2M,8086
|
|
236
237
|
funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/exceptions.cpython-39.pyc,sha256=pzFmbSiuiYivY1RIUFOMXc12EJSIZc0gKO96KitxPoE,3732
|
|
237
238
|
funboost/utils/dependency_packages_in_pythonpath/func_timeout/__pycache__/py3_raise.cpython-39.pyc,sha256=zE4fCYsao9dA1bC8aBT0fzYT8yTYuTQqMk9LFPClCyc,311
|
|
238
239
|
funboost/utils/pysnooper_ydf/__init__.py,sha256=ctbQdJpLVZ5g_PPstj7Xaqcl0sMIgvUGwZXtcogYyHA,909
|
|
@@ -242,9 +243,9 @@ 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=z-KQTxXapfu2ScJxISGe7QGffASuyJYL6JGsLXxD6O0,2332
|
|
244
245
|
funboost/utils/times/version.py,sha256=JiZLGTB-HYyBOV4xS0rnx2K2OqVMTebUj30sZRbrleE,16
|
|
245
|
-
funboost-44.
|
|
246
|
-
funboost-44.
|
|
247
|
-
funboost-44.
|
|
248
|
-
funboost-44.
|
|
249
|
-
funboost-44.
|
|
250
|
-
funboost-44.
|
|
246
|
+
funboost-44.9.dist-info/LICENSE,sha256=9EPP2ktG_lAPB8PjmWV-c9BiaJHc_FP6pPLcUrUwx0E,11562
|
|
247
|
+
funboost-44.9.dist-info/METADATA,sha256=IQaRHQKT466IEEu0J0quGBO6Qm8qZqIpDQXilHEtZFk,31691
|
|
248
|
+
funboost-44.9.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
|
|
249
|
+
funboost-44.9.dist-info/entry_points.txt,sha256=yMSSAGRzRAAhGyNNQHw24MooKlDZsaJ499_D6fPl58A,96
|
|
250
|
+
funboost-44.9.dist-info/top_level.txt,sha256=K8WuKnS6MRcEWxP1NvbmCeujJq6TEfbsB150YROlRw0,9
|
|
251
|
+
funboost-44.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|