改造getpass,强迫症患者再也不用担心少输字符了 (Python getpass源码分析与改造)
原文链接:http://www.juzicode.com/archives/4719
1、引子
在Python中获取键盘输入我们通常使用input()实现,函数返回的值就是输入的内容:
如果用上面这种方式输入密码,你的密码就完全“裸露”在外了,是不是有一种被人偷窥的感觉。
不过Python提供了内置的getpass模块可以帮助我们解决这个问题:
但是从上面的运行情况看,输入的字符并没有任何替代提示,这样每次按键是否确实输入了也不太清楚,通常的做法是用星号*提示按下了一个按键。桔子菌找了一圈没有找到合适的替代库,那就先来看看getpass模块是怎么实现的,再尝试动手改造改造。
2、源码分析
getpass是Python的内置模块,可以在Python安装路径的lib\getpass.py中看到源码:
这个py文件包含了1个GetPassWarning类、get_user、win_getpass等几个函数实现以及172行开始的一段代码。
import该模块时会进入172行,这里是该模块的入口,我们先来看看172行开始的代码段是什么含义。以Windows系统为例,termios是类linux中的终端模块,在Windows中导入termios会触发异常进入177行,179行尝试导入msvcrt模块,该模块封装了Windows系统的标准C库函数。如果导入模块失败,会将get_pass设置为fallback_getpass的别名,如果导入成功,将getpass设置为win_getpass的别名,所以使用getpass.getpass()实际就是使用getpass.win_getpass()。
下面来看下win_getpass是怎么实现的:
102~103行打印提示信息prompt,默认是字符串”Password”。
105~114行进入无限循环等待输入。106行用msvcrt的getwch()获取输入,该方法直接从按键获取输入,不是从缓冲区获取,不会等待换行符,戳这里有类似函数的对比介绍:字符输入函数getchar(),scanf(),getche(),getch()的比较;107~108行是正常退出条件,如果输入换行符表示输入完成,退出循环;109~110行用来接受ctrl-c按键,抛出keyboard中断异常,可用于强制退出程序;111~112行处理退格键,输入一个退格,原来输入的字符串去掉最后一个;113~114处理其他类型的字符,表示接收到一个有效字符。
115行到116行显示回车换行符。
3、改造
分析完源码,知道该在原文件的114行后添加输出替代符号,这里我们输出星号*代替:
while 1:
c = msvcrt.getwch()
if c == '\r' or c == '\n':
break
if c == '\003':
raise KeyboardInterrupt
if c == '\b':
pw = pw[:-1]
else:
pw = pw + c
msvcrt.putwch('*') #增加输出替代字符
msvcrt.putwch('\r')
msvcrt.putwch('\n')
return pw
增加上述这行代码后,运行看下效果,可以看到输入的字符被星号*替代了:
一切看起来还不错,我们再尝试对for循环中的其他几个分支都测试一遍:
直接输入换行符和按下CTRL-C都是正常的,但是在输入退格时,终端上并没有删除多余的星号*,不过返回的inp变量结果是正常的。上图所示红色箭头处输入6个字符后,再次输入6个退格,返回的结果inp为空字符串符合要求,但是终端里多余的星号*没有删除掉,接下来修复这个bug。
在控制台显示出来的字符,并没有方法可以直接删除,但是可以“曲线救国”找到替代方法,用空白符号来填补从而达到“删除”的效果,标准做法是退格、打印空格、再退格。另外为了避免删除过多的位置,需要加入判断语句,如果输入的字符串长度已经为0,不再操作屏幕删除字符,最终的代码是这样的:
while 1:
c = msvcrt.getwch()
if c == '\r' or c == '\n':
break
if c == '\003':
raise KeyboardInterrupt
if c == '\b':
if (len(pw)==0): #避免删除过多字符
continue
msvcrt.putwch('\b') #删除三步曲,退格,打印空格,再退格
msvcrt.putwch(' ')
msvcrt.putwch('\b')
pw = pw[:-1]
else:
pw = pw + c
msvcrt.putwch('*') #输出替代字符
msvcrt.putwch('\r')
msvcrt.putwch('\n')
改造后的win_getpass()函数完整版:
最终的测试效果:
getpass修改源码-星号提示输入
至此改造完成!以后在命令行就可以愉快地输入密码了,对于桔子菌这种强迫症患者来讲再也不用担心少输入字符了。
tips1:环境Windows10,Python3.8.3
tips2:getpass在pycharm自带调试窗口中无法完成输入,需要在pycharm界面的底部找到”Terminal“按钮,打开终端,输入python + py文件名称运行。
tips3:修改源码调试时,如果是Python交互式界面,需要exit()退出重新进入再导入模块,修改才会生效。
扩展阅读: