tags: TagLine constructor now supports multiline taglines and multiple spaces

This commit is contained in:
juk0de 2023-08-19 08:30:24 +02:00
parent 029e54ac14
commit 2c589281b9
2 changed files with 20 additions and 9 deletions

View File

@ -1,7 +1,7 @@
""" """
Module implementing tag related functions and classes. Module implementing tag related functions and classes.
""" """
from typing import Type, TypeVar, Optional from typing import Type, TypeVar, Optional, Final
TagInst = TypeVar('TagInst', bound='Tag') TagInst = TypeVar('TagInst', bound='Tag')
TagLineInst = TypeVar('TagLineInst', bound='TagLine') TagLineInst = TypeVar('TagLineInst', bound='TagLine')
@ -16,9 +16,9 @@ class Tag(str):
A single tag. A string that can contain anything but the default separator (' '). A single tag. A string that can contain anything but the default separator (' ').
""" """
# default separator # default separator
default_separator = ' ' default_separator: Final[str] = ' '
# alternative separators (e. g. for backwards compatibility) # alternative separators (e. g. for backwards compatibility)
alternative_separators = [','] alternative_separators: Final[list[str]] = [',']
def __new__(cls: Type[TagInst], string: str) -> TagInst: def __new__(cls: Type[TagInst], string: str) -> TagInst:
""" """
@ -93,19 +93,21 @@ def match_tags(tags: set[Tag], tags_or: Optional[set[Tag]], tags_and: Optional[s
class TagLine(str): class TagLine(str):
""" """
A line of tags. It starts with a prefix ('TAGS:'), followed by a list of tags, A line of tags in a '.txt' file. It starts with a prefix ('TAGS:'), followed by
separated by the defaut separator (' '). Any operations on a TagLine will sort a list of tags, separated by the defaut separator (' '). Any operations on a
the tags. TagLine will sort the tags.
""" """
# the prefix # the prefix
prefix = 'TAGS:' prefix: Final[str] = 'TAGS:'
def __new__(cls: Type[TagLineInst], string: str) -> TagLineInst: def __new__(cls: Type[TagLineInst], string: str) -> TagLineInst:
""" """
Make sure the tagline string starts with the prefix. Make sure the tagline string starts with the prefix. Also replace newlines
and multiple spaces with ' ', in order to support multiline TagLines.
""" """
if not string.startswith(cls.prefix): if not string.startswith(cls.prefix):
raise TagError(f"TagLine '{string}' is missing prefix '{cls.prefix}'") raise TagError(f"TagLine '{string}' is missing prefix '{cls.prefix}'")
string = ' '.join(string.split())
instance = super().__new__(cls, string) instance = super().__new__(cls, string)
return instance return instance
@ -114,7 +116,7 @@ class TagLine(str):
""" """
Create a new TagLine from a set of tags. Create a new TagLine from a set of tags.
""" """
return cls(' '.join([TagLine.prefix] + sorted([t for t in tags]))) return cls(' '.join([cls.prefix] + sorted([t for t in tags])))
def tags(self) -> set[Tag]: def tags(self) -> set[Tag]:
""" """

View File

@ -256,6 +256,10 @@ class TestTagLine(CmmTestCase):
tagline = TagLine('TAGS: tag1 tag2') tagline = TagLine('TAGS: tag1 tag2')
self.assertEqual(tagline, 'TAGS: tag1 tag2') self.assertEqual(tagline, 'TAGS: tag1 tag2')
def test_valid_tagline_with_newline(self) -> None:
tagline = TagLine('TAGS: tag1\n tag2')
self.assertEqual(tagline, 'TAGS: tag1 tag2')
def test_invalid_tagline(self) -> None: def test_invalid_tagline(self) -> None:
with self.assertRaises(TagError): with self.assertRaises(TagError):
TagLine('tag1 tag2') TagLine('tag1 tag2')
@ -273,6 +277,11 @@ class TestTagLine(CmmTestCase):
tags = tagline.tags() tags = tagline.tags()
self.assertEqual(tags, {Tag('tag1'), Tag('tag2')}) self.assertEqual(tags, {Tag('tag1'), Tag('tag2')})
def test_tags_with_newline(self) -> None:
tagline = TagLine('TAGS: tag1\n tag2')
tags = tagline.tags()
self.assertEqual(tags, {Tag('tag1'), Tag('tag2')})
def test_merge(self) -> None: def test_merge(self) -> None:
tagline1 = TagLine('TAGS: tag1 tag2') tagline1 = TagLine('TAGS: tag1 tag2')
tagline2 = TagLine('TAGS: tag2 tag3') tagline2 = TagLine('TAGS: tag2 tag3')