Compare commits

..

7 Commits

2 changed files with 12 additions and 109 deletions

View File

@ -3,32 +3,19 @@ Module implementing message related functions and classes.
"""
import pathlib
import yaml
from typing import Type, TypeVar, ClassVar, Optional, Any, Union
from typing import Type, TypeVar, ClassVar, Optional, Any
from dataclasses import dataclass, asdict
from .tags import Tag, TagLine
QuestionInst = TypeVar('QuestionInst', bound='Question')
AnswerInst = TypeVar('AnswerInst', bound='Answer')
MessageInst = TypeVar('MessageInst', bound='Message')
YamlDict = dict[str, Union[QuestionInst, AnswerInst, set[Tag]]]
class MessageError(Exception):
pass
def str_presenter(dumper: yaml.Dumper, data: str) -> yaml.ScalarNode:
"""
Changes the YAML dump style to multiline syntax for multiline strings.
"""
if len(data.splitlines()) > 1:
return dumper.represent_scalar('tag:yaml.org,2002:str', data, style='|')
return dumper.represent_scalar('tag:yaml.org,2002:str', data)
yaml.add_representer(str, str_presenter)
def source_code(text: str, include_delims: bool = False) -> list[str]:
"""
Extract all source code sections from the given text, i. e. all lines
@ -167,9 +154,7 @@ class Message():
* Question
* Answer.Header
For '.yaml':
* question: single or multiline string
* answer: single or multiline string
* tags: list of strings
TODO
"""
if not file_path.exists():
raise MessageError(f"Message file '{file_path}' does not exist")
@ -190,6 +175,7 @@ class Message():
return cls(question, answer, tags, file_path)
else: # '.yaml'
with open(file_path, "r") as fd:
# FIXME: use the actual YAML format
data = yaml.load(fd, Loader=yaml.FullLoader)
data['file_path'] = file_path
return cls.from_dict(data)
@ -204,9 +190,7 @@ class Message():
* Answer.Header
* Answer
For '.yaml':
* question: single or multiline string
* answer: single or multiline string
* tags: list of strings
TODO
"""
if file_path:
self.file_path = file_path
@ -220,14 +204,7 @@ class Message():
fd.write(f'{TagLine.from_set(msg_tags)}\n')
fd.write(f'{Question.header}\n{self.question}\n')
fd.write(f'{Answer.header}\n{self.answer}\n')
elif self.file_path.suffix == '.yaml':
with open(self.file_path, "w") as fd:
data: YamlDict = {'question': str(self.question)}
if self.answer:
data['answer'] = str(self.answer)
if self.tags:
data['tags'] = sorted([str(tag) for tag in self.tags])
yaml.dump(data, fd)
# FIXME: write YAML format
def as_dict(self) -> dict[str, Any]:
return asdict(self)

View File

@ -86,8 +86,8 @@ class MessageToFileTxtTestCase(CmmTestCase):
def setUp(self) -> None:
self.file = tempfile.NamedTemporaryFile(delete=False, suffix='.txt')
self.file_path = pathlib.Path(self.file.name)
self.message = Message(Question('This is a question.'),
Answer('This is an answer.'),
self.message = Message(Question('This is a question'),
Answer('This is an answer'),
{Tag('tag1'), Tag('tag2')},
self.file_path)
@ -100,12 +100,7 @@ class MessageToFileTxtTestCase(CmmTestCase):
with open(self.file_path, "r") as fd:
content = fd.read()
expected_content = """TAGS: tag1 tag2
=== QUESTION ===
This is a question.
=== ANSWER ===
This is an answer.
"""
expected_content = "TAGS: tag1 tag2\n=== QUESTION ===\nThis is a question\n=== ANSWER ===\nThis is an answer\n"
self.assertEqual(content, expected_content)
def test_to_file_unsupported_file_type(self) -> None:
@ -127,55 +122,12 @@ This is an answer.
self.message.file_path = self.file_path
class MessageToFileYamlTestCase(CmmTestCase):
def setUp(self) -> None:
self.file = tempfile.NamedTemporaryFile(delete=False, suffix='.yaml')
self.file_path = pathlib.Path(self.file.name)
self.message = Message(Question('This is a question.'),
Answer('This is an answer.'),
{Tag('tag1'), Tag('tag2')},
self.file_path)
self.message_multiline = Message(Question('This is a\nmultiline question.'),
Answer('This is a\nmultiline answer.'),
{Tag('tag1'), Tag('tag2')},
self.file_path)
def tearDown(self) -> None:
self.file.close()
self.file_path.unlink()
def test_to_file_yaml(self) -> None:
self.message.to_file(self.file_path)
with open(self.file_path, "r") as fd:
content = fd.read()
expected_content = "answer: This is an answer.\nquestion: This is a question.\ntags:\n- tag1\n- tag2\n"
self.assertEqual(content, expected_content)
def test_to_file_yaml_multiline(self) -> None:
self.message_multiline.to_file(self.file_path)
with open(self.file_path, "r") as fd:
content = fd.read()
expected_content = """answer: |-
This is a
multiline answer.
question: |-
This is a
multiline question.
tags:
- tag1
- tag2
"""
self.assertEqual(content, expected_content)
class MessageFromFileTxtTestCase(CmmTestCase):
def setUp(self) -> None:
self.file = tempfile.NamedTemporaryFile(delete=False, suffix='.txt')
self.file_path = pathlib.Path(self.file.name)
with open(self.file_path, "w") as fd:
fd.write("TAGS: tag1 tag2\n=== QUESTION ===\nThis is a question.\n=== ANSWER ===\nThis is an answer.\n")
fd.write("TAGS: tag1 tag2\n=== QUESTION ===\nThis is a question\n=== ANSWER ===\nThis is an answer\n")
def tearDown(self) -> None:
self.file.close()
@ -184,39 +136,13 @@ class MessageFromFileTxtTestCase(CmmTestCase):
def test_from_file_txt(self) -> None:
message = Message.from_file(self.file_path)
self.assertIsInstance(message, Message)
self.assertEqual(message.question, 'This is a question.')
self.assertEqual(message.answer, 'This is an answer.')
self.assertEqual(message.question, 'This is a question')
self.assertEqual(message.answer, 'This is an answer')
self.assertSetEqual(cast(set[Tag], message.tags), {Tag('tag1'), Tag('tag2')})
self.assertEqual(message.file_path, self.file_path)
def test_from_file_not_exists(self) -> None:
file_not_exists = pathlib.Path("example.txt")
with self.assertRaises(MessageError) as cm:
Message.from_file(file_not_exists)
self.assertEqual(str(cm.exception), f"Message file '{file_not_exists}' does not exist")
class MessageFromFileYamlTestCase(CmmTestCase):
def setUp(self) -> None:
self.file = tempfile.NamedTemporaryFile(delete=False, suffix='.yaml')
self.file_path = pathlib.Path(self.file.name)
with open(self.file_path, "w") as fd:
fd.write("question: |-\n This is a question.\nanswer: |-\n This is an answer.\ntags:\n- tag1\n- tag2")
def tearDown(self) -> None:
self.file.close()
self.file_path.unlink()
def test_from_file_yaml(self) -> None:
message = Message.from_file(self.file_path)
self.assertIsInstance(message, Message)
self.assertEqual(message.question, 'This is a question.')
self.assertEqual(message.answer, 'This is an answer.')
self.assertSetEqual(cast(set[Tag], message.tags), {Tag('tag1'), Tag('tag2')})
self.assertEqual(message.file_path, self.file_path)
def test_from_file_not_exists(self) -> None:
file_not_exists = pathlib.Path("example.yaml")
file_not_exists = pathlib.Path("example.doc")
with self.assertRaises(MessageError) as cm:
Message.from_file(file_not_exists)
self.assertEqual(str(cm.exception), f"Message file '{file_not_exists}' does not exist")