运行get_data.py单元测试时遇ImportError: No module named pyspark_llap,但主代码可正常执行的问题求助
这个问题我之前在边缘节点上遇到过,本质是主脚本的运行环境和单元测试的运行环境不一致导致的——主脚本能找到pyspark_llap是因为它是在Spark的官方运行上下文(比如spark-submit)中执行的,而直接用python test.py跑测试时,本地Python环境并没有加载Spark的依赖路径。
下面分几个方案来解决,按推荐程度排序:
方案1:用spark-submit运行单元测试(最推荐)
直接用Spark的提交命令来运行你的测试脚本,这样测试会在和主脚本完全一致的环境中执行,自然能找到pyspark_llap模块:
spark-submit test.py
不过你需要稍微调整下test.py的结构,确保测试时能正确初始化SparkSession,避免导入时的问题:
import unittest from pyspark.sql import SparkSession from get_data import get_hive_data # 只导入需要的函数,避免全局导入问题 class TestHiveData(unittest.TestCase): @classmethod def setUpClass(cls): # 初始化SparkSession,和主脚本保持一致 cls.spark = SparkSession\ .builder\ .appName("TestHiveApp")\ .getOrCreate() @classmethod def tearDownClass(cls): # 测试结束后关闭SparkSession cls.spark.stop() def test_get_hive_data(self): # 用一个简单的测试查询验证功能 test_query = "SELECT 1 AS test_id" result_df = get_hive_data(self.spark, test_query) # 断言结果符合预期 self.assertEqual(result_df.count(), 1) self.assertEqual(result_df.collect()[0]["test_id"], 1) if __name__ == "__main__": unittest.main()
同时建议你重构下get_data.py,把HiveWarehouseSession的初始化逻辑封装起来,避免全局变量的问题(你原来的代码里get_hive_data直接用全局的hive变量,其实在非main模块导入时会报错):
from pyspark.sql import SparkSession def get_hive_data(spark, query): # 延迟导入pyspark_llap,只有调用函数时才加载 from pyspark_llap.sql.session import HiveWarehouseSession hive = HiveWarehouseSession.session(spark).build() return hive.executeQuery(query) if __name__ == "__main__": spark = SparkSession\ .builder\ .appName("HiveApp")\ .getOrCreate() # 这里传入实际的查询语句 data = get_hive_data(spark, "SELECT * FROM your_target_table") data.show()
方案2:手动添加pyspark_llap路径到测试脚本
如果你一定要用python test.py直接运行,需要先找到pyspark_llap模块所在的位置(通常在Spark安装目录的python/lib下,比如Cloudera环境可能是/opt/cloudera/parcels/SPARK2/lib/spark2/python/lib/pyspark_llap.zip),然后在测试脚本开头手动添加这个路径:
import sys # 替换成你边缘节点上pyspark_llap的实际路径 sys.path.append("/opt/cloudera/parcels/SPARK2/lib/spark2/python/lib/pyspark_llap.zip") import unittest from pyspark.sql import SparkSession from get_data import *
不过这种方案不推荐,因为路径可能会随Spark版本/部署方式变化,维护成本高,而且测试环境和生产运行环境还是有差异。
为什么主脚本能运行?
当你运行主脚本时(比如spark-submit get_data.py),Spark会自动把自身依赖的Python模块(包括pyspark_llap)添加到Python的sys.path中,所以能正常导入;但直接用python命令运行时,是纯本地Python环境,没有加载这些Spark专属的依赖路径,因此会抛出ImportError。
内容的提问来源于stack exchange,提问作者Chaitanya Patil

