这算是 IRedis 开发系列的第4篇笔记吧。这篇文章来聊一下 IRedis 最近发布 1.0 带来的新 Feature,一些开发过程中的思考,以及最后贴一下发布之后的一些“成就” (炫耀)。上次提到的介绍一些开发中使用的 tricks,就让我再拖到下一篇博客吧!
PS: 如果您还不知道 IRedis 的话,可以理解成是 redis-cli 的一个替代品。类似于 IPython 相比于 python 官方的 REPL,专注于用户体验,支持语法高亮,命令提示,自动补全等。全部的 Feature 可以参考项目 Readme,以及官网。
New Features
Changelog 是从 0.8 开始记录的,这里说一下几个两点功能:
支持了 Peek
命令,用 Redis 的时候不需要先用 type
看一下 key 的类型,然后再去选择对应的命令来操作了;
支持了配置文件 ~/.iredisrc
.
支持了 Redis 所有的命令,支持了时间戳补全(没有人想在命令行输入时间戳!),支持了 int 类型补全。(我发现实现补全器很有意思呢!)
更多的功能演示 gif 可以见这个页面。
Coming Features
- 支持用 URL 来传入 Redis 连接参数,比如
iredis --url redis://username:[email protected]:6379/5
lyqsmy 正在实现这个 Feature。 - 支持通过
alias_dsn
连接 redis,比如把上面这个 url 保存在~/.iredisrc
里面,然后在命令行通过iredis --dsn main
去连接。 - 将 IRedis 打包成单个文件,通过 curl 就可以下载到机器上执行。目前采用的方案是 PyOxidizer,遇到不少问题,mac-chaffee 正在做这项工作。
开发思考
最想分享的是单元测试了,开发一个又一个Feature的时候,测试挂了 N 多次,每次挂了我都庆幸一次幸亏写了测试。分享一下 piglei 的文章,以及我在评论里留下的一些想法:
我也想分享一些有关测试的想法。我很喜欢写测试,最近在写的 https://github.com/laixinta… 搞了一年多,测试挂了无数次。测试这个事情很反直觉的一点是,你觉得测试会增加开发成本,而事实相反。我知道我的测试覆盖了哪些地方,所以,我可以放心大胆的重构,主要测试pass了,我就敢发布新版本。随着代码越写越多,开发效率一点也没有降低,有什么新功能直接写就行了,之间的函数抽象不够直接重构,然后修复测试。这个项目到今天还是能保持着只要合并了master就可以作为新的feature立即发布,甚至我的发布流程也是交给CI的,我只要打tag push就好了。
另外有几点感受:
- 写稳定的测试是很重要的,有时候我们会因为assert 的list顺序不一样而导致测试随机挂掉,一定要找到原因,保持测试的可信度;
- 稳定的CI服务很重要,我从 circleCI 换到travis,现在用Github Action,CircleCI因为自身问题挂过3次,travis从没挂过,Github挂过1次(目前),如果CI变成“这次挂了,但可能不是我的问题,我要re-run试试”,体验是很糟糕的;
CI的速度很重要。现在有200+测试,刚开始需要5min在CI跑完,后来我对venv加了cache,测试3个Python版本只要1min了;- 覆盖率不重要。盲目追求100%是没有意义的。比如有些代码你写了
re.match("xxx", str)
你知道这个正则需要match很多种情况,但是其实只要你写1种,对覆盖率检测来说,它就以为你覆盖了,但其实需要更多的case来发现问题。还有一种情况,在函数的入口做参数检查,比如两个 kwargs 不能同时出现,这种代码很难出错,其实没必要测试(当然写一个也就几行)。所以,我现在觉得100%的测试覆盖并不能说明一个库是质量好的,覆盖率70%也并不能说明这个库测试不够完全;- 应该一开始就写测试。以前我的想法是先写好功能,写的完善了,到1.0了,再开始写测试。这种想法是不对的,应该在POC的时候就写POC的测试,这样可以保证开发质量,和开发效率。有一个额外的好处是,这时候的测试可以作为样例代码,供用户或同事参考。
- 我觉得理想的开发团队(虽然我没遇到过)应该用测试来保护自己的代码,如果别人修改了我的部分,并且全通过了我的测试,但是引入了BUG,那我会认为是我的责任。
—— on 《游戏《蔚蓝山》教我的编程道理》
另外,我发现 Python 里面的 Generator 很容易被忽略。
第一种情况是很容易忽略很多内置的函数返回的是 Generator,只能迭代一次。比如我在写 Completer 的时候写过的下面这个代码:
1 2 3 4 5 6 7 8 |
class IntegerTypeCompleter(LatestUsedFirstWordMixin, WordCompleter): def __init__(self): words = [] for i in range(1, 64): words.append(f"i{i}") # signed integer, 64 bit max words.append(f"u{i}") # unsigned integer, 63 bit max words.append("i64") super().__init__(len(words), reversed(words)) |
导致这个 Completer 只在第一次使用的时候有用,这个现象很奇怪,我花了几十分钟才意识到这 reversed()
返回的不是 list,只是 generator
,只能用一次。
另外情况就是一个函数很长,中间出现了一个 yield,就从一个 function 变成 generator maker 了(我觉得很多地方搞混了“生成器”和“生成器 maker”,def foo(): yield "hello"
这其实不是 generator,只是制造出 generator 的一个东西,每次调用都会出现一个新的 generator,所以我把它叫做 generator maker)。然后现象也很奇怪,就是这个函数根本没跑。找了好久才找到原来是没用 next()
激活。
说到底,说他“坑”的原因就是这种情况完全不会报错,不会有任何错误,完全隐藏在了程序的逻辑中。很难排查。
以下是发布之后受到的关注:
1.0 之后我在 Hackernews 贴了一下,但是一直都没什么人,感觉在 HN 发帖子很难受到关注,结果第二天起床一看,竟然被顶上了首页:
大佬, 我一直在关注你, 从 pycn 到捕蛇者到博客, 从你的博客我学到很多东西, 加油!
互相学习~
顶楼上,这个真的大佬了!继续关注博客,继续学习!
>-< 你才是大佬啊,你的博客我都看不懂……
我的博客实在没空更新,就把之前在公司做的一个跟工作不相关的ppt放上去充数了……我转行码农的,正好讲一讲之前学过的东东。其实没毛用。还是你这个比较厉害哈~
谦虚了~转行的都是大佬,我之前见过一个考古的博士的Python博客,我也都看不懂,看起来太酷了。
发现你的最新评论我不能reply了,看来不能无限回复下去哈。跟你私聊(DM)最方便的是啥?Twitter?
Twitter 和 telegram 都可以的。评论应该是4层,可以一直回复第一层哈哈
请教作者, iredis支持连接集群吗, 和redis6.0的账户密码登陆, 似乎不行现在
集群是支持的,账户密码也很早就支持了。你用的是哪个版本?试试最新的版本。
感谢, 可以了, 相比gui喜欢command line工具, cool
但是会出现一个问题, 没有redis-cli的-c参数, 我的请求命令似乎不能分发到集群节点, 执行scan后访问的是随机不同节点的key
是的,iredis 的集群模式是默认开启的,所以没有 -c 参数。但是只实现了 follow MOVED 基本的功能。
redis-cli 的行为可以具体描述下吗?我用的不多,也许可以在 iredis 里面也实现一下,谢谢。