data: Add PostingMeta.detached() method.
This commit is contained in:
parent
dfdb9b65d5
commit
52adf1f0a5
2 changed files with 36 additions and 9 deletions
|
@ -512,10 +512,9 @@ class PostingMeta(Metadata):
|
|||
self.txn = txn
|
||||
self.index = index
|
||||
self.post = post
|
||||
if post.meta is None:
|
||||
self.meta = self.txn.meta
|
||||
else:
|
||||
self.meta = collections.ChainMap(post.meta, txn.meta)
|
||||
self.meta: collections.ChainMap = collections.ChainMap(txn.meta)
|
||||
if post.meta is not None:
|
||||
self.meta = self.meta.new_child(post.meta)
|
||||
|
||||
def __getitem__(self, key: MetaKey) -> MetaValue:
|
||||
try:
|
||||
|
@ -527,17 +526,16 @@ class PostingMeta(Metadata):
|
|||
raise
|
||||
|
||||
def __setitem__(self, key: MetaKey, value: MetaValue) -> None:
|
||||
if self.post.meta is None:
|
||||
if len(self.meta.maps) == 1:
|
||||
self.post = self.post._replace(meta={key: value})
|
||||
assert self.post.meta is not None
|
||||
self.txn.postings[self.index] = self.post
|
||||
# mypy complains that self.post.meta could be None, but we know
|
||||
# from two lines up that it's not.
|
||||
self.meta = collections.ChainMap(self.post.meta, self.txn.meta) # type:ignore[arg-type]
|
||||
self.meta = self.meta.new_child(self.post.meta)
|
||||
else:
|
||||
super().__setitem__(key, value)
|
||||
|
||||
def __delitem__(self, key: MetaKey) -> None:
|
||||
if self.post.meta is None:
|
||||
if len(self.meta.maps) == 1:
|
||||
raise KeyError(key)
|
||||
else:
|
||||
super().__delitem__(key)
|
||||
|
@ -550,6 +548,17 @@ class PostingMeta(Metadata):
|
|||
def date(self) -> datetime.date:
|
||||
return self.txn.date
|
||||
|
||||
def detached(self) -> 'PostingMeta':
|
||||
"""Create a copy of this PostingMeta detached from the original post
|
||||
|
||||
Changes you make to the detached copy will not propagate to the
|
||||
underlying data structures. This is mostly useful for reporting code
|
||||
that may want to "split" and manipulate the metadata multiple times.
|
||||
"""
|
||||
retval = type(self)(self.txn, self.index, self.post)
|
||||
retval.meta = retval.meta.new_child()
|
||||
return retval
|
||||
|
||||
|
||||
class Posting(BasePosting):
|
||||
"""Enhanced Posting objects
|
||||
|
|
|
@ -126,6 +126,24 @@ def test_date(date):
|
|||
for index, post in enumerate(txn.postings):
|
||||
assert data.PostingMeta(txn, index, post).date == date
|
||||
|
||||
def test_mutable_copy():
|
||||
txn = testutil.Transaction(
|
||||
filename='f', lineno=130, txnkey='one', postings=[
|
||||
('Assets:Cash', 18),
|
||||
('Income:Donations', -18),
|
||||
])
|
||||
meta = data.PostingMeta(txn, 1).detached()
|
||||
meta['layerkey'] = 'two'
|
||||
assert dict(meta) == {
|
||||
'filename': 'f',
|
||||
'lineno': 130,
|
||||
'txnkey': 'one',
|
||||
'layerkey': 'two',
|
||||
}
|
||||
assert 'layerkey' not in txn.meta
|
||||
assert all(post.meta is None for post in txn.postings)
|
||||
assert meta.date == txn.date
|
||||
|
||||
# The .get() tests are arguably testing the stdlib, but they're short and
|
||||
# they confirm that we're using the stdlib as we intend.
|
||||
def test_get_with_meta_value(simple_txn):
|
||||
|
|
Loading…
Reference in a new issue