【AMAD】tenacity -- Python中一个专门用来retry的库
动机
很多时候,我们都喜欢为代码加入retry功能。比如oauth验证,有时候网络不太灵,我们希望多试几次。
这些retry应用的场景看起来不同,其实又很类似。都是判断代码是否正常运行,如果不是则重新开始。
那么,有没有一种通用的办法来实现呢?
简介
Tenacity1是一个通用的retry库,简化为任何任务加入重试的功能。
它还包含如下特性:
- 通用的装饰器API
- 可以设定重试停止的条件(比如设定尝试次数)
- 可以设定重试间的等待时间(比如在尝试之间使用幂数级增长的wait等待)
- 自定义在哪些Exception进行重试
- 自定义在哪些返回值的情况进行重试
- 协程的重试
用法
基本用法
from tenacity import *
# 基础的用法,会一直重试下去,直到函数没有抛出异常,正常返回值
@retry
def never_give_up_never_surrender():
print("一直重试,忽略exceptions,重试间没有等待时间")
raise Exception
何时停止
让我们加入停止的条件.
例如,在达到尝试次数后停下来:
@retry(stop=stop_after_attempt(7))
def stop_after_7_attempts():
print("尝试7次后停下")
raise Exception
在10秒后,如果仍然没有成功,则停下:
@retry(stop=stop_after_delay(10))
def stop_after_10_s():
print("10秒后停止")
raise Exception
可以使用|操作符,来组合多种条件:
@retry(stop=(stop_after_delay(10) | stop_after_attempt(5)))
def stop_after_10_s_or_5_retries():
print("10秒后,或者尝试5次后,停下来")
raise Exception
尝试间的等待
很多事并不是越快越好。所以,让我们在重试的尝试之间加入一些间隔时间:
@retry(wait=wait_fixed(2))
def wait_2_s():
print("每次重试间都有2秒间隔")
raise Exception
间隔可以是随机的:
@retry(wait=wait_random(min=1, max=2))
def wait_random_1_to_2_s():
print("重试间隔1-2秒")
raise Exception
还可以加入指数曲线形式的间隔:
@retry(wait=wait_exponential(multiplier=1, min=4, max=10))
def wait_exponential_1():
print("开始的时候等待 2^x * 1 秒,最少等待4秒,最多10秒,之后都是等待10秒")
raise Exception
多核在竞争一个共享的资源,使用指数间隔可以将冲突最小化:
@retry(wait=wait_random_exponential(multiplier=1, max=60))
def wait_exponential_jitter():
print("随机等待 2^x * 1 秒,最多60秒,之后都是等待60秒")
raise Exception
可以自定义每次等待时长:
@retry(wait=wait_chain(*[wait_fixed(3) for i in range(3)] +
[wait_fixed(7) for i in range(2)] +
[wait_fixed(9)]))
def wait_fixed_chained():
print("前三次等待3秒,后两次等待7秒,最后一次等待9秒")
raise Exception
何时retry
默认情况下,只有函数抛出异常时才会retry。
你可以设置在制定的异常才进行retry:
@retry(retry=retry_if_exception_type(IOError))
def might_io_error():
print("只有在IOError的时候进行retry,其它时候照常抛出错误")
raise Exception
可以在判断返回值是否是需要的情况下进行retry:
def is_none_p(value):
return value is None
@retry(retry=retry_if_result(is_none_p))
def might_return_none():
print("因为返回值是None,所以这个函数会一直retry")
# 这样写也是可以的,不用修改原来的代码
retry_version_func = retry(retry=retry_if_result(is_none_p))(might_return_none)
当然,这里也可以组合多个条件:
def is_none_p(value):
return value is None
@retry(retry=(retry_if_result(is_none_p) | retry_if_exception_type()))
def might_return_none():
print("在抛出任何异常,或者返回值是None的情况下,进行retry")
其它
在函数体内,你可以手动抛出TryAgain错误,进行重试:
@retry
def do_something():
result = something_else()
if result == 23:
raise TryAgain
通过参数reraise=True,可以抛出函数最后一次抛出的异常。如果没有设定,会抛出RetryError:
@retry(reraise=True, stop=stop_after_attempt(3))
def raise_my_exception():
raise MyException("Fail")
try:
raise_my_exception()
except MyException:
print('MyException会被抛出')
在重试的前后,记录日志:
import logging
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
logger = logging.getLogger(__name__)
# 重试前记录
@retry(stop=stop_after_attempt(3), before=before_log(logger, logging.DEBUG))
def raise_my_exception():
raise MyException("Fail")
# 重试后记录
@retry(stop=stop_after_attempt(3), after=after_log(logger, logging.DEBUG))
def raise_my_exception():
raise MyException("Fail")
你可以获取retry的相关统计数据:
@retry(stop=stop_after_attempt(3))
def raise_my_exception():
raise MyException("Fail")
try:
raise_my_exception()
except Exception:
pass
print(raise_my_exception.retry.statistics)
热度分析
这个库已经6岁了,截止2019.5.4日已累计获取1478star, 75fork.
源码分析
这个库在代码和项目方面都是典范,同时API设计的也是相当漂亮。
这个库对python装饰器的用法已经炉火纯青,基本所有的情景都有用到。有兴趣的同学可以通过下面几个点去看:
retry装饰器为什么可以无参数版本/有参数版本混合使用retry装饰器为什么可以作用函数和方法retry装饰器为什么可以作用于asyncio协程,tornado协程,普通函数
个人评分
| 类型 | 评分 |
|---|---|
| 实用性 | ⭐️⭐️⭐️⭐️⭐️ |
| 易用性 | ⭐️⭐️⭐️⭐️⭐️ |
| 有趣性 | ⭐️⭐️⭐️ |
| 代码质量 | ⭐️⭐️⭐️⭐️⭐️ |
【AMAD】tenacity -- Python中一个专门用来retry的库的更多相关文章
- python中一个简单的webserver
python中一个简单的webserver 2013-02-24 15:37:49 分类: Python/Ruby 支持多线程的webserver 1 2 3 4 5 6 7 8 9 10 11 ...
- Python中第三方的用于解析HTML的库:BeautifulSoup
背景 在Python去写爬虫,网页解析等过程中,比如: 如何用Python,C#等语言去实现抓取静态网页+抓取动态网页+模拟登陆网站 常常需要涉及到HTML等网页的解析. 当然,对于简单的HTML中内 ...
- 由浅入深:Python 中如何实现自动导入缺失的库?
在写 Python 项目的时候,我们可能经常会遇到导入模块失败的错误:ImportError: No module named 'xxx' 或者 ModuleNotFoundError: No mod ...
- 如何用Python中自带的Pandas和NumPy库进行数据清洗
一.概况 1.数据清洗到底是在清洗些什么? 通常来说,你所获取到的原始数据不能直接用来分析,因为它们会有各种各样的问题,如包含无效信息,列名不规范.格式不一致,存在重复值,缺失值,异常值等..... ...
- python中一个py文件如何调用其他py文件中的类和函数
HelloWorld 文件名称 Hello是类 from HelloWorld import Hello >>> h = Hello() >>> h.hello ...
- python中一个汉字点3个字节? utf-8
今天发现了一个汉字占了3个字节,一开始以为是两个呢,字符串切片时总出现乱码,后来才发现一个中文占3个字节.这才解决了乱码问题 原来 1. utf-8 编码中,一个汉字占三个字节.英文字母是一个占用一 ...
- Python 中一个逗号引发的悲剧
遇到一个 Python 字符串的坑,记录一下.看看下面这些代码 >>> a = [ ... 'foo' ... 'bar', ... 'tree' ... ] >>> ...
- python中一个经典的参数错误
直接上代码 class Company: def __init__(self, name, staffs=[]): self.name = name self.staffs = staffs def ...
- python中 urllib, urllib2, httplib, httplib2 几个库的区别
转载 摘要: 只用 python3, 只用 urllib 若只使用python3.X, 下面可以不看了, 记住有个urllib的库就行了 python2.X 有这些库名可用: urllib, urll ...
随机推荐
- VMware 虚拟机下载与安装
虚拟机下载 VMware官网地址:https://www.vmware.com/ 进行官网后,点击左边的下载图标,然后 根据操作系统选择合适的产品,在这里以Windows系统为例,点击转至下载,如下图 ...
- 常见的SQL编写和优化
目录 常见SQL编写和优化 常见的SQL优化方式 常见SQL编写和优化 常见的SQL优化方式 对查询进行优化,应尽量避免全表扫描,首先应考虑在where及order by 涉及的列上建立索引. 应尽量 ...
- 课程 6;比特币产生,分配(2100w,10个矿区)
www.8btc.com (每21w个,减半分配)
- 上传项目到码云或GitHub
一.安装Git 官网下载地址:https://git-scm.com/download/win 安装完成后,配置环境变量即可, 打开cmd,输入 git,出现以下提示即表示安装成功: 二.生成ssh公 ...
- .NET(c#) 移动APP开发平台 - Smobiler(1)
转载地址:https://www.cnblogs.com/oudi/p/8288617.html 如果说基于.net的移动开发平台,目前比较流行的可能是xamarin了,不过除了这个,还有一个比xam ...
- 简单消息监听容器--SimpleMessageListenerContainer
这个类非常强大,我们可以对他做很多设置,对于消费者的配置项,这个类都可以满足监听队列(多个队列).自动启动.自动声明功能可以设置事务特性.事务管理器.事务属性.事务容量(并发).是否开启事务.回滚消息 ...
- soa soap http rpc
soa 是一种计算机软件的设计模式,主要应用于不通应用组件中通过某种协议来互操作 它的基本设计原理是:服务提供了一个简单的接口,抽象了底层的复杂性,然后用户可以访问独立的服务,而不需要去了解服务底层平 ...
- linux shell 之流程控制 if if else while
(1)流程控制不可以为空: (2)if [ $(ps -ef | grep -c "ssh") -gt 1 ]; then echo "true"; fi 条件 ...
- LeetCode---Bit Manipulation && Design
**401. Binary Watch 思路:产生两个list分别代表小时和分钟,然后遍历 public List<String> readBinaryWatch(int num) { L ...
- python3笔记十七:python文件读写
一:学习内容 读文件 写文件 编码与解码 二:读文件--步骤分解 1.过程 第一步:打开文件第二步:读文件内容第三步:关闭文件 2.第一步:打开文件 open(path,flag[,encoding] ...