diff --git a/rendercv/__main__.py b/rendercv/__main__.py index 9a54de2..a55fbdf 100644 --- a/rendercv/__main__.py +++ b/rendercv/__main__.py @@ -1,5 +1,6 @@ import os import logging +import re from typing import Annotated from .rendering import read_input_file, render_template, run_latex @@ -18,7 +19,68 @@ app = typer.Typer( ) +def user_friendly_errors(func): + """Function decorator to make Pydantic's error messages more user-friendly. + + Args: + func (function): Function to decorate + Returns: + function: Decorated function + """ + + def wrapper(*args, **kwargs): + try: + func(*args, **kwargs) + except Exception as e: + # Modify Pydantic's error message to make it more user-friendly + error_message = e.__repr__() + error_messages = error_message.split("\n") + for error_line in error_messages.copy(): + new_error_line = None + + if "RenderCVDataModel" in error_line: + new_error_line = error_line.replace(" for RenderCVDataModel", "!") + + 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)}" + + # Add a period at the end of the sentence if there is none + if not (new_error_line[-1] == "." or new_error_line[-1] == "!"): + new_error_line = new_error_line + "." + + # If the input value is not a dictionary, add it to the error + # message + 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: + error_messages[error_messages.index(error_line)] = new_error_line + + error_message = "\n ".join(error_messages) + + # Print the error message + logger.critical(error_message) + + # Abort the program + logger.info("Aborting RenderCV.") + typer.Abort() + + return wrapper + + @app.command(help="Render a YAML input file") +@user_friendly_errors def render( input_file: Annotated[ str, @@ -30,40 +92,33 @@ def render( Args: input_file (str): Name of the YAML input file """ - try: - file_path = os.path.abspath(input_file) - data = read_input_file(file_path) - output_latex_file = render_template(data) - run_latex(output_latex_file) - except Exception as e: - logger.critical(e) - typer.Abort() + file_path = os.path.abspath(input_file) + data = read_input_file(file_path) + output_latex_file = render_template(data) + run_latex(output_latex_file) @app.command(help="Generate a YAML input file to get started") +@user_friendly_errors def new(name: Annotated[str, typer.Argument(help="Full name")]): """Generate a YAML input file to get started. Args: name (str): Full name """ - try: - environment = Environment( - loader=PackageLoader("rendercv", os.path.join("templates")), - ) - environment.variable_start_string = "<<" - environment.variable_end_string = ">>" + environment = Environment( + loader=PackageLoader("rendercv", os.path.join("templates")), + ) + environment.variable_start_string = "<<" + environment.variable_end_string = ">>" - template = environment.get_template("new_input.yaml.j2") - new_input_file = template.render(name=name) + template = environment.get_template("new_input.yaml.j2") + new_input_file = template.render(name=name) - name = name.replace(" ", "_") - file_name = f"{name}_CV.yaml" - with open(file_name, "w", encoding="utf-8") as file: - file.write(new_input_file) - except Exception as e: - logger.critical(e) - typer.Abort() + name = name.replace(" ", "_") + file_name = f"{name}_CV.yaml" + with open(file_name, "w", encoding="utf-8") as file: + file.write(new_input_file) def cli(): @@ -73,5 +128,6 @@ def cli(): """ app() + if __name__ == "__main__": - cli() \ No newline at end of file + cli()