mirror of https://github.com/eyhc1/rendercv.git
python 3.9 support
This commit is contained in:
parent
503c7133b7
commit
ed5b0d48a4
|
@ -175,10 +175,11 @@ cython_debug/
|
||||||
.vscode/
|
.vscode/
|
||||||
|
|
||||||
# Personal CVs
|
# Personal CVs
|
||||||
*_CV.yaml
|
*CV.yaml
|
||||||
*_cv.py
|
*cv.py
|
||||||
*_CV.tex
|
*CV.tex
|
||||||
rendercv_output/
|
rendercv_output/
|
||||||
|
results/
|
||||||
|
|
||||||
# Include reference files
|
# Include reference files
|
||||||
!/tests/testdata/**/*.pdf
|
!/tests/testdata/**/*.pdf
|
||||||
|
|
|
@ -23,7 +23,7 @@ import pathlib
|
||||||
import re
|
import re
|
||||||
import warnings
|
import warnings
|
||||||
from datetime import date as Date
|
from datetime import date as Date
|
||||||
from typing import Annotated, Any, Literal, Optional, Type, get_args
|
from typing import Annotated, Any, Literal, Optional, Type, get_args, Union, Dict
|
||||||
|
|
||||||
import annotated_types as at
|
import annotated_types as at
|
||||||
import pydantic
|
import pydantic
|
||||||
|
@ -43,7 +43,7 @@ warnings.filterwarnings("ignore")
|
||||||
locale_catalog = {}
|
locale_catalog = {}
|
||||||
|
|
||||||
|
|
||||||
def get_date_object(date: str | int) -> Date:
|
def get_date_object(date: Union[str, int]) -> Date:
|
||||||
"""Parse a date string in YYYY-MM-DD, YYYY-MM, or YYYY format and return a
|
"""Parse a date string in YYYY-MM-DD, YYYY-MM, or YYYY format and return a
|
||||||
`datetime.date` object. This function is used throughout the validation process of
|
`datetime.date` object. This function is used throughout the validation process of
|
||||||
the data models.
|
the data models.
|
||||||
|
@ -163,7 +163,7 @@ class BulletEntry(RenderCVBaseModel):
|
||||||
|
|
||||||
|
|
||||||
class EntryWithDate(RenderCVBaseModel):
|
class EntryWithDate(RenderCVBaseModel):
|
||||||
date: Optional[int | str] = pydantic.Field(
|
date: Optional[Union[int, str]] = pydantic.Field(
|
||||||
default=None,
|
default=None,
|
||||||
title="Date",
|
title="Date",
|
||||||
description=(
|
description=(
|
||||||
|
@ -176,8 +176,8 @@ class EntryWithDate(RenderCVBaseModel):
|
||||||
@pydantic.field_validator("date", mode="before")
|
@pydantic.field_validator("date", mode="before")
|
||||||
@classmethod
|
@classmethod
|
||||||
def check_date(
|
def check_date(
|
||||||
cls, date: Optional[int | RenderCVDate | str]
|
cls, date: Optional[Union[int, RenderCVDate, str]]
|
||||||
) -> Optional[int | RenderCVDate | str]:
|
) -> Optional[Union[int, RenderCVDate, str]]:
|
||||||
"""Check if `date` is provided correctly."""
|
"""Check if `date` is provided correctly."""
|
||||||
date_is_provided = date is not None
|
date_is_provided = date is not None
|
||||||
|
|
||||||
|
@ -294,7 +294,7 @@ class EntryBase(EntryWithDate):
|
||||||
description="The location of the event.",
|
description="The location of the event.",
|
||||||
examples=["Istanbul, Türkiye"],
|
examples=["Istanbul, Türkiye"],
|
||||||
)
|
)
|
||||||
start_date: Optional[int | RenderCVDate] = pydantic.Field(
|
start_date: Optional[Union[int, RenderCVDate]] = pydantic.Field(
|
||||||
default=None,
|
default=None,
|
||||||
title="Start Date",
|
title="Start Date",
|
||||||
description=(
|
description=(
|
||||||
|
@ -302,7 +302,7 @@ class EntryBase(EntryWithDate):
|
||||||
),
|
),
|
||||||
examples=["2020-09-24"],
|
examples=["2020-09-24"],
|
||||||
)
|
)
|
||||||
end_date: Optional[Literal["present"] | int | RenderCVDate] = pydantic.Field(
|
end_date: Optional[Union[Literal["present"], int, RenderCVDate]] = pydantic.Field(
|
||||||
default=None,
|
default=None,
|
||||||
title="End Date",
|
title="End Date",
|
||||||
description=(
|
description=(
|
||||||
|
@ -323,8 +323,8 @@ class EntryBase(EntryWithDate):
|
||||||
@classmethod
|
@classmethod
|
||||||
def check_and_parse_dates(
|
def check_and_parse_dates(
|
||||||
cls,
|
cls,
|
||||||
date: Optional[Literal["present"] | int | RenderCVDate],
|
date: Optional[Union[Literal["present"], int, RenderCVDate]],
|
||||||
) -> Optional[Literal["present"] | int | RenderCVDate]:
|
) -> Optional[Union[Literal["present"], int, RenderCVDate]]:
|
||||||
date_is_provided = date is not None
|
date_is_provided = date is not None
|
||||||
|
|
||||||
if date_is_provided:
|
if date_is_provided:
|
||||||
|
@ -615,23 +615,36 @@ class EducationEntry(EntryBase, EducationEntryBase):
|
||||||
|
|
||||||
|
|
||||||
# Create custom types named Entry and ListOfEntries:
|
# Create custom types named Entry and ListOfEntries:
|
||||||
|
# Entry = (
|
||||||
|
# OneLineEntry
|
||||||
|
# | NormalEntry
|
||||||
|
# | ExperienceEntry
|
||||||
|
# | EducationEntry
|
||||||
|
# | PublicationEntry
|
||||||
|
# | BulletEntry
|
||||||
|
# | str
|
||||||
|
# )
|
||||||
Entry = (
|
Entry = (
|
||||||
|
Union[
|
||||||
OneLineEntry
|
OneLineEntry
|
||||||
| NormalEntry
|
, NormalEntry
|
||||||
| ExperienceEntry
|
, ExperienceEntry
|
||||||
| EducationEntry
|
, EducationEntry
|
||||||
| PublicationEntry
|
, PublicationEntry
|
||||||
| BulletEntry
|
, BulletEntry
|
||||||
| str
|
, str
|
||||||
|
]
|
||||||
)
|
)
|
||||||
ListOfEntries = list[
|
ListOfEntries = list[
|
||||||
|
Union[
|
||||||
OneLineEntry
|
OneLineEntry
|
||||||
| NormalEntry
|
, NormalEntry
|
||||||
| ExperienceEntry
|
, ExperienceEntry
|
||||||
| EducationEntry
|
, EducationEntry
|
||||||
| PublicationEntry
|
, PublicationEntry
|
||||||
| BulletEntry
|
, BulletEntry
|
||||||
| str
|
, str
|
||||||
|
]
|
||||||
]
|
]
|
||||||
entry_types = Entry.__args__[:-1] # a tuple of all the entry types except str
|
entry_types = Entry.__args__[:-1] # a tuple of all the entry types except str
|
||||||
entry_type_names = [entry_type.__name__ for entry_type in entry_types] + ["TextEntry"]
|
entry_type_names = [entry_type.__name__ for entry_type in entry_types] + ["TextEntry"]
|
||||||
|
@ -691,7 +704,7 @@ def create_a_section_model(entry_type: Type[Entry]) -> Type[SectionBase]:
|
||||||
|
|
||||||
|
|
||||||
def get_entry_and_section_type(
|
def get_entry_and_section_type(
|
||||||
entry: dict[str, Any] | Entry,
|
entry: Union[dict[str, Any], Entry],
|
||||||
) -> tuple[
|
) -> tuple[
|
||||||
str,
|
str,
|
||||||
Type[SectionBase],
|
Type[SectionBase],
|
||||||
|
@ -738,8 +751,8 @@ def get_entry_and_section_type(
|
||||||
|
|
||||||
|
|
||||||
def validate_section_input(
|
def validate_section_input(
|
||||||
sections_input: SectionBase | list[Any],
|
sections_input: Union[SectionBase, list[Any]],
|
||||||
) -> SectionBase | list[Any]:
|
) -> Union[SectionBase, list[Any]]:
|
||||||
"""Validate a `SectionInput` object and raise an error if it is not valid.
|
"""Validate a `SectionInput` object and raise an error if it is not valid.
|
||||||
|
|
||||||
Sections input is very complex. It is either a `Section` object or a list of
|
Sections input is very complex. It is either a `Section` object or a list of
|
||||||
|
@ -1166,11 +1179,19 @@ LocaleCatalog() # Initialize the locale catalog with the default values
|
||||||
# It is a union of all the design options and the correct design option is determined by
|
# It is a union of all the design options and the correct design option is determined by
|
||||||
# the theme field, thanks to Pydantic's discriminator feature.
|
# the theme field, thanks to Pydantic's discriminator feature.
|
||||||
# See https://docs.pydantic.dev/2.5/concepts/fields/#discriminator for more information
|
# See https://docs.pydantic.dev/2.5/concepts/fields/#discriminator for more information
|
||||||
|
# RenderCVDesign = Annotated[
|
||||||
|
# ClassicThemeOptions
|
||||||
|
# | ModerncvThemeOptions
|
||||||
|
# | Sb2novThemeOptions
|
||||||
|
# | EngineeringresumesThemeOptions,
|
||||||
|
# pydantic.Field(discriminator="theme"),
|
||||||
|
# ]
|
||||||
RenderCVDesign = Annotated[
|
RenderCVDesign = Annotated[
|
||||||
ClassicThemeOptions
|
Union
|
||||||
| ModerncvThemeOptions
|
[ClassicThemeOptions
|
||||||
| Sb2novThemeOptions
|
, ModerncvThemeOptions
|
||||||
| EngineeringresumesThemeOptions,
|
, Sb2novThemeOptions
|
||||||
|
, EngineeringresumesThemeOptions],
|
||||||
pydantic.Field(discriminator="theme"),
|
pydantic.Field(discriminator="theme"),
|
||||||
]
|
]
|
||||||
rendercv_design_validator = pydantic.TypeAdapter(RenderCVDesign)
|
rendercv_design_validator = pydantic.TypeAdapter(RenderCVDesign)
|
||||||
|
@ -1184,7 +1205,7 @@ class RenderCVDataModel(RenderCVBaseModel):
|
||||||
title="Curriculum Vitae",
|
title="Curriculum Vitae",
|
||||||
description="The data of the CV.",
|
description="The data of the CV.",
|
||||||
)
|
)
|
||||||
design: pydantic.json_schema.SkipJsonSchema[Any] | RenderCVDesign = pydantic.Field(
|
design: Union[pydantic.json_schema.SkipJsonSchema[Any], RenderCVDesign] = pydantic.Field(
|
||||||
default=ClassicThemeOptions(theme="classic"),
|
default=ClassicThemeOptions(theme="classic"),
|
||||||
title="Design",
|
title="Design",
|
||||||
description=(
|
description=(
|
||||||
|
@ -1203,8 +1224,8 @@ class RenderCVDataModel(RenderCVBaseModel):
|
||||||
@pydantic.field_validator("design", mode="before")
|
@pydantic.field_validator("design", mode="before")
|
||||||
@classmethod
|
@classmethod
|
||||||
def initialize_if_custom_theme_is_used(
|
def initialize_if_custom_theme_is_used(
|
||||||
cls, design: RenderCVDesign | Any
|
cls, design: Union[RenderCVDesign, Any]
|
||||||
) -> RenderCVDesign | Any:
|
) -> Union[RenderCVDesign, Any]:
|
||||||
"""Initialize the custom theme if it is used and validate it. Otherwise, return
|
"""Initialize the custom theme if it is used and validate it. Otherwise, return
|
||||||
the built-in theme."""
|
the built-in theme."""
|
||||||
# `get_args` for an Annotated object returns the arguments when Annotated is
|
# `get_args` for an Annotated object returns the arguments when Annotated is
|
||||||
|
@ -1212,7 +1233,10 @@ class RenderCVDataModel(RenderCVBaseModel):
|
||||||
# access the first argument to use isinstance function.
|
# access the first argument to use isinstance function.
|
||||||
theme_data_model_types = get_args(RenderCVDesign)[0]
|
theme_data_model_types = get_args(RenderCVDesign)[0]
|
||||||
|
|
||||||
if isinstance(design, theme_data_model_types):
|
# Convert the arguments to a tuple
|
||||||
|
theme_data_model_types_tuple = tuple(theme_data_model_types.__args__) if hasattr(theme_data_model_types, '__args__') else (theme_data_model_types,)
|
||||||
|
|
||||||
|
if isinstance(design, theme_data_model_types_tuple):
|
||||||
# Then it means RenderCVDataModel is already initialized with a design, so
|
# Then it means RenderCVDataModel is already initialized with a design, so
|
||||||
# return it as is:
|
# return it as is:
|
||||||
return design
|
return design
|
||||||
|
@ -1346,10 +1370,10 @@ def dictionary_key_to_proper_section_title(key: str) -> str:
|
||||||
|
|
||||||
|
|
||||||
def set_or_update_a_value(
|
def set_or_update_a_value(
|
||||||
data_model: pydantic.BaseModel | dict | list,
|
data_model: Union[pydantic.BaseModel, dict, list],
|
||||||
key: str,
|
key: str,
|
||||||
value: str,
|
value: str,
|
||||||
sub_model: pydantic.BaseModel | dict | list = None,
|
sub_model: Union[pydantic.BaseModel, dict, list] = None,
|
||||||
):
|
):
|
||||||
"""Set or update a value in a data model for a specific key. For example, a key can
|
"""Set or update a value in a data model for a specific key. For example, a key can
|
||||||
be `cv.sections.education.3.institution` and the value can be "Bogazici University".
|
be `cv.sections.education.3.institution` and the value can be "Bogazici University".
|
||||||
|
@ -1419,7 +1443,7 @@ def set_or_update_a_value(
|
||||||
|
|
||||||
|
|
||||||
def read_input_file(
|
def read_input_file(
|
||||||
file_path_or_contents: pathlib.Path | str,
|
file_path_or_contents: Union[pathlib.Path, str],
|
||||||
) -> RenderCVDataModel:
|
) -> RenderCVDataModel:
|
||||||
"""Read the input file and return two instances of
|
"""Read the input file and return two instances of
|
||||||
[RenderCVDataModel][rendercv.data_models.RenderCVDataModel]. The first instance is
|
[RenderCVDataModel][rendercv.data_models.RenderCVDataModel]. The first instance is
|
||||||
|
@ -1460,7 +1484,7 @@ def read_input_file(
|
||||||
else:
|
else:
|
||||||
file_content = file_path_or_contents
|
file_content = file_path_or_contents
|
||||||
|
|
||||||
input_as_dictionary: dict[str, Any] = ruamel.yaml.YAML().load(file_content) # type: ignore
|
input_as_dictionary: Dict[str, Any] = ruamel.yaml.YAML().load(file_content) # type: ignore
|
||||||
|
|
||||||
# Validate the parsed dictionary by creating an instance of RenderCVDataModel:
|
# Validate the parsed dictionary by creating an instance of RenderCVDataModel:
|
||||||
rendercv_data_model = RenderCVDataModel(**input_as_dictionary)
|
rendercv_data_model = RenderCVDataModel(**input_as_dictionary)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from typing import Literal
|
from typing import Literal, Union
|
||||||
|
|
||||||
import pydantic
|
import pydantic
|
||||||
|
|
||||||
|
@ -23,16 +23,16 @@ class ModerncvThemeOptions(pydantic.BaseModel):
|
||||||
description='The page size of the CV. The default value is "letterpaper".',
|
description='The page size of the CV. The default value is "letterpaper".',
|
||||||
examples=["a4paper", "letterpaper"],
|
examples=["a4paper", "letterpaper"],
|
||||||
)
|
)
|
||||||
color: (
|
color: Union[
|
||||||
Literal["blue"]
|
Literal["blue"],
|
||||||
| Literal["black"]
|
Literal["black"],
|
||||||
| Literal["burgundy"]
|
Literal["burgundy"],
|
||||||
| Literal["green"]
|
Literal["green"],
|
||||||
| Literal["grey"]
|
Literal["grey"],
|
||||||
| Literal["orange"]
|
Literal["orange"],
|
||||||
| Literal["purple"]
|
Literal["purple"],
|
||||||
| Literal["red"]
|
Literal["red"],
|
||||||
) = pydantic.Field(
|
]= pydantic.Field(
|
||||||
default="blue",
|
default="blue",
|
||||||
validate_default=True,
|
validate_default=True,
|
||||||
title="Primary Color",
|
title="Primary Color",
|
||||||
|
|
Loading…
Reference in New Issue