From 582316ae8564e063cc5d40129b0ceaeb29b25d7f 2020-06-15 18:19:10 From: Brett Smith Date: 2020-06-15 18:19:10 Subject: [PATCH] data: Add Metadata.report_links() method. This is just a variant of get_links() that eats TypeError. Higher-level reporting code often wants it. --- diff --git a/conservancy_beancount/data.py b/conservancy_beancount/data.py index 743716569541c0a9791998ec3884b2013c99d751..f6fe716527a6f0ccce0f4bcf4e6e75c547123125 100644 --- a/conservancy_beancount/data.py +++ b/conservancy_beancount/data.py @@ -252,6 +252,19 @@ class Metadata(MutableMapping[MetaKey, MetaValue]): key, type(value).__name__, )) + def report_links(self, key: MetaKey) -> Sequence[str]: + """Return a sequence of link strings under the named metadata key + + get_links raises a TypeError if the metadata is not a string. + This method simply returns the empty sequence. + Validation code (like in the plugin) usually uses get_links() + while reporting code uses report_links(). + """ + try: + return self.get_links(key) + except TypeError: + return () + @overload def first_link(self, key: MetaKey, default: None=None) -> Optional[str]: ... diff --git a/conservancy_beancount/reports/core.py b/conservancy_beancount/reports/core.py index 59d0bc516dd2eacd2b002bf048e7381d0f9550ce..8e05c23ec4efc3ebc797506e03c4d4c1d8cea863 100644 --- a/conservancy_beancount/reports/core.py +++ b/conservancy_beancount/reports/core.py @@ -335,15 +335,10 @@ class RelatedPostings(Sequence[data.Posting]): def __len__(self) -> int: return len(self._postings) - def _all_meta_links(self, key: MetaKey) -> Iterator[str]: - for post in self: - try: - yield from post.meta.get_links(key) - except TypeError: - pass - def all_meta_links(self, key: MetaKey) -> Iterator[str]: - return filters.iter_unique(self._all_meta_links(key)) + return filters.iter_unique( + link for post in self for link in post.meta.report_links(key) + ) @overload def first_meta_links(self, key: MetaKey, default: str='') -> Iterator[str]: ... diff --git a/tests/test_data_metadata.py b/tests/test_data_metadata.py index 9ea94b6aad5220b6cf05daaa504615393ba3091b..4ac7d5ab64a5873f58d6d429337e082d1c564a03 100644 --- a/tests/test_data_metadata.py +++ b/tests/test_data_metadata.py @@ -28,6 +28,15 @@ def simple_txn(index=None, key=None): ]) SIMPLE_TXN_METAKEYS = frozenset(['filename', 'lineno', 'note']) +LINK_STRINGS = [ + '', + 'link', + ' link', + 'link ', + 'link1 link2', + ' link1 link2 link3 ', +] + def test_metadata_transforms_source(): source = {'1': 'one'} meta = data.Metadata(source) @@ -36,14 +45,7 @@ def test_metadata_transforms_source(): del meta['1'] assert set(source) == {'2'} -@pytest.mark.parametrize('value', [ - '', - 'link', - ' link', - 'link ', - 'link1 link2', - ' link1 link2 link3 ', -]) +@pytest.mark.parametrize('value', LINK_STRINGS) def test_get_links(value): meta = data.Metadata({'key': value}) assert list(meta.get_links('key')) == value.split() @@ -58,6 +60,20 @@ def test_get_links_bad_type(value): with pytest.raises(TypeError): meta.get_links('key') +@pytest.mark.parametrize('value', LINK_STRINGS) +def test_report_links(value): + meta = data.Metadata({'key': value}) + assert list(meta.report_links('key')) == value.split() + +def test_report_links_missing(): + meta = data.Metadata({}) + assert not meta.report_links('key') + +@pytest.mark.parametrize('value', testutil.NON_STRING_METADATA_VALUES) +def test_report_links_bad_type(value): + meta = data.Metadata({'key': value}) + assert not meta.report_links('key') + def test_first_link_from_txn(simple_txn): meta = data.PostingMeta(simple_txn, 0) assert meta.first_link('note') == 'txn'