python 命令行神器:Click (三) / docstring & short_help

之前用了两篇文章的篇幅介绍了命令行神器click,有了click,做一个功能强大,文档完善的命令行工具,简直容易的不要不要的。(为啥感觉像是卖假药的呢?)

原本我打算写完之前两篇就结束的,但是在第二篇快结尾的地方遇到一个问题,就是没法给参数加帮助信息,这样就让我这两篇文章看起来不是那么完美,强迫症患者表示不解决掉,睡不好觉。


(图源:bing.com

argparse

看了一下steempy,人家就支持给参数添加帮助信息,比如:
steempy upvote --help

看了一下它的代码,使用的是argparse,据说也是很强大的命令行解析工具,而且是Python 自带。不过问题是,我刚吹嘘完 click 如何如何牛逼,然后再去换argparse,这有点啪啪打脸的嫌疑嘛。

还好,click 网站上自己也吹的很好,Why Click? 我能说你王婆卖瓜自卖自夸吗?不过如果你看一下这篇文章Comparing Python Command-Line Parsing Libraries – Argparse, Docopt, and Click 或许就会了解click还是挺好的(我没看哦,太长了😵)

代码内文档字符串

按照click的文档,代码内的文档字符串会自动生成帮助信息,比如下边这段代码:

1
2
3
4
5
@click.group()
@click.option('--node', default='https://api.steemit.com', help='Steem RPC Node')
def mysteem(node):
"""A simple client tool to demonstrate how to use click!"""
click.echo('The RPC Node: ' + node)

./mysteem.py --help

这不错,我们在这写很多信息了,比如说加上点广告?(黄金广告位出租)

help 与 文档字符串

按照上述思路,我们应该也可以在子命令中加入文档字符串,比如说介绍一下参数咋用。

我来加一些文档字符串试试看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@click.command(help='Vote Post')
@click.option('--account', default='oflyhigh', help='The voter account name')
@click.option('--weight', default=100.00, help='Actual weight (from 0.1 to 100.0)')
@click.option('--b', type=bool, default=True, help='bool test')
@click.argument('post')
def vote(account, weight, b, post):
"""
Vote post with the specified weight.

\b
Arguments:
POST: Permlink of post(e.g. @oflyhigh/python-click)
"""
if b:
click.echo('Voter: ' + account)
click.echo('Weight: ' + str(weight))

click.echo('Post: ', post)

./mysteem.py vote --help

然而并没有如我所期望的那样显示帮助信息!

我想到的一个思路是把帮助文档加到@click.command(help='Vote Post')这个help中,但这样一来,我用./mysteem.py --help就会显示一大堆无关信息了,这不可取。

Command Short Help

经过一番调查,总算搞明白了,help和函数内的文档字符串只能有一个起作用。help缺省时,显示函数内文档字符串,否则help会覆盖函数内文档字符串内容。

这可咋办,我即想在程序总帮助那显示命令的简介,又想在命令帮助那显示参数的详细信息。

找了半天,short_help应该是我想要的东西,改一下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@click.command(short_help='Vote Post')
@click.option('--account', default='oflyhigh', help='The voter account name')
@click.option('--weight', default=100.00, help='Actual weight (from 0.1 to 100.0)')
@click.option('--b', type=bool, default=True, help='bool test')
@click.argument('post')
def vote(account, weight, b, post):
"""
Vote post with the specified weight.

\b
Arguments:
POST: Permlink of post(e.g. @oflyhigh/python-click)
"""
if b:
click.echo('Voter: ' + account)
click.echo('Weight: ' + str(weight))

click.echo('Post: ', post)

再执行一下两级帮助
./mysteem.py --help

./mysteem.py vote --help

耶,成功,这样的帮助信息对我而言足矣,这回终于可以放心结帖了。

(全文终)

参考链接


This page is synchronized from the post: python 命令行神器:Click (三) / docstring & short_help

python 命令行神器:Click (二)

咳,做人做事要有始有终,继续完成今天的click 学习。(偷摸回顾了一下,我挖下的坑似乎很多了😭)

(图源:bing.com

之前的帖子中,我们学习了click的安装,以及实现命令组,以及通过@click.option修饰命令组实现全局的选项,还有在@click.command()中添加类似help="Vote Post"的内容来为命令添加帮助。

Options 选项 (命令)

我们之前用@click.option修饰命令组,其实用来修饰命令是一样一样滴

1
2
3
4
5
6
@click.command(help='Vote Post')
@click.option('--account', default='oflyhigh', help='The voter account name')
@click.option('--weight', default=100.00, help='Actual weight (from 0.1 to 100.0)')
def vote(account, weight):
click.echo('Voter: ' + account)
click.echo('Weight: ' + str(weight))

上述代码给投票指令加了两个选项,一个是投票人,一个是投票权重。

一个很有趣也很有用的特性就是自动生成子命令的帮助信息
./mysteem.py vote --help

(@click.option可以根据默认值自动判断选项的类型)

我们执行一下子命令
./mysteem.py vote
可以看到默认值都已经生效了

@click.option支持指定选项的类型,详情可以参考Parameter Types
比如我们可以加一个逻辑类型的选项:
@click.option('--b', type=bool, default=True, help='bool test')
具体信息就不赘述了。

Arguments 参数

在click中,Parameters分为两种,

  • 一种是Options(选项)
  • 一种是Arguments(参数)。

原谅我的英语,Parameters和Arguments如何翻译成汉语,才能让它们区别开来?

Option更灵活,支持好多强大的功能,可选的。
Argument更严格,必选的。

上边我们在vote命令中实现了--account以及--weight选项,如果不填,就用默认值。
但是投票总涉及到给哪个帖子投啊,所以我们需要再给它加个参数
比如说:

@click.argument('post')

让我们检查一下帮助
./mysteem.py vote --help

似乎有些美中不足,比如这个post到底是啥玩意?
https://steemit.com/python/@oflyhigh/python-click 还是@oflyhigh/python-click

如果能给post加个帮助就好了,比如说:
@click.argument('post', help='Permlink of post(e.g. @oflyhigh/python-click))
然而我被click打脸了,运行出错:

TypeError: init() got an unexpected keyword argument ‘help’

看了一下文档
http://click.pocoo.org/6/documentation/

Arguments cannot be documented this way. This is to follow the general convention of Unix tools of using arguments for only the most necessary things and to document them in the introduction text by referring to them by name.

好吧,你咋说都有理,那难道就没有办法加帮助了吗?

这块我还没测试明白,回头我研究明白再补上吧。😭

总结

有了click,做一个功能强大,文档完善的命令行工具,简直容易的不要不要的。

click还有很多强大的功能,但是我一时半会也发掘不了多少,想必日后使用时,会给我越来越的惊喜吧。

参考链接


This page is synchronized from the post: python 命令行神器:Click (二)

python 命令行神器:Click (一)

说到Python 命令行参数,我只会用 sys.argv,简单粗暴,适合我这种半吊子程序员写写垃圾程序,至于对别人是否友好,哪管得了那么多,反正又没脸开源。据说getopt模块也挺好用,但是作为我这种深得差不多先生思想精髓的人,既然差不多,我何必非得去用呢,程序不出错就是真理!

但是读 @xeroc 大神写的代码,我发现了一个好东西,那就是click,用来做命令行程序,简直堪比神器啊,太优雅了!好东西不敢独享,分享给大家。


(图源:bing.com

安装

click 不是Python 自带的库,所以使用之前要先安装啦

安装指令如下:
pip install click

官网强烈推荐安装在virtualenv 环境下,我也推荐。
有关如何使用virtualenv,可以参考官网相关链接,这里就不再赘述啦。

说明

安装之后,我们来学学咋玩。

为了便于说明,我假装实现steem工具,支持投票以及转账。然后我慢慢加些选项和参数之类的。当然了,这是假装的,只是为了演示click的使用。

命令组

因为要实现好多功能,那么单纯的用一个指令带一堆选项或者参数的方式可能不够用,所以这里引用了命令组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/env python

import click

@click.group()
def mysteem():
pass

@click.command()
def vote():
pass

@click.command()
def transfer():
pass

mysteem.add_command(vote)
mysteem.add_command(transfer)

if __name__ == '__main__':
mysteem()

@click.group()修饰的函数为命令组的入口,@click.command()修饰命令,用mysteem.add_command(add)类似这样的方式,将命令加入到命令组中。

现在一个命令行程序的框架搭起来啦,接下来我给大家演示一个神奇的功能
./mysteem.py --help

妈妈再也不用担心我写的程序没帮助啦!,程序自动生成了非常优雅的帮助。

Options 选项 (命令组)

下边我假装给这个STEEM工具加些选项啦。比如我们指定一下RPC Node

1
2
3
4
@click.group()
@click.option('--node', default='https://api.steemit.com', help='Steem RPC Node')
def mysteem(node):
click.echo("The RPC Node: " + node)

在这里我们用@click.option修饰命令组,这样我们可以在执行各项命令之前指定选项啦。


帮助依旧是自动生成的,太帅了

我们假装执行一下,先用默认值
./mysteem.py vote

输出如下:

The RPC Node: https://api.steemit.com

再手动指定一下:
./mysteem.py --node https://mynode.com vote

输出如下:

The RPC Node: https://mynode.com

除了修饰命令组,@click.option 还可以修饰命令
呸呸,举毛线数据库的例子,我编个啥选项好呢?愁死我了!
这样,自动加序号吧,就叫autoseq吧,大家别纠结数据库是否合理,我只是为了演示命令行,举了个不恰当的例子😭

命令帮助

现在我们似乎搭好了程序的框架,但是感觉少了点什么呢?

仔细找了一下,发现使用./mysteem.py --help 生成的帮助有点单薄

其中命令列表后竟然没有帮助,是可忍孰不可忍,婶也不能忍!

1
2
3
4
5
6
7
@click.command(help="Vote Post")
def vote():
pass

@click.command(help="Transfer Money")
def transfer():
pass

给命令加上帮助

./mysteem.py --help

这回舒服多了!

总结

这篇文章,我们模仿 steempy,做了个命令行程序的框架。实际上一点功能也没有,但是用来理解click的使用,还是略有帮助的。

当然了,限于篇幅,本文只讲了一部分,回头继续更新,敬请期待!

参考链接


This page is synchronized from the post: python 命令行神器:Click (一)

公众号恢复正常啦,宇宙首富的梦想破灭

这两天有好多个朋友上来问我,“O哥,你的微信公众号不好用啦!” 我一看果然不好用啦,一测试,是STEEM RPC节点的问题,我就放心啦,节点问题嘛,等他们修复就好啦。


(图源 :pixabay)

节点出故障啦

结果等啊等啊,等了好久终于可以访问了,然后我回复我的朋友们,公众号好用啦,去用吧。结果人家使用后给我回复,O哥,你的公众号虽然能用,但是这数据完全不对啊。

这怎么可能呢,我可都是从STEEM的节点获得的数据呢,竟然冤枉我的公众号不好用,太气人了😡!然后我自己去测试了一下,哇靠,果然这数据差的不是一点半点啊。

比如说我的Voting Power在70%左右,但是微信公众号算出来的是90%多,更激动人心的是,我的SBD明明100多,微信公众号显示的是500多。我用curl直接连接STEEM的不同节点,发现不是我公众号的问题,而是不同的节点给出的数据不一样!

比如说从A节点读到的VP是70%,从B节点读到的VP却是90%;从A节点读到的SBD是100多,从B节点读到的SBD却是500多;还有其它一些问题等等。

首富美梦

于是我不由得幻想起来,如果这时候,我透过B节点提现,是不是可以转出去500多SBD呢?😍

后来想想,好像这事不靠谱,因为节点可以自己搭建,如果这样可以透支的话,那我建个节点,然后把自己资产改成1000000000000000000000 SBD,然后再提出来,岂不是瞬间成宇宙首富了。这明显不现实。

哎,宇宙首富的美梦持续了不到2分钟就破灭了😭

分析问题

既然成不了宇宙首富,我还是研究研究咋解决公众号的问题吧,毕竟朋友们还很信赖,不能让大家失望。修改公众号有几个思路,一是降级版本,使用老版本RPC NODE; 二是找到并解决当前版本里的BUG;三是找到一个好用的新版本。

降级版本?

降级版本有个问题,我公众号中使用了一些新版本中才有的东西,降级的话,就要全部改掉。而STEEM 的RPC早晚会都升级到新版本,那么我之后还要改回来。

解决BUG?

当前节点的BUG我分析了一下,我测试了一下当前节点的版本信息,貌似没啥问题:

1
2
3
4
5
{'id': 1,
'jsonrpc': '2.0',
'result': {'blockchain_version': '0.19.4',
'fc_revision': '2945196ca5ead5049e78679d69affea98d97e27b',
'steem_revision': '2945196ca5ead5049e78679d69affea98d97e27b'}}

所以我猜测应该是jussi的问题,这个是一个代理层,从后端的STEEMD读取数据。现在的可能是它缓存了一些老数据,和STEEMD的数据不一致,该更新没更新。

但是我不知道咋知道节点用的哪个jussi版本,这挺愁人的。至于去解决这个问题,饶了我吧,我就一半吊子程序员。

换新节点?

既然降级以及解决BUG都不现实,那就只好找一个好用的节点喽。

试了一下api.steemdev.com 版本也是0.19.4,但是返回的数据都是正确的,那就先用这个喽。

公众号好用啦

其实水了大半天,我就是想告诉大家,公众号又好用啦。

不过steemit官方最近更新节点有点勤,一会0.19.4,一会0.19.2的,如果没用一些新东西倒是无所谓,如果用了0.19.4中才有的特性,那么说不定啥时候又失灵了。

不过也管不了那么多了,毕竟搭一个全节点自己用,对我而言还不太现实,那就将就吧。

公众号添加方法

还没加公众号的,快点上车啊

  • 方式一:
    进入微信通讯录->点击公众号->点右上角加号->搜索steemit,关注即可。

  • 方式二:
    直接扫描以下二维码:


This page is synchronized from the post: 公众号恢复正常啦,宇宙首富的梦想破灭

“画家”往事

看到STEEMIT上各种绘画比赛搞得红红火火,各路画家大显身手,我不由得回想起我所结识的一个“画家”。


(图源 :pixabay)

之所以“画家”加了引号,其实他是我的高中同学,一个纯粹的理科生,画家只是我们冠与他的诸多名头之一。画家人瘦瘦的、皮肤黑黑的、眼睛略微有些斜视、头发很长大中分,除了头发看不出哪有艺术家气质。

画家一度是我的同桌,因为他斜视的原因,明明他和你说话却让人感觉他看着其它地方,让我感到没有被尊重,所以刚开始我们的关系不是那么好。后来时间久一些我才知道这不是他不尊重我,而是身体的原因,就释然了。

后来发现画家有很多优点,比如思维敏捷、看问题的视角独特(不是指斜视)、待人真诚,所以慢慢我们就成了好朋友。既然是好朋友,我有好事当然要叫上他了,所以别人都在上课或者上自习的时候,我带着他逃课,去游戏厅打游戏,去市内逛商场,或者坐火车跑出很远去爬山,肆意地挥霍青春。

既然被叫画家,画家当然是有些真本事的,什么人物啊、风景啊,随手画来栩栩如生。有次上课老师在讲台上讲课,画家不安心听课,在下边画老师讲课的场景,结果被老师发现。画家耷拉着脑袋站起来,以为这次肯定要被老师收拾死,结果老师看着他画的画,叹为神作,当场要了下来,视若珍宝的收藏了起来。不但没有批评画家,并且夸奖了他,不过也告诉他上课时间还是要好好学习为主。

画家因此一战成名,很多艺术生都自愧弗如,找他索画的络绎不绝,画家总是来者不拒,所以画家成为了最受欢迎的人。画家也因此飘飘然了,没事就和我吹嘘,将来一定要成为了一个大画家,名气盖过齐白石、张大千、梵高,作品知名度要超过蒙娜丽莎。这时候,作为损友,我都会不吝于打击他,别做梦了,还大画家呢,我看多说就去公园给人画像谋生吧。画家这时候就会一副不和我一般见识的表情。


(图源 :pixabay)

虽然画家接了很多画像的“业务”,可是这帮小抠没有一个提到给钱的。画家家里很穷的,有时候吃饭都成问题,经常管我借饭票,一般情况他想起来就还我,想不起来,我也不去追讨,毕竟相对而言,我的家庭条件要比他好一些,既然是好友能帮就帮吧。

有道是一分钱难倒英雄汉,画家就面临这样的窘境,一方面没钱买饭票,另一方面欠我的饭票不知多少了。于是画家就动起了歪心思。当时学校的食堂被一个县城略有名气的黑涩会成员承包,饭菜难以下咽管理也是及其混乱,当时食堂发行一种饭票,不同颜色的纸上,黑色方框中印着早、中、晚,我们用对应的饭票就可以换取一份早饭、或者午饭或者晚饭。

经过画家的仔细分析,其中白颜色的饭票,所用的纸张,和我们平时试卷用纸完全一致。既然如此,是不是可以仿冒饭票呢?于是画家找来一些做过的试卷,把试卷边缘的空白地方裁剪下来,再裁剪成饭票大小,然后用黑色水性笔以及格尺按饭票的模样画出一圈黑框,再模仿饭票的印刷体在中间写上早中晚的字迹。模仿完毕,让我分辨,我发现不仔细辨别根本看不出来差异。

于是画家开启了他的造饭票大业,一口气造了好几张。到了饭点,非得让我和他一起使用伪饭票,说什么要有福同享有难同当。我俩怀着忐忑的心里去食堂,将饭票递给打饭的师傅,师傅瞅都没瞅就丢到旁边回收饭票的小盆里,于是我们第一次消费伪饭票成功。

这简直是无本万利啊,我对他说要不要全班推广,画家说那样不行,容易被发现,咱俩闷声发大财好了。当时对于我们,真的是一笔“大财”啊,于是每天早自习他都制造出我俩当天的饭票,一天的饭钱,几分钟就解决了。我尝试造了两张,发现除了外框和正版饭票基本一致以外,中间的字歪歪扭扭,一看就是假的。

就这样,我们连续爽了一个多星期,但是世上没有不透风的墙,我们造饭票的成功事例让前后桌的同学眼红不已,于是最终全班都在造饭票,最后席卷到全校。

于是有一天我俩继续拿假饭票打饭时,被打饭师傅扭送到食堂的办公室。食堂老板也就是黑涩会的成员亲自提审了我俩,先问我饭票哪里来的,我一下腿就软了,这可咋办,会不会被黑涩会打死?正当我脑袋里乱糟糟不知道咋回答时,画家接过话,“我给他的,怎么了?”一副很迷惘的样子。

黑涩会成员食堂老板继续问,你的饭票哪里来的,画家继续保持一副我啥也不知道的表情,回到“我在路上捡到的”。食堂老板貌似相信了他的话,继续问“捡到几张,还有没有了?”,“就捡两张,刚才都花掉了”。食堂老板彻底相信了,然后告诉我们这是假饭票,没收了!画家一副惊讶的面孔,“假的,饭票咋能是假的呢?还有假饭票!”,食堂老板一副咬牙切齿的样子,狠狠地说“让我知道谁造假,肯定打死他,你俩走吧。”

于是我俩有惊无险的户口脱身,对望了一眼,惊出一身冷汗。回去之后不两天,就听到各种诸如谁谁谁用假饭票被食堂抓到,被一顿暴打被赔了食堂好多钱的事情。我突然发现,有个机智的伙伴是多么幸福的一件事啊。不过这之后,画家再也没敢造假饭票了,然后时不时的要我接济一下。


(图源 :pixabay)

后来画家刻苦学习,考进了师范学院,学校帮他申请了一笔寒窗基金,他拿出几百块钱给我,说偿还这两天的债务,我说不要他就表示要和我翻脸,最终我只好收下了。读大学以后,联系不太方便,来往过几次信以后就慢慢不联络了。后来从朋友那得知他本科毕业后又读了硕士,然后留在大学里成为了一名数学教师。尽管他没能成为一个举世闻名的大画家,但是还是由衷为他感到高兴。

只是有时候不无恶意的想,他现在还作画吗?他能教学生些什么呢?不会讲讲数学之后又传授他造饭票的神技吧~~


This page is synchronized from the post: “画家”往事

每天进步一点点:Python中使用urllib3访问STEEM RPC

在之前的文章中,我们学习在Python中使用Requests访问STEEM RPCPython中使用urllib访问STEEM RPCPython中使用PycURL访问STEEM RPC,原本这三把板斧足够我用了,但是阅读好些代码都是用的urllib3,所以拿来试试啦。


(图源:bing.com

介绍

urllib3是一个强大的、健全友好的Python HTTP客户端,包括requests、pip在内的很多Python生态系统都使用了urllib3。

urllib3具有如下特性:

  • 线程安全
  • 连接池
  • 客户端SSL/TLS校验
  • 多部分编码文件上传
  • 请求重试以及HTTP重定向
  • gzip以及deflate编码
  • HTTP以及SOCKS代理
  • 100%测试覆盖

安装

urllib3是第三方的库,所以使用之前需要先安装。

pip安装的指令为:
pip install urllib3

因为我安装过requests,所以会提示我已经安装啦。

代码

继续拿我们之前的命令为例来学习:

curl --data '{"jsonrpc": "2.0", "method": "call", "params": ["account_by_key_api", "get_key_references", [["STM6MGdForcZ8HskcguP84QSCb8udgz7W9yUPU5jtsAKQAxth3U16"]]], "id": 1}' https://api.steemit.com

使用urllib3改写后的简单代码为:

1
2
3
4
5
6
7
8
9
import urllib3
import json

payload = {"jsonrpc": "2.0", "method": "call", "params": ["account_by_key_api", "get_key_references", [["STM6MGdForcZ8HskcguP84QSCb8udgz7W9yUPU5jtsAKQAxth3U16"]]], "id": 1}
rpc = "https://api.steemit.com"

http = urllib3.PoolManager()
r = http.request('POST', rpc, body=json.dumps(payload).encode('utf-8'))
print(r.data.decode('utf-8'))

结果

执行结果为:

InsecureRequestWarning: Unverified HTTPS request is being made. Adding certificate verification is strongly advised. See: https://urllib3.readthedocs.io/en/latest/advanced-usage.html#ssl-warnings InsecureRequestWarning) {"id":1,"result":[["oflyhigh"]]}

加上这样一句就好啦
urllib3.disable_warnings()

但是实际使用中,不校验证书是不安全、不被提倡的做法。如何校验证书,将在其它文章中另行阐述。

高级功能

类似keep-alive等高级功能可以通过在构建urllib3.PoolManager类实例时通过参数指定。

**
比如Keep-Alive功能,需要在上述代码中加入如下内容:

1
2
3
4
from urllib3.connection import HTTPConnection
socket_options = HTTPConnection.default_socket_options + \
[(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1), ]
http = urllib3.poolmanager.PoolManager(socket_options=socket_options)

***
注:这段代码我理解有误,并非用于实现Keep-Alive功能

更多功能和详情,参考用户手册吧。

参考链接


This page is synchronized from the post: 每天进步一点点:Python中使用urllib3访问STEEM RPC

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×