Api
BaseModel and FileModel¶
Here we define our Pydantic BaseModel
with custom settings,
as well as a FileModel
that inherits from a BaseModel
but
also represents a file on disk.
BaseModel (BaseModel)
pydantic-model
¶
__init__(self, **data: Any) -> None
special
¶
Initializes a BaseModel with the provided data.
Exceptions:
Type | Description |
---|---|
ValidationError |
A validation error when the data is invalid. |
Source code in hydrolib/core/basemodel.py
def __init__(self, **data: Any) -> None:
"""Initializes a BaseModel with the provided data.
Raises:
ValidationError: A validation error when the data is invalid.
"""
try:
super().__init__(**data)
except ValidationError as e:
id = self._get_identifier(data)
if id is None:
raise e
else:
# If there is an identifier, include this in the ValidationError messages.
raise ValidationError([ErrorWrapper(e, loc=id)], self.__class__)
is_file_link(self) -> bool
¶
Generic attribute for models backed by a file.
Source code in hydrolib/core/basemodel.py
def is_file_link(self) -> bool:
"""Generic attribute for models backed by a file."""
return False
is_intermediate_link(self) -> bool
¶
Generic attribute for models that have children fields that could contain files.
Source code in hydrolib/core/basemodel.py
def is_intermediate_link(self) -> bool:
"""Generic attribute for models that have children fields that could contain files."""
return self.is_file_link()
show_tree(self, indent = 0)
¶
Recursive print function for showing a tree of a model.
Source code in hydrolib/core/basemodel.py
def show_tree(self, indent=0):
"""Recursive print function for showing a tree of a model."""
angle = "∟" if indent > 0 else ""
# Only print if we're backed by a file
if self.is_file_link():
print(" " * indent * 2, angle, self)
# Otherwise we recurse through the fields of a model
for _, value in self:
# Handle lists of items
if not isinstance(value, list):
value = [value]
for v in value:
if hasattr(v, "is_intermediate_link") and v.is_intermediate_link():
# If the field is only an intermediate, print the name only
if not v.is_file_link():
print(" " * (indent * 2 + 2), angle, v.__class__.__name__)
v.show_tree(indent + 1)
FileModel (BaseModel, ABC)
pydantic-model
¶
Base class to represent models with a file representation.
It therefore always has a filepath
and if it is given on
initilization, it will parse that file.
This class extends the validate
option of Pydantic,
so when when a Path is given to a field with type FileModel
,
it doesn't error, but actually initializes the FileModel
.
__init__(self, filepath: Optional[pathlib.Path] = None, *args, **kwargs)
special
¶
Initialize a model.
The model is empty (with defaults) if no filepath
is given,
otherwise the file at filepath
will be parsed.
Source code in hydrolib/core/basemodel.py
def __init__(self, filepath: Optional[Path] = None, *args, **kwargs):
"""Initialize a model.
The model is empty (with defaults) if no `filepath` is given,
otherwise the file at `filepath` will be parsed."""
# Parse the file if path is given
context_dir_reset_token = None
if filepath:
filepath = Path(filepath) # so we also accept strings
if filepath in FileModel._file_models_cache:
return None
# If not set, this is the root file path
if not context_dir.get(None):
logger.info(f"Set context to {filepath.parent}")
context_dir_reset_token = context_dir.set(filepath.parent)
FileModel._file_models_cache[filepath] = self
logger.info(f"Loading data from {filepath}")
data = self._load(filepath)
data["filepath"] = filepath
kwargs.update(data)
try:
super().__init__(*args, **kwargs)
finally:
_reset_context_dir(context_dir_reset_token)
__new__(cls, filepath: Optional[pathlib.Path] = None, *args, **kwargs)
special
staticmethod
¶
Creates a new model. If the file at the provided file path was already parsed, this instance is returned.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
filepath |
Optional[Path] |
The absolute file path to the file. Defaults to None. |
None |
Returns:
Type | Description |
---|---|
FileModel |
A file model. |
Source code in hydrolib/core/basemodel.py
def __new__(cls, filepath: Optional[Path] = None, *args, **kwargs):
"""Creates a new model.
If the file at the provided file path was already parsed, this instance is returned.
Args:
filepath (Optional[Path], optional): The absolute file path to the file. Defaults to None.
Returns:
FileModel: A file model.
"""
if filepath:
filepath = Path(filepath)
if filepath in FileModel._file_models_cache:
filemodel = FileModel._file_models_cache[filepath]
logger.info(
f"Returning existing {type(filemodel).__name__} from cache, because {filepath} was already parsed."
)
return filemodel
return super().__new__(cls)
__str__(self) -> str
special
¶
Return str(self).
Source code in hydrolib/core/basemodel.py
def __str__(self) -> str:
return str(self.filepath if self.filepath else "")
is_file_link(self) -> bool
¶
Generic attribute for models backed by a file.
Source code in hydrolib/core/basemodel.py
def is_file_link(self) -> bool:
return True
save(self, folder: Optional[pathlib.Path] = None) -> Path
¶
Save model and child models to their set filepaths.
If a folder is given, for models with an unset filepath,
we generate one based on the given folder
and a default name.
Otherwise we override the folder part of already set filepaths.
This can thus be used to copy complete models.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
folder |
Optional[pathlib.Path] |
path to the folder where this FileModel will be stored |
None |
Source code in hydrolib/core/basemodel.py
def save(self, folder: Optional[Path] = None) -> Path:
"""Save model and child models to their set filepaths.
If a folder is given, for models with an unset filepath,
we generate one based on the given `folder` and a default name.
Otherwise we override the folder part of already set filepaths.
This can thus be used to copy complete models.
Args:
folder: path to the folder where this FileModel will be stored
"""
if not self.filepath and not folder:
raise ValueError(
"Either set the `filepath` on the model or pass a `folder` when saving."
)
if not folder:
folder = self.filepath.absolute().parent
self._apply_recurse("_save", folder)
return self.filepath.absolute()