更新日志

2023.2.9 增加重试装饰器

防止函数原信息被改变使用:@functools.wraps(func)装饰执行函数

# _*_ coding: UTF-8 _*_
"""
@project -> file : city-test -> wrapper_util
@Author : qinmin.vendor
@Date : 2023/1/28 11:05
@Desc :
""" import re
import time
import functools
import traceback
from utils.operation_logging import operationLogging # 注意事项:装饰器不能装饰classmethod log=operationLogging() def logger_wrapper(log_level,module_obj=None,class_obj=None,is_send_email=False):
def logger_wrapper_main(func):
@functools.wraps(func) #防止函数原信息被改变
def wrapper(*args,**kwargs):
log_content=f' func_obj:{func.__name__}'
if module_obj:
log_content = f' module_obj:{module_obj},' + log_content
if class_obj:
log_content = f' class_obj:{class_obj},' + log_content
log.log_main(log_level,is_send_email,f' start call {log_content}, call params: {func.__code__.co_cellvars}')
exec_result=func(*args,**kwargs)
log.log_main(log_level,is_send_email,f' end call {log_content},call result: {exec_result}')
return exec_result
return wrapper
return logger_wrapper_main def exec_time_wrapper(round_num:int,module_obj=None,class_obj=None,is_send_email=False):
def exec_time_wrapper_main(func):
@functools.wraps(func) #防止函数原信息被改变
def wrapper(*args, **kwargs):
s_time = time.perf_counter()
# 需要定义一个值来接收函数的返回值,否则函数return是无效的
exec_result=func(*args, **kwargs)
e_time=time.perf_counter()
log_content = f' func_obj:{func.__name__} 执行耗时,{round(e_time - s_time, round_num)}s'
if module_obj:
log_content = f' module_obj:{module_obj},' + log_content
if class_obj:
log_content = f' class_obj:{class_obj},' + log_content
log.log_main('info', is_send_email, log_content)
return exec_result
return wrapper return exec_time_wrapper_main def phone_validation_check_wapper(func):
'''手机号验证装饰器'''
@functools.wraps(func)
def wrapper(*args,**kwargs):
is_all_match = all([re.search('1[345789][0-9]{9}$', str(i)) for i in args])
if not is_all_match :
raise Exception('输入的手机号格式不合法')
func(*args,**kwargs)
return wrapper def email_validation_check_wapper(func):
'''邮箱格式验证装饰器'''
@functools.wraps(func)
def wrapper(*args,**kwargs):
is_all_match=all([ re.search('\S+@\S+\.com$',str(i)) for i in args])
if not is_all_match :
raise Exception('输入的邮箱格式不合法')
func(*args,**kwargs)
return wrapper def retry_wrapper(retry_num,retry_conditon,retry_conditon_judge:str='==',retry_sleep_time:(int,float)=1,module_obj=None,class_obj=None,is_send_email=False):
'''
Args:
retry_num: 重试次数
retry_conditon: 触发重试的条件
retry_conditon_judge: 重试条件的判断值,可以是:==、not in、in、!=、括号里面的字符只支持相同类型的比较(>= <= > < %=)
module_obj: 模块对象
class_obj: 类对象
is_send_email: 是否触发发送邮件
Returns:
'''
if not isinstance(retry_conditon_judge,str):
raise Exception("retry_conditon_judge(重试判断条件)必须是str类型")
if not isinstance(retry_sleep_time,(int,float)):
raise Exception("retry_sleep_time(重试等待时间)必须是int、float类型") judge_retry_conditon_type=retry_conditon_judge.lower().strip()
group_response_status='response_status_match'
group_response_content='response_content_match'
py_builtin_types = (str, int, float, list, set, tuple, dict, bool, bytes, type) #定义python内置类型,用于判断 def retry_wrapper_main(func):
@functools.wraps(func) #防止函数原信息被改变
def wrapper(*args,**kwargs):
def retry_func(judge_condition_statement,retry_conditon_str,judge_retry_conditon_type,group_type):
'''函数重试逻辑'''
func_exec_result=None
for i in range(retry_num):
# 重试等待时间随重试次数增加而增加
log.log_main('info',False,f"retry前等待:{retry_sleep_time+i}s")
time.sleep(retry_sleep_time+i)
func_exec_result=func(*args,**kwargs)
judge_condition_statement=judge_condition_statement_dispose(group_type=group_type,judge_retry_conditon_type=judge_retry_conditon_type,
judge_condition_statement=judge_condition_statement,exec_result=func_exec_result,retry_conditon_str=retry_conditon_str) log.log_main('info',False,f"retry_第{i+1}次:{judge_condition_statement}")
if eval(judge_condition_statement):
log.log_main('info', is_send_email, f' {log_content},请求重试成功,重试次数为:{i + 1}')
# print("func_exec_result:",func_exec_result)
return func_exec_result log.log_main('error', is_send_email, f' {log_content},请求重试失败,返回函数最新执行结果')
return func_exec_result def check_retry_conditon_judge_and_return_group(judge_retry_conditon_type,exec_result,legal_chars):
'''校验重试判断条件字符合法性'''
if not judge_retry_conditon_type in legal_chars:
log_content = f'judge_retry_conditon_type只能是:{legal_chars}中的任意一个字符'
log.log_main('error', is_send_email, log_content)
raise Exception(log_content) '''校验重试判断条件in not in时重试条件的数据类型是否合法'''
if judge_retry_conditon_type in ('in', 'not in'):
if (not isinstance(retry_conditon, (list, set, tuple, dict))):
log_content = f'judge_retry_conditon_type为in或者not in时,retry_conditon必须可迭代'
log.log_main('error', is_send_email, log_content)
raise Exception(log_content) elif judge_retry_conditon_type in ('<','>','>=','<='):
if ( isinstance(exec_result,py_builtin_types)) and (type(exec_result)!=type(retry_conditon)):
log_content = f'judge_retry_conditon_type为(<,>,>=,<=)时,retry_conditon与exec_result的类型必须一致'
log.log_main('error', is_send_email, log_content)
raise Exception(log_content)
elif judge_retry_conditon_type=='size':
if not isinstance(retry_conditon,(int,float)):
log_content = f'judge_retry_conditon_type为size时,retry_conditon类型必须为int、float'
log.log_main('error', is_send_email, log_content)
raise Exception(log_content) if judge_retry_conditon_type in ('==','!=', '>=', '<=', '>', '<', 'in','not in'):
return group_response_status
return group_response_content def retry_condition_and_exec_result_dispose(retry_condition,exec_result):
'''处理重试条件与函数执行结果'''
def response_dispose(response:requests.models.Response,attribute='status_code'):
'''如果类型是response且包含特定属性,返回属性'''
is_status_code = hasattr(response, attribute)
args_obj_str = eval(f'int(response.{attribute})') if is_status_code else response
return args_obj_str
def retry_condition_and_exec_result_dispose_main(args_obj):
if not isinstance(args_obj,py_builtin_types):
return args_obj
args_obj_str=args_obj
if isinstance(args_obj_str, str):
args_obj_str=data_util.class_type_str_dispose(str_in=args_obj_str)
import_module_dict = data_util.dynamic_import_module(module_name=args_obj_str)
if import_module_dict:
globals().update(import_module_dict)
if all([i in import_module_dict.keys() for i in args_obj_str.split('.') ]):
args_obj_str=eval(args_obj_str)
else:
args_obj_str = data_util.build_str_obj(args_obj)
else:
args_obj_str = eval(args_obj) if \
data_util.isevaluatable_unsafety(args_obj, is_dynamic_import_module=False) \
else data_util.build_str_obj(args_obj)
return args_obj_str if isinstance(retry_condition,str):
retry_condition = retry_condition_and_exec_result_dispose_main(retry_condition)
exec_result=retry_condition_and_exec_result_dispose_main(exec_result) elif isinstance(retry_condition,(int,list,tuple,set,dict)):
exec_result=response_dispose(exec_result) if type(retry_condition) != type and type(exec_result) != type:
'''处理重试条件类型与函数执行结果类型不一致任意一方是类型的字符串形式的情况'''
if type(retry_condition)!=type(exec_result):
if isinstance(retry_condition,str) and '<class ' in retry_condition:
exec_result=str(type(exec_result))
exec_result=data_util.build_str_obj(exec_result)
elif isinstance(exec_result,str) and '<class ' in exec_result:
retry_condition = str(type(retry_condition))
retry_condition = data_util.build_str_obj(retry_condition)
else:
exec_result = type(exec_result) if type(exec_result) != type else exec_result
retry_condition = type(retry_condition) if type(
retry_condition) != type else retry_condition
retry_condition = data_util.build_str_obj(f'{retry_condition}')
exec_result = data_util.build_str_obj(f'{exec_result}')
return retry_condition,exec_result def judge_condition_statement_dispose(group_type:str,judge_retry_conditon_type:str,judge_condition_statement:str,
exec_result,retry_conditon_str:str):
'''根据重试判断条件、重试判断类型,处理为最终需要的重试判断语句''' # '''用于函数执行结果为type的情况(例如:requests.request的执行结果是requests.models.Response)'''
if group_type==group_response_status:
retry_conditon_str, exec_result = retry_condition_and_exec_result_dispose(retry_conditon,exec_result)
judge_condition_statement = f"{exec_result}{judge_retry_conditon_type}{retry_conditon_str}"
# '''用于函数结果是str、dict、bytes的情况(常规情况下建议使用此方式)'''
elif group_type==group_response_content:
if judge_retry_conditon_type == 'json_path':
exec_result = data_util.json_path_parse_public(json_path=retry_conditon, json_obj=exec_result)
judge_condition_statement = f'{exec_result}' elif judge_retry_conditon_type == 'regex':
exec_result_match = re.search(retry_conditon_str, str(exec_result))
if exec_result_match:
return str(exec_result)
else:
exec_result = None
judge_condition_statement = f'{exec_result}' elif judge_retry_conditon_type == 'size':
exec_result_size = len(str(exec_result).encode('utf-8')) if not \
isinstance(exec_result,bytes) else len(exec_result)
retry_conditon_str = int(retry_conditon_str)
judge_condition_statement = f'{exec_result_size} >= {retry_conditon_str}'
return judge_condition_statement log_content=f'func_obj:{func.__name__}'
if module_obj:
log_content = f' module_obj:{module_obj},' + log_content
if class_obj:
log_content = f' class_obj:{class_obj},' + log_content
legal_chars=('==','!=', '>=', '<=', '>', '<', 'in','not in','json_path','regex','size')
'''检查重试判断条件是否合法'''
exec_result=func(*args,**kwargs)
group_type=check_retry_conditon_judge_and_return_group(judge_retry_conditon_type=judge_retry_conditon_type,exec_result=exec_result,legal_chars=legal_chars)
judge_condition_statement="True"
retry_conditon_str=retry_conditon
judge_condition_statement = judge_condition_statement_dispose(group_type=group_type,judge_retry_conditon_type=judge_retry_conditon_type,
judge_condition_statement=judge_condition_statement,exec_result=exec_result,retry_conditon_str=retry_conditon_str) # log.log_main('info',False,f'before: {judge_condition_statement}')
try:
if eval("not "+judge_condition_statement):
exec_result=retry_func(judge_condition_statement=judge_condition_statement,retry_conditon_str=retry_conditon_str,group_type=group_type,
judge_retry_conditon_type=judge_retry_conditon_type)
except:
log_content='exec_result与retry_conditon类型不一致时只能使用==、!=,退出重试并返回函数原始执行结果,具体异常信息:\n'
log.log_main('error', is_send_email, log_content+traceback.format_exc())
finally:
return exec_result
return wrapper
return retry_wrapper_main class demo():
num=0 @request_retry_wrapper(retry_num=5,retry_conditon=1,judge_retry_conditon_type='==')
def demo1(self):
self.num += 1
return self.num @classmethod
def demo2(cls):
cls.num=2
# print(cls.num) if __name__=="__main__":
demo().demo1()

python实战-编写请求方法重试(用途:请求重试、也可用于其他场景)、日志、执行耗时、手机号与邮箱校验装饰器的更多相关文章

  1. python unittest单元测试框架-3用例执行顺序、多级目录、装饰器、fixtures

    1.用例执行顺序 unittest默认会按照ascii码的顺序,依次执行.类名--方法名排序,使用discover也是默认排序.如果不想使用默认排序,就使用testsuite测试集的方式. impor ...

  2. http请求方法之options请求方法

    需预检的请求”要求必须首先使用 OPTIONS   方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求. https://developer.mozilla.org/zh-CN/docs/W ...

  3. guxh的python笔记三:装饰器

    1,函数作用域 这种情况可以顺利执行: total = 0 def run(): print(total) 这种情况会报错: total = 0 def run(): print(total) tot ...

  4. swift网络数据请求方法

    搭建一个apache服务器,用php编写一个返回给客户端请求数据的脚本 <?php // header("Content-type:text/html;charset=utf-8&qu ...

  5. HTTP请求方法详解

    HTTP请求方法详解 请求方法:指定了客户端想对指定的资源/服务器作何种操作 下面我们介绍HTTP/1.1中可用的请求方法: [GET:获取资源]     GET方法用来请求已被URI识别的资源.指定 ...

  6. httpclient请求方法

    /** * httpclient请求方法 * @param url 请求地址 * @param paramMap 请求参数 * @param ent 编码格式 gbk.utf-8 * @return ...

  7. HTTP/1.1 请求方法

      HTTP(Hypertext Transfer Protocol,超文本传输协议)是一种用于分布式.协作式和超媒体信息系统的 应用层协议.HTTP 是万维网的数据通信的基础.默认端口为 80.   ...

  8. 全面了解HTTP请求方法说明

    超文本传输协议(HTTP, HyperText Transfer Protocol)是一种无状态的协议,它位于OSI七层模型的传输层.HTTP客户端会根据需要构建合适的HTTP请求方法,而HTTP服务 ...

  9. flask系列八之请求方法、g对象和钩子函数

    一.get方法 ,post方法 post请求在模板中要注意几点: (1)input标签中,要写name来标识这个value的key,方便后台获取. (2)在写form表单的时候,要指定method=' ...

  10. Http协议请求方法及body类型(思路比较清晰的)

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/u010244522/article/de ...

随机推荐

  1. 多网卡系统下如何使用tcp协议实现MPI的分布式多机运行(mpi的实现使用openmpi)

    如题: 最近在看MPI方面的东西,主要是Python下的MPI4PY,学校有超算机房可以使用MPI,但是需要申请什么的比较麻烦,目的也本就是为了学习一下,所以就想着在自己的电脑上先配置一下. 现有硬件 ...

  2. 记录实验室深度学习服务器显卡硬件故障的排查——RmInitAdapter failed! rm_init_adapter failed

    实验室突然通知我说是深度学习的服务器无法查看GPU,并且在GPU上运行的程序也halt on,需要解决.于是查询服务器的运行日志得到下面的信息: Nov 10 01:33:23 dell kernel ...

  3. 记录一次实验室linux系统的GPU服务器死机故障的排查——Linux系统的Power States

    实验室的ubuntu服务器不知怎么的突然又崩溃了,死机重启,然后查看日志,发现了下面的情况: 由于从其他的日志中知道是显卡的问题引起的死机,而这个显卡的地址正好是D9:00,这部分的日志就不给出了.结 ...

  4. Edge实验性功能中文翻译

    平行下载 启用平行下载以加速下载速度.- Mac, Windows, Linux, Android #enable-parallel-downloading 已启用 临时恢复 M125 标记 临时恢复 ...

  5. 「TCP/UDP」一个端口号可以同时被两个进程绑定吗?

    一.1个端口号可以同时被两个进程绑定吗? 根据端口号的绑定我们分以下几种情况来讨论: 2个进程分别建立TCP server,使用同一个端口号8888 2个进程分别建立UDP server,使用同一个端 ...

  6. 零基础学习人工智能—Python—Pytorch学习(六)

    前言 本文主要讲神经网络的上半部分. 另外,我发现我前面文章写的有歧义的地方还是挺多,虽然,已经改了一部分,但,可能还有没发现的,大家看的时候尽量多理解着看吧. 本着目的是学会使用神经网络的开发,至于 ...

  7. c++学习笔记(三):函数++

    函数PLUS 函数默认参数 在c++中,函数的形参列表中的形参是可以有默认值的.调用函数时,如果未传递参数的值(传入参数为空),则会使用默认值,如果指定了值,则会忽略默认值,使用传递的值. 语法:返回 ...

  8. python将资源打包进exe

    前言 之前py打包的exe一直是不涉及图片等资源的,直到我引入图片后打包,再双击exe发现直接提示未找到资源. 分析 我py代码中的图片引入使用的是项目相对路径,打包时pyinstaller只会引入p ...

  9. sicp每日一题[1.41]

    Exercise 1.41 Define a procedure double that takes a procedure of one argument as argument and retur ...

  10. C++创建与调用dll动态链接库(MinGW64 Dev-C++)

    本文使用的是dev-c++,如果涉及到VC++中不一样的操作,也会适当进行区分. 项目一:创建DLL 1.创建一个DLL类型的项目,当前命名为dlltest,并选择合适的路径进行保存.  2.在生成的 ...