-
Notifications
You must be signed in to change notification settings - Fork 3
/
no_missing_documents.py
92 lines (61 loc) · 2.7 KB
/
no_missing_documents.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# Copyright (C) 2018 Stefano Zacchiroli <zack@upsilon.cc>
# License: GNU General Public License (GPL), version 2 or above
"""This Beancount plugin validates that documents referenced from Beancount
files correspond to existing files on disks. It will return a
DocumentNotFoundError for each document that cannot be found on disk.
Given a list of Beancount entries, the following documents will be checked by
the plugin:
* Filenames of ``document`` directives.
* Metadata associated to any directive, as long as the metadata key is in the
(configurable) set of keys that the plugin will consider.
By default, the plugin will look only for the `document` metadata. The
default can be overridden passing the plugin a configuration string, which
should be a comma-separated list of metadata keys, e.g.::
plugin "no_missing_documents" "receipt,statement,invoice"
"""
import collections
import os
from beancount.core import data
__plugins__ = ('validate_documents',)
DocumentNotFoundError = collections.namedtuple(
'DocumentNotFoundError',
'source message entry')
# default list of metada keys that are expected to point to existing documents
DEFAULT_KEYS = ['document']
CHECK_DOCUMENT_ENTRIES = False # disabled as Beancount core does this already
def parse_keys(config_str):
return config_str.split(',')
def check_missing(entry, doc_path):
errors = []
doc_path = os.path.expanduser(doc_path)
if 'filename' in entry.meta:
doc_path = os.path.join(os.path.dirname(entry.meta['filename']),
doc_path)
if not os.path.isfile(doc_path):
errors.append(DocumentNotFoundError(
entry.meta,
'Document not found: {}'.format(doc_path),
entry))
return errors
def validate_documents(entries, options_map, config_str=None):
"""Ensure that "document" metadata keys correspond to existing files
Args:
entries: a list of directives
options_map: an options map (unused)
Returns:
a pair formed by the input entries (unchanged) and a list of
DocumentNotFoundError errors (if any)
"""
errors = []
document_keys = DEFAULT_KEYS
if config_str is not None:
document_keys = parse_keys(config_str)
for entry in entries:
# check the filename property of document directives
if isinstance(entry, data.Document) and CHECK_DOCUMENT_ENTRIES:
errors.extend(check_missing(entry, entry.filename))
# check requested metadata of all directives (including documents)
for key in document_keys:
if key in entry.meta:
errors.extend(check_missing(entry, entry.meta[key]))
return entries, errors