项目场景:
在通过使用
os.environ['PATH'] += ';xxx'
或者 sys.path.append('xxx')
添加临时系统环境变量时偶尔失效问题
例如如下代码:
import sys
sys.path.append('D:\\Program Files\\nodejs')
print(sys.path)
from tkinter import *
import execjs
def get_js(fileNme):
jscript = execjs.get("Node")
# jscript = execjs.get()
print(jscript.name)
with open(fileNme, 'r', encoding='utf-8')as f:
content = f.read()
js = jscript.compile(content)
return js
def get_data(entry):
js = get_js("测试.js")
var.set(str(js.call("getData", "1")))
root = Tk()
button = Button(root, text="点击", command=lambda: get_data(entry))
button.pack()
var = StringVar()
entry = Entry(root, textvariable=var)
entry.pack()
root.mainloop()
问题描述
我的nodejs
就放在 D:\\Program Files\\nodejs
中,但是无论如何 print(jscript.name)
的执行结果都是 execjs._exceptions.RuntimeUnavailableError: Node.js (V8) runtime is not available on this system
这个错误,按正常情况在体统环境变量中增加 D:\\Program Files\\nodejs
执行结果应该是 Node.js (V8)
那么问题来了,究竟是什么原因呢?
原因分析:
老规矩,哪里出问题,哪里断点调试,看看究竟是什么在作祟
- 在
jscript = execjs.get("Node")
处断点,单步调试,一直找到execjs
下_runtimes.py
:
def _find_runtime_by_name(name):
for runtime_name, runtime in _runtimes:
if runtime_name.lower() == name.lower():
break
else:
raise exceptions.RuntimeUnavailableError("{name} runtime is not defined".format(name=name))
if not runtime.is_available():
raise exceptions.RuntimeUnavailableError(
"{name} runtime is not available on this system".format(name=runtime.name))
return runtime
- 由以上源代码可知,报错就是在这里报的,关键点在于
runtime.is_available()
这个函数返回值究竟有没有。 - 继续跟进,发现这个函数返回值是
self._available
def is_available(self):
return self._available
- 在文件中搜索查看
self._available
在哪里定义的,发现是这个函数所在的类初始化时候定义的
class ExternalRuntime(AbstractRuntime):
'''Runtime to execute codes with external command.'''
def __init__(self, name, command, runner_source, encoding='utf8', tempfile=False):
self._name = name
if isinstance(command, str):
command = [command]
self._command = command
self._runner_source = runner_source
self._encoding = encoding
self._tempfile = tempfile
self._available = self._binary() is not None
- 在
self._available = self._binary() is not None
打断点,注意这里不能往下调试了,因为这个定义是在我们刚才调试开始的位置之前运行的,所以打上断点后重新开始调试。 - 根据断点后栈的规则,结果发现这个是在
import execjs
的时候运行的。而且self._available
的值是self._binary()
给出的,继续一直往下调试,直到下面这个函数
def _find_executable(prog, pathext=("",)):
"""protected"""
pathlist = _decode_if_not_text(os.environ.get('PATH', '')).split(os.pathsep)
for dir in pathlist:
for ext in pathext:
filename = os.path.join(dir, prog + ext)
try:
st = os.stat(filename)
except os.error:
continue
if stat.S_ISREG(st.st_mode) and (stat.S_IMODE(st.st_mode) & 0o111):
return filename
return None
- 根据上面这个函数知道了在
import execjs
的时候是从os.environ.get('PATH', '')
中读取的系统环境变量而我们一开始是加在了sys.path.append('D:\\Program Files\\nodejs')
这里。 - 到现在为止,已经很明显了,
sys.path
就是原本的系统环境变量,sys.path.append('D:\\Program Files\\nodejs')
这行代码只是给sys.path
增加了一个子元素,并不是真正加在了环境变量中,而这时候我们通过os.environ.get('PATH', '')
读到的也还是系统的环境变量,并没有增加'D:\\Program Files\\nodejs'
。因此会出现错误。
解决方案:
- 知道了问题的原因,解决就简单了,既然
execjs
读的是os.environ.get('PATH', '')
,那么我加的时候就加在os.environ
里就好了。如下
import os
os.environ['PATH'] += ';D:\\Program Files\\nodejs'
print(os.environ['PATH'])
注意:';D:\\Program Files\\nodejs'
最前面一定要有;
,要不然在pycharm
里可以正常运行,但是在控制台或者直接打开的时候就不能运行了,尤其是win10
及以上的系统,究其原因是系统内置的问题,在这里不便展开说了,要想知道自己可以添加环境变量,然后打印结果,分别在pycharm
和控制台打印,找找区别就可以了。这里我贴两张图,大家可以自己看下,差别就在一个;
上。
- pycharm中运行结果:
- 控制台运行结果
总结
python项目文件内部添加环境变量无效时,
- 可以直接自己手动在系统环境变量中添加,这样是可以解决问题的。(手动在系统环境变量中添加环境变量的方法上网一搜一大堆,这也不是这篇文章想表达的重点)
- 可以查看源代码,看哪里需要了环境变量,找到后查看是通过
os
调用还是通过sys
调用的,针对性添加就可以了(主要适用于你的python
文件需要打包给其他客户使用,由懒得给他装环境,直接把环境打包进去,文件内部增加环境变量即可)