From a23d075add0eb6b7bd83aa788efc8fac1066f9af 2020-06-09 13:04:27 From: Brett Smith Date: 2020-06-09 13:04:27 Subject: [PATCH] books: Add Loader.load_none() method. --- diff --git a/conservancy_beancount/books.py b/conservancy_beancount/books.py index 6a46050a30f3cd7c39c03498298148fb87dbb8ec..4cc427f6cba6cc198efe9666dc81a6981f95f7dd 100644 --- a/conservancy_beancount/books.py +++ b/conservancy_beancount/books.py @@ -30,6 +30,8 @@ from typing import ( Union, ) from .beancount_types import ( + Error, + Errors, LoadResult, ) @@ -171,3 +173,22 @@ class Loader: fy_range = self.fiscal_year.range(from_fy, to_fy) fy_paths = self._iter_fy_books(fy_range) return self._load_paths(fy_paths) + + @classmethod + def load_none(cls, config_path: Optional[PathLike]=None, lineno: int=0) -> LoadResult: + """Load no books and generate an error about it + + This is a convenience method for reporting tools that already handle + general Beancount errors. If a configuration problem prevents them from + loading the books, they can call this method in place of a regular + loading method, and then continue on their normal code path. + + The path and line number given in the arguments will be named as the + source of the error. + """ + source = { + 'filename': str(config_path or 'conservancy_beancount.ini'), + 'lineno': lineno, + } + errors: Errors = [Error(source, "no books to load in configuration", None)] + return [], errors, {} diff --git a/conservancy_beancount/reports/accrual.py b/conservancy_beancount/reports/accrual.py index 9f294882144f8b4b6aa496b2c0c466569692d65e..3bd3d8b58308f24a883259adebf694680f091e43 100644 --- a/conservancy_beancount/reports/accrual.py +++ b/conservancy_beancount/reports/accrual.py @@ -113,6 +113,7 @@ import rt from beancount.parser import printer as bc_printer from . import core +from .. import books from .. import cliutil from .. import config as configmod from .. import data @@ -693,12 +694,7 @@ def main(arglist: Optional[Sequence[str]]=None, books_loader = config.books_loader() if books_loader is None: - entries: Entries = [] - source = { - 'filename': str(config.config_file_path()), - 'lineno': 1, - } - load_errors: Errors = [Error(source, "no books to load in configuration", None)] + entries, load_errors, _ = books.Loader.load_none(config.config_file_path()) elif args.report_type is ReportType.AGING: entries, load_errors, _ = books_loader.load_all() else: diff --git a/tests/test_books_loader.py b/tests/test_books_loader.py index c79c7fb71d7dfe8ebaea91a7414ac87c28e8d638..081272d5bc32e95256a02623d02de1536cb3b8aa 100644 --- a/tests/test_books_loader.py +++ b/tests/test_books_loader.py @@ -107,3 +107,17 @@ def test_load_all_from_date(conservancy_loader, from_date): actual_years = txn_years(entries) assert actual_years.issuperset(range(from_year, 2021)) assert min(actual_years) == from_year + +def test_load_none_full_args(): + entries, errors, options_map = books.Loader.load_none('test.cfg', 42) + assert not entries + assert errors + assert all(err.source['filename'] == 'test.cfg' for err in errors) + assert all(err.source['lineno'] == 42 for err in errors) + +def test_load_none_no_args(): + entries, errors, options_map = books.Loader.load_none() + assert not entries + assert errors + assert all(isinstance(err.source['filename'], str) for err in errors) + assert all(isinstance(err.source['lineno'], int) for err in errors) diff --git a/tests/test_reports_accrual.py b/tests/test_reports_accrual.py index 230545ce535fde450c54c69941e0454ddb87456c..5ae73db255d8487f0413b72cf98ec94cb92e7275 100644 --- a/tests/test_reports_accrual.py +++ b/tests/test_reports_accrual.py @@ -737,7 +737,7 @@ def test_main_aging_report(tmp_path, arglist): def test_main_no_books(): check_main_fails([], testutil.TestConfig(), 1 | 8, [ - r':1: +no books to load in configuration\b', + r':[01]: +no books to load in configuration\b', ]) @pytest.mark.parametrize('arglist', [