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()函数完整版:

最终的测试效果:

至此改造完成!以后在命令行就可以愉快地输入密码了,对于桔子菌这种强迫症患者再也不用担心少输入字符了。

tips1:环境Windows10,Python3.8.3

tips2:getpass在pycharm自带调试窗口中无法完成输入,需要在pycharm界面的底部找到”Terminal“按钮,打开终端,输入python + py文件名称运行。

tips3:修改源码调试时,如果是Python交互式界面,需要exit()退出重新进入再导入模块,修改才会生效。

扩展阅读:

  1.  Python错误集锦:pycharm中不能用getpass输入密码
  2.  Python基础教程3–基本输入输出

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注