blob: 69a72f86b9fe0c74339f047f6cb83fd9369ac05f [file] [log] [blame]
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import re
from typing import Dict
from pyspark.errors.error_classes import ERROR_CLASSES_MAP
class ErrorClassesReader:
"""
A reader to load error information from error_classes.py.
"""
def __init__(self) -> None:
self.error_info_map = ERROR_CLASSES_MAP
def get_error_message(self, error_class: str, message_parameters: Dict[str, str]) -> str:
"""
Returns the completed error message by applying message parameters to the message template.
"""
message_template = self.get_message_template(error_class)
# Verify message parameters.
message_parameters_from_template = re.findall("<([a-zA-Z0-9_-]+)>", message_template)
assert set(message_parameters_from_template) == set(message_parameters), (
f"Undifined error message parameter for error class: {error_class}. "
f"Parameters: {message_parameters}"
)
table = str.maketrans("<>", "{}")
return message_template.translate(table).format(**message_parameters)
def get_message_template(self, error_class: str) -> str:
"""
Returns the message template for corresponding error class from error_classes.py.
For example,
when given `error_class` is "EXAMPLE_ERROR_CLASS",
and corresponding error class in error_classes.py looks like the below:
.. code-block:: python
"EXAMPLE_ERROR_CLASS" : {
"message" : [
"Problem <A> because of <B>."
]
}
In this case, this function returns:
"Problem <A> because of <B>."
For sub error class, when given `error_class` is "EXAMPLE_ERROR_CLASS.SUB_ERROR_CLASS",
and corresponding error class in error_classes.py looks like the below:
.. code-block:: python
"EXAMPLE_ERROR_CLASS" : {
"message" : [
"Problem <A> because of <B>."
],
"subClass" : {
"SUB_ERROR_CLASS" : {
"message" : [
"Do <C> to fix the problem."
]
}
}
}
In this case, this function returns:
"Problem <A> because <B>. Do <C> to fix the problem."
"""
error_classes = error_class.split(".")
len_error_classes = len(error_classes)
assert len_error_classes in (1, 2)
# Generate message template for main error class.
main_error_class = error_classes[0]
if main_error_class in self.error_info_map:
main_error_class_info_map = self.error_info_map[main_error_class]
else:
raise ValueError(f"Cannot find main error class '{main_error_class}'")
main_message_template = "\n".join(main_error_class_info_map["message"])
has_sub_class = len_error_classes == 2
if not has_sub_class:
message_template = main_message_template
else:
# Generate message template for sub error class if exists.
sub_error_class = error_classes[1]
main_error_class_subclass_info_map = main_error_class_info_map["subClass"]
if sub_error_class in main_error_class_subclass_info_map:
sub_error_class_info_map = main_error_class_subclass_info_map[sub_error_class]
else:
raise ValueError(f"Cannot find sub error class '{sub_error_class}'")
sub_message_template = "\n".join(sub_error_class_info_map["message"])
message_template = main_message_template + " " + sub_message_template
return message_template