data: Add docstrings.
This commit is contained in:
		
							parent
							
								
									4fee91ad48
								
							
						
					
					
						commit
						a78db2ed36
					
				
					 1 changed files with 68 additions and 6 deletions
				
			
		| 
						 | 
					@ -1,4 +1,9 @@
 | 
				
			||||||
"""Enhanced Beancount data structures for Conservancy"""
 | 
					"""Enhanced Beancount data structures for Conservancy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The classes in this module are interface-compatible with Beancount's core data
 | 
				
			||||||
 | 
					structures, and provide additional business logic that we want to use
 | 
				
			||||||
 | 
					throughout Conservancy tools.
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
# Copyright © 2020  Brett Smith
 | 
					# Copyright © 2020  Brett Smith
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# This program is free software: you can redistribute it and/or modify
 | 
					# This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
| 
						 | 
					@ -33,6 +38,12 @@ from .beancount_types import (
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Account(str):
 | 
					class Account(str):
 | 
				
			||||||
 | 
					    """Account name string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This is a string that names an account, like Accrued:AccountsPayable
 | 
				
			||||||
 | 
					    or Income:Donations. This class provides additional methods for common
 | 
				
			||||||
 | 
					    account name parsing and queries.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
    SEP = bc_account.sep
 | 
					    SEP = bc_account.sep
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def is_income(self) -> bool:
 | 
					    def is_income(self) -> bool:
 | 
				
			||||||
| 
						 | 
					@ -45,6 +56,28 @@ class Account(str):
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def is_under(self, *acct_seq: str) -> Optional[str]:
 | 
					    def is_under(self, *acct_seq: str) -> Optional[str]:
 | 
				
			||||||
 | 
					        """Return a match if this account is "under" a part of the hierarchy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Pass in any number of account name strings as arguments. If this
 | 
				
			||||||
 | 
					        account is under one of those strings in the account hierarchy, the
 | 
				
			||||||
 | 
					        first matching string will be returned. Otherwise, None is returned.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        You can use the return value of this method as a boolean if you don't
 | 
				
			||||||
 | 
					        care which account string is matched.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        An account is considered to be under itself:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          Account('Expenses:Tax').is_under('Expenses:Tax') # returns 'Expenses:Tax'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        To do a "strictly under" search, end your search strings with colons:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          Account('Expenses:Tax').is_under('Expenses:Tax:') # returns None
 | 
				
			||||||
 | 
					          Account('Expenses:Tax').is_under('Expenses:') # returns 'Expenses:'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This method does check that all the account boundaries match:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          Account('Expenses:Tax').is_under('Exp') # returns None
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
        for prefix in acct_seq:
 | 
					        for prefix in acct_seq:
 | 
				
			||||||
            if self.startswith(prefix) and (
 | 
					            if self.startswith(prefix) and (
 | 
				
			||||||
                prefix.endswith(self.SEP)
 | 
					                prefix.endswith(self.SEP)
 | 
				
			||||||
| 
						 | 
					@ -56,6 +89,24 @@ class Account(str):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PostingMeta(collections.abc.MutableMapping):
 | 
					class PostingMeta(collections.abc.MutableMapping):
 | 
				
			||||||
 | 
					    """Combined access to posting metadata with its parent transaction metadata
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This lets you access posting metadata through a single dict-like object.
 | 
				
			||||||
 | 
					    If you try to look up metadata that doesn't exist on the posting, it will
 | 
				
			||||||
 | 
					    look for the value in the parent transaction metadata instead.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    You can set and delete metadata as well. Changes only affect the metadata
 | 
				
			||||||
 | 
					    of the posting, never the transaction. Changes are propagated to the
 | 
				
			||||||
 | 
					    underlying Beancount data structures.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Functionally, you can think of this as identical to:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      collections.ChainMap(post.meta, txn.meta)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Under the hood, this class does a little extra work to avoid creating
 | 
				
			||||||
 | 
					    posting metadata if it doesn't have to.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self,
 | 
					    def __init__(self,
 | 
				
			||||||
                 txn: Transaction,
 | 
					                 txn: Transaction,
 | 
				
			||||||
                 index: int,
 | 
					                 index: int,
 | 
				
			||||||
| 
						 | 
					@ -101,16 +152,27 @@ class PostingMeta(collections.abc.MutableMapping):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Posting(BasePosting):
 | 
					class Posting(BasePosting):
 | 
				
			||||||
 | 
					    """Enhanced Posting objects
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This class is a subclass of Beancount's native Posting class where
 | 
				
			||||||
 | 
					    specific fields are replaced with enhanced versions:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    * The `account` field is an Account object
 | 
				
			||||||
 | 
					    * The `meta` field is a PostingMeta object
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    account: Account
 | 
					    account: Account
 | 
				
			||||||
    # mypy correctly complains that our MutableMapping is not compatible with
 | 
					    # mypy correctly complains that our MutableMapping is not compatible
 | 
				
			||||||
    # Beancount's meta type declaration of Optional[Dict]. IMO this is a case
 | 
					    # with Beancount's meta type declaration of Optional[Dict]. IMO
 | 
				
			||||||
    # of Beancount's type declaration being a smidge too specific: I think it
 | 
					    # Beancount's type declaration is a smidge too specific: I think its type
 | 
				
			||||||
    # would be very unusual for code to actually require a dict over a more
 | 
					    # declaration should also use MutableMapping, because it would be very
 | 
				
			||||||
    # generic mapping. If it did, this would work fine.
 | 
					    # unusual for code to specifically require a Dict over that.
 | 
				
			||||||
 | 
					    # If it did, this declaration would pass without issue.
 | 
				
			||||||
    meta: MutableMapping[MetaKey, MetaValue]  # type:ignore[assignment]
 | 
					    meta: MutableMapping[MetaKey, MetaValue]  # type:ignore[assignment]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def iter_postings(txn: Transaction) -> Iterator[Posting]:
 | 
					def iter_postings(txn: Transaction) -> Iterator[Posting]:
 | 
				
			||||||
 | 
					    """Yield an enhanced Posting object for every posting in the transaction"""
 | 
				
			||||||
    for index, source in enumerate(txn.postings):
 | 
					    for index, source in enumerate(txn.postings):
 | 
				
			||||||
        yield Posting(
 | 
					        yield Posting(
 | 
				
			||||||
            Account(source.account),
 | 
					            Account(source.account),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue