diff --git a/rendercv/cli.py b/rendercv/cli.py index f27e644..d8aba73 100644 --- a/rendercv/cli.py +++ b/rendercv/cli.py @@ -54,7 +54,7 @@ def welcome(): print(table) -def warning(text): +def warning(text: str): """Print a warning message to the terminal. Args: @@ -63,7 +63,7 @@ def warning(text): print(f"[bold yellow]{text}") -def error(text, exception=None): +def error(text: str, exception: Optional[Exception] = None): """Print an error message to the terminal. Args: @@ -80,7 +80,7 @@ def error(text, exception=None): print(f"\n[bold red]{text}\n") -def information(text): +def information(text: str): """Print an information message to the terminal. Args: diff --git a/rendercv/data_models.py b/rendercv/data_models.py index 5cc2975..38946da 100644 --- a/rendercv/data_models.py +++ b/rendercv/data_models.py @@ -53,7 +53,7 @@ def get_date_object(date: str | int) -> Date: data models. Args: - date_string (str): The date string to parse. + date (str): The date string to parse. Returns: datetime.date: The parsed date. """ @@ -528,7 +528,7 @@ class PublicationEntry(RenderCVBaseModel): """Check if the DOI exists in the DOI System.""" # see https://stackoverflow.com/a/60671292/18840665 for the explanation of the # next line: - ssl._create_default_https_context = ssl._create_unverified_context + ssl._create_default_https_context = ssl._create_unverified_context # type: ignore doi_url = f"http://doi.org/{doi}" @@ -550,7 +550,8 @@ class PublicationEntry(RenderCVBaseModel): """Return the date string of the publication.""" if isinstance(self.date, int): date_string = str(self.date) - elif isinstance(self.date, str): + else: + # Then it is a string date_object = get_date_object(self.date) date_string = format_date(date_object) @@ -667,13 +668,9 @@ def get_entry_and_section_type( """Determine the entry and section type based on the entry. Args: - entry (dict[str, Any] | EducationEntry | ExperienceEntry | PublicationEntry | - NormalEntry | OneLineEntry | str): The entry to determine the type. + entry (dict[str, Any] | EducationEntry | ExperienceEntry | PublicationEntry | NormalEntry | OneLineEntry | str): The entry to determine the type. Returns: - tuple[str, Type[SectionWithTextEntries | SectionWithOneLineEntries | - SectionWithExperienceEntries | SectionWithEducationEntries | - SectionWithPublicationEntries | SectionWithNormalEntries]]: The entry type and the - section type. + tuple[str, Type[SectionWithTextEntries | SectionWithOneLineEntries | SectionWithExperienceEntries | SectionWithEducationEntries | SectionWithPublicationEntries | SectionWithNormalEntries]]: The entry type and the section type. """ if isinstance(entry, dict): if "details" in entry: @@ -709,7 +706,7 @@ def get_entry_and_section_type( elif isinstance(entry, PublicationEntry): entry_type = "PublicationEntry" section_type = SectionWithPublicationEntries - elif isinstance(entry, NormalEntry): + elif isinstance(entry, NormalEntry): # type: ignore entry_type = "NormalEntry" section_type = SectionWithNormalEntries else: @@ -892,7 +889,7 @@ class CurriculumVitae(RenderCVBaseModel): title="Social Networks", description="The social networks of the person.", ) - sections_input: dict[str, SectionInput] = pydantic.Field( + sections_input: Optional[dict[str, SectionInput]] = pydantic.Field( default=None, title="Sections", description="The sections of the CV.", @@ -902,27 +899,21 @@ class CurriculumVitae(RenderCVBaseModel): @functools.cached_property def sections(self) -> list[Section]: """Return all the sections of the CV with their titles.""" - sections = [] + sections: list[Section] = [] if self.sections_input is not None: for title, section_or_entries in self.sections_input.items(): title = title.replace("_", " ").title() - if isinstance(section_or_entries, list): - entry_type, section_type = get_entry_and_section_type( - section_or_entries[0] - ) - section = section_type( - title=title, - entry_type=entry_type, # type: ignore - entries=section_or_entries, # type: ignore - ) - sections.append(section) + entry_type, section_type = get_entry_and_section_type( + section_or_entries[0] + ) - else: - raise RuntimeError( - "This error shouldn't have been raised. Please open an" - " issue on GitHub." - ) + section = section_type( + title=title, + entry_type=entry_type, # type: ignore + entries=section_or_entries, # type: ignore + ) + sections.append(section) return sections @@ -981,6 +972,12 @@ class RenderCVDataModel(RenderCVBaseModel): return rendercv_design_validator.validate_python(design) else: theme_name: str = design["theme"] # type: ignore + if not isinstance(theme_name, str): + raise RuntimeError( + "This error shouldn't have been raised. Please open an issue on" + " GitHub." + ) + # check if the theme name is valid: if not theme_name.isalpha(): raise ValueError( @@ -1093,7 +1090,7 @@ def read_input_file( ) file_content = file_path.read_text(encoding="utf-8") - input_as_dictionary: dict[str, Any] = ruamel.yaml.YAML().load(file_content) + input_as_dictionary: dict[str, Any] = ruamel.yaml.YAML().load(file_content) # type: ignore # validate the parsed dictionary by creating an instance of RenderCVDataModel: rendercv_data_model = RenderCVDataModel(**input_as_dictionary) @@ -1224,7 +1221,7 @@ def get_a_sample_data_model(name: str = "John Doe") -> RenderCVDataModel: SocialNetwork(network="LinkedIn", username="yourusername"), SocialNetwork(network="GitHub", username="yourusername"), ], - sections=sections, + sections=sections, # type: ignore ) design = ClassicThemeOptions(theme="classic", show_timespan_in=["Experience"]) @@ -1232,7 +1229,7 @@ def get_a_sample_data_model(name: str = "John Doe") -> RenderCVDataModel: return RenderCVDataModel(cv=cv, design=design) -def generate_json_schema() -> dict: +def generate_json_schema() -> dict[str, Any]: """Generate the JSON schema of RenderCV. JSON schema is generated for the users to make it easier for them to write the input @@ -1245,7 +1242,7 @@ def generate_json_schema() -> dict: """ class RenderCVSchemaGenerator(pydantic.json_schema.GenerateJsonSchema): - def generate(self, schema, mode="validation"): + def generate(self, schema, mode="validation"): # type: ignore json_schema = super().generate(schema, mode=mode) # Basic information about the schema: @@ -1258,7 +1255,7 @@ def generate_json_schema() -> dict: # Loop through $defs and remove docstring descriptions and fix optional # fields - for key, value in json_schema["$defs"].items(): + for _, value in json_schema["$defs"].items(): # Don't allow additional properties value["additionalProperties"] = False diff --git a/rendercv/renderer.py b/rendercv/renderer.py index b47b5f4..3845987 100644 --- a/rendercv/renderer.py +++ b/rendercv/renderer.py @@ -46,7 +46,7 @@ class TemplatedFile: def template( self, - theme_name, + theme_name: str, template_name: Literal[ "EducationEntry", "ExperienceEntry", @@ -75,14 +75,7 @@ class TemplatedFile: Args: template_name (str): The name of the template file. - entry (Optional[ - dm.EducationEntry, - dm.ExperienceEntry, - dm.NormalEntry, - dm.PublicationEntry, - dm.OneLineEntry, - str - ]): The data model of the entry. + entry (Optional[dm.EducationEntry, dm.ExperienceEntry, dm.NormalEntry,dm.PublicationEntry, dm.OneLineEntry, str]): The data model of the entry. section_title (Optional[str]): The title of the section. is_first_entry (Optional[bool]): Whether the entry is the first one in the section. @@ -144,7 +137,7 @@ class LaTeXFile(TemplatedFile): ) super().__init__(data_model, environment) - def render_templates(self): + def render_templates(self) -> tuple[str, str, list[tuple[str, list[str], str]]]: """Render and return all the templates for the $\\LaTeX$ file. Returns: @@ -154,12 +147,12 @@ class LaTeXFile(TemplatedFile): # Template the preamble, header, and sections: preamble = self.template("Preamble") header = self.template("Header") - sections = [] + sections: list[tuple[str, list[str], str]] = [] for section in self.cv.sections: section_beginning = self.template( "SectionBeginning", section_title=section.title ) - entries = [] + entries: list[str] = [] for i, entry in enumerate(section.entries): if i == 0: is_first_entry = True @@ -203,7 +196,17 @@ class LaTeXFile(TemplatedFile): section_title: Optional[str] = None, is_first_entry: Optional[bool] = None, ) -> str: - """Template one of the files in the `themes` directory.""" + """Template one of the files in the `themes` directory. + + Args: + template_name (str): The name of the template file. + entry (Optional[dm.EducationEntry, dm.ExperienceEntry, dm.NormalEntry,dm.PublicationEntry, dm.OneLineEntry, str]): The data model of the entry. + section_title (Optional[str]): The title of the section. + is_first_entry (Optional[bool]): Whether the entry is the first one in the section. + + Returns: + str: The templated file. + """ result = super().template( self.design.theme, template_name, @@ -215,7 +218,11 @@ class LaTeXFile(TemplatedFile): return result def get_latex_code(self): - """Get the $\\LaTeX$ code of the file.""" + """Get the $\\LaTeX$ code of the file. + + Returns: + str: The $\\LaTeX$ code. + """ preamble, header, sections = self.render_templates() return self.get_full_code( "main.j2.tex", @@ -234,18 +241,13 @@ class MarkdownFile(TemplatedFile): data model and Jinja2 templates. It inherits from the TemplatedFile class. Markdown files are generated to produce a PDF which can be copy-pasted to [Grammarly](https://app.grammarly.com/) for proofreading. - - Args: - data_model (dm.RenderCVDataModel): The data model. - environment (jinja2.Environment): The Jinja2 environment. """ def render_templates(self): """Render and return all the templates for the Markdown file. Returns: - Tuple[str, List[Tuple[str, List[str]]]: The header and sections of the - Markdown file. + Tuple[str, List[Tuple[str, List[str]]]]: The header and sections of the Markdown file. """ # Template the header and sections: header = self.template("Header") @@ -297,7 +299,17 @@ class MarkdownFile(TemplatedFile): section_title: Optional[str] = None, is_first_entry: Optional[bool] = None, ) -> str: - """Template one of the files in the `themes` directory.""" + """Template one of the files in the `themes` directory. + + Args: + template_name (str): The name of the template file. + entry (Optional[dm.EducationEntry, dm.ExperienceEntry, dm.NormalEntry,dm.PublicationEntry, dm.OneLineEntry, str]): The data model of the entry. + section_title (Optional[str]): The title of the section. + is_first_entry (Optional[bool]): Whether the entry is the first one in the section. + + Returns: + str: The templated file. + """ result = super().template( "markdown", template_name, @@ -729,7 +741,7 @@ def divide_length_by(length: str, divider: float) -> str: def get_an_item_with_a_specific_attribute_value( - items: list[Any], attribute: str, value: Any + items: Optional[list[Any]], attribute: str, value: Any ) -> Any: """Get an item from a list of items with a specific attribute value.