This is an automated email from the ASF dual-hosted git repository.
gaogaotiantian pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/spark.git
The following commit(s) were added to refs/heads/master by this push:
new f784da87e6db [SPARK-60149][PYTHON][TEST] Add tests for
ErrorClassesReader lookup errors and breaking_change_info fallback
f784da87e6db is described below
commit f784da87e6db27672ab778c79d1a76420d7c3b83
Author: Vito Lin <[email protected]>
AuthorDate: Mon Oct 26 17:19:05 2026 -0700
[SPARK-60149][PYTHON][TEST] Add tests for ErrorClassesReader lookup errors
and breaking_change_info fallback
### What changes were proposed in this pull request?
This PR adds tests to `python/pyspark/errors/tests/test_errors.py` for code
paths in `ErrorClassesReader` (`python/pyspark/errors/utils.py`) that were not
covered:
- `get_message_template`: raising `ValueError` when the main class exists
but the sub-class does not
- `get_breaking_change_info`:
- returning `None` for a `None` error class
- raising `ValueError` for an unknown main class or sub-class
- falling back to the main class's `breaking_change_info` when the
sub-class has none
- returning `None` when neither the main class nor the sub-class declares
it
- `get_sqlstate`: returning `None` for a `None` error class
### Why are the changes needed?
To improve test coverage of `pyspark.errors.utils.ErrorClassesReader`.
These error-handling and fallback branches had no tests, so regressions in them
would not be caught.
### Does this PR introduce _any_ user-facing change?
No. Test-only change.
### How was this patch tested?
Added unit tests in `python/pyspark/errors/tests/test_errors.py`.
Ran the test module with coverage before and after the change:
```
python/run-tests-with-coverage --python-executables=python3 --testnames
pyspark.errors.tests.test_errors
```
Coverage of `pyspark/errors/utils.py`:
| | Stmts | Miss | BrPart | Cover |
|---|---|---|---|---|
| Before | 152 | 72 | 7 | 49% |
| After | 152 | 66 | 0 | 55% |
All tests pass. `dev/reformat-python` and `dev/lint-python` pass for the
changed file.
### Was this patch authored or co-authored using generative AI tooling?
Generated-by: Co-authored by Claude. Author reviewed every line, ran the
tests, and verified the coverage results locally.
Closes #59352 from vito-lin-dev/SPARK-60149.
Authored-by: Vito Lin <[email protected]>
Signed-off-by: Tian Gao <[email protected]>
---
python/pyspark/errors/tests/test_errors.py | 36 +++++++++++++++++++++++++++++-
1 file changed, 35 insertions(+), 1 deletion(-)
diff --git a/python/pyspark/errors/tests/test_errors.py
b/python/pyspark/errors/tests/test_errors.py
index 3e8b17577806..9fe0257a3f4b 100644
--- a/python/pyspark/errors/tests/test_errors.py
+++ b/python/pyspark/errors/tests/test_errors.py
@@ -136,6 +136,9 @@ class ErrorsTest(unittest.TestCase):
"mitigation_config": {"key": "config.key1", "value":
"config.value1"},
"needsAudit": False,
},
+ "sub_class": {
+ "SUBCLASS_WITHOUT_INFO": {"message": ["Subclass message
without info."]},
+ },
},
"TEST_ERROR_WITH_SUB_CLASS": {
"message": ["Error message 2 with <param>."],
@@ -150,7 +153,8 @@ class ErrorsTest(unittest.TestCase):
},
"needsAudit": True,
},
- }
+ },
+ "SUBCLASS_WITHOUT_INFO": {"message": ["Subclass message
without info."]},
},
},
}
@@ -179,6 +183,35 @@ class ErrorsTest(unittest.TestCase):
subclass_map =
error_reader.error_info_map["TEST_ERROR_WITH_SUB_CLASS"]["sub_class"]
self.assertEqual(breaking_change_info2,
subclass_map["SUBCLASS"]["breaking_change_info"])
+ # A sub-class without breaking_change_info falls back to the main
class's one.
+ self.assertEqual(
+
error_reader.get_breaking_change_info("TEST_ERROR.SUBCLASS_WITHOUT_INFO"),
+ breaking_change_info1,
+ )
+ # None when neither the sub-class nor the main class declares it, or
no error class.
+
self.assertIsNone(error_reader.get_breaking_change_info("TEST_ERROR_WITH_SUB_CLASS"))
+ self.assertIsNone(
+
error_reader.get_breaking_change_info("TEST_ERROR_WITH_SUB_CLASS.SUBCLASS_WITHOUT_INFO")
+ )
+ self.assertIsNone(error_reader.get_breaking_change_info(None))
+ # Unknown main or sub error classes raise.
+ with self.assertRaisesRegex(ValueError, "Cannot find main error class
'NON_EXISTENT'"):
+ error_reader.get_breaking_change_info("NON_EXISTENT")
+ with self.assertRaisesRegex(ValueError, "Cannot find sub error class
'NON_EXISTENT_SUB'"):
+
error_reader.get_breaking_change_info("TEST_ERROR_WITH_SUB_CLASS.NON_EXISTENT_SUB")
+
+ def test_message_template_sub_class_not_found(self):
+ # get_message_template raises when the main class exists but the
sub-class does not.
+ error_reader = ErrorClassesReader()
+ error_reader.error_info_map = {
+ "TEST_ERROR": {
+ "message": ["Error message."],
+ "sub_class": {"SUBCLASS": {"message": ["Subclass message."]}},
+ },
+ }
+ with self.assertRaisesRegex(ValueError, "Cannot find sub error class
'NON_EXISTENT_SUB'"):
+ error_reader.get_message_template("TEST_ERROR.NON_EXISTENT_SUB")
+
def test_sqlstate(self):
error = PySparkRuntimeError(errorClass="APPLICATION_NAME_NOT_SET",
messageParameters={})
self.assertIsNone(error.getSqlState())
@@ -211,6 +244,7 @@ class ErrorsTest(unittest.TestCase):
self.assertEqual(error_reader.get_sqlstate("TEST_ERROR.SUBCLASS"),
"42604")
self.assertEqual(error_reader.get_sqlstate("TEST_ERROR.NON_EXISTENT_SUB"),
"42604")
self.assertIsNone(error_reader.get_sqlstate("NON_EXISTENT_ERROR.SUBCLASS"))
+ self.assertIsNone(error_reader.get_sqlstate(None))
if __name__ == "__main__":
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]