From 3fbc14d377ac85ca9c54991502590e70f10b9e0e Mon Sep 17 00:00:00 2001 From: Brett Smith Date: Sun, 15 Mar 2020 16:01:40 -0400 Subject: [PATCH] Improve organization between modules. * Rename _typing to beancount_types to better reflect what it is. * LessComparable isn't a Beancount type, so move that to plugin.core with its dependent helper classes. * Errors are a core Beancount concept, so move that module to the top level and have it include appropriate type definitions. --- .../{_typing.py => beancount_types.py} | 13 +-------- conservancy_beancount/{plugin => }/errors.py | 10 +++++-- conservancy_beancount/plugin/__init__.py | 6 ++-- conservancy_beancount/plugin/core.py | 28 +++++++++++-------- .../plugin/meta_expense_allocation.py | 2 +- .../plugin/meta_tax_implication.py | 2 +- tests/test_plugin_run.py | 6 ++-- 7 files changed, 35 insertions(+), 32 deletions(-) rename conservancy_beancount/{_typing.py => beancount_types.py} (80%) rename conservancy_beancount/{plugin => }/errors.py (92%) diff --git a/conservancy_beancount/_typing.py b/conservancy_beancount/beancount_types.py similarity index 80% rename from conservancy_beancount/_typing.py rename to conservancy_beancount/beancount_types.py index 6bae7cc..e89197b 100644 --- a/conservancy_beancount/_typing.py +++ b/conservancy_beancount/beancount_types.py @@ -1,4 +1,4 @@ -"""Type definitions for Conservancy Beancount code""" +"""Type definitions for Beancount data structures""" # Copyright © 2020 Brett Smith # # This program is free software: you can redistribute it and/or modify @@ -18,7 +18,6 @@ import abc import datetime import beancount.core.data as bc_data -from .plugin import errors from typing import ( Any, @@ -33,21 +32,11 @@ from typing import ( ) Account = bc_data.Account -Error = errors._BaseError -ErrorIter = Iterable[Error] MetaKey = str MetaValue = Any MetaValueEnum = str Posting = bc_data.Posting -class LessComparable(metaclass=abc.ABCMeta): - @abc.abstractmethod - def __le__(self, other: Any) -> bool: ... - - @abc.abstractmethod - def __lt__(self, other: Any) -> bool: ... - - class Directive(NamedTuple): meta: bc_data.Meta date: datetime.date diff --git a/conservancy_beancount/plugin/errors.py b/conservancy_beancount/errors.py similarity index 92% rename from conservancy_beancount/plugin/errors.py rename to conservancy_beancount/errors.py index 6bd6842..e1eb348 100644 --- a/conservancy_beancount/plugin/errors.py +++ b/conservancy_beancount/errors.py @@ -14,7 +14,11 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -class _BaseError(Exception): +from typing import ( + Iterable, +) + +class Error(Exception): def __init__(self, message, entry, source=None): self.message = message self.entry = entry @@ -28,7 +32,9 @@ class _BaseError(Exception): ) -class InvalidMetadataError(_BaseError): +Iter = Iterable[Error] + +class InvalidMetadataError(Error): def __init__(self, txn, post, key, value=None, source=None): if value is None: msg_fmt = "{post.account} missing {key}" diff --git a/conservancy_beancount/plugin/__init__.py b/conservancy_beancount/plugin/__init__.py index b4370f9..797bf57 100644 --- a/conservancy_beancount/plugin/__init__.py +++ b/conservancy_beancount/plugin/__init__.py @@ -28,15 +28,17 @@ from typing import ( Tuple, Type, ) -from .._typing import ( +from ..beancount_types import ( ALL_DIRECTIVES, Directive, - Error, ) from .core import ( Hook, HookName, ) +from ..errors import ( + Error, +) __plugins__ = ['run'] diff --git a/conservancy_beancount/plugin/core.py b/conservancy_beancount/plugin/core.py index 41b8dff..0f3c3f9 100644 --- a/conservancy_beancount/plugin/core.py +++ b/conservancy_beancount/plugin/core.py @@ -18,9 +18,10 @@ import abc import datetime import re -from . import errors as errormod +from .. import errors as errormod from typing import ( + Any, FrozenSet, Generic, Iterable, @@ -29,12 +30,9 @@ from typing import ( Optional, TypeVar, ) -from .._typing import ( +from ..beancount_types import ( Account, Directive, - Error, - ErrorIter, - LessComparable, MetaKey, MetaValue, MetaValueEnum, @@ -62,7 +60,7 @@ class Hook(Generic[Entry], metaclass=abc.ABCMeta): HOOK_GROUPS: FrozenSet[HookName] = frozenset() @abc.abstractmethod - def run(self, entry: Entry) -> ErrorIter: ... + def run(self, entry: Entry) -> errormod.Iter: ... def __init_subclass__(cls): cls.DIRECTIVE = cls.__orig_bases__[0].__args__[0] @@ -72,6 +70,14 @@ TransactionHook = Hook[Transaction] ### HELPER CLASSES +class LessComparable(metaclass=abc.ABCMeta): + @abc.abstractmethod + def __le__(self, other: Any) -> bool: ... + + @abc.abstractmethod + def __lt__(self, other: Any) -> bool: ... + + CT = TypeVar('CT', bound=LessComparable) class _GenericRange(Generic[CT]): """Convenience class to check whether a value is within a range. @@ -200,14 +206,14 @@ class _PostingHook(TransactionHook, metaclass=abc.ABCMeta): def _run_on_post(self, txn: Transaction, post: Posting) -> bool: return True - def run(self, txn: Transaction) -> ErrorIter: + def run(self, txn: Transaction) -> errormod.Iter: if self._run_on_txn(txn): for index, post in enumerate(txn.postings): if self._run_on_post(txn, post): yield from self.post_run(txn, post, index) @abc.abstractmethod - def post_run(self, txn: Transaction, post: Posting, post_index: int) -> ErrorIter: ... + def post_run(self, txn: Transaction, post: Posting, post_index: int) -> errormod.Iter: ... class _NormalizePostingMetadataHook(_PostingHook): @@ -229,14 +235,14 @@ class _NormalizePostingMetadataHook(_PostingHook): def _default_value(self, txn: Transaction, post: Posting) -> MetaValueEnum: raise errormod.InvalidMetadataError(txn, post, self.METADATA_KEY) - def post_run(self, txn: Transaction, post: Posting, post_index: int) -> ErrorIter: + def post_run(self, txn: Transaction, post: Posting, post_index: int) -> errormod.Iter: source_value = self._meta_get(txn, post, self.METADATA_KEY) set_value = source_value - error: Optional[Error] = None + error: Optional[errormod.Error] = None if source_value is None: try: set_value = self._default_value(txn, post) - except errormod._BaseError as error_: + except errormod.Error as error_: error = error_ else: try: diff --git a/conservancy_beancount/plugin/meta_expense_allocation.py b/conservancy_beancount/plugin/meta_expense_allocation.py index 74ede4d..0a08823 100644 --- a/conservancy_beancount/plugin/meta_expense_allocation.py +++ b/conservancy_beancount/plugin/meta_expense_allocation.py @@ -15,7 +15,7 @@ # along with this program. If not, see . from . import core -from .._typing import ( +from ..beancount_types import ( MetaValueEnum, Posting, Transaction, diff --git a/conservancy_beancount/plugin/meta_tax_implication.py b/conservancy_beancount/plugin/meta_tax_implication.py index 044eeca..a9121f3 100644 --- a/conservancy_beancount/plugin/meta_tax_implication.py +++ b/conservancy_beancount/plugin/meta_tax_implication.py @@ -17,7 +17,7 @@ import decimal from . import core -from .._typing import ( +from ..beancount_types import ( Posting, Transaction, ) diff --git a/tests/test_plugin_run.py b/tests/test_plugin_run.py index f5f910d..9c9d229 100644 --- a/tests/test_plugin_run.py +++ b/tests/test_plugin_run.py @@ -18,14 +18,14 @@ import pytest from . import testutil -from conservancy_beancount import plugin, _typing +from conservancy_beancount import beancount_types, plugin CONFIG_MAP = {} HOOK_REGISTRY = plugin.HookRegistry() @HOOK_REGISTRY.add_hook class TransactionCounter: - DIRECTIVE = _typing.Transaction + DIRECTIVE = beancount_types.Transaction HOOK_GROUPS = frozenset() def run(self, txn): @@ -34,7 +34,7 @@ class TransactionCounter: @HOOK_REGISTRY.add_hook class PostingCounter(TransactionCounter): - DIRECTIVE = _typing.Transaction + DIRECTIVE = beancount_types.Transaction HOOK_GROUPS = frozenset(['posting']) def run(self, txn):