abstract IO for easier testing in the future

This commit is contained in:
Denis-Cosmin Nutiu 2023-05-31 18:30:30 +03:00
parent fc4f2fd7a7
commit a524db1722
5 changed files with 119 additions and 29 deletions

View file

@ -5,6 +5,8 @@ from pathlib import Path
from app import utils
from app.config import Configurator
from app.converter.wordpress_markdown import WordpressMarkdownConverter
from app.io.reader import FileReader
from app.io.writer import FileWriter
class Converter:
@ -44,7 +46,11 @@ class Converter:
_, _, files = next(os.walk(source_path))
for file in files:
source_abs_path = source_path / Path(file)
file_reader = FileReader(str(source_abs_path))
file_writer = FileWriter(output_path.joinpath(source_abs_path.name))
self.markdown_converter.convert_jekyll_to_hugo(
source_abs_path,
output_path,
file_reader,
file_writer,
)

View file

@ -1,10 +1,10 @@
from pathlib import Path
import yaml
from bs4 import BeautifulSoup, Tag
from app import utils
from app.config import Configurator
from app.io.reader import IoReader
from app.io.writer import IoWriter
from app.utils import key_error_silence
@ -106,53 +106,46 @@ class WordpressMarkdownConverter:
return "\n".join(fixed_lines)
def read_jekyll_post(self, path: Path):
def read_jekyll_post(self, reader: IoReader):
"""
Read a Jekyll post from the specified path
Read a Jekyll post from the reader.
Parameters
----------
path : Path
The path to the Jekyll post
reader : IoReader
The IoReader instance for reading.
"""
# read source
with open(path, "r") as fh:
contents = fh.read()
return contents
return reader.read()
def write_hugo_post(self, output_path, post_header: dict, post_content: str):
def write_hugo_post(self, writer: IoWriter, post_header: dict, post_content: str):
"""
Write a Hugo post to the specified path
Write a Hugo post to the specified writer.
Parameters
----------
output_path : Path
The path to the Hugo post
writer : IoWriter
The IoWriter instance for writing.
post_header : dict
The post header
post_content : str
The post content
"""
# ensure that output path exists
output_path.parent.mkdir(parents=True, exist_ok=True)
data = ["---\n", yaml.dump(post_header), "---\n", post_content]
writer.write("".join(data))
with open(output_path, "w") as fo:
header = ["---\n", yaml.dump(post_header), "---\n"]
fo.writelines(header)
fo.write(post_content)
def convert_jekyll_to_hugo(self, jekyll_post_path: Path, hugo_post_output: Path):
def convert_jekyll_to_hugo(self, reader: IoReader, writer: IoWriter):
"""
Convert a Jekyll post to a Hugo post
Parameters
----------
jekyll_post_path : Path
The path to the Jekyll post
hugo_post_output : Path
The path to the Hugo post
reader : IoReader
The IoReader instance for reading.
writer : IoWriter
The IoWriter instance for writing.
"""
contents = self.read_jekyll_post(jekyll_post_path)
contents = self.read_jekyll_post(reader)
# fix header
header = yaml.safe_load(contents.split("---")[1])
@ -162,7 +155,7 @@ class WordpressMarkdownConverter:
fixed_post_content = self.convert_post_content(post_content)
self.write_hugo_post(
hugo_post_output.joinpath(jekyll_post_path.name),
writer,
fixed_header,
fixed_post_content,
)

0
app/io/__init__.py Normal file
View file

39
app/io/reader.py Normal file
View file

@ -0,0 +1,39 @@
from abc import ABCMeta, abstractmethod
class IoReader(metaclass=ABCMeta):
"""
Abstract class for reading posts.
"""
@abstractmethod
def read(self) -> str:
"""
Reads a post.
"""
raise NotImplementedError
class StringReader(IoReader):
"""
Reads a post from a string.
"""
def __init__(self, content: str):
self._content = content
def read(self) -> str:
return self._content
class FileReader(IoReader):
"""
Reads a post from a local file.
"""
def __init__(self, file_path: str):
self._file_path = file_path
def read(self) -> str:
with open(self._file_path, "r") as file:
return file.read()

52
app/io/writer.py Normal file
View file

@ -0,0 +1,52 @@
from abc import abstractmethod, ABCMeta
from pathlib import Path
from typing import Callable
from app import utils
class IoWriter(metaclass=ABCMeta):
"""
Abstract class for writing posts.
"""
@abstractmethod
def write(self, data: str):
"""
Write a post
Parameters
----------
data: str
The post data to write
"""
raise NotImplementedError
class FileWriter(IoWriter):
"""
Writes a post to a file.
"""
def __init__(self, output_path: Path):
utils.guard_against_none(output_path, "output_path")
self.output_path = output_path
output_path.parent.mkdir(parents=True, exist_ok=True)
def write(self, data: str):
with open(self.output_path, "w") as fo:
fo.write(data)
class CallbackWriter(IoWriter):
"""
Writes a post to a string.
"""
def __init__(self, callback: Callable[[str], None]):
utils.guard_against_none(callback, "callback")
self.callback = callback
def write(self, data: str):
self.callback(data)