82 lines
2.5 KiB
Python
82 lines
2.5 KiB
Python
"""
|
|
Module implementing message related functions and classes.
|
|
"""
|
|
from typing import Type, TypeVar
|
|
|
|
QuestionInst = TypeVar('QuestionInst', bound='Question')
|
|
AnswerInst = TypeVar('AnswerInst', bound='Answer')
|
|
|
|
|
|
class MessageError(Exception):
|
|
pass
|
|
|
|
|
|
def source_code(text: str, include_delims: bool = False) -> list[str]:
|
|
"""
|
|
Extract all source code sections from the given text, i. e. all lines
|
|
surrounded by lines tarting with '```'. If 'include_delims' is True,
|
|
the surrounding lines are included, otherwise they are omitted. The
|
|
result list contains every source code section as a single string.
|
|
The order in the list represents the order of the sections in the text.
|
|
"""
|
|
code_sections: list[str] = []
|
|
code_lines: list[str] = []
|
|
in_code_block = False
|
|
|
|
for line in text.split('\n'):
|
|
if line.strip().startswith('```'):
|
|
if include_delims:
|
|
code_lines.append(line)
|
|
if in_code_block:
|
|
code_sections.append('\n'.join(code_lines) + '\n')
|
|
code_lines.clear()
|
|
in_code_block = not in_code_block
|
|
elif in_code_block:
|
|
code_lines.append(line)
|
|
|
|
return code_sections
|
|
|
|
|
|
class Question(str):
|
|
"""
|
|
A single question with a defined prefix.
|
|
"""
|
|
prefix = '=== QUESTION ==='
|
|
|
|
def __new__(cls: Type[QuestionInst], string: str) -> QuestionInst:
|
|
"""
|
|
Make sure the question string does not contain the prefix.
|
|
"""
|
|
if cls.prefix in string:
|
|
raise MessageError(f"Question '{string}' contains the prefix '{cls.prefix}'")
|
|
instance = super().__new__(cls, string)
|
|
return instance
|
|
|
|
def source_code(self, include_delims: bool = False) -> list[str]:
|
|
"""
|
|
Extract and return all source code sections.
|
|
"""
|
|
return source_code(self, include_delims)
|
|
|
|
|
|
class Answer(str):
|
|
"""
|
|
A single answer with a defined prefix.
|
|
"""
|
|
prefix = '=== ANSWER ==='
|
|
|
|
def __new__(cls: Type[AnswerInst], string: str) -> AnswerInst:
|
|
"""
|
|
Make sure the answer string does not contain the prefix.
|
|
"""
|
|
if cls.prefix in string:
|
|
raise MessageError(f"Answer '{string}' contains the prefix '{cls.prefix}'")
|
|
instance = super().__new__(cls, string)
|
|
return instance
|
|
|
|
def source_code(self, include_delims: bool = False) -> list[str]:
|
|
"""
|
|
Extract and return all source code sections.
|
|
"""
|
|
return source_code(self, include_delims)
|