Pytest套件中SparkSession无法从仓库拉取依赖包问题排查
spark.jars.packages的实例,依赖未拉取问题 我有一个Pytest测试套件,大部分测试不需要额外Maven依赖,但有2个测试需要通过spark.jars.packages拉取AWS S3相关的JAR包。我的思路是:
- 在这两个测试的
tearDown()中调用spark.stop()停止默认的SparkSession - 在
setUp()中创建带spark.jars.packages和spark.jars.repositories配置的新SparkSession
但实际运行时,新创建的SparkSession并未拉取依赖包:只有先运行这些需要特殊配置的测试才能通过;如果后运行,会抛出ClassNotFoundException,提示找不到S3相关类。我已经确认:
- 创建第二个SparkSession前,没有现存的SparkSession/SparkContext
- 第二个SparkSession的配置中已正确包含
spark.jars.packages
问题原因
spark.jars.packages是SparkContext初始化阶段的专属配置,只有在Spark关联的JVM进程首次启动、SparkContext第一次初始化时,才会触发Maven依赖的解析和下载流程。
当你调用spark.stop()时,只是停止了当前的SparkContext,但PySpark底层的Py4J JVM进程并不会退出。后续重新创建SparkSession时,虽然会生成新的SparkContext,但JVM已经处于运行状态,Spark不会重新执行spark.jars.packages的依赖拉取逻辑——因为类加载器和依赖解析逻辑仅在JVM启动时的SparkContext初始化阶段运行一次。
这就解释了为什么先运行带特殊配置的测试能正常工作:此时JVM首次启动就加载了所需依赖;而如果先运行默认配置的测试,JVM启动时没有加载该依赖,后续即使创建新的SparkContext也无法补充拉取。
解决方案
针对Pytest场景,推荐以下几种可行方案:
1. 使用pytest-forked插件隔离进程
每个需要特殊依赖的测试用单独的Python进程运行,这样每个进程的JVM都是全新启动的,首次创建SparkSession时就能正常解析并拉取spark.jars.packages配置的依赖。
安装插件:
pip install pytest-forked
在测试函数上标记@pytest.mark.forked,或者通过命令行参数运行:
pytest --forked
2. 提前下载依赖并添加到ClassPath
手动下载所需的Maven依赖包(包括依赖链中的所有JAR),然后在Spark配置中通过spark.driver.extraClassPath指定这些JAR的路径,这样无论何时创建SparkSession,都能直接加载这些类,无需动态拉取。
示例配置:
def spark_session_s3_config(): spark_builder = ( SparkSession.builder .config("spark.driver.extraClassPath", "/path/to/spark-hadoop-cloud-3.3.0.jar:/path/to/other-dependency.jar") ) return spark_builder.getOrCreate()
3. 统一初始化所有需要的依赖(不推荐但简单)
如果测试套件的资源允许,可以在所有测试开始前,直接创建包含所有依赖的SparkSession,后续所有测试复用这个实例。这种方式无需处理进程隔离,但会增加初始化时间和资源占用。
示例代码验证
你的示例代码中,注释掉spark = spark_session_default()和spark.stop()后能正常运行,本质是因为首次创建的SparkSession就包含了spark.jars.packages配置,JVM启动时拉取了依赖;而如果先启动默认Session再停止,JVM未重启,后续的Session无法触发依赖拉取,导致ClassNotFoundException。
内容的提问来源于stack exchange,提问作者mondal.alex

