通过 PHP shell_exec 调用 Python3 时可能出现 pandas 和 matplotlib 导入错误

问题描述 投票:0回答:1

当尝试通过 PHP shell_exec() 函数执行两个不同的 Python 3 脚本时,我遇到了奇怪的行为。第一个 Python 脚本在 PHP 中的调用方式如下:

$jsondatash = escapeshellarg($jsondata);
// Execute the python script with the JSON data
$resultpy = shell_exec("/usr/bin/python3 /var/www/testsite/py/script1.py 2>&1 $jsondatash");

我转义了一些 json 数据 ($jsondatash) 并通过 shell_exec() 对此数据运行 Python 函数 (script1.py)。 Python 工作流程的结果存储在 PHP 数组 $resultpy 中。 Python 测试脚本如下所示:

from collections import OrderedDict
import sys, json
import scipy
import scipy.cluster.hierarchy as sch
import pandas as pd
import matplotlib


# Load the data that PHP sent
try:
     data = json.loads(sys.argv[1], object_pairs_hook=OrderedDict) 

except (ValueError, TypeError, IndexError, KeyError) as e:
     print (json.dumps({'error': str(e)}))
     sys.exit(1)

print (json.dumps(data))

它只是读取数据并通过 shell 将其发送回 PHP。然而,结果我得到一个空对象,表明 Python 3 代码无法执行。这是 Apache2 错误日志:

PHP Fatal error: Uncaught TypeError: array_keys(): Argument #1 ($array) must be of type array, null given 

没有Python特有的错误信息,只有PHP中空数组的错误。当我从 Python 测试脚本中注释掉 pandas 和 matplotlib 时

from collections import OrderedDict
import sys, json
import scipy
import scipy.cluster.hierarchy as sch
#import pandas as pd
#import matplotlib


# Load the data that PHP sent
try:
     data = json.loads(sys.argv[1], object_pairs_hook=OrderedDict) 

except (ValueError, TypeError, IndexError, KeyError) as e:
     print (json.dumps({'error': str(e)}))
     sys.exit(1)

print (json.dumps(data))

,我可以通过 shell_exec() 成功运行脚本。因此,当通过 PHP 执行 Python 脚本时,两个 Python 3 库必定存在某种加载错误。

在将 PHP 中的 json 数据写入文件(inputfile.json)、用 Python 读取该文件、将 Python 中的数据写入另一个文件(outputfile.json)以及将该文件读回 PHP 时,我遇到了同样的行为。这是 PHP 代码:

$jsondata = fopen('/var/www/testsite/files/inputfile.json', 'w');
fwrite($jsondata, $data);
fclose($jsondata);    

shell_exec("/usr/bin/python3 /var/www/testsite/py/script2.py 2>&1");
$resultpy = file_get_contents('/var/www/testsite/files/outputfile.json');

这是对应的script2.py Python代码:

from collections import OrderedDict
import sys, json
import scipy
import scipy.cluster.hierarchy as sch
import pandas as pd
import matplotlib

# Load the data from json file
try:
   with open('/var/www/testsite/files/inputfile.json', 'r') as inputfile:
   data = json.load(inputfile, object_pairs_hook=OrderedDict) 
except (ValueError, TypeError, IndexError, KeyError) as e:
   print (json.dumps({'error': str(e)}))
   sys.exit(1)

# write data to json outputfile
with open('/var/www/testsite/files/outputfile.json', 'w') as outputfile:
json.dump(data, outputfile)

直接在shell中执行Python代码时

/usr/bin/python3 /var/www/testsite/py/script2.py 2>&1

,我得到了预期的输出,但是当使用 shell_exec() 在 PHP 中执行 Python 代码时,输出文件 outputfile.json 为空。同样,如果我从 Python 脚本中删除 pandas 和 matplotlib,它也可以通过 PHP shell_exec() 成功运行。

这些测试显示以下内容: (1) 两个 Python 脚本都可以通过 PHP shell_exec() 执行,但前提是我从 Python 脚本中删除加载 pandas 和 matplotlib。 (2) 加载 pandas 和 matplotlib 本身不是问题,因为我可以直接在 shell 中执行包含这两个库的 Python script2.py 并获得预期的输出。

为什么我可以直接在 shell 中在 Apache2 上运行完全相同的 python 代码,但不能通过 PHP 中的 shell_exec() 运行?从我的测试来看,这似乎与 pandas 和 matplotlib 未能加载和终止 python 脚本有关。

编辑: Python3版本是3.8.10,PHP版本是8.2.8,这是安装pandas和matplotlib后的输出:

pip install pandas
> Successfully installed pandas-2.0.3 python-dateutil-2.8.2 pytz-2023.3 tzdata-2023.3
pip install matplotlib
> Successfully installed contourpy-1.1.0 cycler-0.11.0 fonttools-4.40.0 importlib-resources-6.0.0
> kiwisolver-1.4.4 matplotlib-3.7.2 packaging-23.1 pillow-10.0.0 pyparsing-3.0.9 zipp-3.15.0

Edit2: 我想知道可以加载的 scipy 和加载失败的 pandas 是否位于同一个路径,确实是:

Name: scipy
Version: 1.10.1
Location: /home/ubuntu/.local/lib/python3.8/site-packages

Name: pandas
Version: 1.5.3
Location: /home/ubuntu/.local/lib/python3.8/site-packages

我测试了旧版本的 pandas(v.1.5.3 - 见上文),现在已经安装了当前版本 v.2.0.3。两者都会在导入时终止 python 脚本。 pandas 所需的所有附加软件包均已安装。

Name: pandas
Version: 2.0.3
Location: /home/ubuntu/.local/lib/python3.8/site-packages

Python 3 sys.path 是:

['', '/usr/lib/python38.zip', '/usr/lib/python3.8', '/usr/lib/python3.8/lib-dynload', '/home/ubuntu/.local/lib/python3.8/site-packages', '/usr/local/lib/python3.8/dist-packages', '/usr/lib/python3/dist-packages']

Edit3:我发现以下方法(https://stackoverflow.com/a/44128762/8008652)允许我检索PHP中的Python 3错误并将其打印在html输出中:

exec('/usr/bin/python3 /var/www/testsite/py/script2.py 2>&1', $output, $return_var);
  if ($return_var>0) {
    var_dump($output);
  }

这是 Python 3 错误:

 array(14) { [0]=> string(34) "Traceback (most recent call last):" [1]=> string(77) " File "/var/www/testsite/py/script2.py", line 6, in " [2]=> string(23) " import pandas as pd" [3]=> string(80) " File "/usr/lib/python3/dist-packages/pandas/__init__.py", line 55, in " [4]=> string(33) " from pandas.core.api import (" [5]=> string(79) " File "/usr/lib/python3/dist-packages/pandas/core/api.py", line 5, in " [6]=> string(44) " from pandas.core.arrays.integer import (" [7]=> string(92) " File "/usr/lib/python3/dist-packages/pandas/core/arrays/__init__.py", line 10, in " [8]=> string(53) " from .interval import IntervalArray # noqa: F401" [9]=> string(92) " File "/usr/lib/python3/dist-packages/pandas/core/arrays/interval.py", line 38, in " [10]=> string(60) " from pandas.core.indexes.base import Index, ensure_index" [11]=> string(89) " File "/usr/lib/python3/dist-packages/pandas/core/indexes/base.py", line 74, in " [12]=> string(49) " from pandas.core.strings import StringMethods" [13]=> string(139) "ImportError: cannot import name 'StringMethods' from 'pandas.core.strings' (/usr/lib/python3/dist-packages/pandas/core/strings/__init__.py)" } 

正如我从之前的测试中怀疑的那样,这是与 pandas 库相关的导入错误(ImportError:无法从 'pandas.core.strings 导入名称 'StringMethods')。

php python-3.x pandas matplotlib
1个回答
0
投票

我发现Python库安装在服务器上的两个不同路径中:一个是'/usr/local/lib/python3.8/dist-packages',另一个是'/usr/lib/python3/dist-包'.

当我直接从服务器上的命令行执行脚本时,它确实从“/usr/local/lib/python3.8/dist-packages”访问 Pandas 包,而它是使用 shell_exec( )来自“/usr/lib/python3/dist-packages”,这意味着它在从 PHP 执行 Python 脚本时确实加载了旧版本的 Pandas。

因此,从命令行和 PHP 执行相同的 Python3 脚本确实从不同的目录导入了包。

我确实删除了 Python3 和两个包目录,重新安装了 Python3 以及 Pandas 和 Matplotlib 包,这解决了我的问题。

© www.soinside.com 2019 - 2024. All rights reserved.