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 import utils
from app.config import Configurator from app.config import Configurator
from app.converter.wordpress_markdown import WordpressMarkdownConverter from app.converter.wordpress_markdown import WordpressMarkdownConverter
from app.io.reader import FileReader
from app.io.writer import FileWriter
class Converter: class Converter:
@ -44,7 +46,11 @@ class Converter:
_, _, files = next(os.walk(source_path)) _, _, files = next(os.walk(source_path))
for file in files: for file in files:
source_abs_path = source_path / Path(file) 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( self.markdown_converter.convert_jekyll_to_hugo(
source_abs_path, file_reader,
output_path, file_writer,
) )

View file

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