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 CHANGED
@@ -13,7 +13,7 @@ set_frame_config这个模块的 use_config_form_funboost_config_module() 是核
13
13
  这段注释说明和使用的用户无关,只和框架开发人员有关.
14
14
  '''
15
15
 
16
- __version__ = "44.7"
16
+ __version__ = "44.9"
17
17
 
18
18
  from funboost.set_frame_config import show_frame_config
19
19
 
funboost/constant.py CHANGED
@@ -1,6 +1,6 @@
1
1
  # coding= utf-8
2
2
  class BrokerEnum:
3
- EMPTY = 'empty' # 空的实现,需要搭配 boost入参的 consumer_override_cls 和 publisher_override_cls使用,或者被继承。
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, is_main_process,
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 time.time() - self._last_show_remaining_execution_time > self._show_remaining_execution_time_interval:
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 : # 有的中间件无法统计或没实现统计队列剩余数量的,统一返回的是-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
- fct.function_params = function_only_params
657
- fct.full_msg = kw['body']
658
- fct.function_result_status = function_result_status
659
- fct.logger = self.logger
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
- fct._fct_local_data._asyncio_use_thread_concurrent_mode = True
692
+ fct_context.asyncio_use_thread_concurrent_mode = True
664
693
  function_run = sync_or_async_fun_deco(function_run)
665
694
  else:
666
- fct._fct_local_data._asynco_use_thread_concurrent_mode = False
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
- await self.consumer_params.user_custom_record_process_info_func(current_function_result_status)
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
- fct.function_params = function_only_params
826
- fct.full_msg = kw['body']
827
- fct.function_result_status = function_result_status
828
- fct.logger = self.logger
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
@@ -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
- class __ThreadCurrentTask:
53
+ @dataclass
54
+ class FctContext:
51
55
  """
52
- 用于在用户自己函数内部去获取 消息的完整体,当前重试次数等.
56
+ fct 是 funboost current task 的简写
53
57
  """
54
58
 
55
-
56
- _fct_local_data = threading.local()
57
- _fct_local_data._asyncio_use_thread_concurrent_mode = False
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._fct_local_data.function_params
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._fct_local_data.full_msg
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._fct_local_data.function_result_status
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._fct_local_data.logger
109
+ return self.get_fct_context().logger
90
110
 
91
- @logger.setter
92
- def logger(self, logger: logging.Logger):
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
- class __AsyncioCurrentTask:
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
- @property
111
- def function_params(self):
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
- @function_params.setter
115
- def function_params(self, function_params: dict):
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
- @full_msg.setter
123
- def full_msg(self, full_msg: dict):
124
- self._full_msg.set(full_msg)
129
+ class __AsyncioCurrentTask(_BaseCurrentTask):
130
+ _fct_context = contextvars.ContextVar('fct_context')
125
131
 
126
- @property
127
- def function_result_status(self) -> FunctionResultStatus:
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
- @function_result_status.setter
131
- def function_result_status(self, function_result_status: FunctionResultStatus):
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
- @property
139
- def logger(self) -> logging.Logger:
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
- asyncio_current_task = __AsyncioCurrentTask()
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 getattr(__ThreadCurrentTask._fct_local_data,'_asyncio_use_thread_concurrent_mode',None) is True:
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(100000):
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
@@ -16,8 +16,14 @@ class FunboostLazyImpoter(SingletonBaseNew):
16
16
  @property
17
17
  @cached_method_result
18
18
  def BoostersManager(self):
19
- from funboost.core.booster import BoostersManager
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
- for index, arg in enumerate(func_args):
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
 
@@ -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=None)} ''')
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')
@@ -1,51 +1,101 @@
1
- #
2
- #
3
- # # def get_child_custom_attr(child_cls:type,):
4
- # # __dict = child_cls.__dict__
5
- #
6
- # def merge_cls(cls1:type,cls2:type):
7
- # class Cls(cls2):
8
- # pass
9
- # for k,v in cls1.__dict__.items():
10
- # if k.startswith('__') and k.endswith('__'):
11
- # continue
12
- # print(k,v)
13
- # setattr(Cls,k,v)
14
- # return Cls
15
- #
16
- #
17
- # if __name__ == '__main__':
18
- # class Parent:
19
- # attr1=1
20
- # def method_from_parent(self):
21
- # print('method_from_parent')
22
- #
23
- # def method_from_parent2(self):
24
- # print('method_from_parent2')
25
- #
26
- #
27
- # class Child(Parent):
28
- # attr1=2
29
- # attr2=22
30
- #
31
- # def method_from_parent2(self):
32
- # print('method_from_parent2chile')
33
- # def method_from_child(self:Parent):
34
- # print('method_from_child')
35
- #
36
- # class Child2(Parent,Parent):
37
- # attr1 = 3
38
- #
39
- # class Child2b(Child2):
40
- # attr1 = '2b'
41
- #
42
- #
43
- # class Child2New(Child2b,Child):
44
- # pass
45
- #
46
- #
47
- # print(Child2().method_from_parent2())
48
- # print(Child2New().method_from_parent2())
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(threading.Thread):
84
+ class JoinThread(FctContextThread):
84
85
  '''
85
86
  JoinThread - The workhouse that stops the StoppableThread.
86
87
 
@@ -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,_fct_local_data_dict):
70
- fct = thread_current_task
71
- fct._fct_local_data.__dict__.update(_fct_local_data_dict) # 把funboost的消费线程上下文需要传递到超时线程上下文里面来.
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,thread_current_task._fct_local_data.__dict__))
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.7
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=VvKY_geEn7zzd9pPmdIQtFCoFxFSf-fycagti5lnwsY,3957
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=-QFaxH3tX9AlK9Fmq2hNjxuBWXpmnpCpuZayj7c-xW8,7768
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=ueaAjIOSGXrOj-Wyz5rov2tQFnkQHBePJrJ8k0tO3jM,14344
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=ZauakWwhNTpZMPNzTwL9US2YqQWxBVBZJ7pnayaUW3E,76980
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=dEr3uFzGg3Tik3fvCm20WREIPpWO825Crj-DzBtby1w,17224
87
- funboost/core/current_task.py,sha256=MF8WfMtdmJaO779OoUNmRswbZcToyRx8TbJwxqemfo8,5255
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=T-P-prhqCIPf0r5ZKPOTF7nawest4S1w0SRE0iynpvc,19849
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=wH7u8nm6rLGtWmF643d5ga4CrJEmAhaSsDEBEeuiD4Q,4825
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=Y0pzWgvJi1v3UrSrOewPsWpxnKgfUseV6pP28dXL5G0,15081
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=EZ98CzeoaN2H8iOABTOPefMVfdrii9XDwATqZ0_CkUQ,1153
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=v15aAZDBtS42hDn4x5tjKBs6MGUKICztv0rUJ7aPcAQ,5379
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=-epJVpSkE4wxlfLx-n_0cRbYHuBcMonghmsL-idqbcQ,9913
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=kvj4jhPh6HHMUw3aWdBImNa5T-vQyuu85ozqEjG4lSg,5083
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=OVbQyeN-uKwLRaqqVlpjAG0hTHAvomyq1CcHl79vynA,8185
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.7.dist-info/LICENSE,sha256=9EPP2ktG_lAPB8PjmWV-c9BiaJHc_FP6pPLcUrUwx0E,11562
246
- funboost-44.7.dist-info/METADATA,sha256=sWXcozUL0NVSYgPv7MgdgduVHExNH9_VPravfYjjPlI,31691
247
- funboost-44.7.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
248
- funboost-44.7.dist-info/entry_points.txt,sha256=yMSSAGRzRAAhGyNNQHw24MooKlDZsaJ499_D6fPl58A,96
249
- funboost-44.7.dist-info/top_level.txt,sha256=K8WuKnS6MRcEWxP1NvbmCeujJq6TEfbsB150YROlRw0,9
250
- funboost-44.7.dist-info/RECORD,,
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,,