-
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
new: Basic implementation of saving files created by the processor
- Loading branch information
Showing
8 changed files
with
124 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
"""Classes that implement the X interface to provide file writing functionality.""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
from typing import Type, Iterable, Optional, Callable | ||
from dataclasses import dataclass | ||
from abc import ABC, abstractmethod | ||
from pathlib import Path | ||
from devana.preprocessing.preprocessor import IDestination | ||
|
||
class IDestiny(ABC): | ||
"""The basic element on which FileSaver operates.""" | ||
|
||
@property | ||
@abstractmethod | ||
def name(self) -> str: | ||
"""The name of the file, including the extension. This will be used to save as the file name.""" | ||
|
||
@property | ||
@abstractmethod | ||
def content(self) -> str: | ||
"""The contents of the file as test - it will be saved.""" | ||
|
||
@property | ||
@abstractmethod | ||
def path_prefix(self) -> Optional[Path]: | ||
"""If set, it will be added to the root write path after which further path modifications are allowed.""" | ||
|
||
|
||
class FileSaver(IDestination): | ||
"""Implementation that provides text file saving. It supports dynamically generated paths | ||
(in real use, probably based on the file extension extracted from the name) and supports defined path prefixes.""" | ||
|
||
@dataclass | ||
class Configuration: | ||
"""Core FileSaver configuration""" | ||
root_path: Path | ||
"""Base path relative to which prefixes are added.""" | ||
path_prefix_generator: Optional[Callable[[IDestiny], Path]] = None | ||
"""If it exists, the function is called for each IDestiny and the generated prefix is | ||
appended after the fixed prefix from the IDestiny.""" | ||
|
||
def __init__(self, configuration: Configuration): | ||
self._configuration = configuration | ||
|
||
@property | ||
def configuration(self) -> Configuration: | ||
"""Current configuration.""" | ||
return self._configuration | ||
|
||
@classmethod | ||
def get_required_type(cls) -> Type: | ||
return IDestiny | ||
|
||
def consume(self, data: Iterable[IDestiny]) -> Optional[IDestination.Artifacts]: | ||
result = IDestination.Artifacts() | ||
for d in data: | ||
root_path = self._configuration.root_path | ||
if d.path_prefix is not None: | ||
root_path /= d.path_prefix | ||
if self._configuration.path_prefix_generator is not None: | ||
root_path /= self._configuration.path_prefix_generator(d) | ||
path = root_path / d.name | ||
with open(root_path / d.name, "tw", encoding="utf-8") as f: | ||
f.write(d.content) | ||
result.files.append(path) | ||
return result |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
54 changes: 54 additions & 0 deletions
54
tests/preprocessing/unit/components/savers/test_file_saver.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import unittest | ||
from dataclasses import dataclass | ||
import dataclasses | ||
from pathlib import Path | ||
from typing import Optional | ||
from unittest.mock import patch, mock_open | ||
from devana.preprocessing.components.savers.file_saver import FileSaver, IDestiny | ||
|
||
|
||
@dataclass | ||
class TestDestiny(IDestiny): | ||
name: str = dataclasses.MISSING | ||
content: str = dataclasses.MISSING | ||
path_prefix: Optional[Path] = dataclasses.MISSING | ||
|
||
|
||
class TestFileSaver(unittest.TestCase): | ||
|
||
@patch("builtins.open", new_callable=mock_open) | ||
def test_basic_file_saver(self, mock_open_file): | ||
saver = FileSaver(FileSaver.Configuration(Path("/test"))) | ||
destiny = TestDestiny("TestFileName.cpp", "Content tested", None) | ||
result = saver.consume([destiny]) | ||
mock_open_file.assert_called_with(Path("/test/TestFileName.cpp"), "tw", encoding="utf-8") | ||
self.assertEqual(len(result.files), 1) | ||
self.assertEqual(result.files[0].name, "TestFileName.cpp") | ||
|
||
@patch("builtins.open", new_callable=mock_open) | ||
def test_add_prefix_for_file_saver(self, mock_open_file): | ||
saver = FileSaver(FileSaver.Configuration(Path("/test"))) | ||
destiny = TestDestiny("TestFileName.cpp", "Content tested", Path("test_prefix")) | ||
result = saver.consume([destiny]) | ||
mock_open_file.assert_called_with(Path("/test/test_prefix/TestFileName.cpp"), "tw", encoding="utf-8") | ||
self.assertEqual(len(result.files), 1) | ||
self.assertEqual(result.files[0].name, "TestFileName.cpp") | ||
|
||
|
||
@patch("builtins.open", new_callable=mock_open) | ||
def test_add_dynamic_prefix_for_file_saver(self, mock_open_file): | ||
saver = FileSaver(FileSaver.Configuration(Path("/test"), lambda e: Path("dynamic_prefix"))) | ||
destiny = TestDestiny("TestFileName.cpp", "Content tested", None) | ||
result = saver.consume([destiny]) | ||
mock_open_file.assert_called_with(Path("/test/dynamic_prefix/TestFileName.cpp"), "tw", encoding="utf-8") | ||
self.assertEqual(len(result.files), 1) | ||
self.assertEqual(result.files[0].name, "TestFileName.cpp") | ||
|
||
@patch("builtins.open", new_callable=mock_open) | ||
def test_add_dynamic_ans_static_prefix_for_file_saver(self, mock_open_file): | ||
saver = FileSaver(FileSaver.Configuration(Path("/test"), lambda e: Path("dynamic_prefix"))) | ||
destiny = TestDestiny("TestFileName.cpp", "Content tested", Path("test_prefix")) | ||
result = saver.consume([destiny]) | ||
mock_open_file.assert_called_with(Path("/test/test_prefix/dynamic_prefix/TestFileName.cpp"), "tw", encoding="utf-8") | ||
self.assertEqual(len(result.files), 1) | ||
self.assertEqual(result.files[0].name, "TestFileName.cpp") |