其实这个注意点并不止限于matplotlib包。今天在云主机上运行某个调用了matplotlib。其生成的某个文件一直显示无法找到,导致matplotlib一直编译错误无法运行,开始以为是程序的使用方法错误或者是云平台的问题,死活找不到解决方法。在leader的帮助下【其实全程大佬调试】,发现问题。
首先在matplotlib的__init__.py的某个出错的函数中添加输出,检查程序走向,并且输出在哪一步创建了文件,哪一步删除了文件,哪一步文件消失。其结果是,matplotlib包初始化时会绘制一张图,可能是空白图,这个图需要临时创建一个文件夹来存放,在调用完成后会被删除,因此存活时间基本很短,但是也是作为临时存储的重要部件,文件夹名为matplotlib-xxxxx其中后面的xxxxx表示一段随机字符,由字母和数字组成。
在程序执行的关键部分输出了生成文件的路径和是否存在,发现大部分刚生成时是存在的,而某段时间下又不存在了,而文件被删除时并没有报错,也就是说其实际可能又是存在的。这就可能是云平台的文件同步机制出现的延迟,但是正常情况下Linux中该文件不应该在/tmp下生成。阅读matplotlib后发现,某个函数写明了该文件的生成路径和方式,其是一个if else结构,正常情况下,该文件应该生成在某个变量指代的路径下,该路径位于~/目录下,的matplotlib/文件夹内,而后续代码使用了get_home之类的函数,表示其一般是不会跑到tmp这种root目录下生成临时文件的。代码中最后的else才会有这样的操作,但是遇到了什么意外才会导致matplotlib选择了如此无可奈何的路径,并且该路径在云平台上刚好因为机制问题导致了报错。
我们检查了正确路径下的matplotlib/文件属性,发现其只有root的写入权限,也就是说正常普通user运行程序是根本无法写入这个文件,这就是个很矛盾的做法,既然matplotlib需要在一个路径下创建临时文件,而且连文件夹都准备好了,也有相应的if分支,但是现在的情况是该文件又不允许其写入。造成这种问题的原因只有一个,那么即安装matplotlib时使用了sudo命令。当我们在pip install某个模块时,也许Linux会报错说没有权限之类的,此时会图方便直接用sudo来执行安装,这是不正确的,应该使用pip install 包名 --user 命令,表示在当前用户目录下安装,赋予了相应合理的权限,而不是遇到文件就root,这样会出现很多难以发现的问题,比如现在。
我们可以还原出事情的整个经过了。安装matplotlib包时出现权限错误,安装者为了省事直接用sudo安装,此时安装就是以root的权限进行了,那么该包需要的一个文件夹matplotlib/用于存放临时文件或运行组件的东西,也就顺理成章的只具备root的读写权限,接着,在使用者跑程序时,因为不可能时时刻刻都用root权限运行程序,而matplotlib正巧需要对某个文件进行临时数据的创建和写入,发现权限不足以写入matplotlib/目录,而在__init__中的创建规则给了几个解决方案,正常在matplotlib/目录下创建,当遇到问题时用别的解决方案,保证程序能正常运行,那么无奈之举就是保存在tmp,因此,当matplotlib初始化时发现权限不足无法写入matplotlib/时,就会进入备选方案,写入不正常的路径tmp,刚巧在云平台上运行又有/tmp/目录下的一些文件可能会无法及时同步,导致了云平台的运行节点无法读取本地机上的所有文件,即出现了找不到tmp目录下某些文件的错误,换句话说,如果在本地机器上因为某些原因采用了/tmp内创建临时文件的方案,也不会出错,因为正巧是云平台,暴露了安装时失误的问题。