Compare commits

..

3 Commits

2 changed files with 55 additions and 11 deletions

View File

@ -64,3 +64,6 @@ class Config():
def to_file(self, path: str) -> None:
with open(path, 'w') as f:
yaml.dump(asdict(self), f)
def asdict(self) -> dict[str, Any]:
return asdict(self)

View File

@ -1,10 +1,14 @@
"""
Module implementing message related functions and classes.
"""
from typing import Type, TypeVar
import pathlib
from typing import Type, TypeVar, Optional, Any
from dataclasses import dataclass, asdict
from .tags import Tag
QuestionInst = TypeVar('QuestionInst', bound='Question')
AnswerInst = TypeVar('AnswerInst', bound='Answer')
MessageInst = TypeVar('MessageInst', bound='Message')
class MessageError(Exception):
@ -39,16 +43,16 @@ def source_code(text: str, include_delims: bool = False) -> list[str]:
class Question(str):
"""
A single question with a defined prefix.
A single question with a defined header.
"""
prefix = '=== QUESTION ==='
header = '=== QUESTION ==='
def __new__(cls: Type[QuestionInst], string: str) -> QuestionInst:
"""
Make sure the question string does not contain the prefix.
Make sure the question string does not contain the header.
"""
if cls.prefix in string:
raise MessageError(f"Question '{string}' contains the prefix '{cls.prefix}'")
if cls.header in string:
raise MessageError(f"Question '{string}' contains the header '{cls.header}'")
instance = super().__new__(cls, string)
return instance
@ -61,16 +65,16 @@ class Question(str):
class Answer(str):
"""
A single answer with a defined prefix.
A single answer with a defined header.
"""
prefix = '=== ANSWER ==='
header = '=== ANSWER ==='
def __new__(cls: Type[AnswerInst], string: str) -> AnswerInst:
"""
Make sure the answer string does not contain the prefix.
Make sure the answer string does not contain the header.
"""
if cls.prefix in string:
raise MessageError(f"Answer '{string}' contains the prefix '{cls.prefix}'")
if cls.header in string:
raise MessageError(f"Answer '{string}' contains the header '{cls.header}'")
instance = super().__new__(cls, string)
return instance
@ -79,3 +83,40 @@ class Answer(str):
Extract and return all source code sections.
"""
return source_code(self, include_delims)
@dataclass
class Message():
"""
Single message. Consists of a question and optionally an answer, a set of tags
and a file path.
"""
question: Question
answer: Optional[Answer]
tags: Optional[set[Tag]]
path: Optional[pathlib.Path]
# @classmethod
# def from_file(cls: Type[MessageInst], path: str) -> MessageInst:
# """
# Create a Message from the given file. Expects the following file structure:
# * TagLine (from 'self.tags')
# * Question.Header
# * Question
# * Answer.Header
# """
# pass
def to_file(self, path: str) -> None:
"""
Write Message to the given file. Creates the following file structure:
* TagLine (from 'self.tags')
* Question.Header
* Question
* Answer.Header
* Answer
"""
pass
def asdict(self) -> dict[str, Any]:
return asdict(self)