更新日志

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. HTB-Permx靶机笔记

    Permx靶机笔记 概述 permx靶机是HTB的简单靶机,这台靶机整体考验渗透人员的信息搜集能力,可以收只有信息搜集的快速,才能快速拿到它的flag. 整体是比较简单的靶机 靶机连接:https:/ ...

  2. 后端开发学习敏捷需求-->干系人分析与识别

    干系人分析与识别 5W1H 干系人分析与识别 1. 干系人是什么 直接或者间接影响专题,以及被专题影响的人和组织,用户也是属于干系人,是产品直接或者间接的使用者 又叫利益相关者,指积极参与专题或者在专 ...

  3. 面试官:说说volatile应用和实现原理?

    volatile 是并发编程中的重要关键字,它的名气甚至是可以与 synchronized.ReentrantLock 等齐名,也是属于并发编程五杰之一. 需要注意的是 volatile 并不能保证原 ...

  4. 08-canvas绘制表格

    1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="U ...

  5. SCC缩点模板

    struct SCC { int top = 0, cntscc = 0, dfncnt = 0, n; vector<int> dfn, low, stk, instk; vector& ...

  6. SMU Summer 2023 Contest Round 1

    SMU Summer 2023 Contest Round 1 A. The Contest 当 \(m\) 为 \(0\) 和 完成时间大于最后一个时刻时,说明都无法在规定条件内完成,输出\(-1\ ...

  7. JavaScript设计模式样例十五 —— 状态模式

    状态模式(State Pattern) 定义:创建表示各种状态的对象和一个行为随着状态对象改变而改变的 context 对象.目的:允许对象在内部状态发生改变时改变它的行为,对象看起来好像修改了它的类 ...

  8. Python被远程主机强制关闭后怎么自动重新运行进程

    要实现Python程序在被远程主机强制关闭后能够自动重新运行,我们可以采用几种方法,但最直接且常用的方法之一是结合操作系统级的工具或脚本.在Linux系统中,我们可以使用cron作业或者systemd ...

  9. WIN32下的模拟时钟

    #include <Windows.h> #include <math.h> #include <tchar.h> #include "resource. ...

  10. 这才是java对象正解

    这才是 Java 对象正解 在深入讨论对象之前,让我们先明确对对象和实例的理解. 什么是对象? 对象(Object)是内存中分配的实际数据结构,它包含了数据和方法.在 Java 中,对象是类的一个实例 ...