源码阅读 howdoi

柿子跳软的捏(然而感受到来自自己的弱者气息)

howdoi是一个命令行中查询编程问题的工具,使用stackoverflow和google检索出最适合问题的答案并显示其中的代码部分

虽说是个200多行的小工具,还是能学到一些东西的。比如CLI工具的写法,选取网页中的元素(爬虫基础),如何打包发布一个工具等等

流程

command_line_runner 入口函数,调用 get_parser处理了传入的参数,并对显示版本号,清除缓存做单独处理。有传入查询的话调用howdoi处理,否则返回帮助文档。

howdoi把查询拼成了一个字符串,并且去掉了问号。(_get_instructions)然后进行查询:_get_links返回google搜索到的stackoverflow链接;get_answer用来获取链接中的问题答案:如果命令需要返回链接就直接返回,否则开始解析网页中的回答部分。如果回答部分包括代码就_format_output来输出,普通文本直接输出。完成查询流程

调用的库

argparse

Python标准库中用来解析参数的工具。我之前使用过看起来更优雅的click。但是在对外发布包的话。没有第三方依赖也是很好的选择

glob

1
2
3
4
In [1]: import glob

In [2]: glob.glob('/Users/kdwycz/M*')
Out[2]: ['/Users/kdwycz/Movies', '/Users/kdwycz/Music']

看起来是一个查找路径的库,比更为人熟知的os.listdir()再过滤要优雅太多。还有一个glob.iglob方法返回的是一个迭代器,适合文件数量很大的情况

requests-cache

一个神奇的给网络查询加缓存的包。我很诧异它在没有影响requests正常使用的情况下怎么加缓存的。就去看了下源代码。原来是通过hook来实现

pyquery

是一个用jQuery语法对xml进行操作的库。用法非常简洁

pygments

代码高亮工具(不知道怎么演示的路过)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
In [1]: from pygments import highlight

In [2]: from pygments.lexers import PythonLexer

In [3]: from pygments.formatters.terminal import TerminalFormatter

In [4]: code = "def _format_output(code, args):\n if not args['color']:\n return code\n lexer = None\n # try to find a lexer using the StackOverflow tags\n # or the query arguments\n for
...: keyword in args['query'].split() + args['tags']:\n try:\n lexer = get_lexer_by_name(keyword)\n break\n except ClassNotFound:\n pass\n\n # no lexer fo
...: und above, use the guesser\n if not lexer:\n try:\n lexer = guess_lexer(code)\n except ClassNotFound:\n return code\n return highlight(code,\n
...: lexer,\n TerminalFormatter(bg='dark'))"

In [5]: print(highlight(code, PythonLexer(), TerminalFormatter()))
def _format_output(code, args):
if not args['color']:
return code
lexer = None
# try to find a lexer using the StackOverflow tags
# or the query arguments
for keyword in args['query'].split() + args['tags']:
try:
lexer = get_lexer_by_name(keyword)
break
except ClassNotFound:
pass

# no lexer found above, use the guesser
if not lexer:
try:
lexer = guess_lexer(code)
except ClassNotFound:
return code
return highlight(code,
lexer,
TerminalFormatter(bg='dark'))

其他

Python版本兼容

作者使用了sys模块中的version来判定版本。我以前见到的代码大多是try下面import看是否异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Handle imports for Python 2 and 3
if sys.version < '3':
import codecs
from urllib import quote as url_quote
from urllib import getproxies

# Handling Unicode: http://stackoverflow.com/a/6633040/305414
def u(x):
return codecs.unicode_escape_decode(x)[0]
else:
from urllib.request import getproxies
from urllib.parse import quote as url_quote

def u(x):
return x

requests-cache/compat.py 做的更进一步了些,判断了Python小版本,系统环境和解释器

后记

虽然很早就想看源代码总结,但是直接促使我写这篇文章的确是看到别人对这个项目的阅读博文……写的时候并没有参考(写完看看也没有他写的好T_T)一开始还想把流程画成图,尝试了hexo的绘图插件和手绘后放弃,其实还是不晓得如何梳理结构。好在这个项目逻辑非常简单。给我更多的帮助是发现了些以前没用过的库

扩展资料

Github gleitz/howdoi

六系小白 阅读源代码之开篇Howdoi

Python爬虫利器六之PyQuery的用法

Comparing Python Command-Line Parsing Libraries - Argparse, Docopt, and Click


源码阅读 howdoi
https://blog.kdwycz.com/archives/code-001/
作者
kdwycz
发布于
2017年1月20日
许可协议