Verified Commit 9cff77d7 authored by David Runge's avatar David Runge
Browse files

Simplify convert and add class for template rendering

repo_management/convert.py:
Add the class `RepoDbFile` which is used to initialize a jinja
environment and allows to render the 'desc.j2' and 'files.j2' templates.
Add `_desc_data_line_to_dicts()` which allows for assigning the data
from a line in a 'desc' file to be assigned to a dict, that corresponds
to the line's designated data type.
Simplify `_desc_data_to_model()` by making use of
`_desc_data_line_to_dicts()`.
Change `_desc_data_to_model()` by guarding against pydantic
ValidationErrors and raising a RepoManagementValidationError instead.

tests/test_convert.py:
Add tests for `RepoDbFile`.
parent 5c491242
import io
from typing import Dict, List, Optional, Union
from repo_management import defaults, models
from jinja2 import Environment, PackageLoader
from pydantic.error_wrappers import ValidationError
from repo_management import defaults, errors, models
def _files_data_to_model(data: io.StringIO) -> models.Files:
......@@ -43,6 +46,43 @@ def _files_data_to_model(data: io.StringIO) -> models.Files:
return models.Files(**output)
def _desc_data_line_to_dicts(
current_header: str,
current_type: defaults.FieldType,
line: str,
string_list_types: Dict[str, List[str]],
string_types: Dict[str, str],
int_types: Dict[str, int],
) -> None:
"""Add data retrieved from a line in a 'desc' file in a repository database to respective dicts for specific types
Parameters
----------
current_header: str
The current header under which the line is found
current_type: str
The type by which the header is defined by
line: str
The data
string_list_types: Dict[str, List[str]]
A dict for instances of type list string
string_types: Dict[str, str]
A dict for instances of type string
int_types: Dict[str, int]
A dict for instances of type int
"""
if current_type == defaults.FieldType.STRING_LIST:
if current_header in string_list_types.keys():
string_list_types[current_header] += [line]
else:
string_list_types[current_header] = [line]
if current_type == defaults.FieldType.STRING:
string_types[current_header] = line
if current_type == defaults.FieldType.INT:
int_types[current_header] = int(line)
def _desc_data_to_model(data: io.StringIO) -> models.PackageDesc:
"""Read the contents of a 'desc' file (represented as an instance of io.StringIO) and convert it to a pydantic model
......@@ -81,19 +121,22 @@ def _desc_data_to_model(data: io.StringIO) -> models.PackageDesc:
continue
if current_header:
if current_type == defaults.FieldType.STRING_LIST:
if current_header in string_list_types.keys():
string_list_types[current_header] += [line]
else:
string_list_types[current_header] = [line]
if current_type == defaults.FieldType.STRING:
string_types[current_header] = line
if current_type == defaults.FieldType.INT:
int_types[current_header] = int(line)
_desc_data_line_to_dicts(
current_header=current_header,
current_type=current_type,
line=line,
string_list_types=string_list_types,
string_types=string_types,
int_types=int_types,
)
data.close()
merged_dict: Dict[str, Union[int, str, List[str]]] = {**int_types, **string_types, **string_list_types}
return models.PackageDesc(**merged_dict)
try:
return models.PackageDesc(**merged_dict)
except ValidationError as e:
raise errors.RepoManagementValidationError(
f"An error occured while validating the file: {data.getvalue()}\n{e}"
)
def _transform_package_desc_to_output_package(
......@@ -125,3 +168,58 @@ def _transform_package_desc_to_output_package(
return models.OutputPackage(**desc_dict, **files.dict())
else:
return models.OutputPackage(**desc_dict)
class RepoDbFile:
"""A class for handling templates for files used in repository database files (such as 'desc' or 'files')
Attributes
----------
env: jinja2.Environment
A jinja2 Environment, that makes the templates available
"""
def __init__(self, enable_async: bool = False) -> None:
"""Initialize an instance of RepDbFile
Parameters
----------
enable_async: bool
A bool indicating whether the jinja2.Environment is instantiated with enable_async (defaults to False)
"""
self.env = Environment(
loader=PackageLoader("repo_management", "templates"),
trim_blocks=True,
lstrip_blocks=True,
enable_async=enable_async,
)
def render_desc_template(self, model: models.PackageDesc, output: io.StringIO) -> None:
"""Use the 'desc' template to write a string to an output stream based on a model
Parameters
----------
model: models.PackageDesc
A pydantic model with the required attributes to properly render a template for a 'desc' file
output: io.StringIO
An output stream to write to
"""
template = self.env.get_template("desc.j2")
output.write(template.render(model.dict()))
def render_files_template(self, model: models.Files, output: io.StringIO) -> None:
"""Use the 'files' template to write a string to an output stream based on a model
Parameters
----------
model: models.Files
A pydantic model with the required attributes to properly render a template for a 'files' file
output: io.StringIO
An output stream to write to
"""
template = self.env.get_template("files.j2")
output.write(template.render(model.dict()))
......@@ -3,10 +3,9 @@ from contextlib import nullcontext as does_not_raise
from os.path import dirname, join, realpath
from typing import ContextManager
from pydantic.error_wrappers import ValidationError
from pytest import mark, raises
from repo_management import convert, models
from repo_management import convert, errors, models
RESOURCES = join(dirname(realpath(__file__)), "resources")
......@@ -84,7 +83,7 @@ def test__files_data_to_dict(
),
raises(ValueError),
),
("%FOO%\nbar\n", raises(ValidationError)),
("%FOO%\nbar\n", raises(errors.RepoManagementValidationError)),
(
(
"%BACKUP%\nfoo\nbar\n%BASE%\nfoo\n"
......@@ -96,7 +95,7 @@ def test__files_data_to_dict(
"%PGPSIG%\nfoo\n%PROVIDES%\nfoo\nbar\n%REPLACES%\nfoo\nbar\n"
"%SHA256SUM%\nfoo\n%URL%\nfoo\n%VERSION%\nfoo\n"
),
raises(ValidationError),
raises(errors.RepoManagementValidationError),
),
],
)
......@@ -163,3 +162,47 @@ def test__transform_package_desc_to_output_package(
assert output.files
else:
assert not output.files
def test_repodbfile__init() -> None:
assert convert.RepoDbFile()
def test_repodbfile_render_desc_template() -> None:
repodbfile = convert.RepoDbFile()
assert repodbfile
output = io.StringIO()
assert not output.getvalue()
repodbfile.render_desc_template(
model=models.PackageDesc(
arch="foo",
base="foo",
builddate=1,
csize=1,
desc="foo",
filename="foo",
isize=1,
licenses=["foo"],
md5sum="foo",
name="foo",
packager="foo",
pgpsig="foo",
sha256sum="foo",
url="foo",
version="foo",
),
output=output,
)
assert output.getvalue()
def test_repodbfile_render_files_template() -> None:
repodbfile = convert.RepoDbFile()
assert repodbfile
output = io.StringIO()
assert not output.getvalue()
repodbfile.render_files_template(
model=models.Files(files=["foo", "bar"]),
output=output,
)
assert output.getvalue()
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment