improve error messages

This commit is contained in:
Sina Atalay 2023-10-27 21:09:52 +02:00
parent 0a9a2926fe
commit bb4c3c94f8
1 changed files with 54 additions and 48 deletions

View File

@ -8,6 +8,8 @@ from .rendering import read_input_file, render_template, run_latex
import typer import typer
from jinja2 import Environment, PackageLoader from jinja2 import Environment, PackageLoader
from pydantic import ValidationError
from pydantic_core import ErrorDetails
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -21,71 +23,74 @@ app = typer.Typer(
def user_friendly_errors(func: Callable) -> Callable: def user_friendly_errors(func: Callable) -> Callable:
"""Function decorator to make Pydantic's error messages more user-friendly. """Function decorator to make RenderCV's error messages more user-friendly.
Args: Args:
func (Callable): Function to decorate func (Callable): Function to decorate
Returns: Returns:
Callable: Decorated function Callable: Decorated function
""" """
@wraps(func) @wraps(func)
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
try: try:
func(*args, **kwargs) func(*args, **kwargs)
except Exception as e: except ValidationError as e:
# Modify Pydantic's error message to make it more user-friendly # It is a Pydantic error
error_message = e.__repr__() error_messages = []
error_messages = error_message.split("\n") error_messages.append("There are validation errors!")
for error_line in error_messages.copy():
new_error_line = None
if "function-after" in error_line: # Translate Pydantic's error messages to make them more user-friendly
# Remove this line and the next one custom_error_messages_by_type = {
next_error_line = error_messages[error_messages.index(error_line) + 1] "url_scheme": "This is not a valid URL 😿",
error_messages.remove(error_line) }
error_messages.remove(next_error_line) custom_error_messages_by_msg = {
"value is not a valid phone number": (
if "validation" in error_line: "This is not a valid phone number 👺"
new_error_line = "There are validation errors!"
if "For further information" in error_line:
# Remove further information line
error_messages.remove(error_line)
# Modify Pydantic's error message:
match = re.match(
r"(.*) \[type=\w+, input_value=(.*), input_type=\w+\]",
error_line,
) )
if match: }
new_error_line = f"{match.group(1)}" new_errors: list[ErrorDetails] = []
for error in e.errors():
# Modify Pydantic's error message to make it more user-friendly
# Add a period at the end of the sentence if there is none # Remove url:
if not (new_error_line[-1] == "." or new_error_line[-1] == "!"): error["url"] = None
new_error_line = new_error_line + "."
# If the input value is not a dictionary, add it to the error # Make sure the entries of loc are strings
# message error["loc"] = [str(loc) for loc in error["loc"]]
if "{" not in match.group(2):
new_error_line = (
new_error_line + f" The input was {match.group(2)}!"
)
# If the error line was modified, replace it
if new_error_line is not None:
try:
error_messages[error_messages.index(error_line)] = new_error_line
except ValueError:
# This error line was already removed
pass
error_message = "\n ".join(error_messages) # Assign a custom error message if there is one
custom_message = None
if error["type"] in custom_error_messages_by_type:
custom_message = custom_error_messages_by_type[error["type"]]
elif error["msg"] in custom_error_messages_by_msg:
custom_message = custom_error_messages_by_msg[error["msg"]]
# Print the error message if custom_message:
logger.critical(error_message) ctx = error.get("ctx")
error["msg"] = (
custom_message.format(**ctx) if ctx else custom_message
)
# Abort the program # If the input value is a dictionary or if the input value is in the
logger.info("Aborting RenderCV.") # error message, remove it
typer.Abort() if isinstance(error["input"], dict) or error["input"] in error["msg"]:
error["input"] = None
new_errors.append(error)
# Create a custom error message for RenderCV users
for error in new_errors:
location = ".".join(error["loc"])
error_messages.append(f"{location}:\n {error['msg']}")
if error["input"]:
error_messages[-1] += f"\n Your input was \"{error['input']}\""
error_message = "\n\n ".join(error_messages)
logger.error(error_message)
except Exception as e:
# It is not a Pydantic error
logging.error(e)
return wrapper return wrapper
@ -133,6 +138,7 @@ def new(name: Annotated[str, typer.Argument(help="Full name")]):
logger.info(f"New input file created: {file_name}") logger.info(f"New input file created: {file_name}")
def cli(): def cli():
"""Start the CLI application. """Start the CLI application.