Changeset - 46cfc558ecbb
[Not reviewed]
0 5 0
Brett Smith - 4 years ago 2020-03-28 17:36:56
brettcsmith@brettcsmith.org
plugin: Link checkers use Metadata class.
5 files changed with 70 insertions and 24 deletions:
0 comments (0 inline, 0 general)
conservancy_beancount/errors.py
Show inline comments
...
 
@@ -67,13 +67,17 @@ class ConfigurationError(Error):
 

	
 

	
 
class InvalidMetadataError(Error):
 
    def __init__(self, txn, key, value=None, post=None, source=None):
 
    def __init__(self, txn, key, value=None, post=None, need_type=str, source=None):
 
        if post is None:
 
            srcname = 'transaction'
 
        else:
 
            srcname = post.account
 
        if value is None:
 
            msg = "{} missing {}".format(srcname, key)
 
        else:
 
        elif isinstance(value, need_type):
 
            msg = "{} has invalid {}: {}".format(srcname, key, value)
 
        else:
 
            msg = "{} has wrong type of {}: expected {} but is a {}".format(
 
                srcname, key, need_type.__name__, type(value).__name__,
 
            )
 
        super().__init__(msg, txn, source)
conservancy_beancount/plugin/meta_repo_links.py
Show inline comments
...
 
@@ -23,11 +23,13 @@ from .. import errors as errormod
 
from ..beancount_types import (
 
    MetaKey,
 
    MetaValue,
 
    Posting,
 
    Transaction,
 
)
 

	
 
from typing import (
 
    Mapping,
 
    MutableMapping,
 
    Optional,
 
)
 

	
 
class MetaRepoLinks(core.TransactionHook):
...
 
@@ -41,19 +43,26 @@ class MetaRepoLinks(core.TransactionHook):
 
        self.repo_path = repo_path
 

	
 
    def _check_links(self,
 
                     meta: MutableMapping[MetaKey, MetaValue],
 
                     txn: Transaction,
 
                     meta: Mapping[MetaKey, MetaValue],
 
                     post: Optional[Posting]=None,
 
    ) -> errormod.Iter:
 
        for key in data.LINK_METADATA.intersection(meta):
 
            for link in str(meta[key]).split():
 
                match = self.PATH_PUNCT_RE.search(link)
 
                if match and match.group(0) == ':':
 
                    pass
 
                elif not (self.repo_path / link).exists():
 
                    yield errormod.BrokenLinkError(txn, key, link)
 
        metadata = data.Metadata(meta)
 
        for key in data.LINK_METADATA:
 
            try:
 
                links = metadata.get_links(key)
 
            except TypeError:
 
                yield errormod.InvalidMetadataError(txn, key, meta[key], post)
 
            else:
 
                for link in links:
 
                    match = self.PATH_PUNCT_RE.search(link)
 
                    if match and match.group(0) == ':':
 
                        pass
 
                    elif not (self.repo_path / link).exists():
 
                        yield errormod.BrokenLinkError(txn, key, link)
 

	
 
    def run(self, txn: Transaction) -> errormod.Iter:
 
        yield from self._check_links(txn, txn.meta)
 
        yield from self._check_links(txn.meta, txn)
 
        for post in txn.postings:
 
            if post.meta is not None:
 
                yield from self._check_links(txn, post.meta)
 
                yield from self._check_links(post.meta, txn, post)
conservancy_beancount/plugin/meta_rt_links.py
Show inline comments
...
 
@@ -21,11 +21,13 @@ from .. import errors as errormod
 
from ..beancount_types import (
 
    MetaKey,
 
    MetaValue,
 
    Posting,
 
    Transaction,
 
)
 

	
 
from typing import (
 
    Mapping,
 
    MutableMapping,
 
    Optional,
 
)
 

	
 
class MetaRTLinks(core.TransactionHook):
...
 
@@ -39,19 +41,26 @@ class MetaRTLinks(core.TransactionHook):
 
        self.rt = rt_wrapper
 

	
 
    def _check_links(self,
 
                     meta: MutableMapping[MetaKey, MetaValue],
 
                     txn: Transaction,
 
                     meta: Mapping[MetaKey, MetaValue],
 
                     post: Optional[Posting]=None,
 
    ) -> errormod.Iter:
 
        for key in self.LINK_METADATA.intersection(meta):
 
            for link in str(meta[key]).split():
 
                if not link.startswith('rt:'):
 
                    continue
 
                parsed = self.rt.parse(link)
 
                if parsed is None or not self.rt.exists(*parsed):
 
                    yield errormod.BrokenRTLinkError(txn, key, link, parsed)
 
        metadata = data.Metadata(meta)
 
        for key in self.LINK_METADATA:
 
            try:
 
                links = metadata.get_links(key)
 
            except TypeError:
 
                yield errormod.InvalidMetadataError(txn, key, meta[key], post)
 
            else:
 
                for link in links:
 
                    if not link.startswith('rt:'):
 
                        continue
 
                    parsed = self.rt.parse(link)
 
                    if parsed is None or not self.rt.exists(*parsed):
 
                        yield errormod.BrokenRTLinkError(txn, key, link, parsed)
 

	
 
    def run(self, txn: Transaction) -> errormod.Iter:
 
        yield from self._check_links(txn, txn.meta)
 
        yield from self._check_links(txn.meta, txn)
 
        for post in txn.postings:
 
            if post.meta is not None:
 
                yield from self._check_links(txn, post.meta)
 
                yield from self._check_links(post.meta, txn, post)
tests/test_meta_repo_links.py
Show inline comments
...
 
@@ -100,6 +100,18 @@ def test_bad_post_links(hook):
 
    actual = {error.message for error in hook.run(txn)}
 
    assert expected == actual
 

	
 
@pytest.mark.parametrize('value', testutil.NON_STRING_METADATA_VALUES)
 
def test_bad_metadata_type(hook, value):
 
    txn = testutil.Transaction(**{'check': value}, postings=[
 
        ('Income:Donations', -5),
 
        ('Assets:Cash', 5),
 
    ])
 
    expected = {'transaction has wrong type of check: expected str but is a {}'.format(
 
        type(value).__name__,
 
    )}
 
    actual = {error.message for error in hook.run(txn)}
 
    assert expected == actual
 

	
 
@pytest.mark.parametrize('ext_doc', [
 
    'rt:123',
 
    'rt:456/789',
tests/test_meta_rt_links.py
Show inline comments
...
 
@@ -119,6 +119,18 @@ def test_bad_post_links(hook, link_source, format_error):
 
    actual = {error.message for error in hook.run(txn)}
 
    assert expected == actual
 

	
 
@pytest.mark.parametrize('value', testutil.NON_STRING_METADATA_VALUES)
 
def test_bad_metadata_type(hook, value):
 
    txn = testutil.Transaction(**{'rt-id': value}, postings=[
 
        ('Income:Donations', -5),
 
        ('Assets:Cash', 5),
 
    ])
 
    expected = {'transaction has wrong type of rt-id: expected str but is a {}'.format(
 
        type(value).__name__,
 
    )}
 
    actual = {error.message for error in hook.run(txn)}
 
    assert expected == actual
 

	
 
@pytest.mark.parametrize('ext_doc', [
 
    'statement.txt',
 
    'https://example.org/',
0 comments (0 inline, 0 general)