This is an automated email from the ASF dual-hosted git repository.

gaogaotiantian pushed a commit to branch branch-4.x
in repository https://gitbox.apache.org/repos/asf/spark.git


The following commit(s) were added to refs/heads/branch-4.x by this push:
     new b4a692959b4a [SPARK-60149][PYTHON][TEST] Add tests for 
ErrorClassesReader lookup errors and breaking_change_info fallback
b4a692959b4a is described below

commit b4a692959b4a553cf136837c4bd7d55591ebbc1f
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]>
    (cherry picked from commit f784da87e6db27672ab778c79d1a76420d7c3b83)
    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]

Reply via email to