记两次因权限导致的惨痛经历

1. 找不到模块

事情说起来有点复杂,这是我在对jupyter进行二次开发时的惨痛经历,事后回想,琢磨了那么久才解决,归根结底是自己对jupyterhub源码阅读和理解的还不够,我先来对开发环境和事情起因做一个简单介绍。

针对jupyter的二次开发涉及到jupyterhub和notebook, 我在开发环境下已经安装好了这两个库,但显然,我不能直接在安装文件里进行开发,之所以要安装它们,是因为需要安装他们的依赖,不然,就算有源码,也无法运行。

开发只能在源码上进行,因此,我下载了这两个库的源码,但你应该明白,调试的时候,应当从源码启动,这个很好解决,我写了一个run.py文件,内容如下

import sys

sys.path.insert(0, '/root/jupyter_dev/jupyterhub')
from jupyterhub.app import main

main()

我将jupyerhub的源码路径添加到sys.path中,并且插入到列表的最前面,这样,再执行from jupyterhub.app import main 时就会从源码import,而不是从系统安装的jupyterhub中import

到目前为止,一切顺利,问题出在在jupyterhub中启动一个用户的server时。jupyerhub默认使用LocalProcessSpawner来启动一个notebook 来为一个用户提供服务,LocalProcessSpawner 会用Popen执行命令jupyterhub-singleuser来启动notebook, 在我的开发环境下,它的目录是/usr/local/python3/bin/jupyterhub-singleuser,其内容是

#!/usr/bin/python3
# -*- coding: utf-8 -*-
import re
import sys

from jupyterhub.singleuser import main
if __name__ == '__main__':
    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
    sys.exit(main())

jupyterhub.singleuser.py文件中会import notebook的内容,为了防止从系统安装环境下import notebook 和 jupyterhub, 我在这个文件夹添加了两行代码

#!/usr/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
sys.path.insert(0, '/root/jupyter_dev/jupyterhub')
sys.path.insert(1, '/root/jupyter_dev/notebook')

from jupyterhub.singleuser import main
if __name__ == '__main__':
    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
    sys.exit(main())

理论上,jupyerhub 和 notebook 都将从源码里import, 但是奇怪的事情发生了,程序总是从系统的jupyterhub库里import, 而不是从我指定的源码。

你敢想象,在执行run.py时,我修改sys.path的内容起到了作用,但是对于jupyterhub-singleuser 却毫无效果,理论与实践,一会相符,一会冲突,这就让人抓狂了,折腾了很久,猛然间想到,有可能是用户权限问题导致的。

执行run.py时,我用的是root账号,启动jupyterhub服务后,从浏览器登录服务的是账号a,a是普通账号,jupyterhub服务启动一个notebook专门为a服务,因此执行jupyterhub-singleuser时,用的是a账号,但a账号对源码没有访问权限。。。。。。这就导致尽管源码的目录被添加到了sys.path的最前面,但是由于没有访问权限,因此不能正常import,最后还是从系统环境下执行了import,在import时,python会依次从sys.path里的路径下执行import,只有都招不到时才会报错。

最终,我将源码目录的访问权限修改为777,问题得以解决。

2. pyhanlp

pyanlp是hanlp的接口,hanlp是一个自然语言处理库,提供了 中文分词,词性标注,命名实体识别,依存句法分析,新词发现,关键词短语提取,自动摘要,文本分类聚类,拼音简繁等自然语言处理方法。安装后首次使用时,执行代码

from pyhanlp import *

程序会下载两个压缩包,data-for-1.7.7.zip和hanlp-1.7.7-release.zip,然后解压,得到模型和词典,这本是一个非常简单的过程,但是,在使用其中的一个方法时,却报错

from pyhanlp import *

print(HanLP.parseDependency("徐先生还具体帮助他确定了把画雄鹰、松鼠和麻雀作为主攻目标。"))

事情又变得十分诡异,其他的方法都能正常使用,唯独这个方法不行,报错内容为

java.lang.NoClassDefFoundError: Could not initialize class com.hankcs.hanlp.tokenizer.NLPTokenizer

这个库的核心是java编写,python只是包了一层,这个错误说明一度让我以为是解压后的jar包版本太低,没有这个类,最后的真相却是权限不够。。。。。。安装这个库时用的是root账号,而使用这个库时用的是普通用户,而在data目录下的某一个文件夹里,有一个pos.bin文件,是加载NLPTokenizer类的所需要的,可普通用户恰好没有这个文件的访问权限,结果,导致NLPTokenizer初始化失败。

3. 总结

两次问题,均是由于普通账号访问root账号创建的文件且缺少权限导致的,都很诡异,在解决问题的过程中,很难像现在这样事后诸葛亮似的去分析,希望这两次教训能记忆的持久些。

扫描关注, 与我技术互动

QQ交流群: 211426309

加入知识星球, 每天收获更多精彩内容

分享日常研究的python技术和遇到的问题及解决方案