diff --git a/api_reference/beancount.core.html b/api_reference/beancount.core.html index 17260b0..8f17d1b 100644 --- a/api_reference/beancount.core.html +++ b/api_reference/beancount.core.html @@ -218,8 +218,6 @@
beancount.core.account.AccountTransformer.parse(self, transformed_name)
+beancount.core.account.AccountTransformer.parse(self, transformed_name)
beancount/core/account.py
def parse(self, transformed_name: str) -> Account:
- "Convert the transform account name to an account name."
- return (
- transformed_name
- if self.rsep is None
- else transformed_name.replace(self.rsep, sep)
- )
-
def parse(self, transformed_name):
+ "Convert the transform account name to an account name."
+ return (transformed_name
+ if self.rsep is None
+ else transformed_name.replace(self.rsep, sep))
+
beancount.core.account.AccountTransformer.render(self, account_name)
+beancount.core.account.AccountTransformer.render(self, account_name)
beancount/core/account.py
def render(self, account_name: Account) -> str:
- "Convert the account name to a transformed account name."
- return account_name if self.rsep is None else account_name.replace(sep, self.rsep)
-
def render(self, account_name):
+ "Convert the account name to a transformed account name."
+ return (account_name
+ if self.rsep is None
+ else account_name.replace(sep, self.rsep))
+
@@ -1124,7 +1081,7 @@ beancount.core.account.commonprefix(accounts)
+beancount.core.account.commonprefix(accounts)
@@ -1143,7 +1100,7 @@ accounts (Iterable[str]
) – A sequence of account name strings.
accounts – A sequence of account name strings.
str
– A string, the common parent account. If none, returns an empty string.
A string, the common parent account. If none, returns an empty string.
beancount/core/account.py
def commonprefix(accounts: Iterable[Account]) -> Account:
- """Return the common prefix of a list of account names.
-
- Args:
- accounts: A sequence of account name strings.
- Returns:
- A string, the common parent account. If none, returns an empty string.
- """
- accounts_lists = [account_.split(sep) for account_ in accounts]
- # Note: the os.path.commonprefix() function just happens to work here.
- # Inspect its code, and even the special case of no common prefix
- # works well with str.join() below.
- common_list = path.commonprefix(accounts_lists)
- return sep.join(common_list)
-
def commonprefix(accounts):
+ """Return the common prefix of a list of account names.
+
+ Args:
+ accounts: A sequence of account name strings.
+ Returns:
+ A string, the common parent account. If none, returns an empty string.
+ """
+ accounts_lists = [account_.split(sep)
+ for account_ in accounts]
+ # Note: the os.path.commonprefix() function just happens to work here.
+ # Inspect its code, and even the special case of no common prefix
+ # works well with str.join() below.
+ common_list = path.commonprefix(accounts_lists)
+ return sep.join(common_list)
+
beancount.core.account.has_component(account_name, component)
+beancount.core.account.has_component(account_name, component)
account_name (str
) – A string, an account name.
component (str
) – A string, a component of an account name. For instance,
+
account_name – A string, an account name.
component – A string, a component of an account name. For instance,
Food
in Expenses:Food:Restaurant
. All components are considered.
beancount/core/account.py
def has_component(account_name: Account, component: str) -> bool:
- """Return true if one of the account contains a given component.
-
- Args:
- account_name: A string, an account name.
- component: A string, a component of an account name. For instance,
- ``Food`` in ``Expenses:Food:Restaurant``. All components are considered.
- Returns:
- Boolean: true if the component is in the account. Note that a component
- name must be whole, that is ``NY`` is not in ``Expenses:Taxes:StateNY``.
- """
- return bool(re.search("(^|:){}(:|$)".format(component), account_name))
-
def has_component(account_name, component):
+ """Return true if one of the account contains a given component.
+
+ Args:
+ account_name: A string, an account name.
+ component: A string, a component of an account name. For instance,
+ ``Food`` in ``Expenses:Food:Restaurant``. All components are considered.
+ Returns:
+ Boolean: true if the component is in the account. Note that a component
+ name must be whole, that is ``NY`` is not in ``Expenses:Taxes:StateNY``.
+ """
+ return bool(re.search('(^|:){}(:|$)'.format(component), account_name))
+
beancount.core.account.is_valid(string)
+beancount.core.account.is_valid(string)
string (str
) – A string, to be checked for account name pattern.
string – A string, to be checked for account name pattern.
bool
– A boolean, true if the string has the form of an account's name.
A boolean, true if the string has the form of an account's name.
beancount/core/account.py
def is_valid(string: Account) -> bool:
- """Return true if the given string is a valid account name.
- This does not check for the root account types, just the general syntax.
-
- Args:
- string: A string, to be checked for account name pattern.
- Returns:
- A boolean, true if the string has the form of an account's name.
- """
- return isinstance(string, str) and bool(regex.match("{}$".format(ACCOUNT_RE), string))
-
def is_valid(string):
+ """Return true if the given string is a valid account name.
+ This does not check for the root account types, just the general syntax.
+
+ Args:
+ string: A string, to be checked for account name pattern.
+ Returns:
+ A boolean, true if the string has the form of an account's name.
+ """
+ return (isinstance(string, str) and
+ bool(re.match('{}$'.format(ACCOUNT_RE), string)))
+
beancount.core.account.join(*components)
+beancount.core.account.join(*components)
*components (Tuple[str]
) – Strings, the components of an account name.
*components – Strings, the components of an account name.
str
– A string, joined in a single account name.
A string, joined in a single account name.
beancount/core/account.py
def join(*components: Tuple[str]) -> Account:
- """Join the names with the account separator.
-
- Args:
- *components: Strings, the components of an account name.
- Returns:
- A string, joined in a single account name.
- """
- return sep.join(components)
-
def join(*components):
+ """Join the names with the account separator.
+
+ Args:
+ *components: Strings, the components of an account name.
+ Returns:
+ A string, joined in a single account name.
+ """
+ return sep.join(components)
+
beancount.core.account.leaf(account_name)
+beancount.core.account.leaf(account_name)
account_name (str
) – A string, the name of the account whose leaf name to return.
account_name – A string, the name of the account whose leaf name to return.
str
– A string, the name of the leaf of the account.
A string, the name of the leaf of the account.
beancount/core/account.py
def leaf(account_name: Account) -> Account:
- """Get the name of the leaf of this account.
-
- Args:
- account_name: A string, the name of the account whose leaf name to return.
- Returns:
- A string, the name of the leaf of the account.
- """
- assert isinstance(account_name, str)
- return account_name.split(sep)[-1] if account_name else None
-
def leaf(account_name):
+ """Get the name of the leaf of this account.
+
+ Args:
+ account_name: A string, the name of the account whose leaf name to return.
+ Returns:
+ A string, the name of the leaf of the account.
+ """
+ assert isinstance(account_name, str)
+ return account_name.split(sep)[-1] if account_name else None
+
beancount.core.account.parent(account_name)
+beancount.core.account.parent(account_name)
account_name (str
) – A string, the name of the account whose parent to return.
account_name – A string, the name of the account whose parent to return.
str
– A string, the name of the parent account of this account.
A string, the name of the parent account of this account.
beancount/core/account.py
def parent(account_name: Account) -> Account:
- """Return the name of the parent account of the given account.
-
- Args:
- account_name: A string, the name of the account whose parent to return.
- Returns:
- A string, the name of the parent account of this account.
- """
- assert isinstance(account_name, str), account_name
- if not account_name:
- return None
- components = account_name.split(sep)
- components.pop(-1)
- return sep.join(components)
-
def parent(account_name):
+ """Return the name of the parent account of the given account.
+
+ Args:
+ account_name: A string, the name of the account whose parent to return.
+ Returns:
+ A string, the name of the parent account of this account.
+ """
+ assert isinstance(account_name, str), account_name
+ if not account_name:
+ return None
+ components = account_name.split(sep)
+ components.pop(-1)
+ return sep.join(components)
+
beancount.core.account.parent_matcher(account_name)
+beancount.core.account.parent_matcher(account_name)
account_name (str
) – The name of the parent account we want to check for.
account_name – The name of the parent account we want to check for.
Callable[[str], Any]
– A callable, which, when called, will return true if the given account is a
+
A callable, which, when called, will return true if the given account is a
child of account_name
.
beancount/core/account.py
def parent_matcher(account_name: Account) -> Callable[[str], Any]:
- """Build a predicate that returns whether an account is under the given one.
-
- Args:
- account_name: The name of the parent account we want to check for.
- Returns:
- A callable, which, when called, will return true if the given account is a
- child of ``account_name``.
- """
- return re.compile(r"{}($|{})".format(re.escape(account_name), sep)).match
-
def parent_matcher(account_name):
+ """Build a predicate that returns whether an account is under the given one.
+
+ Args:
+ account_name: The name of the parent account we want to check for.
+ Returns:
+ A callable, which, when called, will return true if the given account is a
+ child of ``account_name``.
+ """
+ return re.compile(r'{}($|{})'.format(re.escape(account_name), sep)).match
+
beancount.core.account.parents(account_name)
+beancount.core.account.parents(account_name)
account_name (str
) – The name of the account we want to start iterating from.
account_name – The name of the account we want to start iterating from.
Iterator[str]
– A generator of account name strings.
A generator of account name strings.
beancount/core/account.py
def parents(account_name: Account) -> Iterator[Account]:
- """A generator of the names of the parents of this account, including this account.
-
- Args:
- account_name: The name of the account we want to start iterating from.
- Returns:
- A generator of account name strings.
- """
- while account_name:
- yield account_name
- account_name = parent(account_name)
-
def parents(account_name):
+ """A generator of the names of the parents of this account, including this account.
+
+ Args:
+ account_name: The name of the account we want to start iterating from.
+ Returns:
+ A generator of account name strings.
+ """
+ while account_name:
+ yield account_name
+ account_name = parent(account_name)
+
beancount.core.account.root(num_components, account_name)
+beancount.core.account.root(num_components, account_name)
num_components (int
) – An integer, the number of components to return.
account_name (str
) – A string, an account name.
num_components – An integer, the number of components to return.
account_name – A string, an account name.
str
– A string, the account root up to 'num_components' components.
A string, the account root up to 'num_components' components.
beancount/core/account.py
def root(num_components: int, account_name: Account) -> str:
- """Return the first few components of an account's name.
-
- Args:
- num_components: An integer, the number of components to return.
- account_name: A string, an account name.
- Returns:
- A string, the account root up to 'num_components' components.
- """
- return join(*(split(account_name)[:num_components]))
-
def root(num_components, account_name):
+ """Return the first few components of an account's name.
+
+ Args:
+ num_components: An integer, the number of components to return.
+ account_name: A string, an account name.
+ Returns:
+ A string, the account root up to 'num_components' components.
+ """
+ return join(*(split(account_name)[:num_components]))
+
beancount.core.account.sans_root(account_name)
+beancount.core.account.sans_root(account_name)
account_name (str
) – A string, the name of the account whose leaf name to return.
account_name – A string, the name of the account whose leaf name to return.
str
– A string, the name of the non-root portion of this account name.
A string, the name of the non-root portion of this account name.
beancount/core/account.py
def sans_root(account_name: Account) -> Account:
- """Get the name of the account without the root.
-
- For example, an input of 'Assets:BofA:Checking' will produce 'BofA:Checking'.
-
- Args:
- account_name: A string, the name of the account whose leaf name to return.
- Returns:
- A string, the name of the non-root portion of this account name.
- """
- assert isinstance(account_name, str)
- components = account_name.split(sep)[1:]
- return join(*components) if account_name else None
-
def sans_root(account_name):
+ """Get the name of the account without the root.
+
+ For example, an input of 'Assets:BofA:Checking' will produce 'BofA:Checking'.
+
+ Args:
+ account_name: A string, the name of the account whose leaf name to return.
+ Returns:
+ A string, the name of the non-root portion of this account name.
+ """
+ assert isinstance(account_name, str)
+ components = account_name.split(sep)[1:]
+ return join(*components) if account_name else None
+
beancount.core.account.split(account_name)
+beancount.core.account.split(account_name)
account_name (str
) – A string, an account name.
account_name – A string, an account name.
List[str]
– A list of strings, the components of the account name (without the separators).
A list of strings, the components of the account name (without the separators).
beancount/core/account.py
def split(account_name: Account) -> List[str]:
- """Split an account's name into its components.
-
- Args:
- account_name: A string, an account name.
- Returns:
- A list of strings, the components of the account name (without the separators).
- """
- return account_name.split(sep)
-
def split(account_name):
+ """Split an account's name into its components.
+
+ Args:
+ account_name: A string, an account name.
+ Returns:
+ A list of strings, the components of the account name (without the separators).
+ """
+ return account_name.split(sep)
+
beancount.core.account.walk(root_directory)
+beancount.core.account.walk(root_directory)
root_directory (str
) – A string, the name of the root of the hierarchy to be walked.
root_directory – A string, the name of the root of the hierarchy to be walked.
beancount/core/account.py
def walk(root_directory: Account) -> Iterator[Tuple[str, Account, List[str], List[str]]]:
- """A version of os.walk() which yields directories that are valid account names.
-
- This only yields directories that are accounts... it skips the other ones.
- For convenience, it also yields you the account's name.
-
- Args:
- root_directory: A string, the name of the root of the hierarchy to be walked.
- Yields:
- Tuples of (root, account-name, dirs, files), similar to os.walk().
- """
- for root, dirs, files in os.walk(root_directory):
- dirs.sort()
- files.sort()
- relroot = root[len(root_directory) + 1 :]
- account_name = relroot.replace(os.sep, sep)
- # The regex module does not handle Unicode characters in decomposed
- # form. Python uses the normal form for representing string. However,
- # some filesystems use the canonical decomposition form.
- # See https://docs.python.org/3/library/unicodedata.html#unicodedata.normalize
- account_name = unicodedata.normalize("NFKC", account_name)
- if is_valid(account_name):
- yield (root, account_name, dirs, files)
-
def walk(root_directory):
+ """A version of os.walk() which yields directories that are valid account names.
+
+ This only yields directories that are accounts... it skips the other ones.
+ For convenience, it also yields you the account's name.
+
+ Args:
+ root_directory: A string, the name of the root of the hierarchy to be walked.
+ Yields:
+ Tuples of (root, account-name, dirs, files), similar to os.walk().
+ """
+ for root, dirs, files in os.walk(root_directory):
+ dirs.sort()
+ files.sort()
+ relroot = root[len(root_directory)+1:]
+ account_name = relroot.replace(os.sep, sep)
+ if is_valid(account_name):
+ yield (root, account_name, dirs, files)
+
beancount.core.account_types.AccountTypes.__getnewargs__(self)
+beancount.core.account_types.AccountTypes.__getnewargs__(self)
special
@@ -2024,10 +1978,10 @@ beancount/core/account_types.py
def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return _tuple(self)
-
def __getnewargs__(self):
+ 'Return self as a plain tuple. Used by copy and pickle.'
+ return _tuple(self)
+
@@ -2040,7 +1994,7 @@ beancount.core.account_types.AccountTypes.__new__(_cls, assets, liabilities, equity, income, expenses)
+beancount.core.account_types.AccountTypes.__new__(_cls, assets, liabilities, equity, income, expenses)
special
@@ -2064,7 +2018,7 @@ beancount.core.account_types.AccountTypes.__repr__(self)
+beancount.core.account_types.AccountTypes.__repr__(self)
special
@@ -2078,10 +2032,10 @@ beancount/core/account_types.py
def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + repr_fmt % self
-
def __repr__(self):
+ 'Return a nicely formatted representation string'
+ return self.__class__.__name__ + repr_fmt % self
+
@@ -2105,7 +2059,7 @@ beancount.core.account_types.get_account_sign(account_name, account_types=None)
+beancount.core.account_types.get_account_sign(account_name, account_types=None)
account_name (str
) – A string, the name of the account whose sign is to return.
account_types (AccountTypes
) – An optional instance of the current account_types.
account_name – A string, the name of the account whose sign is to return.
account_types – An optional instance of the current account_types.
int
– +1 or -1, depending on the account's type.
+1 or -1, depending on the account's type.
beancount/core/account_types.py
def get_account_sign(account_name: Account, account_types: AccountTypes = None) -> int:
- """Return the sign of the normal balance of a particular account.
-
- Args:
- account_name: A string, the name of the account whose sign is to return.
- account_types: An optional instance of the current account_types.
- Returns:
- +1 or -1, depending on the account's type.
- """
- if account_types is None:
- account_types = DEFAULT_ACCOUNT_TYPES
- assert isinstance(account_name, str), "Account is not a string: {}".format(account_name)
- account_type = get_account_type(account_name)
- return +1 if account_type in (account_types.assets, account_types.expenses) else -1
-
def get_account_sign(account_name, account_types=None):
+ """Return the sign of the normal balance of a particular account.
+
+ Args:
+ account_name: A string, the name of the account whose sign is to return.
+ account_types: An optional instance of the current account_types.
+ Returns:
+ +1 or -1, depending on the account's type.
+ """
+ if account_types is None:
+ account_types = DEFAULT_ACCOUNT_TYPES
+ assert isinstance(account_name, str), "Account is not a string: {}".format(account_name)
+ account_type = get_account_type(account_name)
+ return (+1
+ if account_type in (account_types.assets,
+ account_types.expenses)
+ else -1)
+
beancount.core.account_types.get_account_sort_key(account_types, account_name)
+beancount.core.account_types.get_account_sort_key(account_types, account_name)
account_types (AccountTypes
) – An instance of AccountTypes, a tuple of account type names.
account_types – An instance of AccountTypes, a tuple of account type names.
Tuple[str, str]
– An object to use as the 'key' argument to the sort function.
A function object to use as the optional 'key' argument to the sort +function. It accepts a single argument, the account name to sort and +produces a sortable key.
beancount/core/account_types.py
def get_account_sort_key(
- account_types: AccountTypes, account_name: Account
-) -> Tuple[str, Account]:
- """Return a tuple that can be used to order/sort account names.
-
- Args:
- account_types: An instance of AccountTypes, a tuple of account type names.
- Returns:
- An object to use as the 'key' argument to the sort function.
- """
- return (account_types.index(get_account_type(account_name)), account_name)
-
def get_account_sort_key(account_types, account_name):
+ """Return a tuple that can be used to order/sort account names.
+
+ Args:
+ account_types: An instance of AccountTypes, a tuple of account type names.
+ Returns:
+ A function object to use as the optional 'key' argument to the sort
+ function. It accepts a single argument, the account name to sort and
+ produces a sortable key.
+ """
+ return (account_types.index(get_account_type(account_name)), account_name)
+
@@ -2243,7 +2202,7 @@ beancount.core.account_types.get_account_type(account_name)
+beancount.core.account_types.get_account_type(account_name)
account_name (str
) – A string, the name of the account whose type is to return.
account_name – A string, the name of the account whose type is to return.
beancount/core/account_types.py
def get_account_type(account_name: Account):
- """Return the type of this account's name.
+ def get_account_type(account_name):
+ """Return the type of this account's name.
- Warning: No check is made on the validity of the account type. This merely
- returns the root account of the corresponding account name.
+ Warning: No check is made on the validity of the account type. This merely
+ returns the root account of the corresponding account name.
- Args:
- account_name: A string, the name of the account whose type is to return.
- Returns:
- A string, the type of the account in 'account_name'.
+ Args:
+ account_name: A string, the name of the account whose type is to return.
+ Returns:
+ A string, the type of the account in 'account_name'.
- """
- assert isinstance(account_name, str), "Account is not a string: {}".format(account_name)
- return account.split(account_name)[0]
-
beancount.core.account_types.is_account_type(account_type, account_name)
+beancount.core.account_types.is_account_type(account_type, account_name)
account_type (str
) – A string, the prefix type of the account.
account_name (str
) – A string, the name of the account whose type is to return.
account_type – A string, the prefix type of the account.
account_name – A string, the name of the account whose type is to return.
bool
– A boolean, true if the account is of the given type.
A boolean, true if the account is of the given type.
beancount/core/account_types.py
def is_account_type(account_type: str, account_name: Account) -> bool:
- """Return the type of this account's name.
-
- Warning: No check is made on the validity of the account type. This merely
- returns the root account of the corresponding account name.
-
- Args:
- account_type: A string, the prefix type of the account.
- account_name: A string, the name of the account whose type is to return.
- Returns:
- A boolean, true if the account is of the given type.
- """
- return bool(re.match("^{}{}".format(account_type, account.sep), account_name))
-
def is_account_type(account_type, account_name):
+ """Return the type of this account's name.
+
+ Warning: No check is made on the validity of the account type. This merely
+ returns the root account of the corresponding account name.
+
+ Args:
+ account_type: A string, the prefix type of the account.
+ account_name: A string, the name of the account whose type is to return.
+ Returns:
+ A boolean, true if the account is of the given type.
+ """
+ return bool(re.match('^{}{}'.format(account_type, account.sep), account_name))
+
beancount.core.account_types.is_balance_sheet_account(account_name, account_types)
+beancount.core.account_types.is_balance_sheet_account(account_name, account_types)
account_name (str
) – A string, an account name.
account_types (AccountTypes
) – An instance of AccountTypes.
account_name – A string, an account name.
account_types – An instance of AccountTypes.
bool
– A boolean, true if the account is a balance sheet account.
A boolean, true if the account is a balance sheet account.
beancount/core/account_types.py
def is_balance_sheet_account(account_name: Account, account_types: AccountTypes) -> bool:
- """Return true if the given account is a balance sheet account.
- Assets, liabilities and equity accounts are balance sheet accounts.
-
- Args:
- account_name: A string, an account name.
- account_types: An instance of AccountTypes.
- Returns:
- A boolean, true if the account is a balance sheet account.
- """
- assert isinstance(account_name, str), "Account is not a string: {}".format(account_name)
- assert isinstance(
- account_types, AccountTypes
- ), "Account types has invalid type: {}".format(account_types)
- account_type = get_account_type(account_name)
- return account_type in (
- account_types.assets,
- account_types.liabilities,
- account_types.equity,
- )
-
def is_balance_sheet_account(account_name, account_types):
+ """Return true if the given account is a balance sheet account.
+ Assets, liabilities and equity accounts are balance sheet accounts.
+
+ Args:
+ account_name: A string, an account name.
+ account_types: An instance of AccountTypes.
+ Returns:
+ A boolean, true if the account is a balance sheet account.
+ """
+ assert isinstance(account_name, str), "Account is not a string: {}".format(account_name)
+ assert isinstance(account_types, AccountTypes), (
+ "Account types has invalid type: {}".format(account_types))
+ account_type = get_account_type(account_name)
+ return account_type in (account_types.assets,
+ account_types.liabilities,
+ account_types.equity)
+
@@ -2465,7 +2421,7 @@ beancount.core.account_types.is_equity_account(account_name, account_types)
+beancount.core.account_types.is_equity_account(account_name, account_types)
account_name (str
) – A string, an account name.
account_types (AccountTypes
) – An instance of AccountTypes.
account_name – A string, an account name.
account_types – An instance of AccountTypes.
bool
– A boolean, true if the account is an equity account.
A boolean, true if the account is an equity account.
beancount/core/account_types.py
def is_equity_account(account_name: Account, account_types: AccountTypes) -> bool:
- """Return true if the given account is an equity account.
-
- Args:
- account_name: A string, an account name.
- account_types: An instance of AccountTypes.
- Returns:
- A boolean, true if the account is an equity account.
- """
- assert isinstance(account_name, str), "Account is not a string: {}".format(account_name)
- assert isinstance(
- account_types, AccountTypes
- ), "Account types has invalid type: {}".format(account_types)
- account_type = get_account_type(account_name)
- return account_type == account_types.equity
-
def is_equity_account(account_name, account_types):
+ """Return true if the given account is an equity account.
+
+ Args:
+ account_name: A string, an account name.
+ account_types: An instance of AccountTypes.
+ Returns:
+ A boolean, true if the account is an equity account.
+ """
+ assert isinstance(account_name, str), "Account is not a string: {}".format(account_name)
+ assert isinstance(account_types, AccountTypes), (
+ "Account types has invalid type: {}".format(account_types))
+ account_type = get_account_type(account_name)
+ return account_type == account_types.equity
+
beancount.core.account_types.is_income_statement_account(account_name, account_types)
+beancount.core.account_types.is_income_statement_account(account_name, account_types)
account_name (str
) – A string, an account name.
account_types (AccountTypes
) – An instance of AccountTypes.
Returns: | -
-
|
-
---|
beancount/core/account_types.py
def is_income_statement_account(account_name: Account, account_types: AccountTypes) -> bool:
- """Return true if the given account is an income statement account.
- Income and expense accounts are income statement accounts.
-
- Args:
- account_name: A string, an account name.
- account_types: An instance of AccountTypes.
- Returns:
- A boolean, true if the account is an income statement account.
- """
- assert isinstance(account_name, str), "Account is not a string: {}".format(account_name)
- assert isinstance(
- account_types, AccountTypes
- ), "Account types has invalid type: {}".format(account_types)
- account_type = get_account_type(account_name)
- return account_type in (account_types.income, account_types.expenses)
-
beancount.core.account_types.is_inverted_account(account_name, account_types)
-
-
-Return true if the given account has inverted signs.
-An inverted sign is the inverse as you'd expect in an external report, i.e., -with all positive signs expected.
- -Parameters: | -
-
|
|
@@ -2657,29 +2537,23 @@
---|
Parameters: |
|
@@ -2729,7 +2607,7 @@ Returns: |
|
@@ -2737,20 +2615,29 @@
---|
beancount/core/account_types.py
def is_root_account(account_name: Account) -> bool:
- """Return true if the account name is a root account.
-
- This function does not verify whether the account root is a valid
- one, just that it is a root account or not.
-
- Args:
- account_name: A string, the name of the account to check for.
- Returns:
- A boolean, true if the account is root account.
- """
- assert isinstance(account_name, str), "Account is not a string: {}".format(account_name)
- return account_name and bool(re.match(r"([A-Z][A-Za-z0-9\-]+)$", account_name))
-
def is_root_account(account_name, account_types=None):
+ """Return true if the account name is a root account.
+ This function does not verify whether the account root is a valid
+ one, just that it is a root account or not.
+
+ Args:
+ account_name: A string, the name of the account to check for.
+ account_types: An optional instance of the current account_types;
+ if provided, we check against these values. If not provided, we
+ merely check that name pattern is that of an account component with
+ no separator.
+ Returns:
+ A boolean, true if the account is root account.
+ """
+ assert isinstance(account_name, str), "Account is not a string: {}".format(account_name)
+ if account_types is not None:
+ assert isinstance(account_types, AccountTypes), (
+ "Account types has invalid type: {}".format(account_types))
+ return account_name in account_types
+ else:
+ return (account_name and
+ bool(re.match(r'([A-Z][A-Za-z0-9\-]+)$', account_name)))
+
beancount.core.amount.Amount.__bool__(self)
+beancount.core.amount.Amount.__bool__(self)
special
@@ -2870,13 +2758,13 @@ beancount/core/amount.py
def __bool__(self):
- """Boolean predicate returns true if the number is non-zero.
- Returns:
- A boolean, true if non-zero number.
- """
- return self.number != ZERO
-
def __bool__(self):
+ """Boolean predicate returns true if the number is non-zero.
+ Returns:
+ A boolean, true if non-zero number.
+ """
+ return self.number != ZERO
+
beancount.core.amount.Amount.__eq__(self, other)
+beancount.core.amount.Amount.__eq__(self, other)
special
@@ -2919,15 +2807,15 @@ beancount/core/amount.py
def __eq__(self, other):
- """Equality predicate. Returns true if both number and currency are equal.
- Returns:
- A boolean.
- """
- if other is None:
- return False
- return (self.number, self.currency) == (other.number, other.currency)
-
def __eq__(self, other):
+ """Equality predicate. Returns true if both number and currency are equal.
+ Returns:
+ A boolean.
+ """
+ if other is None:
+ return False
+ return (self.number, self.currency) == (other.number, other.currency)
+
beancount.core.amount.Amount.__hash__(self)
+beancount.core.amount.Amount.__hash__(self)
special
@@ -2970,13 +2858,13 @@ beancount/core/amount.py
def __hash__(self):
- """A hashing function for amounts. The hash includes the currency.
- Returns:
- An integer, the hash for this amount.
- """
- return hash((self.number, self.currency))
-
def __hash__(self):
+ """A hashing function for amounts. The hash includes the currency.
+ Returns:
+ An integer, the hash for this amount.
+ """
+ return hash((self.number, self.currency))
+
beancount.core.amount.Amount.__lt__(self, other)
+beancount.core.amount.Amount.__lt__(self, other)
special
@@ -3035,15 +2923,15 @@ beancount/core/amount.py
def __lt__(self, other):
- """Ordering comparison. This is used in the sorting key of positions.
- Args:
- other: An instance of Amount.
- Returns:
- True if this is less than the other Amount.
- """
- return sortkey(self) < sortkey(other)
-
def __lt__(self, other):
+ """Ordering comparison. This is used in the sorting key of positions.
+ Args:
+ other: An instance of Amount.
+ Returns:
+ True if this is less than the other Amount.
+ """
+ return sortkey(self) < sortkey(other)
+
beancount.core.amount.Amount.__neg__(self)
+beancount.core.amount.Amount.__neg__(self)
special
@@ -3086,13 +2974,13 @@ beancount/core/amount.py
def __neg__(self):
- """Return the negative of this amount.
- Returns:
- A new instance of Amount, with the negative number of units.
- """
- return Amount(-self.number, self.currency)
-
def __neg__(self):
+ """Return the negative of this amount.
+ Returns:
+ A new instance of Amount, with the negative number of units.
+ """
+ return Amount(-self.number, self.currency)
+
beancount.core.amount.Amount.__new__(cls, number, currency)
+beancount.core.amount.Amount.__new__(cls, number, currency)
special
@@ -3128,7 +3016,7 @@ number – A Decimal instance.
number – A string or Decimal instance. Will get converted automatically.
currency – A string, the currency symbol to use.
beancount/core/amount.py
def __new__(cls, number, currency):
- """Constructor from a number and currency.
-
- Args:
- number: A Decimal instance.
- currency: A string, the currency symbol to use.
- """
- assert isinstance(number, Amount.valid_types_number), repr(number)
- assert isinstance(currency, Amount.valid_types_currency), repr(currency)
- return _Amount.__new__(cls, number, currency)
-
def __new__(cls, number, currency):
+ """Constructor from a number and currency.
+
+ Args:
+ number: A string or Decimal instance. Will get converted automatically.
+ currency: A string, the currency symbol to use.
+ """
+ assert isinstance(number, Amount.valid_types_number), repr(number)
+ assert isinstance(currency, Amount.valid_types_currency), repr(currency)
+ return _Amount.__new__(cls, number, currency)
+
beancount.core.amount.Amount.__repr__(self)
+beancount.core.amount.Amount.__repr__(self)
special
@@ -3190,14 +3078,14 @@ beancount/core/amount.py
def __str__(self):
- """Convert an Amount instance to a printable string with the defaults.
-
- Returns:
- A formatted string of the quantized amount and symbol.
- """
- return self.to_string()
-
def __str__(self):
+ """Convert an Amount instance to a printable string with the defaults.
+
+ Returns:
+ A formatted string of the quantized amount and symbol.
+ """
+ return self.to_string()
+
beancount.core.amount.Amount.__str__(self)
+beancount.core.amount.Amount.__str__(self)
special
@@ -3240,14 +3128,14 @@ beancount/core/amount.py
def __str__(self):
- """Convert an Amount instance to a printable string with the defaults.
-
- Returns:
- A formatted string of the quantized amount and symbol.
- """
- return self.to_string()
-
def __str__(self):
+ """Convert an Amount instance to a printable string with the defaults.
+
+ Returns:
+ A formatted string of the quantized amount and symbol.
+ """
+ return self.to_string()
+
beancount.core.amount.Amount.from_string(string)
+beancount.core.amount.Amount.from_string(string)
staticmethod
@@ -3307,25 +3195,24 @@ beancount/core/amount.py
@staticmethod
-def from_string(string):
- """Create an amount from a string.
-
- This is a miniature parser used for building tests.
-
- Args:
- string: A string of <number> <currency>.
- Returns:
- A new instance of Amount.
- """
- match = re.match(
- r"\s*([-+]?[0-9.]+)\s+({currency})".format(currency=CURRENCY_RE), string
- )
- if not match:
- raise ValueError("Invalid string for amount: '{}'".format(string))
- number, currency = match.group(1, 2)
- return Amount(D(number), currency)
-
@staticmethod
+def from_string(string):
+ """Create an amount from a string.
+
+ This is a miniature parser used for building tests.
+
+ Args:
+ string: A string of <number> <currency>.
+ Returns:
+ A new instance of Amount.
+ """
+ match = re.match(r'\s*([-+]?[0-9.]+)\s+({currency})'.format(currency=CURRENCY_RE),
+ string)
+ if not match:
+ raise ValueError("Invalid string for amount: '{}'".format(string))
+ number, currency = match.group(1, 2)
+ return Amount(D(number), currency)
+
beancount.core.amount.Amount.to_string(self, dformat=<beancount.core.display_context.DisplayFormatter object at 0x78fcde6b7290>)
+beancount.core.amount.Amount.to_string(self, dformat=<beancount.core.display_context.DisplayFormatter object at 0x7a32089c1e20>)
beancount/core/amount.py
def to_string(self, dformat=DEFAULT_FORMATTER):
- """Convert an Amount instance to a printable string.
-
- Args:
- dformat: An instance of DisplayFormatter.
- Returns:
- A formatted string of the quantized amount and symbol.
- """
- if isinstance(self.number, Decimal):
- number_fmt = dformat.format(self.number, self.currency)
- elif self.number is MISSING:
- number_fmt = ""
- else:
- number_fmt = str(self.number)
- return "{} {}".format(number_fmt, self.currency)
-
def to_string(self, dformat=DEFAULT_FORMATTER):
+ """Convert an Amount instance to a printable string.
+
+ Args:
+ dformat: An instance of DisplayFormatter.
+ Returns:
+ A formatted string of the quantized amount and symbol.
+ """
+ number_fmt = (dformat.format(self.number, self.currency)
+ if isinstance(self.number, Decimal)
+ else str(self.number))
+ return "{} {}".format(number_fmt, self.currency)
+
beancount.core.amount.A(string)
+beancount.core.amount.A(string)
beancount/core/amount.py
@staticmethod
-def from_string(string):
- """Create an amount from a string.
-
- This is a miniature parser used for building tests.
-
- Args:
- string: A string of <number> <currency>.
- Returns:
- A new instance of Amount.
- """
- match = re.match(
- r"\s*([-+]?[0-9.]+)\s+({currency})".format(currency=CURRENCY_RE), string
- )
- if not match:
- raise ValueError("Invalid string for amount: '{}'".format(string))
- number, currency = match.group(1, 2)
- return Amount(D(number), currency)
-
@staticmethod
+def from_string(string):
+ """Create an amount from a string.
+
+ This is a miniature parser used for building tests.
+
+ Args:
+ string: A string of <number> <currency>.
+ Returns:
+ A new instance of Amount.
+ """
+ match = re.match(r'\s*([-+]?[0-9.]+)\s+({currency})'.format(currency=CURRENCY_RE),
+ string)
+ if not match:
+ raise ValueError("Invalid string for amount: '{}'".format(string))
+ number, currency = match.group(1, 2)
+ return Amount(D(number), currency)
+
beancount.core.amount.abs(amount)
+beancount.core.amount.abs(amount)
beancount/core/amount.py
def abs(amount):
- """Return the absolute value of the given amount.
-
- Args:
- amount: An instance of Amount.
- Returns:
- An instance of Amount.
- """
- return amount if amount.number >= ZERO else Amount(-amount.number, amount.currency)
-
def abs(amount):
+ """Return the absolute value of the given amount.
+
+ Args:
+ amount: An instance of Amount.
+ Returns:
+ An instance of Amount.
+ """
+ return (amount
+ if amount.number >= ZERO
+ else Amount(-amount.number, amount.currency))
+
beancount.core.amount.add(amount1, amount2)
+beancount.core.amount.add(amount1, amount2)
beancount/core/amount.py
def add(amount1, amount2):
- """Add the given amounts with the same currency.
-
- Args:
- amount1: An instance of Amount.
- amount2: An instance of Amount.
- Returns:
- An instance of Amount, with the sum the two amount's numbers, in the same
- currency.
- """
- assert isinstance(
- amount1.number, Decimal
- ), "Amount1's number is not a Decimal instance: {}".format(amount1.number)
- assert isinstance(
- amount2.number, Decimal
- ), "Amount2's number is not a Decimal instance: {}".format(amount2.number)
- if amount1.currency != amount2.currency:
- raise ValueError(
- "Unmatching currencies for operation on {} and {}".format(amount1, amount2)
- )
- return Amount(amount1.number + amount2.number, amount1.currency)
-
def add(amount1, amount2):
+ """Add the given amounts with the same currency.
+
+ Args:
+ amount1: An instance of Amount.
+ amount2: An instance of Amount.
+ Returns:
+ An instance of Amount, with the sum the two amount's numbers, in the same
+ currency.
+ """
+ assert isinstance(amount1.number, Decimal), (
+ "Amount1's number is not a Decimal instance: {}".format(amount1.number))
+ assert isinstance(amount2.number, Decimal), (
+ "Amount2's number is not a Decimal instance: {}".format(amount2.number))
+ if amount1.currency != amount2.currency:
+ raise ValueError(
+ "Unmatching currencies for operation on {} and {}".format(
+ amount1, amount2))
+ return Amount(amount1.number + amount2.number, amount1.currency)
+
beancount.core.amount.div(amount, number)
+beancount.core.amount.div(amount, number)
beancount/core/amount.py
def div(amount, number):
- """Divide the given amount by a number.
-
- Args:
- amount: An instance of Amount.
- number: A decimal number.
- Returns:
- An Amount, with the same currency, but with amount units divided by 'number'.
- """
- assert isinstance(
- amount.number, Decimal
- ), "Amount's number is not a Decimal instance: {}".format(amount.number)
- assert isinstance(number, Decimal), "Number is not a Decimal instance: {}".format(
- number
- )
- return Amount(amount.number / number, amount.currency)
-
def div(amount, number):
+ """Divide the given amount by a number.
+
+ Args:
+ amount: An instance of Amount.
+ number: A decimal number.
+ Returns:
+ An Amount, with the same currency, but with amount units divided by 'number'.
+ """
+ assert isinstance(amount.number, Decimal), (
+ "Amount's number is not a Decimal instance: {}".format(amount.number))
+ assert isinstance(number, Decimal), (
+ "Number is not a Decimal instance: {}".format(number))
+ return Amount(amount.number / number, amount.currency)
+
beancount.core.amount.from_string(string)
+beancount.core.amount.from_string(string)
beancount/core/amount.py
@staticmethod
-def from_string(string):
- """Create an amount from a string.
-
- This is a miniature parser used for building tests.
-
- Args:
- string: A string of <number> <currency>.
- Returns:
- A new instance of Amount.
- """
- match = re.match(
- r"\s*([-+]?[0-9.]+)\s+({currency})".format(currency=CURRENCY_RE), string
- )
- if not match:
- raise ValueError("Invalid string for amount: '{}'".format(string))
- number, currency = match.group(1, 2)
- return Amount(D(number), currency)
-
@staticmethod
+def from_string(string):
+ """Create an amount from a string.
+
+ This is a miniature parser used for building tests.
+
+ Args:
+ string: A string of <number> <currency>.
+ Returns:
+ A new instance of Amount.
+ """
+ match = re.match(r'\s*([-+]?[0-9.]+)\s+({currency})'.format(currency=CURRENCY_RE),
+ string)
+ if not match:
+ raise ValueError("Invalid string for amount: '{}'".format(string))
+ number, currency = match.group(1, 2)
+ return Amount(D(number), currency)
+
beancount.core.amount.mul(amount, number)
+beancount.core.amount.mul(amount, number)
beancount/core/amount.py
def mul(amount, number):
- """Multiply the given amount by a number.
-
- Args:
- amount: An instance of Amount.
- number: A decimal number.
- Returns:
- An Amount, with the same currency, but with 'number' times units.
- """
- assert isinstance(
- amount.number, Decimal
- ), "Amount's number is not a Decimal instance: {}".format(amount.number)
- assert isinstance(number, Decimal), "Number is not a Decimal instance: {}".format(
- number
- )
- return Amount(amount.number * number, amount.currency)
-
def mul(amount, number):
+ """Multiply the given amount by a number.
+
+ Args:
+ amount: An instance of Amount.
+ number: A decimal number.
+ Returns:
+ An Amount, with the same currency, but with 'number' times units.
+ """
+ assert isinstance(amount.number, Decimal), (
+ "Amount's number is not a Decimal instance: {}".format(amount.number))
+ assert isinstance(number, Decimal), (
+ "Number is not a Decimal instance: {}".format(number))
+ return Amount(amount.number * number, amount.currency)
+
beancount.core.amount.sortkey(amount)
+beancount.core.amount.sortkey(amount)
beancount/core/amount.py
def sortkey(amount):
- """A comparison function that sorts by currency first.
-
- Args:
- amount: An instance of Amount.
- Returns:
- A sort key, composed of the currency first and then the number.
- """
- return (amount.currency, amount.number)
-
def sortkey(amount):
+ """A comparison function that sorts by currency first.
+
+ Args:
+ amount: An instance of Amount.
+ Returns:
+ A sort key, composed of the currency first and then the number.
+ """
+ return (amount.currency, amount.number)
+
beancount.core.amount.sub(amount1, amount2)
+beancount.core.amount.sub(amount1, amount2)
beancount/core/amount.py
def sub(amount1, amount2):
- """Subtract the given amounts with the same currency.
-
- Args:
- amount1: An instance of Amount.
- amount2: An instance of Amount.
- Returns:
- An instance of Amount, with the difference between the two amount's
- numbers, in the same currency.
- """
- assert isinstance(
- amount1.number, Decimal
- ), "Amount1's number is not a Decimal instance: {}".format(amount1.number)
- assert isinstance(
- amount2.number, Decimal
- ), "Amount2's number is not a Decimal instance: {}".format(amount2.number)
- if amount1.currency != amount2.currency:
- raise ValueError(
- "Unmatching currencies for operation on {} and {}".format(amount1, amount2)
- )
- return Amount(amount1.number - amount2.number, amount1.currency)
-
def sub(amount1, amount2):
+ """Subtract the given amounts with the same currency.
+
+ Args:
+ amount1: An instance of Amount.
+ amount2: An instance of Amount.
+ Returns:
+ An instance of Amount, with the difference between the two amount's
+ numbers, in the same currency.
+ """
+ assert isinstance(amount1.number, Decimal), (
+ "Amount1's number is not a Decimal instance: {}".format(amount1.number))
+ assert isinstance(amount2.number, Decimal), (
+ "Amount2's number is not a Decimal instance: {}".format(amount2.number))
+ if amount1.currency != amount2.currency:
+ raise ValueError(
+ "Unmatching currencies for operation on {} and {}".format(
+ amount1, amount2))
+ return Amount(amount1.number - amount2.number, amount1.currency)
+
beancount.core.compare.CompareError.__getnewargs__(self)
+beancount.core.compare.CompareError.__getnewargs__(self)
special
@@ -4087,10 +3963,10 @@ beancount/core/compare.py
def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return _tuple(self)
-
def __getnewargs__(self):
+ 'Return self as a plain tuple. Used by copy and pickle.'
+ return _tuple(self)
+
@@ -4103,7 +3979,7 @@ beancount.core.compare.CompareError.__new__(_cls, source, message, entry)
+beancount.core.compare.CompareError.__new__(_cls, source, message, entry)
special
@@ -4127,7 +4003,7 @@ beancount.core.compare.CompareError.__repr__(self)
+beancount.core.compare.CompareError.__repr__(self)
special
@@ -4141,10 +4017,10 @@ beancount/core/compare.py
def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + repr_fmt % self
-
def __repr__(self):
+ 'Return a nicely formatted representation string'
+ return self.__class__.__name__ + repr_fmt % self
+
beancount.core.compare.compare_entries(entries1, entries2)
+beancount.core.compare.compare_entries(entries1, entries2)
beancount/core/compare.py
def compare_entries(entries1, entries2):
- """Compare two lists of entries. This is used for testing.
-
- The entries are compared with disregard for their file location.
-
- Args:
- entries1: A list of directives of any type.
- entries2: Another list of directives of any type.
- Returns:
- A tuple of (success, not_found1, not_found2), where the fields are:
- success: A boolean, true if all the values are equal.
- missing1: A list of directives from 'entries1' not found in
- 'entries2'.
- missing2: A list of directives from 'entries2' not found in
- 'entries1'.
- Raises:
- ValueError: If a duplicate entry is found.
- """
- hashes1, errors1 = hash_entries(entries1, exclude_meta=True)
- hashes2, errors2 = hash_entries(entries2, exclude_meta=True)
- keys1 = set(hashes1.keys())
- keys2 = set(hashes2.keys())
-
- if errors1 or errors2:
- error = (errors1 + errors2)[0]
- raise ValueError(str(error))
-
- same = keys1 == keys2
- missing1 = data.sorted([hashes1[key] for key in keys1 - keys2])
- missing2 = data.sorted([hashes2[key] for key in keys2 - keys1])
- return (same, missing1, missing2)
-
def compare_entries(entries1, entries2):
+ """Compare two lists of entries. This is used for testing.
+
+ The entries are compared with disregard for their file location.
+
+ Args:
+ entries1: A list of directives of any type.
+ entries2: Another list of directives of any type.
+ Returns:
+ A tuple of (success, not_found1, not_found2), where the fields are:
+ success: A boolean, true if all the values are equal.
+ missing1: A list of directives from 'entries1' not found in
+ 'entries2'.
+ missing2: A list of directives from 'entries2' not found in
+ 'entries1'.
+ Raises:
+ ValueError: If a duplicate entry is found.
+ """
+ hashes1, errors1 = hash_entries(entries1, exclude_meta=True)
+ hashes2, errors2 = hash_entries(entries2, exclude_meta=True)
+ keys1 = set(hashes1.keys())
+ keys2 = set(hashes2.keys())
+
+ if errors1 or errors2:
+ error = (errors1 + errors2)[0]
+ raise ValueError(str(error))
+
+ same = keys1 == keys2
+ missing1 = data.sorted([hashes1[key] for key in keys1 - keys2])
+ missing2 = data.sorted([hashes2[key] for key in keys2 - keys1])
+ return (same, missing1, missing2)
+
beancount.core.compare.excludes_entries(subset_entries, entries)
+beancount.core.compare.excludes_entries(subset_entries, entries)
beancount/core/compare.py
def excludes_entries(subset_entries, entries):
- """Check that a list of entries does not appear in another list.
-
- Args:
- subset_entries: The set of entries to look for in 'entries'.
- entries: The larger list of entries that should not include 'subset_entries'.
- Returns:
- A boolean and a list of entries that are not supposed to appear.
- Raises:
- ValueError: If a duplicate entry is found.
- """
- subset_hashes, subset_errors = hash_entries(subset_entries, exclude_meta=True)
- subset_keys = set(subset_hashes.keys())
- hashes, errors = hash_entries(entries, exclude_meta=True)
- keys = set(hashes.keys())
-
- if subset_errors or errors:
- error = (subset_errors + errors)[0]
- raise ValueError(str(error))
-
- intersection = keys.intersection(subset_keys)
- excludes = not bool(intersection)
- extra = data.sorted([subset_hashes[key] for key in intersection])
- return (excludes, extra)
-
def excludes_entries(subset_entries, entries):
+ """Check that a list of entries does not appear in another list.
+
+ Args:
+ subset_entries: The set of entries to look for in 'entries'.
+ entries: The larger list of entries that should not include 'subset_entries'.
+ Returns:
+ A boolean and a list of entries that are not supposed to appear.
+ Raises:
+ ValueError: If a duplicate entry is found.
+ """
+ subset_hashes, subset_errors = hash_entries(subset_entries, exclude_meta=True)
+ subset_keys = set(subset_hashes.keys())
+ hashes, errors = hash_entries(entries, exclude_meta=True)
+ keys = set(hashes.keys())
+
+ if subset_errors or errors:
+ error = (subset_errors + errors)[0]
+ raise ValueError(str(error))
+
+ intersection = keys.intersection(subset_keys)
+ excludes = not bool(intersection)
+ extra = data.sorted([subset_hashes[key] for key in intersection])
+ return (excludes, extra)
+
beancount.core.compare.hash_entries(entries, exclude_meta=False)
+beancount.core.compare.hash_entries(entries, exclude_meta=False)
beancount/core/compare.py
def hash_entries(entries, exclude_meta=False):
- """Compute unique hashes of each of the entries and return a map of them.
-
- This is used for comparisons between sets of entries.
-
- Args:
- entries: A list of directives.
- exclude_meta: If set, exclude the metadata from the hash. Use this for
- unit tests comparing entries coming from different sources as the
- filename and lineno will be distinct. However, when you're using the
- hashes to uniquely identify transactions, you want to include the
- filenames and line numbers (the default).
- Returns:
- A dict of hash-value to entry (for all entries) and a list of errors.
- Errors are created when duplicate entries are found.
- """
- entry_hash_dict = {}
- errors = []
- num_legal_duplicates = 0
- for entry in entries:
- hash_ = hash_entry(entry, exclude_meta)
-
- if hash_ in entry_hash_dict:
- if isinstance(entry, Price):
- # Note: Allow duplicate Price entries, they should be common
- # because of the nature of stock markets (if they're closed, the
- # data source is likely to return an entry for the previously
- # available date, which may already have been fetched).
- num_legal_duplicates += 1
- else:
- other_entry = entry_hash_dict[hash_]
- errors.append(
- CompareError(
- entry.meta,
- "Duplicate entry: {} == {}".format(entry, other_entry),
- entry,
- )
- )
- entry_hash_dict[hash_] = entry
-
- if not errors:
- assert len(entry_hash_dict) + num_legal_duplicates == len(entries), (
- len(entry_hash_dict),
- len(entries),
- num_legal_duplicates,
- )
- return entry_hash_dict, errors
-
def hash_entries(entries, exclude_meta=False):
+ """Compute unique hashes of each of the entries and return a map of them.
+
+ This is used for comparisons between sets of entries.
+
+ Args:
+ entries: A list of directives.
+ exclude_meta: If set, exclude the metadata from the hash. Use this for
+ unit tests comparing entries coming from different sources as the
+ filename and lineno will be distinct. However, when you're using the
+ hashes to uniquely identify transactions, you want to include the
+ filenames and line numbers (the default).
+ Returns:
+ A dict of hash-value to entry (for all entries) and a list of errors.
+ Errors are created when duplicate entries are found.
+ """
+ entry_hash_dict = {}
+ errors = []
+ num_legal_duplicates = 0
+ for entry in entries:
+ hash_ = hash_entry(entry, exclude_meta)
+
+ if hash_ in entry_hash_dict:
+ if isinstance(entry, Price):
+ # Note: Allow duplicate Price entries, they should be common
+ # because of the nature of stock markets (if they're closed, the
+ # data source is likely to return an entry for the previously
+ # available date, which may already have been fetched).
+ num_legal_duplicates += 1
+ else:
+ other_entry = entry_hash_dict[hash_]
+ errors.append(
+ CompareError(entry.meta,
+ "Duplicate entry: {} == {}".format(entry, other_entry),
+ entry))
+ entry_hash_dict[hash_] = entry
+
+ if not errors:
+ assert len(entry_hash_dict) + num_legal_duplicates == len(entries), (
+ len(entry_hash_dict), len(entries), num_legal_duplicates)
+ return entry_hash_dict, errors
+
beancount.core.compare.hash_entry(entry, exclude_meta=False)
+beancount.core.compare.hash_entry(entry, exclude_meta=False)
beancount/core/compare.py
def hash_entry(entry, exclude_meta=False):
- """Compute the stable hash of a single entry.
-
- Args:
- entry: A directive instance.
- exclude_meta: If set, exclude the metadata from the hash. Use this for
- unit tests comparing entries coming from different sources as the
- filename and lineno will be distinct. However, when you're using the
- hashes to uniquely identify transactions, you want to include the
- filenames and line numbers (the default).
- Returns:
- A stable hexadecimal hash of this entry.
-
- """
- return stable_hash_namedtuple(
- entry, IGNORED_FIELD_NAMES if exclude_meta else frozenset()
- )
-
def hash_entry(entry, exclude_meta=False):
+ """Compute the stable hash of a single entry.
+
+ Args:
+ entry: A directive instance.
+ exclude_meta: If set, exclude the metadata from the hash. Use this for
+ unit tests comparing entries coming from different sources as the
+ filename and lineno will be distinct. However, when you're using the
+ hashes to uniquely identify transactions, you want to include the
+ filenames and line numbers (the default).
+ Returns:
+ A stable hexadecimal hash of this entry.
+
+ """
+ return stable_hash_namedtuple(entry,
+ IGNORED_FIELD_NAMES if exclude_meta else frozenset())
+
beancount.core.compare.includes_entries(subset_entries, entries)
+beancount.core.compare.includes_entries(subset_entries, entries)
beancount/core/compare.py
def includes_entries(subset_entries, entries):
- """Check if a list of entries is included in another list.
-
- Args:
- subset_entries: The set of entries to look for in 'entries'.
- entries: The larger list of entries that could include 'subset_entries'.
- Returns:
- A boolean and a list of missing entries.
- Raises:
- ValueError: If a duplicate entry is found.
- """
- subset_hashes, subset_errors = hash_entries(subset_entries, exclude_meta=True)
- subset_keys = set(subset_hashes.keys())
- hashes, errors = hash_entries(entries, exclude_meta=True)
- keys = set(hashes.keys())
-
- if subset_errors or errors:
- error = (subset_errors + errors)[0]
- raise ValueError(str(error))
-
- includes = subset_keys.issubset(keys)
- missing = data.sorted([subset_hashes[key] for key in subset_keys - keys])
- return (includes, missing)
-
def includes_entries(subset_entries, entries):
+ """Check if a list of entries is included in another list.
+
+ Args:
+ subset_entries: The set of entries to look for in 'entries'.
+ entries: The larger list of entries that could include 'subset_entries'.
+ Returns:
+ A boolean and a list of missing entries.
+ Raises:
+ ValueError: If a duplicate entry is found.
+ """
+ subset_hashes, subset_errors = hash_entries(subset_entries, exclude_meta=True)
+ subset_keys = set(subset_hashes.keys())
+ hashes, errors = hash_entries(entries, exclude_meta=True)
+ keys = set(hashes.keys())
+
+ if subset_errors or errors:
+ error = (subset_errors + errors)[0]
+ raise ValueError(str(error))
+
+ includes = subset_keys.issubset(keys)
+ missing = data.sorted([subset_hashes[key] for key in subset_keys - keys])
+ return (includes, missing)
+
beancount.core.compare.stable_hash_namedtuple(objtuple, ignore=frozenset())
+beancount.core.compare.stable_hash_namedtuple(objtuple, ignore=frozenset())
beancount/core/compare.py
def stable_hash_namedtuple(objtuple, ignore=frozenset()):
- """Hash the given namedtuple and its child fields.
-
- This iterates over all the members of objtuple, skipping the attributes from
- the 'ignore' set, and computes a unique hash string code. If the elements
- are lists or sets, sorts them for stability.
-
- Args:
- objtuple: A tuple object or other.
- ignore: A set of strings, attribute names to be skipped in
- computing a stable hash. For instance, circular references to objects
- or irrelevant data.
-
- """
- # Note: this routine is slow and would stand to be implemented in C.
- hashobj = hashlib.md5()
- for attr_name, attr_value in zip(objtuple._fields, objtuple):
- if attr_name in ignore:
- continue
- if isinstance(attr_value, (list, set, frozenset)):
- subhashes = []
- for element in attr_value:
- if isinstance(element, tuple):
- subhashes.append(stable_hash_namedtuple(element, ignore))
- else:
- md5 = hashlib.md5()
- md5.update(str(element).encode())
- subhashes.append(md5.hexdigest())
- for subhash in sorted(subhashes):
- hashobj.update(subhash.encode())
- else:
- hashobj.update(str(attr_value).encode())
- return hashobj.hexdigest()
-
def stable_hash_namedtuple(objtuple, ignore=frozenset()):
+ """Hash the given namedtuple and its child fields.
+
+ This iterates over all the members of objtuple, skipping the attributes from
+ the 'ignore' set, and computes a unique hash string code. If the elements
+ are lists or sets, sorts them for stability.
+
+ Args:
+ objtuple: A tuple object or other.
+ ignore: A set of strings, attribute names to be skipped in
+ computing a stable hash. For instance, circular references to objects
+ or irrelevant data.
+
+ """
+ # Note: this routine is slow and would stand to be implemented in C.
+ hashobj = hashlib.md5()
+ for attr_name, attr_value in zip(objtuple._fields, objtuple):
+ if attr_name in ignore:
+ continue
+ if isinstance(attr_value, (list, set, frozenset)):
+ subhashes = set()
+ for element in attr_value:
+ if isinstance(element, tuple):
+ subhashes.add(stable_hash_namedtuple(element, ignore))
+ else:
+ md5 = hashlib.md5()
+ md5.update(str(element).encode())
+ subhashes.add(md5.hexdigest())
+ for subhash in sorted(subhashes):
+ hashobj.update(subhash.encode())
+ else:
+ hashobj.update(str(attr_value).encode())
+ return hashobj.hexdigest()
+
beancount.core.convert.convert_amount(amt, target_currency, price_map, date=None, via=None)
+beancount.core.convert.convert_amount(amt, target_currency, price_map, date=None, via=None)
beancount/core/convert.py
def convert_amount(amt, target_currency, price_map, date=None, via=None):
- """Return the market value of an Amount in a particular currency.
-
- In addition, if a conversion rate isn't available, you can provide a list of
- currencies to attempt to synthesize a rate for via implied rates.
-
- Args:
- amt: An instance of Amount.
- target_currency: The target currency to convert to.
- price_map: A dict of prices, as built by prices.build_price_map().
- date: A datetime.date instance to evaluate the value at, or None.
- via: A list of currencies to attempt to synthesize an implied rate if the
- direct conversion fails.
- Returns:
- An Amount, either with a successful value currency conversion, or if we
- could not convert the value, the amount itself, unmodified.
-
- """
- # First, attempt to convert directly. This should be the most
- # straightforward conversion.
- base_quote = (amt.currency, target_currency)
- _, rate = prices.get_price(price_map, base_quote, date)
- if rate is not None:
- # On success, just make the conversion directly.
- return Amount(amt.number * rate, target_currency)
- elif via:
- assert isinstance(via, (tuple, list))
-
- # A price is unavailable, attempt to convert via cost/price currency
- # hop, if the value currency isn't the target currency.
- for implied_currency in via:
- if implied_currency == target_currency:
- continue
- base_quote1 = (amt.currency, implied_currency)
- _, rate1 = prices.get_price(price_map, base_quote1, date)
- if rate1 is not None:
- base_quote2 = (implied_currency, target_currency)
- _, rate2 = prices.get_price(price_map, base_quote2, date)
- if rate2 is not None:
- return Amount(amt.number * rate1 * rate2, target_currency)
-
- # We failed to infer a conversion rate; return the amt.
- return amt
-
def convert_amount(amt, target_currency, price_map, date=None, via=None):
+ """Return the market value of an Amount in a particular currency.
+
+ In addition, if a conversion rate isn't available, you can provide a list of
+ currencies to attempt to synthesize a rate for via implied rates.
+
+ Args:
+ amt: An instance of Amount.
+ target_currency: The target currency to convert to.
+ price_map: A dict of prices, as built by prices.build_price_map().
+ date: A datetime.date instance to evaluate the value at, or None.
+ via: A list of currencies to attempt to synthesize an implied rate if the
+ direct conversion fails.
+ Returns:
+ An Amount, either with a successful value currency conversion, or if we
+ could not convert the value, the amount itself, unmodified.
+
+ """
+ # First, attempt to convert directly. This should be the most
+ # straightforward conversion.
+ base_quote = (amt.currency, target_currency)
+ _, rate = prices.get_price(price_map, base_quote, date)
+ if rate is not None:
+ # On success, just make the conversion directly.
+ return Amount(amt.number * rate, target_currency)
+ elif via:
+ assert isinstance(via, (tuple, list))
+
+ # A price is unavailable, attempt to convert via cost/price currency
+ # hop, if the value currency isn't the target currency.
+ for implied_currency in via:
+ if implied_currency == target_currency:
+ continue
+ base_quote1 = (amt.currency, implied_currency)
+ _, rate1 = prices.get_price(price_map, base_quote1, date)
+ if rate1 is not None:
+ base_quote2 = (implied_currency, target_currency)
+ _, rate2 = prices.get_price(price_map, base_quote2, date)
+ if rate2 is not None:
+ return Amount(amt.number * rate1 * rate2, target_currency)
+
+ # We failed to infer a conversion rate; return the amt.
+ return amt
+
beancount.core.convert.convert_position(pos, target_currency, price_map, date=None)
+beancount.core.convert.convert_position(pos, target_currency, price_map, date=None)
beancount/core/convert.py
def convert_position(pos, target_currency, price_map, date=None):
- """Return the market value of a Position or Posting in a particular currency.
-
- In addition, if the rate from the position's currency to target_currency
- isn't available, an attempt is made to convert from its cost currency, if
- one is available.
-
- Args:
- pos: An instance of Position or Posting, equivalently.
- target_currency: The target currency to convert to.
- price_map: A dict of prices, as built by prices.build_price_map().
- date: A datetime.date instance to evaluate the value at, or None.
- Returns:
- An Amount, either with a successful value currency conversion, or if we
- could not convert the value, just the units, unmodified. (See get_value()
- above for details.)
- """
- cost = pos.cost
- value_currency = (
- (isinstance(cost, Cost) and cost.currency)
- or (hasattr(pos, "price") and pos.price and pos.price.currency)
- or None
- )
- return convert_amount(
- pos.units, target_currency, price_map, date=date, via=(value_currency,)
- )
-
def convert_position(pos, target_currency, price_map, date=None):
+ """Return the market value of a Position or Posting in a particular currency.
+
+ In addition, if the rate from the position's currency to target_currency
+ isn't available, an attempt is made to convert from its cost currency, if
+ one is available.
+
+ Args:
+ pos: An instance of Position or Posting, equivalently.
+ target_currency: The target currency to convert to.
+ price_map: A dict of prices, as built by prices.build_price_map().
+ date: A datetime.date instance to evaluate the value at, or None.
+ Returns:
+ An Amount, either with a successful value currency conversion, or if we
+ could not convert the value, just the units, unmodified. (See get_value()
+ above for details.)
+ """
+ cost = pos.cost
+ value_currency = (
+ (isinstance(cost, Cost) and cost.currency) or
+ (hasattr(pos, 'price') and pos.price and pos.price.currency) or
+ None)
+ return convert_amount(pos.units, target_currency, price_map,
+ date=date, via=(value_currency,))
+
beancount.core.convert.get_cost(pos)
+beancount.core.convert.get_cost(pos)
beancount/core/convert.py
def get_cost(pos):
- """Return the total cost of a Position or Posting.
-
- Args:
- pos: An instance of Position or Posting, equivalently.
- Returns:
- An Amount.
- """
- assert isinstance(pos, Position) or type(pos).__name__ == "Posting"
- cost = pos.cost
- return (
- Amount(cost.number * pos.units.number, cost.currency)
- if (isinstance(cost, Cost) and isinstance(cost.number, Decimal))
- else pos.units
- )
-
def get_cost(pos):
+ """Return the total cost of a Position or Posting.
+
+ Args:
+ pos: An instance of Position or Posting, equivalently.
+ Returns:
+ An Amount.
+ """
+ assert isinstance(pos, Position) or type(pos).__name__ == 'Posting'
+ cost = pos.cost
+ return (Amount(cost.number * pos.units.number, cost.currency)
+ if (isinstance(cost, Cost) and isinstance(cost.number, Decimal))
+ else pos.units)
+
beancount.core.convert.get_units(pos)
+beancount.core.convert.get_units(pos)
beancount/core/convert.py
def get_units(pos):
- """Return the units of a Position or Posting.
-
- Args:
- pos: An instance of Position or Posting, equivalently.
- Returns:
- An Amount.
- """
- assert isinstance(pos, Position) or type(pos).__name__ == "Posting"
- return pos.units
-
def get_units(pos):
+ """Return the units of a Position or Posting.
+
+ Args:
+ pos: An instance of Position or Posting, equivalently.
+ Returns:
+ An Amount.
+ """
+ assert isinstance(pos, Position) or type(pos).__name__ == 'Posting'
+ return pos.units
+
beancount.core.convert.get_value(pos, price_map, date=None, output_date_prices=None)
+beancount.core.convert.get_value(pos, price_map, date=None)
Note that if the position is not held at cost, this does not convert
anything, even if a price is available in the 'price_map'. We don't specify
a target currency here. If you're attempting to make such a conversion, see
-convert_*()
functions below. However, is the object is a posting and it
-has a price, we will use that price to infer the target currency and those
-will be converted.
convert_*()
functions below.
beancount/core/convert.py
def get_value(pos, price_map, date=None, output_date_prices=None):
- """Return the market value of a Position or Posting.
-
- Note that if the position is not held at cost, this does not convert
- anything, even if a price is available in the 'price_map'. We don't specify
- a target currency here. If you're attempting to make such a conversion, see
- ``convert_*()`` functions below. However, is the object is a posting and it
- has a price, we will use that price to infer the target currency and those
- will be converted.
-
- Args:
- pos: An instance of Position or Posting, equivalently.
- price_map: A dict of prices, as built by prices.build_price_map().
- date: A datetime.date instance to evaluate the value at, or None.
- output_date_prices: An optional output list of (date, price). If this list
- is provided, it will be appended to (mutated) to output the price
- pulled in making the conversions.
- Returns:
- An Amount, either with a successful value currency conversion, or if we
- could not convert the value, just the units, unmodified. This is designed
- so that you could reduce an inventory with this and not lose any
- information silently in case of failure to convert (possibly due to an
- empty price map). Compare the returned currency to that of the input
- position if you need to check for success.
-
- """
- assert isinstance(pos, Position) or type(pos).__name__ == "Posting"
- units = pos.units
- cost = pos.cost
-
- # Try to infer what the cost/price currency should be.
- value_currency = (
- (isinstance(cost, Cost) and cost.currency)
- or (hasattr(pos, "price") and pos.price and pos.price.currency)
- or None
- )
-
- if isinstance(value_currency, str):
- # We have a value currency; hit the price database.
- base_quote = (units.currency, value_currency)
- price_date, price_number = prices.get_price(price_map, base_quote, date)
- if output_date_prices is not None:
- output_date_prices.append((price_date, price_number))
- if price_number is not None:
- return Amount(units.number * price_number, value_currency)
-
- # We failed to infer a conversion rate; return the units.
- return units
-
def get_value(pos, price_map, date=None):
+ """Return the market value of a Position or Posting.
+
+ Note that if the position is not held at cost, this does not convert
+ anything, even if a price is available in the 'price_map'. We don't specify
+ a target currency here. If you're attempting to make such a conversion, see
+ ``convert_*()`` functions below.
+
+ Args:
+ pos: An instance of Position or Posting, equivalently.
+ price_map: A dict of prices, as built by prices.build_price_map().
+ date: A datetime.date instance to evaluate the value at, or None.
+ Returns:
+ An Amount, either with a successful value currency conversion, or if we
+ could not convert the value, just the units, unmodified. This is designed
+ so that you could reduce an inventory with this and not lose any
+ information silently in case of failure to convert (possibly due to an
+ empty price map). Compare the returned currency to that of the input
+ position if you need to check for success.
+ """
+ assert isinstance(pos, Position) or type(pos).__name__ == 'Posting'
+ units = pos.units
+ cost = pos.cost
+
+ # Try to infer what the cost/price currency should be.
+ value_currency = (
+ (isinstance(cost, Cost) and cost.currency) or
+ (hasattr(pos, 'price') and pos.price and pos.price.currency) or
+ None)
+
+ if isinstance(value_currency, str):
+ # We have a value currency; hit the price database.
+ base_quote = (units.currency, value_currency)
+ _, price_number = prices.get_price(price_map, base_quote, date)
+ if price_number is not None:
+ return Amount(units.number * price_number, value_currency)
+
+ # We failed to infer a conversion rate; return the units.
+ return units
+
beancount.core.convert.get_weight(pos)
+beancount.core.convert.get_weight(pos)
beancount/core/convert.py
def get_weight(pos):
- """Return the weight of a Position or Posting.
-
- This is the amount that will need to be balanced from a posting of a
- transaction.
-
- This is a *key* element of the semantics of transactions in this software. A
- balance amount is the amount used to check the balance of a transaction.
- Here are all relevant examples, with the amounts used to balance the
- postings:
-
- Assets:Account 5234.50 USD -> 5234.50 USD
- Assets:Account 3877.41 EUR @ 1.35 USD -> 5234.50 USD
- Assets:Account 10 HOOL {523.45 USD} -> 5234.50 USD
- Assets:Account 10 HOOL {523.45 USD} @ 545.60 CAD -> 5234.50 USD
-
- Args:
- pos: An instance of Position or Posting, equivalently.
- Returns:
- An Amount.
- """
- assert isinstance(pos, Position) or type(pos).__name__ == "Posting"
- units = pos.units
- cost = pos.cost
-
- # It the object has a cost, use that as the weight, to balance.
- if isinstance(cost, Cost) and isinstance(cost.number, Decimal):
- weight = Amount(cost.number * pos.units.number, cost.currency)
- else:
- # Otherwise use the postings.
- weight = units
-
- # Unless there is a price available; use that if present.
- if not isinstance(pos, Position):
- price = pos.price
- if price is not None:
- # Note: Here we could assert that price.currency == units.currency.
- if price.number is MISSING or units.number is MISSING:
- converted_number = MISSING
- else:
- converted_number = price.number * units.number
- weight = Amount(converted_number, price.currency)
-
- return weight
-
def get_weight(pos):
+ """Return the weight of a Position or Posting.
+
+ This is the amount that will need to be balanced from a posting of a
+ transaction.
+
+ This is a *key* element of the semantics of transactions in this software. A
+ balance amount is the amount used to check the balance of a transaction.
+ Here are all relevant examples, with the amounts used to balance the
+ postings:
+
+ Assets:Account 5234.50 USD -> 5234.50 USD
+ Assets:Account 3877.41 EUR @ 1.35 USD -> 5234.50 USD
+ Assets:Account 10 HOOL {523.45 USD} -> 5234.50 USD
+ Assets:Account 10 HOOL {523.45 USD} @ 545.60 CAD -> 5234.50 USD
+
+ Args:
+ pos: An instance of Position or Posting, equivalently.
+ Returns:
+ An Amount.
+ """
+ assert isinstance(pos, Position) or type(pos).__name__ == 'Posting'
+ units = pos.units
+ cost = pos.cost
+
+ # It the object has a cost, use that as the weight, to balance.
+ if isinstance(cost, Cost) and isinstance(cost.number, Decimal):
+ weight = Amount(cost.number * pos.units.number, cost.currency)
+ else:
+ # Otherwise use the postings.
+ weight = units
+
+ # Unless there is a price available; use that if present.
+ if not isinstance(pos, Position):
+ price = pos.price
+ if price is not None:
+ # Note: Here we could assert that price.currency == units.currency.
+ if price.number is MISSING or units.number is MISSING:
+ converted_number = MISSING
+ else:
+ converted_number = price.number * units.number
+ weight = Amount(converted_number, price.currency)
+
+ return weight
+
A "check the balance of this account" directive. This directive asserts that -the declared account should have a known number of units of a particular -currency at the beginning of its date. This is essentially an assertion, and -corresponds to the final "Statement Balance" line of a real-world statement. -These assertions act as checkpoints to help ensure that you have entered your -transactions correctly.
+Balance(meta, date, account, amount, tolerance, diff_amount)
-Attributes:
-Name | -Type | -Description | -
---|---|---|
meta |
- Dict[str, Any] |
- See above. |
-
date |
- date |
- See above. |
-
account |
- str |
- A string, the account whose balance to check at the given date. |
-
amount |
- Amount |
- An Amount, the number of units of the given currency you're -expecting 'account' to have at this date. |
-
diff_amount |
- Optional[beancount.core.amount.Amount] |
- None if the balance check succeeds. This value is set to -an Amount instance if the balance fails, the amount of the difference. |
-
tolerance |
- Optional[decimal.Decimal] |
- A Decimal object, the amount of tolerance to use in the -verification. |
-
beancount.core.data.Balance.__getnewargs__(self)
+beancount.core.data.Balance.__getnewargs__(self)
special
@@ -5507,10 +5300,10 @@ beancount/core/data.py
def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return _tuple(self)
-
def __getnewargs__(self):
+ 'Return self as a plain tuple. Used by copy and pickle.'
+ return _tuple(self)
+
beancount.core.data.Balance.__new__(_cls, meta, date, account, amount, tolerance, diff_amount)
+beancount.core.data.Balance.__new__(_cls, meta, date, account, amount, tolerance, diff_amount)
special
@@ -5547,7 +5340,7 @@ beancount.core.data.Balance.__repr__(self)
+beancount.core.data.Balance.__repr__(self)
special
@@ -5561,10 +5354,10 @@ beancount/core/data.py
def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + repr_fmt % self
-
def __repr__(self):
+ 'Return a nicely formatted representation string'
+ return self.__class__.__name__ + repr_fmt % self
+
A "close account" directive.
+Close(meta, date, account)
-Attributes:
-Name | -Type | -Description | -
---|---|---|
meta |
- Dict[str, Any] |
- See above. |
-
date |
- date |
- See above. |
-
account |
- str |
- A string, the name of the account that is being closed. |
-
beancount.core.data.Close.__getnewargs__(self)
+beancount.core.data.Close.__getnewargs__(self)
special
@@ -5662,10 +5425,10 @@ beancount/core/data.py
def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return _tuple(self)
-
def __getnewargs__(self):
+ 'Return self as a plain tuple. Used by copy and pickle.'
+ return _tuple(self)
+
beancount.core.data.Close.__new__(_cls, meta, date, account)
+beancount.core.data.Close.__new__(_cls, meta, date, account)
special
@@ -5702,7 +5465,7 @@ beancount.core.data.Close.__repr__(self)
+beancount.core.data.Close.__repr__(self)
special
@@ -5716,10 +5479,10 @@ beancount/core/data.py
def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + repr_fmt % self
-
def __repr__(self):
+ 'Return a nicely formatted representation string'
+ return self.__class__.__name__ + repr_fmt % self
+
An optional commodity declaration directive. Commodities generally do not need -to be declared, but they may, and this is mainly created as intended to be -used to attach meta-data on a commodity name. Whenever a plugin needs -per-commodity meta-data, you would define such a commodity directive. Another -use is to define a commodity that isn't otherwise (yet) used anywhere in an -input file. (At the moment the date is meaningless but is specified for -coherence with all the other directives; if you can think of a good use case, -let us know).
+Commodity(meta, date, currency)
-Attributes:
-Name | -Type | -Description | -
---|---|---|
meta |
- Dict[str, Any] |
- See above. |
-
date |
- date |
- See above. |
-
currency |
- str |
- A string, the commodity under consideration. |
-
beancount.core.data.Commodity.__getnewargs__(self)
+beancount.core.data.Commodity.__getnewargs__(self)
special
@@ -5823,10 +5549,10 @@ beancount/core/data.py
def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return _tuple(self)
-
def __getnewargs__(self):
+ 'Return self as a plain tuple. Used by copy and pickle.'
+ return _tuple(self)
+
beancount.core.data.Commodity.__new__(_cls, meta, date, currency)
+beancount.core.data.Commodity.__new__(_cls, meta, date, currency)
special
@@ -5863,7 +5589,7 @@ beancount.core.data.Commodity.__repr__(self)
+beancount.core.data.Commodity.__repr__(self)
special
@@ -5877,10 +5603,10 @@ beancount/core/data.py
def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + repr_fmt % self
-
def __repr__(self):
+ 'Return a nicely formatted representation string'
+ return self.__class__.__name__ + repr_fmt % self
+
A custom directive. This directive can be used to implement new experimental -dated features in the Beancount file. This is meant as an intermediate measure -to be used when you would need to implement a new directive in a plugin. These -directives will be parsed liberally... any list of tokens are supported. All -that is required is some unique name for them that acts as a "type". These -directives are included in the stream and a plugin should be able to gather -them.
+Custom(meta, date, type, values)
-Attributes:
-Name | -Type | -Description | -
---|---|---|
meta |
- Dict[str, Any] |
- See above. |
-
type |
- str |
- A string that represents the type of the directive. |
-
values |
- List |
- A list of values of various simple types supported by the grammar. -(Note that this list is not enforced to be consistent for all directives -of the same type by the parser.) |
-
beancount.core.data.Custom.__getnewargs__(self)
+beancount.core.data.Custom.__getnewargs__(self)
special
@@ -5986,10 +5673,10 @@ beancount/core/data.py
def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return _tuple(self)
-
def __getnewargs__(self):
+ 'Return self as a plain tuple. Used by copy and pickle.'
+ return _tuple(self)
+
beancount.core.data.Custom.__new__(_cls, meta, date, type, values)
+beancount.core.data.Custom.__new__(_cls, meta, date, type, values)
special
@@ -6026,7 +5713,7 @@ beancount.core.data.Custom.__repr__(self)
+beancount.core.data.Custom.__repr__(self)
special
@@ -6040,10 +5727,10 @@ beancount/core/data.py
def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + repr_fmt % self
-
def __repr__(self):
+ 'Return a nicely formatted representation string'
+ return self.__class__.__name__ + repr_fmt % self
+
A document file declaration directive. This directive is used to attach a -statement to an account, at a particular date. A typical usage would be to -render PDF files or scans of your bank statements into the account's journal. -While you can explicitly create those directives in the input syntax, it is -much more convenient to provide Beancount with a root directory to search for -filenames in a hierarchy mirroring the chart of accounts, filenames which -should match the following dated format: "YYYY-MM-DD.*". See options for -detail. Beancount will automatically create these documents directives based -on the file hierarchy, and you can get them by parsing the list of entries.
+Document(meta, date, account, filename, tags, links)
-Attributes:
-Name | -Type | -Description | -
---|---|---|
meta |
- Dict[str, Any] |
- See above. |
-
date |
- date |
- See above. |
-
account |
- str |
- A string, the account which the statement or document is associated -with. |
-
filename |
- str |
- The absolute filename of the document file. |
-
tags |
- Optional[Set] |
- A set of tag strings (without the '#'), or None, if an empty set. |
-
links |
- Optional[Set] |
- A set of link strings (without the '^'), or None, if an empty set. |
-
beancount.core.data.Document.__getnewargs__(self)
+beancount.core.data.Document.__getnewargs__(self)
special
@@ -6167,10 +5797,10 @@ beancount/core/data.py
def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return _tuple(self)
-
def __getnewargs__(self):
+ 'Return self as a plain tuple. Used by copy and pickle.'
+ return _tuple(self)
+
beancount.core.data.Document.__new__(_cls, meta, date, account, filename, tags, links)
+beancount.core.data.Document.__new__(_cls, meta, date, account, filename, tags, links)
special
@@ -6207,7 +5837,7 @@ beancount.core.data.Document.__repr__(self)
+beancount.core.data.Document.__repr__(self)
special
@@ -6221,10 +5851,10 @@ beancount/core/data.py
def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + repr_fmt % self
-
def __repr__(self):
+ 'Return a nicely formatted representation string'
+ return self.__class__.__name__ + repr_fmt % self
+
An "event value change" directive. These directives are used as string -variables that have different values over time. You can use these to track an -address, your location, your current employer, anything you like. The kind of -reporting that is made of these generic events is based on days and a -timeline. For instance, if you need to track the number of days you spend in -each country or state, create a "location" event and whenever you travel, add -an event directive to indicate its new value. You should be able to write -simple scripts against those in order to compute if you were present somewhere -for a particular number of days. Here's an illustrative example usage, in -order to maintain your health insurance coverage in Canada, you need to be -present in the country for 183 days or more, excluding trips of less than 30 -days. There is a similar test to be done in the US by aliens to figure out if -they need to be considered as residents for tax purposes (the so-called -"substantial presence test"). By integrating these directives into your -bookkeeping, you can easily have a little program that computes the tests for -you. This is, of course, entirely optional and somewhat auxiliary to the main -purpose of double-entry bookkeeping, but correlates strongly with the -transactions you insert in it, and so it's a really convenient thing to have -in the same input file.
+Event(meta, date, type, description)
-Attributes:
-Name | -Type | -Description | -
---|---|---|
meta |
- Dict[str, Any] |
- See above. |
-
date |
- date |
- See above. |
-
"type" |
- - | A short string, typically a single lowercase word, that defines a -unique variable whose value changes over time. For example, 'location'. |
-
description |
- str |
- A free-form string, the value of the variable as of the date -of the transaction. |
-
beancount.core.data.Event.__getnewargs__(self)
+beancount.core.data.Event.__getnewargs__(self)
special
@@ -6347,10 +5921,10 @@ beancount/core/data.py
def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return _tuple(self)
-
def __getnewargs__(self):
+ 'Return self as a plain tuple. Used by copy and pickle.'
+ return _tuple(self)
+
beancount.core.data.Event.__new__(_cls, meta, date, type, description)
+beancount.core.data.Event.__new__(_cls, meta, date, type, description)
special
@@ -6387,7 +5961,7 @@ beancount.core.data.Event.__repr__(self)
+beancount.core.data.Event.__repr__(self)
special
@@ -6401,10 +5975,10 @@ beancount/core/data.py
def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + repr_fmt % self
-
def __repr__(self):
+ 'Return a nicely formatted representation string'
+ return self.__class__.__name__ + repr_fmt % self
+
A note directive, a general note that is attached to an account. These are -used to attach text at a particular date in a specific account. The notes can -be anything; a typical use would be to jot down an answer from a phone call to -the institution represented by the account. It should show up in an account's -journal. If you don't want this rendered, use the comment syntax in the input -file, which does not get parsed and stored.
+Note(meta, date, account, comment)
-Attributes:
-Name | -Type | -Description | -
---|---|---|
meta |
- Dict[str, Any] |
- See above. |
-
date |
- date |
- See above. |
-
account |
- str |
- A string, the account which the note is to be attached to. This is -never None, notes always have an account they correspond to. |
-
comment |
- str |
- A free-form string, the text of the note. This can be long if you -want it to. |
-
beancount.core.data.Note.__getnewargs__(self)
+beancount.core.data.Note.__getnewargs__(self)
special
@@ -6516,10 +6045,10 @@ beancount/core/data.py
def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return _tuple(self)
-
def __getnewargs__(self):
+ 'Return self as a plain tuple. Used by copy and pickle.'
+ return _tuple(self)
+
beancount.core.data.Note.__new__(_cls, meta, date, account, comment, tags, links)
+beancount.core.data.Note.__new__(_cls, meta, date, account, comment)
special
@@ -6543,7 +6072,7 @@ Create new instance of Note(meta, date, account, comment, tags, links)
+Create new instance of Note(meta, date, account, comment)
beancount.core.data.Note.__repr__(self)
+beancount.core.data.Note.__repr__(self)
special
@@ -6570,10 +6099,10 @@ beancount/core/data.py
def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + repr_fmt % self
-
def __repr__(self):
+ 'Return a nicely formatted representation string'
+ return self.__class__.__name__ + repr_fmt % self
+
An "open account" directive.
+Open(meta, date, account, currencies, booking)
-Attributes:
-Name | -Type | -Description | -
---|---|---|
meta |
- Dict[str, Any] |
- See above. |
-
date |
- date |
- See above. |
-
account |
- str |
- A string, the name of the account that is being opened. |
-
currencies |
- List[str] |
- A list of strings, currencies that are allowed in this account. -May be None, in which case it means that there are no restrictions on which -currencies may be stored in this account. |
-
booking |
- Optional[beancount.core.data.Booking] |
- A Booking enum, the booking method to use to disambiguate -postings to this account (when zero or more than one postings match the -specification), or None if not specified. In practice, this attribute will -be should be left unspecified (None) in the vast majority of cases. See -Booking below for a selection of valid methods. |
-
beancount.core.data.Open.__getnewargs__(self)
+beancount.core.data.Open.__getnewargs__(self)
special
@@ -6688,10 +6169,10 @@ beancount/core/data.py
def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return _tuple(self)
-
def __getnewargs__(self):
+ 'Return self as a plain tuple. Used by copy and pickle.'
+ return _tuple(self)
+
beancount.core.data.Open.__new__(_cls, meta, date, account, currencies, booking)
+beancount.core.data.Open.__new__(_cls, meta, date, account, currencies, booking)
special
@@ -6728,7 +6209,7 @@ beancount.core.data.Open.__repr__(self)
+beancount.core.data.Open.__repr__(self)
special
@@ -6742,10 +6223,10 @@ beancount/core/data.py
def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + repr_fmt % self
-
def __repr__(self):
+ 'Return a nicely formatted representation string'
+ return self.__class__.__name__ + repr_fmt % self
+
A "pad this account with this other account" directive. This directive -automatically inserts transactions that will make the next chronological -balance directive succeeds. It can be used to fill in missing date ranges of -transactions, as a convenience. You don't have to use this, it's sugar coating -in case you need it, while you're entering past history into your Ledger.
+Pad(meta, date, account, source_account)
-Attributes:
-Name | -Type | -Description | -
---|---|---|
meta |
- Dict[str, Any] |
- See above. |
-
date |
- date |
- See above. |
-
account |
- str |
- A string, the name of the account which needs to be filled. |
-
source_account |
- str |
- A string, the name of the account which is used to debit from -in order to fill 'account'. |
-
beancount.core.data.Pad.__getnewargs__(self)
+beancount.core.data.Pad.__getnewargs__(self)
special
@@ -6853,10 +6293,10 @@ beancount/core/data.py
def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return _tuple(self)
-
def __getnewargs__(self):
+ 'Return self as a plain tuple. Used by copy and pickle.'
+ return _tuple(self)
+
beancount.core.data.Pad.__new__(_cls, meta, date, account, source_account)
+beancount.core.data.Pad.__new__(_cls, meta, date, account, source_account)
special
@@ -6893,7 +6333,7 @@ beancount.core.data.Pad.__repr__(self)
+beancount.core.data.Pad.__repr__(self)
special
@@ -6907,10 +6347,10 @@ beancount/core/data.py
def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + repr_fmt % self
-
def __repr__(self):
+ 'Return a nicely formatted representation string'
+ return self.__class__.__name__ + repr_fmt % self
+
Postings are contained in Transaction entries. These represent the individual -legs of a transaction. Note: a posting may only appear within a single entry -(multiple transactions may not share a Posting instance), and that's what the -entry field should be set to.
+Posting(account, units, cost, price, flag, meta)
-Attributes:
-Name | -Type | -Description | -
---|---|---|
account |
- str |
- A string, the account that is modified by this posting. |
-
units |
- Optional[beancount.core.amount.Amount] |
- An Amount, the units of the position, or None if it is to be -inferred from the other postings in the transaction. |
-
cost |
- Union[beancount.core.position.Cost, beancount.core.position.CostSpec] |
- A Cost or CostSpec instances, the units of the position. |
-
price |
- Optional[beancount.core.amount.Amount] |
- An Amount, the price at which the position took place, or -None, where not relevant. Providing a price member to a posting -automatically adds a price in the prices database at the date of the -transaction. |
-
flag |
- Optional[str] |
- An optional flag, a one-character string or None, which is to be -associated with the posting. Most postings don't have a flag, but it can -be convenient to mark a particular posting as problematic or pending to -be reconciled for a future import of its account. |
-
meta |
- Optional[Dict[str, Any]] |
- A dict of strings to values, the metadata that was attached -specifically to that posting, or None, if not provided. In practice, most -of the instances will be unlikely to have metadata. |
-
beancount.core.data.Posting.__getnewargs__(self)
+beancount.core.data.Posting.__getnewargs__(self)
special
@@ -7037,10 +6417,10 @@ beancount/core/data.py
def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return _tuple(self)
-
def __getnewargs__(self):
+ 'Return self as a plain tuple. Used by copy and pickle.'
+ return _tuple(self)
+
beancount.core.data.Posting.__new__(_cls, account, units, cost, price, flag, meta)
+beancount.core.data.Posting.__new__(_cls, account, units, cost, price, flag, meta)
special
@@ -7077,7 +6457,7 @@ beancount.core.data.Posting.__repr__(self)
+beancount.core.data.Posting.__repr__(self)
special
@@ -7091,10 +6471,10 @@ beancount/core/data.py
def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + repr_fmt % self
-
def __repr__(self):
+ 'Return a nicely formatted representation string'
+ return self.__class__.__name__ + repr_fmt % self
+
A price declaration directive. This establishes the price of a currency in -terms of another currency as of the directive's date. A history of the prices -for each currency pairs is built and can be queried within the bookkeeping -system. Note that because Beancount does not store any data at time-of-day -resolution, it makes no sense to have multiple price directives at the same -date. (Beancount will not attempt to solve this problem; this is beyond the -general scope of double-entry bookkeeping and if you need to build a day -trading system, you should probably use something else).
- -Attributes:
-Name | -Type | -Description | -
---|---|---|
meta |
- Dict[str, Any] |
- See above. |
-
date |
- date |
- See above. |
-
currency: A string, the currency that is being priced, e.g. HOOL. - amount: An instance of Amount, the number of units and currency that - 'currency' is worth, for instance 1200.12 USD.
+Price(meta, date, currency, amount)
@@ -7174,16 +6522,12 @@beancount.core.data.Price.__getnewargs__(self)
+beancount.core.data.Price.__getnewargs__(self)
special
@@ -7197,10 +6541,10 @@ beancount/core/data.py
def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return _tuple(self)
-
def __getnewargs__(self):
+ 'Return self as a plain tuple. Used by copy and pickle.'
+ return _tuple(self)
+
beancount.core.data.Price.__new__(_cls, meta, date, currency, amount)
+beancount.core.data.Price.__new__(_cls, meta, date, currency, amount)
special
@@ -7237,7 +6581,7 @@ beancount.core.data.Price.__repr__(self)
+beancount.core.data.Price.__repr__(self)
special
@@ -7251,10 +6595,10 @@ beancount/core/data.py
def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + repr_fmt % self
-
def __repr__(self):
+ 'Return a nicely formatted representation string'
+ return self.__class__.__name__ + repr_fmt % self
+
A named query declaration. This directive is used to create pre-canned queries -that can then be automatically run or made available to the shell, or perhaps be -rendered as part of a web interface. The purpose of this routine is to define -useful queries for the context of the particular given Beancount input file.
+Query(meta, date, name, query_string)
-Attributes:
-Name | -Type | -Description | -
---|---|---|
meta |
- Dict[str, Any] |
- See above. |
-
date |
- date |
- The date at which this query should be run. All directives following -this date will be ignored automatically. This is essentially equivalent to -the CLOSE modifier in the shell syntax. |
-
name |
- str |
- A string, the unique identifier for the query. |
-
query_string |
- str |
- The SQL query string to be run or made available. |
-
beancount.core.data.Query.__getnewargs__(self)
+beancount.core.data.Query.__getnewargs__(self)
special
@@ -7362,10 +6665,10 @@ beancount/core/data.py
def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return _tuple(self)
-
def __getnewargs__(self):
+ 'Return self as a plain tuple. Used by copy and pickle.'
+ return _tuple(self)
+
beancount.core.data.Query.__new__(_cls, meta, date, name, query_string)
+beancount.core.data.Query.__new__(_cls, meta, date, name, query_string)
special
@@ -7402,7 +6705,7 @@ beancount.core.data.Query.__repr__(self)
+beancount.core.data.Query.__repr__(self)
special
@@ -7416,10 +6719,10 @@ beancount/core/data.py
def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + repr_fmt % self
-
def __repr__(self):
+ 'Return a nicely formatted representation string'
+ return self.__class__.__name__ + repr_fmt % self
+
A transaction! This is the main type of object that we manipulate, and the -entire reason this whole project exists in the first place, because -representing these types of structures with a spreadsheet is difficult.
+Transaction(meta, date, flag, payee, narration, tags, links, postings)
-Attributes:
-Name | -Type | -Description | -
---|---|---|
meta |
- Dict[str, Any] |
- See above. |
-
date |
- date |
- See above. |
-
flag |
- str |
- A single-character string or None. This user-specified string -represents some custom/user-defined state of the transaction. You can use -this for various purposes. Otherwise common, pre-defined flags are defined -under beancount.core.flags, to flags transactions that are automatically -generated. |
-
payee |
- Optional[str] |
- A free-form string that identifies the payee, or None, if absent. |
-
narration |
- str |
- A free-form string that provides a description for the transaction. -All transactions have at least a narration string, this is never None. |
-
tags |
- FrozenSet |
- A set of tag strings (without the '#'), or EMPTY_SET. |
-
links |
- FrozenSet |
- A set of link strings (without the '^'), or EMPTY_SET. |
-
postings |
- List[beancount.core.data.Posting] |
- A list of Posting instances, the legs of this transaction. See the -doc under Posting above. |
-
beancount.core.data.Transaction.__getnewargs__(self)
+beancount.core.data.Transaction.__getnewargs__(self)
special
@@ -7554,10 +6789,10 @@ beancount/core/data.py
def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return _tuple(self)
-
def __getnewargs__(self):
+ 'Return self as a plain tuple. Used by copy and pickle.'
+ return _tuple(self)
+
beancount.core.data.Transaction.__new__(_cls, meta, date, flag, payee, narration, tags, links, postings)
+beancount.core.data.Transaction.__new__(_cls, meta, date, flag, payee, narration, tags, links, postings)
special
@@ -7594,7 +6829,7 @@ beancount.core.data.Transaction.__repr__(self)
+beancount.core.data.Transaction.__repr__(self)
special
@@ -7608,10 +6843,10 @@ beancount/core/data.py
def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + repr_fmt % self
-
def __repr__(self):
+ 'Return a nicely formatted representation string'
+ return self.__class__.__name__ + repr_fmt % self
+
A pair of a Posting and its parent Transaction. This is inserted as -temporaries in lists of postings-of-entries, which is the product of a -realization.
- -Attributes:
-Name | -Type | -Description | -
---|---|---|
txn |
- Transaction |
- The parent Transaction instance. |
-
posting |
- Posting |
- The Posting instance. |
-
beancount.core.data.TxnPosting.__getnewargs__(self)
-
-
- special
-
-
-Return self as a plain tuple. Used by copy and pickle.
- -beancount/core/data.py
def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return _tuple(self)
-
beancount.core.data.TxnPosting.__new__(_cls, txn, posting)
-
-
- special
- staticmethod
-
-
-Create new instance of TxnPosting(txn, posting)
- -beancount.core.data.TxnPosting.__repr__(self)
-
-
- special
-
-
-Return a nicely formatted representation string
- -beancount/core/data.py
def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + repr_fmt % self
-
-beancount.core.data.dtypes
-
-
-
-Types of directives.
- - - - -
-beancount.core.data.dtypes.Balance (tuple)
-
-
-
-
-A "check the balance of this account" directive. This directive asserts that -the declared account should have a known number of units of a particular -currency at the beginning of its date. This is essentially an assertion, and -corresponds to the final "Statement Balance" line of a real-world statement. -These assertions act as checkpoints to help ensure that you have entered your -transactions correctly.
- -Attributes:
-Name | -Type | -Description | -
---|---|---|
meta |
- Dict[str, Any] |
- See above. |
-
date |
- date |
- See above. |
-
account |
- str |
- A string, the account whose balance to check at the given date. |
-
amount |
- Amount |
- An Amount, the number of units of the given currency you're -expecting 'account' to have at this date. |
-
diff_amount |
- Optional[beancount.core.amount.Amount] |
- None if the balance check succeeds. This value is set to -an Amount instance if the balance fails, the amount of the difference. |
-
tolerance |
- Optional[decimal.Decimal] |
- A Decimal object, the amount of tolerance to use in the -verification. |
-
beancount.core.data.dtypes.Balance.__getnewargs__(self)
-
-
- special
-
-
-Return self as a plain tuple. Used by copy and pickle.
- -beancount/core/data.py
def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return _tuple(self)
-
beancount.core.data.dtypes.Balance.__new__(_cls, meta, date, account, amount, tolerance, diff_amount)
-
-
- special
- staticmethod
-
-
-Create new instance of Balance(meta, date, account, amount, tolerance, diff_amount)
- -beancount.core.data.dtypes.Balance.__repr__(self)
-
-
- special
-
-
-Return a nicely formatted representation string
- -beancount/core/data.py
def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + repr_fmt % self
-
-beancount.core.data.dtypes.Close (tuple)
-
-
-
-
-A "close account" directive.
- -Attributes:
-Name | -Type | -Description | -
---|---|---|
meta |
- Dict[str, Any] |
- See above. |
-
date |
- date |
- See above. |
-
account |
- str |
- A string, the name of the account that is being closed. |
-
beancount.core.data.dtypes.Close.__getnewargs__(self)
-
-
- special
-
-
-Return self as a plain tuple. Used by copy and pickle.
- -beancount/core/data.py
def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return _tuple(self)
-
beancount.core.data.dtypes.Close.__new__(_cls, meta, date, account)
-
-
- special
- staticmethod
-
-
-Create new instance of Close(meta, date, account)
- -beancount.core.data.dtypes.Close.__repr__(self)
-
-
- special
-
-
-Return a nicely formatted representation string
- -beancount/core/data.py
def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + repr_fmt % self
-
-beancount.core.data.dtypes.Commodity (tuple)
-
-
-
-
-An optional commodity declaration directive. Commodities generally do not need -to be declared, but they may, and this is mainly created as intended to be -used to attach meta-data on a commodity name. Whenever a plugin needs -per-commodity meta-data, you would define such a commodity directive. Another -use is to define a commodity that isn't otherwise (yet) used anywhere in an -input file. (At the moment the date is meaningless but is specified for -coherence with all the other directives; if you can think of a good use case, -let us know).
- -Attributes:
-Name | -Type | -Description | -
---|---|---|
meta |
- Dict[str, Any] |
- See above. |
-
date |
- date |
- See above. |
-
currency |
- str |
- A string, the commodity under consideration. |
-
beancount.core.data.dtypes.Commodity.__getnewargs__(self)
-
-
- special
-
-
-Return self as a plain tuple. Used by copy and pickle.
- -beancount/core/data.py
def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return _tuple(self)
-
beancount.core.data.dtypes.Commodity.__new__(_cls, meta, date, currency)
-
-
- special
- staticmethod
-
-
-Create new instance of Commodity(meta, date, currency)
- -beancount.core.data.dtypes.Commodity.__repr__(self)
-
-
- special
-
-
-Return a nicely formatted representation string
- -beancount/core/data.py
def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + repr_fmt % self
-
-beancount.core.data.dtypes.Custom (tuple)
-
-
-
-
-A custom directive. This directive can be used to implement new experimental -dated features in the Beancount file. This is meant as an intermediate measure -to be used when you would need to implement a new directive in a plugin. These -directives will be parsed liberally... any list of tokens are supported. All -that is required is some unique name for them that acts as a "type". These -directives are included in the stream and a plugin should be able to gather -them.
- -Attributes:
-Name | -Type | -Description | -
---|---|---|
meta |
- Dict[str, Any] |
- See above. |
-
type |
- str |
- A string that represents the type of the directive. |
-
values |
- List |
- A list of values of various simple types supported by the grammar. -(Note that this list is not enforced to be consistent for all directives -of the same type by the parser.) |
-
beancount.core.data.dtypes.Custom.__getnewargs__(self)
-
-
- special
-
-
-Return self as a plain tuple. Used by copy and pickle.
- -beancount/core/data.py
def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return _tuple(self)
-
beancount.core.data.dtypes.Custom.__new__(_cls, meta, date, type, values)
-
-
- special
- staticmethod
-
-
-Create new instance of Custom(meta, date, type, values)
- -beancount.core.data.dtypes.Custom.__repr__(self)
-
-
- special
-
-
-Return a nicely formatted representation string
- -beancount/core/data.py
def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + repr_fmt % self
-
-beancount.core.data.dtypes.Document (tuple)
-
-
-
-
-A document file declaration directive. This directive is used to attach a -statement to an account, at a particular date. A typical usage would be to -render PDF files or scans of your bank statements into the account's journal. -While you can explicitly create those directives in the input syntax, it is -much more convenient to provide Beancount with a root directory to search for -filenames in a hierarchy mirroring the chart of accounts, filenames which -should match the following dated format: "YYYY-MM-DD.*". See options for -detail. Beancount will automatically create these documents directives based -on the file hierarchy, and you can get them by parsing the list of entries.
- -Attributes:
-Name | -Type | -Description | -
---|---|---|
meta |
- Dict[str, Any] |
- See above. |
-
date |
- date |
- See above. |
-
account |
- str |
- A string, the account which the statement or document is associated -with. |
-
filename |
- str |
- The absolute filename of the document file. |
-
tags |
- Optional[Set] |
- A set of tag strings (without the '#'), or None, if an empty set. |
-
links |
- Optional[Set] |
- A set of link strings (without the '^'), or None, if an empty set. |
-
beancount.core.data.dtypes.Document.__getnewargs__(self)
-
-
- special
-
-
-Return self as a plain tuple. Used by copy and pickle.
- -beancount/core/data.py
def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return _tuple(self)
-
beancount.core.data.dtypes.Document.__new__(_cls, meta, date, account, filename, tags, links)
-
-
- special
- staticmethod
-
-
-Create new instance of Document(meta, date, account, filename, tags, links)
- -beancount.core.data.dtypes.Document.__repr__(self)
-
-
- special
-
-
-Return a nicely formatted representation string
- -beancount/core/data.py
def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + repr_fmt % self
-
-beancount.core.data.dtypes.Event (tuple)
-
-
-
-
-An "event value change" directive. These directives are used as string -variables that have different values over time. You can use these to track an -address, your location, your current employer, anything you like. The kind of -reporting that is made of these generic events is based on days and a -timeline. For instance, if you need to track the number of days you spend in -each country or state, create a "location" event and whenever you travel, add -an event directive to indicate its new value. You should be able to write -simple scripts against those in order to compute if you were present somewhere -for a particular number of days. Here's an illustrative example usage, in -order to maintain your health insurance coverage in Canada, you need to be -present in the country for 183 days or more, excluding trips of less than 30 -days. There is a similar test to be done in the US by aliens to figure out if -they need to be considered as residents for tax purposes (the so-called -"substantial presence test"). By integrating these directives into your -bookkeeping, you can easily have a little program that computes the tests for -you. This is, of course, entirely optional and somewhat auxiliary to the main -purpose of double-entry bookkeeping, but correlates strongly with the -transactions you insert in it, and so it's a really convenient thing to have -in the same input file.
- -Attributes:
-Name | -Type | -Description | -
---|---|---|
meta |
- Dict[str, Any] |
- See above. |
-
date |
- date |
- See above. |
-
"type" |
- - | A short string, typically a single lowercase word, that defines a -unique variable whose value changes over time. For example, 'location'. |
-
description |
- str |
- A free-form string, the value of the variable as of the date -of the transaction. |
-
beancount.core.data.dtypes.Event.__getnewargs__(self)
-
-
- special
-
-
-Return self as a plain tuple. Used by copy and pickle.
- -beancount/core/data.py
def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return _tuple(self)
-
beancount.core.data.dtypes.Event.__new__(_cls, meta, date, type, description)
-
-
- special
- staticmethod
-
-
-Create new instance of Event(meta, date, type, description)
- -beancount.core.data.dtypes.Event.__repr__(self)
-
-
- special
-
-
-Return a nicely formatted representation string
- -beancount/core/data.py
def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + repr_fmt % self
-
-beancount.core.data.dtypes.Note (tuple)
-
-
-
-
-A note directive, a general note that is attached to an account. These are -used to attach text at a particular date in a specific account. The notes can -be anything; a typical use would be to jot down an answer from a phone call to -the institution represented by the account. It should show up in an account's -journal. If you don't want this rendered, use the comment syntax in the input -file, which does not get parsed and stored.
- -Attributes:
-Name | -Type | -Description | -
---|---|---|
meta |
- Dict[str, Any] |
- See above. |
-
date |
- date |
- See above. |
-
account |
- str |
- A string, the account which the note is to be attached to. This is -never None, notes always have an account they correspond to. |
-
comment |
- str |
- A free-form string, the text of the note. This can be long if you -want it to. |
-
beancount.core.data.dtypes.Note.__getnewargs__(self)
-
-
- special
-
-
-Return self as a plain tuple. Used by copy and pickle.
- -beancount/core/data.py
def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return _tuple(self)
-
beancount.core.data.dtypes.Note.__new__(_cls, meta, date, account, comment, tags, links)
-
-
- special
- staticmethod
-
-
-Create new instance of Note(meta, date, account, comment, tags, links)
- -beancount.core.data.dtypes.Note.__repr__(self)
-
-
- special
-
-
-Return a nicely formatted representation string
- -beancount/core/data.py
def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + repr_fmt % self
-
-beancount.core.data.dtypes.Open (tuple)
-
-
-
-
-An "open account" directive.
- -Attributes:
-Name | -Type | -Description | -
---|---|---|
meta |
- Dict[str, Any] |
- See above. |
-
date |
- date |
- See above. |
-
account |
- str |
- A string, the name of the account that is being opened. |
-
currencies |
- List[str] |
- A list of strings, currencies that are allowed in this account. -May be None, in which case it means that there are no restrictions on which -currencies may be stored in this account. |
-
booking |
- Optional[beancount.core.data.Booking] |
- A Booking enum, the booking method to use to disambiguate -postings to this account (when zero or more than one postings match the -specification), or None if not specified. In practice, this attribute will -be should be left unspecified (None) in the vast majority of cases. See -Booking below for a selection of valid methods. |
-
beancount.core.data.dtypes.Open.__getnewargs__(self)
-
-
- special
-
-
-Return self as a plain tuple. Used by copy and pickle.
- -beancount/core/data.py
def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return _tuple(self)
-
beancount.core.data.dtypes.Open.__new__(_cls, meta, date, account, currencies, booking)
-
-
- special
- staticmethod
-
-
-Create new instance of Open(meta, date, account, currencies, booking)
- -beancount.core.data.dtypes.Open.__repr__(self)
-
-
- special
-
-
-Return a nicely formatted representation string
- -beancount/core/data.py
def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + repr_fmt % self
-
-beancount.core.data.dtypes.Pad (tuple)
-
-
-
-
-A "pad this account with this other account" directive. This directive -automatically inserts transactions that will make the next chronological -balance directive succeeds. It can be used to fill in missing date ranges of -transactions, as a convenience. You don't have to use this, it's sugar coating -in case you need it, while you're entering past history into your Ledger.
- -Attributes:
-Name | -Type | -Description | -
---|---|---|
meta |
- Dict[str, Any] |
- See above. |
-
date |
- date |
- See above. |
-
account |
- str |
- A string, the name of the account which needs to be filled. |
-
source_account |
- str |
- A string, the name of the account which is used to debit from -in order to fill 'account'. |
-
beancount.core.data.dtypes.Pad.__getnewargs__(self)
-
-
- special
-
-
-Return self as a plain tuple. Used by copy and pickle.
- -beancount/core/data.py
def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return _tuple(self)
-
beancount.core.data.dtypes.Pad.__new__(_cls, meta, date, account, source_account)
-
-
- special
- staticmethod
-
-
-Create new instance of Pad(meta, date, account, source_account)
- -beancount.core.data.dtypes.Pad.__repr__(self)
-
-
- special
-
-
-Return a nicely formatted representation string
- -beancount/core/data.py
def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + repr_fmt % self
-
-beancount.core.data.dtypes.Price (tuple)
-
-
-
-
-A price declaration directive. This establishes the price of a currency in -terms of another currency as of the directive's date. A history of the prices -for each currency pairs is built and can be queried within the bookkeeping -system. Note that because Beancount does not store any data at time-of-day -resolution, it makes no sense to have multiple price directives at the same -date. (Beancount will not attempt to solve this problem; this is beyond the -general scope of double-entry bookkeeping and if you need to build a day -trading system, you should probably use something else).
- -Attributes:
-Name | -Type | -Description | -
---|---|---|
meta |
- Dict[str, Any] |
- See above. |
-
date |
- date |
- See above. |
-
currency: A string, the currency that is being priced, e.g. HOOL. - amount: An instance of Amount, the number of units and currency that - 'currency' is worth, for instance 1200.12 USD.
- - - - -beancount.core.data.dtypes.Price.__getnewargs__(self)
-
-
- special
-
-
-Return self as a plain tuple. Used by copy and pickle.
- -beancount/core/data.py
def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return _tuple(self)
-
beancount.core.data.dtypes.Price.__new__(_cls, meta, date, currency, amount)
-
-
- special
- staticmethod
-
-
-Create new instance of Price(meta, date, currency, amount)
- -beancount.core.data.dtypes.Price.__repr__(self)
-
-
- special
-
-
-Return a nicely formatted representation string
- -beancount/core/data.py
def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + repr_fmt % self
-
TxnPosting(txn, posting)
-
-beancount.core.data.dtypes.Query (tuple)
-
-
-
-
-A named query declaration. This directive is used to create pre-canned queries -that can then be automatically run or made available to the shell, or perhaps be -rendered as part of a web interface. The purpose of this routine is to define -useful queries for the context of the particular given Beancount input file.
- -Attributes:
-Name | -Type | -Description | -
---|---|---|
meta |
- Dict[str, Any] |
- See above. |
-
date |
- date |
- The date at which this query should be run. All directives following -this date will be ignored automatically. This is essentially equivalent to -the CLOSE modifier in the shell syntax. |
-
name |
- str |
- A string, the unique identifier for the query. |
-
query_string |
- str |
- The SQL query string to be run or made available. |
-
beancount.core.data.dtypes.Query.__getnewargs__(self)
-
-
- special
-
-
-Return self as a plain tuple. Used by copy and pickle.
- -beancount/core/data.py
def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return _tuple(self)
-
beancount.core.data.dtypes.Query.__new__(_cls, meta, date, name, query_string)
-
-
- special
- staticmethod
-
-
-Create new instance of Query(meta, date, name, query_string)
- -beancount.core.data.dtypes.Query.__repr__(self)
-
-
- special
-
-
-Return a nicely formatted representation string
- -beancount/core/data.py
def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + repr_fmt % self
-
-beancount.core.data.dtypes.Transaction (tuple)
-
-
-
-
-A transaction! This is the main type of object that we manipulate, and the -entire reason this whole project exists in the first place, because -representing these types of structures with a spreadsheet is difficult.
- -Attributes:
-Name | -Type | -Description | -
---|---|---|
meta |
- Dict[str, Any] |
- See above. |
-
date |
- date |
- See above. |
-
flag |
- str |
- A single-character string or None. This user-specified string -represents some custom/user-defined state of the transaction. You can use -this for various purposes. Otherwise common, pre-defined flags are defined -under beancount.core.flags, to flags transactions that are automatically -generated. |
-
payee |
- Optional[str] |
- A free-form string that identifies the payee, or None, if absent. |
-
narration |
- str |
- A free-form string that provides a description for the transaction. -All transactions have at least a narration string, this is never None. |
-
tags |
- FrozenSet |
- A set of tag strings (without the '#'), or EMPTY_SET. |
-
links |
- FrozenSet |
- A set of link strings (without the '^'), or EMPTY_SET. |
-
postings |
- List[beancount.core.data.Posting] |
- A list of Posting instances, the legs of this transaction. See the -doc under Posting above. |
-
beancount.core.data.dtypes.Transaction.__getnewargs__(self)
+beancount.core.data.TxnPosting.__getnewargs__(self)
special
-beancount/core/data.py
def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return _tuple(self)
-
def __getnewargs__(self):
+ 'Return self as a plain tuple. Used by copy and pickle.'
+ return _tuple(self)
+
beancount.core.data.dtypes.Transaction.__new__(_cls, meta, date, flag, payee, narration, tags, links, postings)
+beancount.core.data.TxnPosting.__new__(_cls, txn, posting)
special
staticmethod
-Create new instance of Transaction(meta, date, flag, payee, narration, tags, links, postings)
+Create new instance of TxnPosting(txn, posting)
beancount.core.data.dtypes.Transaction.__repr__(self)
+beancount.core.data.TxnPosting.__repr__(self)
special
-beancount/core/data.py
def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + repr_fmt % self
-
def __repr__(self):
+ 'Return a nicely formatted representation string'
+ return self.__class__.__name__ + repr_fmt % self
+
beancount.core.data.create_simple_posting(entry, account, number, currency)
+beancount.core.data.create_simple_posting(entry, account, number, currency)
@@ -9913,31 +7041,31 @@ beancount/core/data.py
def create_simple_posting(entry, account, number, currency):
- """Create a simple posting on the entry, with just a number and currency (no cost).
-
- Args:
- entry: The entry instance to add the posting to.
- account: A string, the account to use on the posting.
- number: A Decimal number or string to use in the posting's Amount.
- currency: A string, the currency for the Amount.
- Returns:
- An instance of Posting, and as a side-effect the entry has had its list of
- postings modified with the new Posting instance.
- """
- if isinstance(account, str):
- pass
- if number is None:
- units = None
- else:
- if not isinstance(number, Decimal):
- number = D(number)
- units = Amount(number, currency)
- posting = Posting(account, units, None, None, None, None)
- if entry is not None:
- entry.postings.append(posting)
- return posting
-
def create_simple_posting(entry, account, number, currency):
+ """Create a simple posting on the entry, with just a number and currency (no cost).
+
+ Args:
+ entry: The entry instance to add the posting to.
+ account: A string, the account to use on the posting.
+ number: A Decimal number or string to use in the posting's Amount.
+ currency: A string, the currency for the Amount.
+ Returns:
+ An instance of Posting, and as a side-effect the entry has had its list of
+ postings modified with the new Posting instance.
+ """
+ if isinstance(account, str):
+ pass
+ if number is None:
+ units = None
+ else:
+ if not isinstance(number, Decimal):
+ number = D(number)
+ units = Amount(number, currency)
+ posting = Posting(account, units, None, None, None, None)
+ if entry is not None:
+ entry.postings.append(posting)
+ return posting
+
beancount.core.data.create_simple_posting_with_cost(entry, account, number, currency, cost_number, cost_currency)
+beancount.core.data.create_simple_posting_with_cost(entry, account, number, currency, cost_number, cost_currency)
beancount/core/data.py
def create_simple_posting_with_cost(
- entry, account, number, currency, cost_number, cost_currency
-):
- """Create a simple posting on the entry, with just a number and currency (no cost).
-
- Args:
- entry: The entry instance to add the posting to.
- account: A string, the account to use on the posting.
- number: A Decimal number or string to use in the posting's Amount.
- currency: A string, the currency for the Amount.
- cost_number: A Decimal number or string to use for the posting's cost Amount.
- cost_currency: a string, the currency for the cost Amount.
- Returns:
- An instance of Posting, and as a side-effect the entry has had its list of
- postings modified with the new Posting instance.
- """
- if isinstance(account, str):
- pass
- if not isinstance(number, Decimal):
- number = D(number)
- if cost_number and not isinstance(cost_number, Decimal):
- cost_number = D(cost_number)
- units = Amount(number, currency)
- cost = Cost(cost_number, cost_currency, None, None)
- posting = Posting(account, units, cost, None, None, None)
- if entry is not None:
- entry.postings.append(posting)
- return posting
-
def create_simple_posting_with_cost(entry, account,
+ number, currency,
+ cost_number, cost_currency):
+ """Create a simple posting on the entry, with just a number and currency (no cost).
+
+ Args:
+ entry: The entry instance to add the posting to.
+ account: A string, the account to use on the posting.
+ number: A Decimal number or string to use in the posting's Amount.
+ currency: A string, the currency for the Amount.
+ cost_number: A Decimal number or string to use for the posting's cost Amount.
+ cost_currency: a string, the currency for the cost Amount.
+ Returns:
+ An instance of Posting, and as a side-effect the entry has had its list of
+ postings modified with the new Posting instance.
+ """
+ if isinstance(account, str):
+ pass
+ if not isinstance(number, Decimal):
+ number = D(number)
+ if cost_number and not isinstance(cost_number, Decimal):
+ cost_number = D(cost_number)
+ units = Amount(number, currency)
+ cost = Cost(cost_number, cost_currency, None, None)
+ posting = Posting(account, units, cost, None, None, None)
+ if entry is not None:
+ entry.postings.append(posting)
+ return posting
+
beancount.core.data.entry_sortkey(entry)
+beancount.core.data.entry_sortkey(entry)
beancount/core/data.py
def entry_sortkey(entry):
- """Sort-key for entries. We sort by date, except that checks
- should be placed in front of every list of entries of that same day,
- in order to balance linearly.
-
- Args:
- entry: An entry instance.
- Returns:
- A tuple of (date, integer, integer), that forms the sort key for the
- entry.
- """
- return (entry.date, SORT_ORDER.get(type(entry), 0), entry.meta["lineno"])
-
def entry_sortkey(entry):
+ """Sort-key for entries. We sort by date, except that checks
+ should be placed in front of every list of entries of that same day,
+ in order to balance linearly.
+
+ Args:
+ entry: An entry instance.
+ Returns:
+ A tuple of (date, integer, integer), that forms the sort key for the
+ entry.
+ """
+ return (entry.date, SORT_ORDER.get(type(entry), 0), entry.meta["lineno"])
+
beancount.core.data.filter_txns(entries)
+beancount.core.data.filter_txns(entries)
beancount/core/data.py
def filter_txns(entries):
- """A generator that yields only the Transaction instances.
-
- This is such an incredibly common operation that it deserves a terse
- filtering mechanism.
-
- Args:
- entries: A list of directives.
- Yields:
- A sorted list of only the Transaction directives.
- """
- for entry in entries:
- if isinstance(entry, Transaction):
- yield entry
-
def filter_txns(entries):
+ """A generator that yields only the Transaction instances.
+
+ This is such an incredibly common operation that it deserves a terse
+ filtering mechanism.
+
+ Args:
+ entries: A list of directives.
+ Yields:
+ A sorted list of only the Transaction directives.
+ """
+ for entry in entries:
+ if isinstance(entry, Transaction):
+ yield entry
+
beancount.core.data.find_closest(entries, filename, lineno)
+beancount.core.data.find_closest(entries, filename, lineno)
beancount/core/data.py
def find_closest(entries, filename, lineno):
- """Find the closest entry from entries to (filename, lineno).
-
- Args:
- entries: A list of directives.
- filename: A string, the name of the ledger file to look for. Be careful
- to provide the very same filename, and note that the parser stores the
- absolute path of the filename here.
- lineno: An integer, the line number closest after the directive we're
- looking for. This may be the exact/first line of the directive.
- Returns:
- The closest entry found in the given file for the given filename, or
- None, if none could be found.
- """
- min_diffline = sys.maxsize
- closest_entry = None
- for entry in entries:
- emeta = entry.meta
- if emeta["filename"] == filename and emeta["lineno"] > 0:
- diffline = lineno - emeta["lineno"]
- if 0 <= diffline < min_diffline:
- min_diffline = diffline
- closest_entry = entry
- return closest_entry
-
def find_closest(entries, filename, lineno):
+ """Find the closest entry from entries to (filename, lineno).
+
+ Args:
+ entries: A list of directives.
+ filename: A string, the name of the ledger file to look for. Be careful
+ to provide the very same filename, and note that the parser stores the
+ absolute path of the filename here.
+ lineno: An integer, the line number closest after the directive we're
+ looking for. This may be the exact/first line of the directive.
+ Returns:
+ The closest entry found in the given file for the given filename, or
+ None, if none could be found.
+ """
+ min_diffline = sys.maxsize
+ closest_entry = None
+ for entry in entries:
+ emeta = entry.meta
+ if emeta["filename"] == filename and emeta["lineno"] > 0:
+ diffline = lineno - emeta["lineno"]
+ if 0 <= diffline < min_diffline:
+ min_diffline = diffline
+ closest_entry = entry
+ return closest_entry
+
beancount.core.data.get_entry(posting_or_entry)
+beancount.core.data.get_entry(posting_or_entry)
beancount/core/data.py
def get_entry(posting_or_entry):
- """Return the entry associated with the posting or entry.
-
- Args:
- entry: A TxnPosting or entry instance
- Returns:
- A datetime instance.
- """
- return (
- posting_or_entry.txn
- if isinstance(posting_or_entry, TxnPosting)
- else posting_or_entry
- )
-
def get_entry(posting_or_entry):
+ """Return the entry associated with the posting or entry.
+
+ Args:
+ entry: A TxnPosting or entry instance
+ Returns:
+ A datetime instance.
+ """
+ return (posting_or_entry.txn
+ if isinstance(posting_or_entry, TxnPosting)
+ else posting_or_entry)
+
beancount.core.data.has_entry_account_component(entry, component)
+beancount.core.data.has_entry_account_component(entry, component)
beancount/core/data.py
def has_entry_account_component(entry, component):
- """Return true if one of the entry's postings has an account component.
-
- Args:
- entry: A Transaction entry.
- component: A string, a component of an account name. For instance,
- ``Food`` in ``Expenses:Food:Restaurant``. All components are considered.
- Returns:
- Boolean: true if the component is in the account. Note that a component
- name must be whole, that is ``NY`` is not in ``Expenses:Taxes:StateNY``.
- """
- return isinstance(entry, Transaction) and any(
- has_component(posting.account, component) for posting in entry.postings
- )
-
def has_entry_account_component(entry, component):
+ """Return true if one of the entry's postings has an account component.
+
+ Args:
+ entry: A Transaction entry.
+ component: A string, a component of an account name. For instance,
+ ``Food`` in ``Expenses:Food:Restaurant``. All components are considered.
+ Returns:
+ Boolean: true if the component is in the account. Note that a component
+ name must be whole, that is ``NY`` is not in ``Expenses:Taxes:StateNY``.
+ """
+ return (isinstance(entry, Transaction) and
+ any(has_component(posting.account, component)
+ for posting in entry.postings))
+
beancount.core.data.iter_entry_dates(entries, date_begin, date_end)
+beancount.core.data.iter_entry_dates(entries, date_begin, date_end)
beancount/core/data.py
def iter_entry_dates(entries, date_begin, date_end):
- """Iterate over the entries in a date window.
-
- Args:
- entries: A date-sorted list of dated directives.
- date_begin: A datetime.date instance, the first date to include.
- date_end: A datetime.date instance, one day beyond the last date.
- Yields:
- Instances of the dated directives, between the dates, and in the order in
- which they appear.
- """
- getdate = lambda entry: entry.date
- index_begin = bisect_left_with_key(entries, date_begin, key=getdate)
- index_end = bisect_left_with_key(entries, date_end, key=getdate)
- for index in range(index_begin, index_end):
- yield entries[index]
-
def iter_entry_dates(entries, date_begin, date_end):
+ """Iterate over the entries in a date window.
+
+ Args:
+ entries: A date-sorted list of dated directives.
+ date_begin: A datetime.date instance, the first date to include.
+ date_end: A datetime.date instance, one day beyond the last date.
+ Yields:
+ Instances of the dated directives, between the dates, and in the order in
+ which they appear.
+ """
+ getdate = lambda entry: entry.date
+ index_begin = bisect_left_with_key(entries, date_begin, key=getdate)
+ index_end = bisect_left_with_key(entries, date_end, key=getdate)
+ for index in range(index_begin, index_end):
+ yield entries[index]
+
+ beancount.core.data.new_directive(clsname, fields)
+
+
+Create a directive class. Do not include default fields. +This should probably be carried out through inheritance.
+ +Parameters: | +
+
|
+
---|
Returns: | +
+
|
+
---|
beancount/core/data.py
def new_directive(clsname, fields: List[Tuple]) -> NamedTuple:
+ """Create a directive class. Do not include default fields.
+ This should probably be carried out through inheritance.
+
+ Args:
+ name: A string, the capitalized name of the directive.
+ fields: A string or the list of strings, names for the fields
+ to add to the base tuple.
+ Returns:
+ A type object for the new directive type.
+ """
+ return NamedTuple(
+ clsname,
+ [('meta', Meta), ('date', datetime.date)] + fields)
+
beancount.core.data.new_metadata(filename, lineno, kvlist=None)
+beancount.core.data.new_metadata(filename, lineno, kvlist=None)
beancount/core/data.py
def new_metadata(filename, lineno, kvlist=None):
- """Create a new metadata container from the filename and line number.
-
- Args:
- filename: A string, the filename for the creator of this directive.
- lineno: An integer, the line number where the directive has been created.
- kvlist: An optional container of key-values.
- Returns:
- A metadata dict.
- """
- meta = {"filename": filename, "lineno": lineno}
- if kvlist:
- meta.update(kvlist)
- return meta
-
def new_metadata(filename, lineno, kvlist=None):
+ """Create a new metadata container from the filename and line number.
+
+ Args:
+ filename: A string, the filename for the creator of this directive.
+ lineno: An integer, the line number where the directive has been created.
+ kvlist: An optional container of key-values.
+ Returns:
+ A metadata dict.
+ """
+ meta = {'filename': filename,
+ 'lineno': lineno}
+ if kvlist:
+ meta.update(kvlist)
+ return meta
+
beancount.core.data.posting_has_conversion(posting)
+beancount.core.data.posting_has_conversion(posting)
beancount/core/data.py
def posting_has_conversion(posting):
- """Return true if this position involves a conversion.
-
- A conversion is when there is a price attached to the amount but no cost.
- This is used on transactions to convert between units.
-
- Args:
- posting: an instance of Posting
- Return:
- A boolean, true if this posting has a price conversion.
- """
- return posting.cost is None and posting.price is not None
-
def posting_has_conversion(posting):
+ """Return true if this position involves a conversion.
+
+ A conversion is when there is a price attached to the amount but no cost.
+ This is used on transactions to convert between units.
+
+ Args:
+ posting: an instance of Posting
+ Return:
+ A boolean, true if this posting has a price conversion.
+ """
+ return (posting.cost is None and
+ posting.price is not None)
+
beancount.core.data.posting_sortkey(entry)
+beancount.core.data.posting_sortkey(entry)
beancount/core/data.py
def posting_sortkey(entry):
- """Sort-key for entries or postings. We sort by date, except that checks
- should be placed in front of every list of entries of that same day,
- in order to balance linearly.
-
- Args:
- entry: A Posting or entry instance
- Returns:
- A tuple of (date, integer, integer), that forms the sort key for the
- posting or entry.
- """
- if isinstance(entry, TxnPosting):
- entry = entry.txn
- return (entry.date, SORT_ORDER.get(type(entry), 0), entry.meta["lineno"])
-
def posting_sortkey(entry):
+ """Sort-key for entries or postings. We sort by date, except that checks
+ should be placed in front of every list of entries of that same day,
+ in order to balance linearly.
+
+ Args:
+ entry: A Posting or entry instance
+ Returns:
+ A tuple of (date, integer, integer), that forms the sort key for the
+ posting or entry.
+ """
+ if isinstance(entry, TxnPosting):
+ entry = entry.txn
+ return (entry.date, SORT_ORDER.get(type(entry), 0), entry.meta["lineno"])
+
beancount.core.data.remove_account_postings(account, entries)
+beancount.core.data.remove_account_postings(account, entries)
beancount/core/data.py
def remove_account_postings(account, entries):
- """Remove all postings with the given account.
-
- Args:
- account: A string, the account name whose postings we want to remove.
- Returns:
- A list of entries without the rounding postings.
- """
- new_entries = []
- for entry in entries:
- if isinstance(entry, Transaction) and (
- any(posting.account == account for posting in entry.postings)
- ):
- entry = entry._replace(
- postings=[
- posting for posting in entry.postings if posting.account != account
- ]
- )
- new_entries.append(entry)
- return new_entries
-
def remove_account_postings(account, entries):
+ """Remove all postings with the given account.
+
+ Args:
+ account: A string, the account name whose postings we want to remove.
+ Returns:
+ A list of entries without the rounding postings.
+ """
+ new_entries = []
+ for entry in entries:
+ if isinstance(entry, Transaction) and (
+ any(posting.account == account for posting in entry.postings)):
+ entry = entry._replace(postings=[posting
+ for posting in entry.postings
+ if posting.account != account])
+ new_entries.append(entry)
+ return new_entries
+
beancount.core.data.sanity_check_types(entry, allow_none_for_tags_and_links=False)
+beancount.core.data.sanity_check_types(entry, allow_none_for_tags_and_links=False)
beancount/core/data.py
def sanity_check_types(entry, allow_none_for_tags_and_links=False):
- """Check that the entry and its postings has all correct data types.
-
- Args:
- entry: An instance of one of the entries to be checked.
- allow_none_for_tags_and_links: A boolean, whether to allow plugins to
- generate Transaction objects with None as value for the 'tags' or 'links'
- attributes.
- Raises:
- AssertionError: If there is anything that is unexpected, raises an exception.
- """
- assert isinstance(entry, ALL_DIRECTIVES), "Invalid directive type"
- assert isinstance(entry.meta, dict), "Invalid type for meta"
- assert "filename" in entry.meta, "Missing filename in metadata"
- assert "lineno" in entry.meta, "Missing line number in metadata"
- assert isinstance(entry.date, datetime.date), "Invalid date type"
- if isinstance(entry, Transaction):
- assert isinstance(entry.flag, (NoneType, str)), "Invalid flag type"
- assert isinstance(entry.payee, (NoneType, str)), "Invalid payee type"
- assert isinstance(entry.narration, (NoneType, str)), "Invalid narration type"
- set_types = (
- (NoneType, set, frozenset)
- if allow_none_for_tags_and_links
- else (set, frozenset)
- )
- assert isinstance(entry.tags, set_types), "Invalid tags type: {}".format(
- type(entry.tags)
- )
- assert isinstance(entry.links, set_types), "Invalid links type: {}".format(
- type(entry.links)
- )
- assert isinstance(entry.postings, list), "Invalid postings list type"
- for posting in entry.postings:
- assert isinstance(posting, Posting), "Invalid posting type"
- assert isinstance(posting.account, str), "Invalid account type"
- assert isinstance(posting.units, (Amount, NoneType)), "Invalid units type"
- assert isinstance(posting.cost, (Cost, CostSpec, NoneType)), "Invalid cost type"
- assert isinstance(posting.price, (Amount, NoneType)), "Invalid price type"
- assert isinstance(posting.flag, (str, NoneType)), "Invalid flag type"
-
def sanity_check_types(entry, allow_none_for_tags_and_links=False):
+ """Check that the entry and its postings has all correct data types.
+
+ Args:
+ entry: An instance of one of the entries to be checked.
+ allow_none_for_tags_and_links: A boolean, whether to allow plugins to
+ generate Transaction objects with None as value for the 'tags' or 'links'
+ attributes.
+ Raises:
+ AssertionError: If there is anything that is unexpected, raises an exception.
+ """
+ assert isinstance(entry, ALL_DIRECTIVES), "Invalid directive type"
+ assert isinstance(entry.meta, dict), "Invalid type for meta"
+ assert 'filename' in entry.meta, "Missing filename in metadata"
+ assert 'lineno' in entry.meta, "Missing line number in metadata"
+ assert isinstance(entry.date, datetime.date), "Invalid date type"
+ if isinstance(entry, Transaction):
+ assert isinstance(entry.flag, (NoneType, str)), "Invalid flag type"
+ assert isinstance(entry.payee, (NoneType, str)), "Invalid payee type"
+ assert isinstance(entry.narration, (NoneType, str)), "Invalid narration type"
+ set_types = ((NoneType, set, frozenset)
+ if allow_none_for_tags_and_links
+ else (set, frozenset))
+ assert isinstance(entry.tags, set_types), (
+ "Invalid tags type: {}".format(type(entry.tags)))
+ assert isinstance(entry.links, set_types), (
+ "Invalid links type: {}".format(type(entry.links)))
+ assert isinstance(entry.postings, list), "Invalid postings list type"
+ for posting in entry.postings:
+ assert isinstance(posting, Posting), "Invalid posting type"
+ assert isinstance(posting.account, str), "Invalid account type"
+ assert isinstance(posting.units, (Amount, NoneType)), "Invalid units type"
+ assert isinstance(posting.cost, (Cost, CostSpec, NoneType)), "Invalid cost type"
+ assert isinstance(posting.price, (Amount, NoneType)), "Invalid price type"
+ assert isinstance(posting.flag, (str, NoneType)), "Invalid flag type"
+
beancount.core.data.sorted(entries)
+beancount.core.data.sorted(entries)
beancount/core/data.py
def sorted(entries):
- """A convenience to sort a list of entries, using entry_sortkey().
-
- Args:
- entries: A list of directives.
- Returns:
- A sorted list of directives.
- """
- return builtins.sorted(entries, key=entry_sortkey)
-
def sorted(entries):
+ """A convenience to sort a list of entries, using entry_sortkey().
+
+ Args:
+ entries: A list of directives.
+ Returns:
+ A sorted list of directives.
+ """
+ return builtins.sorted(entries, key=entry_sortkey)
+
beancount.core.data.transaction_has_conversion(transaction)
+beancount.core.data.transaction_has_conversion(transaction)
beancount/core/data.py
def transaction_has_conversion(transaction):
- """Given a Transaction entry, return true if at least one of
- the postings has a price conversion (without an associated
- cost). These are the source of non-zero conversion balances.
-
- Args:
- transaction: an instance of a Transaction entry.
- Returns:
- A boolean, true if this transaction contains at least one posting with a
- price conversion.
- """
- assert isinstance(
- transaction, Transaction
- ), "Invalid type of entry for transaction: {}".format(transaction)
- for posting in transaction.postings:
- if posting_has_conversion(posting):
- return True
- return False
-
def transaction_has_conversion(transaction):
+ """Given a Transaction entry, return true if at least one of
+ the postings has a price conversion (without an associated
+ cost). These are the source of non-zero conversion balances.
+
+ Args:
+ transaction: an instance of a Transaction entry.
+ Returns:
+ A boolean, true if this transaction contains at least one posting with a
+ price conversion.
+ """
+ assert isinstance(transaction, Transaction), (
+ "Invalid type of entry for transaction: {}".format(transaction))
+ for posting in transaction.postings:
+ if posting_has_conversion(posting):
+ return True
+ return False
+
beancount.core.display_context.DisplayContext.build(self, alignment=<Align.NATURAL: 1>, precision=<Precision.MOST_COMMON: 1>, commas=None, reserved=0)
+beancount.core.display_context.DisplayContext.build(self, alignment=<Align.NATURAL: 1>, precision=<Precision.MOST_COMMON: 1>, commas=None, reserved=0)
beancount/core/display_context.py
def build(
- self,
- alignment=Align.NATURAL,
- precision=Precision.MOST_COMMON,
- commas=None,
- reserved=0,
-):
- """Build a formatter for the given display context.
-
- Args:
- alignment: The desired alignment.
- precision: The desired precision.
- commas: Whether to render commas or not. If 'None', the default value carried
- by the context will be used.
- reserved: An integer, the number of extra digits to be allocated in
- the maximum width calculations.
- """
- if commas is None:
- commas = self.commas
- if alignment == Align.NATURAL:
- build_method = self._build_natural
- elif alignment == Align.RIGHT:
- build_method = self._build_right
- elif alignment == Align.DOT:
- build_method = self._build_dot
- else:
- raise ValueError("Unknown alignment: {}".format(alignment))
- fmtstrings = build_method(precision, commas, reserved)
-
- return DisplayFormatter(self, precision, fmtstrings)
-
def build(self,
+ alignment=Align.NATURAL,
+ precision=Precision.MOST_COMMON,
+ commas=None,
+ reserved=0):
+ """Build a formatter for the given display context.
+
+ Args:
+ alignment: The desired alignment.
+ precision: The desired precision.
+ commas: Whether to render commas or not. If 'None', the default value carried
+ by the context will be used.
+ reserved: An integer, the number of extra digits to be allocated in
+ the maximum width calculations.
+ """
+ if commas is None:
+ commas = self.commas
+ if alignment == Align.NATURAL:
+ build_method = self._build_natural
+ elif alignment == Align.RIGHT:
+ build_method = self._build_right
+ elif alignment == Align.DOT:
+ build_method = self._build_dot
+ else:
+ raise ValueError("Unknown alignment: {}".format(alignment))
+ fmtstrings = build_method(precision, commas, reserved)
+
+ return DisplayFormatter(self, precision, fmtstrings)
+
@@ -11253,7 +8444,7 @@ beancount.core.display_context.DisplayContext.quantize(self, number, currency, precision=<Precision.MOST_COMMON: 1>)
+beancount.core.display_context.DisplayContext.quantize(self, number, currency, precision=<Precision.MOST_COMMON: 1>)
beancount/core/display_context.py
def quantize(self, number, currency, precision=Precision.MOST_COMMON):
- """Quantize the given number to the given precision.
-
- Args:
- number: A Decimal instance, the number to be quantized.
- currency: A currency string.
- precision: Which precision to use.
- Returns:
- A Decimal instance, the quantized number.
- """
- assert isinstance(number, Decimal), "Invalid data: {}".format(number)
- ccontext = self.ccontexts[currency]
- num_fractional_digits = ccontext.get_fractional(precision)
- if num_fractional_digits is None:
- # Note: We could probably logging.warn() this situation here.
- return number
- qdigit = Decimal(1).scaleb(-num_fractional_digits)
-
- with decimal.localcontext() as ctx:
- # Allow precision for numbers as large as 1 billion in addition to
- # the required number of fractional digits.
- #
- # TODO(blais): Review this to assess performance impact, and whether
- # we could fold this outside a calling loop.
- ctx.prec = num_fractional_digits + 9
- return number.quantize(qdigit)
-
def quantize(self, number, currency, precision=Precision.MOST_COMMON):
+ """Quantize the given number to the given precision.
+
+ Args:
+ number: A Decimal instance, the number to be quantized.
+ currency: A currency string.
+ precision: Which precision to use.
+ Returns:
+ A Decimal instance, the quantized number.
+ """
+ assert isinstance(number, Decimal), "Invalid data: {}".format(number)
+ ccontext = self.ccontexts[currency]
+ num_fractional_digits = ccontext.get_fractional(precision)
+ if num_fractional_digits is None:
+ # Note: We could probably logging.warn() this situation here.
+ return number
+ qdigit = Decimal(1).scaleb(-num_fractional_digits)
+ return number.quantize(qdigit)
+
@@ -11337,7 +8520,7 @@ beancount.core.display_context.DisplayContext.set_commas(self, commas)
+beancount.core.display_context.DisplayContext.set_commas(self, commas)
beancount/core/display_context.py
def set_commas(self, commas):
- """Set the default value for rendering commas."""
- self.commas = commas
-
def set_commas(self, commas):
+ """Set the default value for rendering commas."""
+ self.commas = commas
+
@@ -11364,7 +8547,7 @@ beancount.core.display_context.DisplayContext.update(self, number, currency='__default__')
+beancount.core.display_context.DisplayContext.update(self, number, currency='__default__')
beancount/core/display_context.py
def update(self, number, currency="__default__"):
- """Update the builder with the given number for the given currency.
-
- Args:
- number: An instance of Decimal to consider for this currency.
- currency: An optional string, the currency this numbers applies to.
- """
- self.ccontexts[currency].update(number)
-
beancount.core.display_context.DisplayContext.update_from(self, other)
-
-
-Update the builder with the other given DisplayContext.
- -Parameters: | -
-
|
-
---|
beancount/core/display_context.py
def update_from(self, other):
- """Update the builder with the other given DisplayContext.
-
- Args:
- other: Another DisplayContext.
- """
- for currency, ccontext in other.ccontexts.items():
- self.ccontexts[currency].update_from(ccontext)
-
def update(self, number, currency='__default__'):
+ """Update the builder with the given number for the given currency.
+
+ Args:
+ number: An instance of Decimal to consider for this currency.
+ currency: An optional string, the currency this numbers applies to.
+ """
+ self.ccontexts[currency].update(number)
+
beancount.core.distribution.Distribution.empty(self)
+beancount.core.distribution.Distribution.empty(self)
beancount/core/distribution.py
def empty(self):
- """Return true if the distribution is empty.
-
- Returns:
- A boolean.
- """
- return len(self.hist) == 0
-
def empty(self):
+ """Return true if the distribution is empty.
+
+ Returns:
+ A boolean.
+ """
+ return len(self.hist) == 0
+
beancount.core.distribution.Distribution.max(self)
+beancount.core.distribution.Distribution.max(self)
beancount/core/distribution.py
def max(self):
- """Return the minimum value seen in the distribution.
-
- Returns:
- An element of the value type, or None, if the distribution was empty.
- """
- if not self.hist:
- return None
- value, _ = sorted(self.hist.items())[-1]
- return value
-
def max(self):
+ """Return the minimum value seen in the distribution.
+
+ Returns:
+ An element of the value type, or None, if the distribution was empty.
+ """
+ if not self.hist:
+ return None
+ value, _ = sorted(self.hist.items())[-1]
+ return value
+
beancount.core.distribution.Distribution.min(self)
+beancount.core.distribution.Distribution.min(self)
beancount/core/distribution.py
def min(self):
- """Return the minimum value seen in the distribution.
-
- Returns:
- An element of the value type, or None, if the distribution was empty.
- """
- if not self.hist:
- return None
- value, _ = sorted(self.hist.items())[0]
- return value
-
def min(self):
+ """Return the minimum value seen in the distribution.
+
+ Returns:
+ An element of the value type, or None, if the distribution was empty.
+ """
+ if not self.hist:
+ return None
+ value, _ = sorted(self.hist.items())[0]
+ return value
+
beancount.core.distribution.Distribution.mode(self)
+beancount.core.distribution.Distribution.mode(self)
beancount/core/distribution.py
def mode(self):
- """Return the mode of the distribution.
-
- Returns:
- An element of the value type, or None, if the distribution was empty.
- """
- if not self.hist:
- return None
- max_value = 0
- max_count = 0
- for value, count in sorted(self.hist.items()):
- if count >= max_count:
- max_count = count
- max_value = value
- return max_value
-
def mode(self):
+ """Return the mode of the distribution.
+
+ Returns:
+ An element of the value type, or None, if the distribution was empty.
+ """
+ if not self.hist:
+ return None
+ max_value = 0
+ max_count = 0
+ for value, count in sorted(self.hist.items()):
+ if count >= max_count:
+ max_count = count
+ max_value = value
+ return max_value
+
beancount.core.distribution.Distribution.update(self, value)
+beancount.core.distribution.Distribution.update(self, value)
beancount/core/distribution.py
def update(self, value):
- """Add a sample to the distribution.
-
- Args:
- value: A value of the function.
- """
- self.hist[value] += 1
-
beancount.core.distribution.Distribution.update_from(self, other)
-
+ def update(self, value):
+ """Add a sample to the distribution.
-
Add samples from the other distribution to this one.
- -Parameters: | -
-
|
-
---|
beancount/core/distribution.py
def update_from(self, other):
- """Add samples from the other distribution to this one.
-
- Args:
- other: Another distribution.
- """
- for value, count in other.hist.items():
- self.hist[value] += count
-
beancount.core.getters.GetAccounts.Balance(_, entry)
+beancount.core.getters.GetAccounts.Balance(_, entry)
beancount/core/getters.py
def _one(_, entry):
- """Process directives with a single account attribute.
-
- Args:
- entry: An instance of a directive.
- Returns:
- The single account of this directive.
- """
- return (entry.account,)
-
def _one(_, entry):
+ """Process directives with a single account attribute.
+
+ Args:
+ entry: An instance of a directive.
+ Returns:
+ The single account of this directive.
+ """
+ return (entry.account,)
+
beancount.core.getters.GetAccounts.Close(_, entry)
+beancount.core.getters.GetAccounts.Close(_, entry)
beancount/core/getters.py
def _one(_, entry):
- """Process directives with a single account attribute.
-
- Args:
- entry: An instance of a directive.
- Returns:
- The single account of this directive.
- """
- return (entry.account,)
-
def _one(_, entry):
+ """Process directives with a single account attribute.
+
+ Args:
+ entry: An instance of a directive.
+ Returns:
+ The single account of this directive.
+ """
+ return (entry.account,)
+
beancount.core.getters.GetAccounts.Commodity(_, entry)
+beancount.core.getters.GetAccounts.Commodity(_, entry)
beancount/core/getters.py
def _zero(_, entry):
- """Process directives with no accounts.
-
- Args:
- entry: An instance of a directive.
- Returns:
- An empty list
- """
- return ()
-
def _zero(_, entry):
+ """Process directives with no accounts.
+
+ Args:
+ entry: An instance of a directive.
+ Returns:
+ An empty list
+ """
+ return ()
+
beancount.core.getters.GetAccounts.Custom(_, entry)
+beancount.core.getters.GetAccounts.Custom(_, entry)
beancount/core/getters.py
def _zero(_, entry):
- """Process directives with no accounts.
-
- Args:
- entry: An instance of a directive.
- Returns:
- An empty list
- """
- return ()
-
def _zero(_, entry):
+ """Process directives with no accounts.
+
+ Args:
+ entry: An instance of a directive.
+ Returns:
+ An empty list
+ """
+ return ()
+
beancount.core.getters.GetAccounts.Document(_, entry)
+beancount.core.getters.GetAccounts.Document(_, entry)
beancount/core/getters.py
def _one(_, entry):
- """Process directives with a single account attribute.
-
- Args:
- entry: An instance of a directive.
- Returns:
- The single account of this directive.
- """
- return (entry.account,)
-
def _one(_, entry):
+ """Process directives with a single account attribute.
+
+ Args:
+ entry: An instance of a directive.
+ Returns:
+ The single account of this directive.
+ """
+ return (entry.account,)
+
beancount.core.getters.GetAccounts.Event(_, entry)
+beancount.core.getters.GetAccounts.Event(_, entry)
beancount/core/getters.py
def _zero(_, entry):
- """Process directives with no accounts.
-
- Args:
- entry: An instance of a directive.
- Returns:
- An empty list
- """
- return ()
-
def _zero(_, entry):
+ """Process directives with no accounts.
+
+ Args:
+ entry: An instance of a directive.
+ Returns:
+ An empty list
+ """
+ return ()
+
beancount.core.getters.GetAccounts.Note(_, entry)
+beancount.core.getters.GetAccounts.Note(_, entry)
beancount/core/getters.py
def _one(_, entry):
- """Process directives with a single account attribute.
-
- Args:
- entry: An instance of a directive.
- Returns:
- The single account of this directive.
- """
- return (entry.account,)
-
def _one(_, entry):
+ """Process directives with a single account attribute.
+
+ Args:
+ entry: An instance of a directive.
+ Returns:
+ The single account of this directive.
+ """
+ return (entry.account,)
+
beancount.core.getters.GetAccounts.Open(_, entry)
+beancount.core.getters.GetAccounts.Open(_, entry)
beancount/core/getters.py
def _one(_, entry):
- """Process directives with a single account attribute.
-
- Args:
- entry: An instance of a directive.
- Returns:
- The single account of this directive.
- """
- return (entry.account,)
-
def _one(_, entry):
+ """Process directives with a single account attribute.
+
+ Args:
+ entry: An instance of a directive.
+ Returns:
+ The single account of this directive.
+ """
+ return (entry.account,)
+
beancount.core.getters.GetAccounts.Pad(_, entry)
+beancount.core.getters.GetAccounts.Pad(_, entry)
beancount/core/getters.py
def Pad(_, entry):
- """Process a Pad directive.
-
- Args:
- entry: An instance of Pad.
- Returns:
- The two accounts of the Pad directive.
- """
- return (entry.account, entry.source_account)
-
def Pad(_, entry):
+ """Process a Pad directive.
+
+ Args:
+ entry: An instance of Pad.
+ Returns:
+ The two accounts of the Pad directive.
+ """
+ return (entry.account, entry.source_account)
+
beancount.core.getters.GetAccounts.Price(_, entry)
+beancount.core.getters.GetAccounts.Price(_, entry)
beancount/core/getters.py
def _zero(_, entry):
- """Process directives with no accounts.
-
- Args:
- entry: An instance of a directive.
- Returns:
- An empty list
- """
- return ()
-
def _zero(_, entry):
+ """Process directives with no accounts.
+
+ Args:
+ entry: An instance of a directive.
+ Returns:
+ An empty list
+ """
+ return ()
+
beancount.core.getters.GetAccounts.Query(_, entry)
+beancount.core.getters.GetAccounts.Query(_, entry)
beancount/core/getters.py
def _zero(_, entry):
- """Process directives with no accounts.
-
- Args:
- entry: An instance of a directive.
- Returns:
- An empty list
- """
- return ()
-
def _zero(_, entry):
+ """Process directives with no accounts.
+
+ Args:
+ entry: An instance of a directive.
+ Returns:
+ An empty list
+ """
+ return ()
+
beancount.core.getters.GetAccounts.Transaction(_, entry)
+beancount.core.getters.GetAccounts.Transaction(_, entry)
beancount/core/getters.py
def Transaction(_, entry):
- """Process a Transaction directive.
-
- Args:
- entry: An instance of Transaction.
- Yields:
- The accounts of the legs of the transaction.
- """
- for posting in entry.postings:
- yield posting.account
-
def Transaction(_, entry):
+ """Process a Transaction directive.
+
+ Args:
+ entry: An instance of Transaction.
+ Yields:
+ The accounts of the legs of the transaction.
+ """
+ for posting in entry.postings:
+ yield posting.account
+
beancount.core.getters.GetAccounts.get_accounts_use_map(self, entries)
+beancount.core.getters.GetAccounts.get_accounts_use_map(self, entries)
beancount/core/getters.py
def get_accounts_use_map(self, entries):
- """Gather the list of accounts from the list of entries.
-
- Args:
- entries: A list of directive instances.
- Returns:
- A pair of dictionaries of account name to date, one for first date
- used and one for last date used. The keys should be identical.
- """
- accounts_first = {}
- accounts_last = {}
- for entry in entries:
- method = getattr(self, entry.__class__.__name__)
- for account_ in method(entry):
- if account_ not in accounts_first:
- accounts_first[account_] = entry.date
- accounts_last[account_] = entry.date
- return accounts_first, accounts_last
-
def get_accounts_use_map(self, entries):
+ """Gather the list of accounts from the list of entries.
+
+ Args:
+ entries: A list of directive instances.
+ Returns:
+ A pair of dictionaries of account name to date, one for first date
+ used and one for last date used. The keys should be identical.
+ """
+ accounts_first = {}
+ accounts_last = {}
+ for entry in entries:
+ method = getattr(self, entry.__class__.__name__)
+ for account_ in method(entry):
+ if account_ not in accounts_first:
+ accounts_first[account_] = entry.date
+ accounts_last[account_] = entry.date
+ return accounts_first, accounts_last
+
@@ -12926,7 +10015,7 @@ beancount.core.getters.GetAccounts.get_entry_accounts(self, entry)
+beancount.core.getters.GetAccounts.get_entry_accounts(self, entry)
beancount/core/getters.py
def get_entry_accounts(self, entry):
- """Gather all the accounts references by a single directive.
-
- Note: This should get replaced by a method on each directive eventually,
- that would be the clean way to do this.
-
- Args:
- entry: A directive instance.
- Returns:
- A set of account name strings.
- """
- method = getattr(self, entry.__class__.__name__)
- return set(method(entry))
-
def get_entry_accounts(self, entry):
+ """Gather all the accounts references by a single directive.
+
+ Note: This should get replaced by a method on each directive eventually,
+ that would be the clean way to do this.
+
+ Args:
+ entry: A directive instance.
+ Returns:
+ A set of account name strings.
+ """
+ method = getattr(self, entry.__class__.__name__)
+ return set(method(entry))
+
@@ -13008,7 +10097,7 @@ beancount.core.getters.get_account_components(entries)
+beancount.core.getters.get_account_components(entries)
beancount/core/getters.py
def get_account_components(entries):
- """Gather all the account components available in the given directives.
-
- Args:
- entries: A list of directive instances.
- Returns:
- A list of strings, the unique account components, including the root
- account names.
- """
- accounts = get_accounts(entries)
- components = set()
- for account_name in accounts:
- components.update(account.split(account_name))
- return sorted(components)
-
def get_account_components(entries):
+ """Gather all the account components available in the given directives.
+
+ Args:
+ entries: A list of directive instances.
+ Returns:
+ A list of strings, the unique account components, including the root
+ account names.
+ """
+ accounts = get_accounts(entries)
+ components = set()
+ for account_name in accounts:
+ components.update(account.split(account_name))
+ return sorted(components)
+
beancount.core.getters.get_account_open_close(entries)
+beancount.core.getters.get_account_open_close(entries)
beancount/core/getters.py
def get_account_open_close(entries):
- """Fetch the open/close entries for each of the accounts.
-
- If an open or close entry happens to be duplicated, accept the earliest
- entry (chronologically).
-
- Args:
- entries: A list of directive instances.
- Returns:
- A map of account name strings to pairs of (open-directive, close-directive)
- tuples.
- """
- # A dict of account name to (open-entry, close-entry).
- open_close_map = defaultdict(lambda: [None, None])
- for entry in entries:
- if not isinstance(entry, (Open, Close)):
- continue
- open_close = open_close_map[entry.account]
- index = 0 if isinstance(entry, Open) else 1
- previous_entry = open_close[index]
- if previous_entry is not None:
- if previous_entry.date <= entry.date:
- entry = previous_entry
- open_close[index] = entry
-
- return dict(open_close_map)
-
def get_account_open_close(entries):
+ """Fetch the open/close entries for each of the accounts.
+
+ If an open or close entry happens to be duplicated, accept the earliest
+ entry (chronologically).
+
+ Args:
+ entries: A list of directive instances.
+ Returns:
+ A map of account name strings to pairs of (open-directive, close-directive)
+ tuples.
+ """
+ # A dict of account name to (open-entry, close-entry).
+ open_close_map = defaultdict(lambda: [None, None])
+ for entry in entries:
+ if not isinstance(entry, (Open, Close)):
+ continue
+ open_close = open_close_map[entry.account]
+ index = 0 if isinstance(entry, Open) else 1
+ previous_entry = open_close[index]
+ if previous_entry is not None:
+ if previous_entry.date <= entry.date:
+ entry = previous_entry
+ open_close[index] = entry
+
+ return dict(open_close_map)
+
beancount.core.getters.get_accounts(entries)
+beancount.core.getters.get_accounts(entries)
beancount/core/getters.py
def get_accounts(entries):
- """Gather all the accounts references by a list of directives.
-
- Args:
- entries: A list of directive instances.
- Returns:
- A set of account strings.
- """
- _, accounts_last = _GetAccounts.get_accounts_use_map(entries)
- return accounts_last.keys()
-
def get_accounts(entries):
+ """Gather all the accounts references by a list of directives.
+
+ Args:
+ entries: A list of directive instances.
+ Returns:
+ A set of account strings.
+ """
+ _, accounts_last = _GetAccounts.get_accounts_use_map(entries)
+ return accounts_last.keys()
+
beancount.core.getters.get_accounts_use_map(entries)
+beancount.core.getters.get_accounts_use_map(entries)
beancount/core/getters.py
def get_accounts_use_map(entries):
- """Gather all the accounts references by a list of directives.
-
- Args:
- entries: A list of directive instances.
- Returns:
- A pair of dictionaries of account name to date, one for first date
- used and one for last date used. The keys should be identical.
- """
- return _GetAccounts.get_accounts_use_map(entries)
-
def get_accounts_use_map(entries):
+ """Gather all the accounts references by a list of directives.
+
+ Args:
+ entries: A list of directive instances.
+ Returns:
+ A pair of dictionaries of account name to date, one for first date
+ used and one for last date used. The keys should be identical.
+ """
+ return _GetAccounts.get_accounts_use_map(entries)
+
beancount.core.getters.get_active_years(entries)
+beancount.core.getters.get_active_years(entries)
beancount/core/getters.py
def get_active_years(entries):
- """Yield all the years that have at least one entry in them.
-
- Args:
- entries: A list of directive instances.
- Yields:
- Unique dates see in the list of directives.
- """
- seen = set()
- prev_year = None
- for entry in entries:
- year = entry.date.year
- if year != prev_year:
- prev_year = year
- assert year not in seen
- seen.add(year)
- yield year
-
def get_active_years(entries):
+ """Yield all the years that have at least one entry in them.
+
+ Args:
+ entries: A list of directive instances.
+ Yields:
+ Unique dates see in the list of directives.
+ """
+ seen = set()
+ prev_year = None
+ for entry in entries:
+ year = entry.date.year
+ if year != prev_year:
+ prev_year = year
+ assert year not in seen
+ seen.add(year)
+ yield year
+
beancount.core.getters.get_all_links(entries)
+beancount.core.getters.get_all_links(entries)
beancount/core/getters.py
def get_all_links(entries):
- """Return a list of all the links seen in the given entries.
-
- Args:
- entries: A list of directive instances.
- Returns:
- A set of links strings.
- """
- all_links = set()
- for entry in entries:
- if not isinstance(entry, Transaction):
- continue
- if entry.links:
- all_links.update(entry.links)
- return sorted(all_links)
-
def get_all_links(entries):
+ """Return a list of all the links seen in the given entries.
+
+ Args:
+ entries: A list of directive instances.
+ Returns:
+ A set of links strings.
+ """
+ all_links = set()
+ for entry in entries:
+ if not isinstance(entry, Transaction):
+ continue
+ if entry.links:
+ all_links.update(entry.links)
+ return sorted(all_links)
+
beancount.core.getters.get_all_payees(entries)
+beancount.core.getters.get_all_payees(entries)
beancount/core/getters.py
def get_all_payees(entries):
- """Return a list of all the unique payees seen in the given entries.
-
- Args:
- entries: A list of directive instances.
- Returns:
- A set of payee strings.
- """
- all_payees = set()
- for entry in entries:
- if not isinstance(entry, Transaction):
- continue
- all_payees.add(entry.payee)
- all_payees.discard(None)
- return sorted(all_payees)
-
def get_all_payees(entries):
+ """Return a list of all the unique payees seen in the given entries.
+
+ Args:
+ entries: A list of directive instances.
+ Returns:
+ A set of payee strings.
+ """
+ all_payees = set()
+ for entry in entries:
+ if not isinstance(entry, Transaction):
+ continue
+ all_payees.add(entry.payee)
+ all_payees.discard(None)
+ return sorted(all_payees)
+
beancount.core.getters.get_all_tags(entries)
+beancount.core.getters.get_all_tags(entries)
beancount/core/getters.py
def get_all_tags(entries):
- """Return a list of all the tags seen in the given entries.
-
- Args:
- entries: A list of directive instances.
- Returns:
- A set of tag strings.
- """
- all_tags = set()
- for entry in entries:
- if not isinstance(entry, Transaction):
- continue
- if entry.tags:
- all_tags.update(entry.tags)
- return sorted(all_tags)
-
def get_all_tags(entries):
+ """Return a list of all the tags seen in the given entries.
+
+ Args:
+ entries: A list of directive instances.
+ Returns:
+ A set of tag strings.
+ """
+ all_tags = set()
+ for entry in entries:
+ if not isinstance(entry, Transaction):
+ continue
+ if entry.tags:
+ all_tags.update(entry.tags)
+ return sorted(all_tags)
+
beancount.core.getters.get_commodity_directives(entries)
+beancount.core.getters.get_commodity_map(entries, create_missing=True)
-entries – A list of directive instances.
create_missing – A boolean, true if you want to automatically generate +missing commodity directives if not present in the output map.
beancount/core/getters.py
def get_commodity_directives(entries):
- """Create map of commodity names to Commodity entries.
-
- Args:
- entries: A list of directive instances.
- Returns:
- A map of commodity name strings to Commodity directives.
- """
- return {entry.currency: entry for entry in entries if isinstance(entry, Commodity)}
-
def get_commodity_map(entries, create_missing=True):
+ """Create map of commodity names to Commodity entries.
+
+ Args:
+ entries: A list of directive instances.
+ create_missing: A boolean, true if you want to automatically generate
+ missing commodity directives if not present in the output map.
+ Returns:
+ A map of commodity name strings to Commodity directives.
+ """
+ if not entries:
+ return {}
+
+ commodities_map = {}
+ for entry in entries:
+ if isinstance(entry, Commodity):
+ commodities_map[entry.currency] = entry
+
+ elif isinstance(entry, Transaction):
+ for posting in entry.postings:
+
+ # Main currency.
+ units = posting.units
+ commodities_map.setdefault(units.currency, None)
+
+ # Currency in cost.
+ cost = posting.cost
+ if cost:
+ commodities_map.setdefault(cost.currency, None)
+
+ # Currency in price.
+ price = posting.price
+ if price:
+ commodities_map.setdefault(price.currency, None)
+
+ elif isinstance(entry, Balance):
+ commodities_map.setdefault(entry.amount.currency, None)
+
+ elif isinstance(entry, Price):
+ commodities_map.setdefault(entry.currency, None)
+
+ if create_missing:
+ # Create missing Commodity directives when they haven't been specified explicitly.
+ # (I think it might be better to always do this from the loader.)
+ date = entries[0].date
+ meta = data.new_metadata('<getters>', 0)
+ commodities_map = {
+ commodity: (entry
+ if entry is not None
+ else Commodity(meta, date, commodity))
+ for commodity, entry in commodities_map.items()}
+
+ return commodities_map
+
beancount.core.getters.get_dict_accounts(account_names)
+beancount.core.getters.get_dict_accounts(account_names)
beancount/core/getters.py
def get_dict_accounts(account_names):
- """Return a nested dict of all the unique leaf names.
- account names are labelled with LABEL=True
-
- Args:
- account_names: An iterable of account names (strings)
- Returns:
- A nested OrderedDict of account leafs
- """
- leveldict = OrderedDict()
- for account_name in account_names:
- nested_dict = leveldict
- for component in account.split(account_name):
- nested_dict = nested_dict.setdefault(component, OrderedDict())
- nested_dict[get_dict_accounts.ACCOUNT_LABEL] = True
- return leveldict
-
def get_dict_accounts(account_names):
+ """Return a nested dict of all the unique leaf names.
+ account names are labelled with LABEL=True
+
+ Args:
+ account_names: An iterable of account names (strings)
+ Returns:
+ A nested OrderedDict of account leafs
+ """
+ leveldict = OrderedDict()
+ for account_name in account_names:
+ nested_dict = leveldict
+ for component in account.split(account_name):
+ nested_dict = nested_dict.setdefault(component, OrderedDict())
+ nested_dict[get_dict_accounts.ACCOUNT_LABEL] = True
+ return leveldict
+
beancount.core.getters.get_entry_accounts(entry)
+beancount.core.getters.get_entry_accounts(entry)
beancount/core/getters.py
def get_entry_accounts(entry):
- """Gather all the accounts references by a single directive.
-
- Note: This should get replaced by a method on each directive eventually,
- that would be the clean way to do this.
-
- Args:
- entries: A directive instance.
- Returns:
- A set of account strings.
- """
- return _GetAccounts.get_entry_accounts(entry)
-
def get_entry_accounts(entry):
+ """Gather all the accounts references by a single directive.
+
+ Note: This should get replaced by a method on each directive eventually,
+ that would be the clean way to do this.
+
+ Args:
+ entries: A directive instance.
+ Returns:
+ A set of account strings.
+ """
+ return _GetAccounts.get_entry_accounts(entry)
+
beancount.core.getters.get_leveln_parent_accounts(account_names, level, nrepeats=0)
+beancount.core.getters.get_leveln_parent_accounts(account_names, level, nrepeats=0)
beancount/core/getters.py
def get_leveln_parent_accounts(account_names, level, nrepeats=0):
- """Return a list of all the unique leaf names at level N in an account hierarchy.
-
- Args:
- account_names: A list of account names (strings)
- level: The level to cross-cut. 0 is for root accounts.
- nrepeats: A minimum number of times a leaf is required to be present in the
- the list of unique account names in order to be returned by this function.
- Returns:
- A list of leaf node names.
- """
- leveldict = defaultdict(int)
- for account_name in set(account_names):
- components = account.split(account_name)
- if level < len(components):
- leveldict[components[level]] += 1
- levels = {level_ for level_, count in leveldict.items() if count > nrepeats}
- return sorted(levels)
-
def get_leveln_parent_accounts(account_names, level, nrepeats=0):
+ """Return a list of all the unique leaf names at level N in an account hierarchy.
+
+ Args:
+ account_names: A list of account names (strings)
+ level: The level to cross-cut. 0 is for root accounts.
+ nrepeats: A minimum number of times a leaf is required to be present in the
+ the list of unique account names in order to be returned by this function.
+ Returns:
+ A list of leaf node names.
+ """
+ leveldict = defaultdict(int)
+ for account_name in set(account_names):
+ components = account.split(account_name)
+ if level < len(components):
+ leveldict[components[level]] += 1
+ levels = {level_
+ for level_, count in leveldict.items()
+ if count > nrepeats}
+ return sorted(levels)
+
@@ -13854,7 +10991,7 @@ beancount.core.getters.get_min_max_dates(entries, types=None)
+beancount.core.getters.get_min_max_dates(entries, types=None)
beancount/core/getters.py
def get_min_max_dates(entries, types=None):
- """Return the minimum and maximum dates in the list of entries.
-
- Args:
- entries: A list of directive instances.
- types: An optional tuple of types to restrict the entries to.
- Returns:
- A pair of datetime.date dates, the minimum and maximum dates seen in the
- directives.
- """
- date_first = date_last = None
-
- for entry in entries:
- if types and not isinstance(entry, types):
- continue
- date_first = entry.date
- break
-
- for entry in reversed(entries):
- if types and not isinstance(entry, types):
- continue
- date_last = entry.date
- break
-
- return (date_first, date_last)
-
def get_min_max_dates(entries, types=None):
+ """Return the minimum and maximum dates in the list of entries.
+
+ Args:
+ entries: A list of directive instances.
+ types: An optional tuple of types to restrict the entries to.
+ Returns:
+ A pair of datetime.date dates, the minimum and maximum dates seen in the
+ directives.
+ """
+ date_first = date_last = None
+
+ for entry in entries:
+ if types and not isinstance(entry, types):
+ continue
+ date_first = entry.date
+ break
+
+ for entry in reversed(entries):
+ if types and not isinstance(entry, types):
+ continue
+ date_last = entry.date
+ break
+
+ return (date_first, date_last)
+
beancount.core.getters.get_values_meta(name_to_entries_map, *meta_keys, *, default=None)
+beancount.core.getters.get_values_meta(name_to_entries_map, *meta_keys, *, default=None)
beancount/core/getters.py
def get_values_meta(name_to_entries_map, *meta_keys, default=None):
- """Get a map of the metadata from a map of entries values.
-
- Given a dict of some key to a directive instance (or None), return a mapping
- of the key to the metadata extracted from each directive, or a default
- value. This can be used to gather a particular piece of metadata from an
- accounts map or a commodities map.
-
- Args:
- name_to_entries_map: A dict of something to an entry or None.
- meta_keys: A list of strings, the keys to fetch from the metadata.
- default: The default value to use if the metadata is not available or if
- the value/entry is None.
- Returns:
- A mapping of the keys of name_to_entries_map to the values of the 'meta_keys'
- metadata. If there are multiple 'meta_keys', each value is a tuple of them.
- On the other hand, if there is only a single one, the value itself is returned.
- """
- value_map = {}
- for key, entry in name_to_entries_map.items():
- value_list = []
- for meta_key in meta_keys:
- value_list.append(
- entry.meta.get(meta_key, default) if entry is not None else default
- )
- value_map[key] = value_list[0] if len(meta_keys) == 1 else tuple(value_list)
- return value_map
-
def get_values_meta(name_to_entries_map, *meta_keys, default=None):
+ """Get a map of the metadata from a map of entries values.
+
+ Given a dict of some key to a directive instance (or None), return a mapping
+ of the key to the metadata extracted from each directive, or a default
+ value. This can be used to gather a particular piece of metadata from an
+ accounts map or a commodities map.
+
+ Args:
+ name_to_entries_map: A dict of something to an entry or None.
+ meta_keys: A list of strings, the keys to fetch from the metadata.
+ default: The default value to use if the metadata is not available or if
+ the value/entry is None.
+ Returns:
+ A mapping of the keys of name_to_entries_map to the values of the 'meta_keys'
+ metadata. If there are multiple 'meta_keys', each value is a tuple of them.
+ On the other hand, if there is only a single one, the value itself is returned.
+ """
+ value_map = {}
+ for key, entry in name_to_entries_map.items():
+ value_list = []
+ for meta_key in meta_keys:
+ value_list.append(entry.meta.get(meta_key, default)
+ if entry is not None
+ else default)
+ value_map[key] = (value_list[0]
+ if len(meta_keys) == 1
+ else tuple(value_list))
+ return value_map
+
beancount.core.interpolate.BalanceError.__getnewargs__(self)
+beancount.core.interpolate.BalanceError.__getnewargs__(self)
special
@@ -14116,10 +11255,10 @@ beancount/core/interpolate.py
def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return _tuple(self)
-
def __getnewargs__(self):
+ 'Return self as a plain tuple. Used by copy and pickle.'
+ return _tuple(self)
+
@@ -14132,7 +11271,7 @@ beancount.core.interpolate.BalanceError.__new__(_cls, source, message, entry)
+beancount.core.interpolate.BalanceError.__new__(_cls, source, message, entry)
special
@@ -14156,7 +11295,7 @@ beancount.core.interpolate.BalanceError.__repr__(self)
+beancount.core.interpolate.BalanceError.__repr__(self)
special
@@ -14170,10 +11309,10 @@ beancount/core/interpolate.py
def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + repr_fmt % self
-
def __repr__(self):
+ 'Return a nicely formatted representation string'
+ return self.__class__.__name__ + repr_fmt % self
+
@@ -14197,7 +11336,7 @@ beancount.core.interpolate.compute_entries_balance(entries, prefix=None, date=None)
+beancount.core.interpolate.compute_entries_balance(entries, prefix=None, date=None)
beancount/core/interpolate.py
def compute_entries_balance(entries, prefix=None, date=None):
- """Compute the balance of all postings of a list of entries.
-
- Sum up all the positions in all the postings of all the transactions in the
- list of entries and return an inventory of it.
-
- Args:
- entries: A list of directives.
- prefix: If specified, a prefix string to restrict by account name. Only
- postings with an account that starts with this prefix will be summed up.
- date: A datetime.date instance at which to stop adding up the balance.
- The date is exclusive.
- Returns:
- An instance of Inventory.
- """
- total_balance = Inventory()
- for entry in entries:
- if not (date is None or entry.date < date):
- break
- if isinstance(entry, Transaction):
- for posting in entry.postings:
- if prefix is None or posting.account.startswith(prefix):
- total_balance.add_position(posting)
- return total_balance
-
def compute_entries_balance(entries, prefix=None, date=None):
+ """Compute the balance of all postings of a list of entries.
+
+ Sum up all the positions in all the postings of all the transactions in the
+ list of entries and return an inventory of it.
+
+ Args:
+ entries: A list of directives.
+ prefix: If specified, a prefix string to restrict by account name. Only
+ postings with an account that starts with this prefix will be summed up.
+ date: A datetime.date instance at which to stop adding up the balance.
+ The date is exclusive.
+ Returns:
+ An instance of Inventory.
+ """
+ total_balance = Inventory()
+ for entry in entries:
+ if not (date is None or entry.date < date):
+ break
+ if isinstance(entry, Transaction):
+ for posting in entry.postings:
+ if prefix is None or posting.account.startswith(prefix):
+ total_balance.add_position(posting)
+ return total_balance
+
@@ -14283,7 +11422,7 @@ beancount.core.interpolate.compute_entry_context(entries, context_entry, additional_accounts=None)
+beancount.core.interpolate.compute_entry_context(entries, context_entry)
beancount/core/interpolate.py
def compute_entry_context(entries, context_entry, additional_accounts=None):
- """Compute the balances of all accounts referenced by entry up to entry.
-
- This provides the inventory of the accounts to which the entry is to be
- applied, before and after.
-
- Args:
- entries: A list of directives.
- context_entry: The entry for which we want to obtain the before and after
- context.
- additional_accounts: Additional list of accounts to include in calculating
- the balance. This is used when invoked for debugging, in case the booked
- & interpolated transaction doesn't have all the accounts we need because
- it had an error (the booking code will remove invalid postings).
- Returns:
- Two dicts of account-name to Inventory instance, one which represents the
- context before the entry is applied, and one that represents the context
- after it has been applied.
- """
- assert context_entry is not None, "context_entry is missing."
-
- # Get the set of accounts for which to compute the context.
- context_accounts = getters.get_entry_accounts(context_entry)
- if additional_accounts:
- context_accounts.update(additional_accounts)
-
- # Iterate over the entries until we find the target one and accumulate the
- # balance.
- context_before = collections.defaultdict(inventory.Inventory)
- for entry in entries:
- if entry is context_entry:
- break
- if isinstance(entry, Transaction):
- for posting in entry.postings:
- if not any(posting.account == account for account in context_accounts):
- continue
- balance = context_before[posting.account]
- balance.add_position(posting)
-
- # Compute the after context for the entry.
- context_after = copy.deepcopy(context_before)
- if isinstance(context_entry, Transaction):
- for posting in entry.postings:
- balance = context_after[posting.account]
- balance.add_position(posting)
-
- return context_before, context_after
-
def compute_entry_context(entries, context_entry):
+ """Compute the balances of all accounts referenced by entry up to entry.
+
+ This provides the inventory of the accounts to which the entry is to be
+ applied, before and after.
+
+ Args:
+ entries: A list of directives.
+ context_entry: The entry for which we want to obtain the before and after
+ context.
+ Returns:
+ Two dicts of account-name to Inventory instance, one which represents the
+ context before the entry is applied, and one that represents the context
+ after it has been applied.
+ """
+ assert context_entry is not None, "context_entry is missing."
+
+ # Get the set of accounts for which to compute the context.
+ context_accounts = getters.get_entry_accounts(context_entry)
+
+ # Iterate over the entries until we find the target one and accumulate the
+ # balance.
+ context_before = collections.defaultdict(inventory.Inventory)
+ for entry in entries:
+ if entry is context_entry:
+ break
+ if isinstance(entry, Transaction):
+ for posting in entry.postings:
+ if not any(posting.account == account
+ for account in context_accounts):
+ continue
+ balance = context_before[posting.account]
+ balance.add_position(posting)
+
+ # Compute the after context for the entry.
+ context_after = copy.deepcopy(context_before)
+ if isinstance(context_entry, Transaction):
+ for posting in entry.postings:
+ balance = context_after[posting.account]
+ balance.add_position(posting)
+
+ return context_before, context_after
+
@@ -14396,7 +11526,7 @@ beancount.core.interpolate.compute_residual(postings)
+beancount.core.interpolate.compute_residual(postings)
beancount/core/interpolate.py
def compute_residual(postings):
- """Compute the residual of a set of complete postings, and the per-currency precision.
-
- This is used to cross-check a balanced transaction.
-
- The precision is the maximum fraction that is being used for each currency
- (a dict). We use the currency of the weight amount in order to infer the
- quantization precision for each currency. Integer amounts aren't
- contributing to the determination of precision.
-
- Args:
- postings: A list of Posting instances.
- Returns:
- An instance of Inventory, with the residual of the given list of postings.
- """
- inventory = Inventory()
- for posting in postings:
- # Skip auto-postings inserted to absorb the residual (rounding error).
- if posting.meta and posting.meta.get(AUTOMATIC_RESIDUAL, False):
- continue
- # Add to total residual balance.
- inventory.add_amount(convert.get_weight(posting))
- return inventory
-
def compute_residual(postings):
+ """Compute the residual of a set of complete postings, and the per-currency precision.
+
+ This is used to cross-check a balanced transaction.
+
+ The precision is the maximum fraction that is being used for each currency
+ (a dict). We use the currency of the weight amount in order to infer the
+ quantization precision for each currency. Integer amounts aren't
+ contributing to the determination of precision.
+
+ Args:
+ postings: A list of Posting instances.
+ Returns:
+ An instance of Inventory, with the residual of the given list of postings.
+ """
+ inventory = Inventory()
+ for posting in postings:
+ # Skip auto-postings inserted to absorb the residual (rounding error).
+ if posting.meta and posting.meta.get(AUTOMATIC_RESIDUAL, False):
+ continue
+ # Add to total residual balance.
+ inventory.add_amount(convert.get_weight(posting))
+ return inventory
+
beancount.core.interpolate.fill_residual_posting(entry, account_rounding)
+beancount.core.interpolate.fill_residual_posting(entry, account_rounding)
beancount/core/interpolate.py
def fill_residual_posting(entry, account_rounding):
- """If necessary, insert a posting to absorb the residual.
- This makes the transaction balance exactly.
-
- Note: This was developed in order to tweak transactions before exporting
- them to Ledger. A better method would be to enable the feature that
- automatically inserts these rounding postings on all transactions, and so
- maybe this method can be deprecated if we do so.
-
- Args:
- entry: An instance of a Transaction.
- account_rounding: A string, the name of the rounding account that
- absorbs residuals / rounding errors.
- Returns:
- A possibly new, modified entry with a new posting. If a residual
- was not needed - the transaction already balanced perfectly - no new
- leg is inserted.
-
- """
- residual = compute_residual(entry.postings)
- if not residual.is_empty():
- new_postings = list(entry.postings)
- new_postings.extend(get_residual_postings(residual, account_rounding))
- entry = entry._replace(postings=new_postings)
- return entry
-
def fill_residual_posting(entry, account_rounding):
+ """If necessary, insert a posting to absorb the residual.
+ This makes the transaction balance exactly.
+
+ Note: This was developed in order to tweak transactions before exporting
+ them to Ledger. A better method would be to enable the feature that
+ automatically inserts these rounding postings on all transactions, and so
+ maybe this method can be deprecated if we do so.
+
+ Args:
+ entry: An instance of a Transaction.
+ account_rounding: A string, the name of the rounding account that
+ absorbs residuals / rounding errors.
+ Returns:
+ A possibly new, modified entry with a new posting. If a residual
+ was not needed - the transaction already balanced perfectly - no new
+ leg is inserted.
+
+ """
+ residual = compute_residual(entry.postings)
+ if not residual.is_empty():
+ new_postings = list(entry.postings)
+ new_postings.extend(get_residual_postings(residual, account_rounding))
+ entry = entry._replace(postings=new_postings)
+ return entry
+
@@ -14570,7 +11700,7 @@ beancount.core.interpolate.get_residual_postings(residual, account_rounding)
+beancount.core.interpolate.get_residual_postings(residual, account_rounding)
beancount/core/interpolate.py
def get_residual_postings(residual, account_rounding):
- """Create postings to book the given residuals.
-
- Args:
- residual: An Inventory, the residual positions.
- account_rounding: A string, the name of the rounding account that
- absorbs residuals / rounding errors.
- Returns:
- A list of new postings to be inserted to reduce the given residual.
- """
- meta = {AUTOMATIC_META: True, AUTOMATIC_RESIDUAL: True}
- return [
- Posting(account_rounding, -position.units, position.cost, None, None, meta.copy())
- for position in residual.get_positions()
- ]
-
def get_residual_postings(residual, account_rounding):
+ """Create postings to book the given residuals.
+
+ Args:
+ residual: An Inventory, the residual positions.
+ account_rounding: A string, the name of the rounding account that
+ absorbs residuals / rounding errors.
+ Returns:
+ A list of new postings to be inserted to reduce the given residual.
+ """
+ meta = {AUTOMATIC_META: True,
+ AUTOMATIC_RESIDUAL: True}
+ return [Posting(account_rounding, -position.units, position.cost, None, None,
+ meta.copy())
+ for position in residual.get_positions()]
+
@@ -14643,7 +11773,7 @@ beancount.core.interpolate.has_nontrivial_balance(posting)
+beancount.core.interpolate.has_nontrivial_balance(posting)
beancount/core/interpolate.py
def has_nontrivial_balance(posting):
- """Return True if a Posting has a balance amount that would have to be calculated.
-
- Args:
- posting: A Posting instance.
- Returns:
- A boolean.
- """
- return posting.cost or posting.price
-
def has_nontrivial_balance(posting):
+ """Return True if a Posting has a balance amount that would have to be calculated.
+
+ Args:
+ posting: A Posting instance.
+ Returns:
+ A boolean.
+ """
+ return posting.cost or posting.price
+
@@ -14708,7 +11838,7 @@ beancount.core.interpolate.infer_tolerances(postings, options_map, use_cost=None)
+beancount.core.interpolate.infer_tolerances(postings, options_map, use_cost=None)
beancount/core/interpolate.py
def infer_tolerances(postings, options_map, use_cost=None):
- """Infer tolerances from a list of postings.
-
- The tolerance is the maximum fraction that is being used for each currency
- (a dict). We use the currency of the weight amount in order to infer the
- quantization precision for each currency. Integer amounts aren't
- contributing to the determination of precision.
-
- The 'use_cost' option allows one to experiment with letting postings at cost
- and at price influence the maximum value of the tolerance. It's tricky to
- use and alters the definition of the tolerance in a non-trivial way, if you
- use it. The tolerance is expanded by the sum of the cost times a fraction 'M'
- of the smallest digits in the number of units for all postings held at cost.
-
- For example, in this transaction:
-
- 2006-01-17 * "Plan Contribution"
- Assets:Investments:VWELX 18.572 VWELX {30.96 USD}
- Assets:Investments:VWELX 18.572 VWELX {30.96 USD}
- Assets:Investments:Cash -1150.00 USD
-
- The tolerance for units of USD will calculated as the MAXIMUM of:
-
- 0.01 * M = 0.005 (from the 1150.00 USD leg)
-
- The sum of
- 0.001 * M x 30.96 = 0.01548 +
- 0.001 * M x 30.96 = 0.01548
- = 0.03096
-
- So the tolerance for USD in this case is max(0.005, 0.03096) = 0.03096. Prices
- contribute similarly to the maximum tolerance allowed.
-
- Note that 'M' above is the inferred_tolerance_multiplier and its default
- value is 0.5.
-
- Args:
- postings: A list of Posting instances.
- options_map: A dict of options.
- use_cost: A boolean, true if we should be using a combination of the smallest
- digit of the number times the cost or price in order to infer the tolerance.
- If the value is left unspecified (as 'None'), the default value can be
- overridden by setting an option.
- Returns:
- A dict of currency to the tolerated difference amount to be used for it,
- e.g. 0.005.
- """
- if use_cost is None:
- use_cost = options_map["infer_tolerance_from_cost"]
-
- inferred_tolerance_multiplier = options_map["inferred_tolerance_multiplier"]
-
- default_tolerances = options_map["inferred_tolerance_default"]
- tolerances = default_tolerances.copy()
-
- cost_tolerances = collections.defaultdict(D)
- for posting in postings:
- # Skip the precision on automatically inferred postings.
- if posting.meta and AUTOMATIC_META in posting.meta:
- continue
- units = posting.units
- if not (isinstance(units, Amount) and isinstance(units.number, Decimal)):
- continue
-
- # Compute bounds on the number.
- currency = units.currency
- expo = units.number.as_tuple().exponent
- if expo < 0:
- # Note: the exponent is a negative value.
- tolerance = ONE.scaleb(expo) * inferred_tolerance_multiplier
-
- # Note that we take the max() and not the min() here because the
- # tolerance has a dual purpose: it's used to infer the resolution
- # for interpolation (where we might want the min()) and also for
- # balance checks (where we favor the looser/larger tolerance).
- tolerances[currency] = max(tolerance, tolerances.get(currency, -1024))
-
- if not use_cost:
- continue
-
- # Compute bounds on the smallest digit of the number implied as cost.
- cost = posting.cost
- if cost is not None:
- cost_currency = cost.currency
- if isinstance(cost, Cost):
- cost_tolerance = min(tolerance * cost.number, MAXIMUM_TOLERANCE)
- else:
- assert isinstance(cost, CostSpec)
- cost_tolerance = MAXIMUM_TOLERANCE
- for cost_number in cost.number_total, cost.number_per:
- if cost_number is None or cost_number is MISSING:
- continue
- cost_tolerance = min(tolerance * cost_number, cost_tolerance)
- cost_tolerances[cost_currency] += cost_tolerance
-
- # Compute bounds on the smallest digit of the number implied as cost.
- price = posting.price
- if isinstance(price, Amount) and isinstance(price.number, Decimal):
- price_currency = price.currency
- price_tolerance = min(tolerance * price.number, MAXIMUM_TOLERANCE)
- cost_tolerances[price_currency] += price_tolerance
-
- for currency, tolerance in cost_tolerances.items():
- tolerances[currency] = max(tolerance, tolerances.get(currency, -1024))
-
- default = tolerances.pop("*", ZERO)
- return defdict.ImmutableDictWithDefault(tolerances, default=default)
-
def infer_tolerances(postings, options_map, use_cost=None):
+ """Infer tolerances from a list of postings.
+
+ The tolerance is the maximum fraction that is being used for each currency
+ (a dict). We use the currency of the weight amount in order to infer the
+ quantization precision for each currency. Integer amounts aren't
+ contributing to the determination of precision.
+
+ The 'use_cost' option allows one to experiment with letting postings at cost
+ and at price influence the maximum value of the tolerance. It's tricky to
+ use and alters the definition of the tolerance in a non-trivial way, if you
+ use it. The tolerance is expanded by the sum of the cost times a fraction 'M'
+ of the smallest digits in the number of units for all postings held at cost.
+
+ For example, in this transaction:
+
+ 2006-01-17 * "Plan Contribution"
+ Assets:Investments:VWELX 18.572 VWELX {30.96 USD}
+ Assets:Investments:VWELX 18.572 VWELX {30.96 USD}
+ Assets:Investments:Cash -1150.00 USD
+
+ The tolerance for units of USD will calculated as the MAXIMUM of:
+
+ 0.01 * M = 0.005 (from the 1150.00 USD leg)
+
+ The sum of
+ 0.001 * M x 30.96 = 0.01548 +
+ 0.001 * M x 30.96 = 0.01548
+ = 0.03096
+
+ So the tolerance for USD in this case is max(0.005, 0.03096) = 0.03096. Prices
+ contribute similarly to the maximum tolerance allowed.
+
+ Note that 'M' above is the inferred_tolerance_multiplier and its default
+ value is 0.5.
+
+ Args:
+ postings: A list of Posting instances.
+ options_map: A dict of options.
+ use_cost: A boolean, true if we should be using a combination of the smallest
+ digit of the number times the cost or price in order to infer the tolerance.
+ If the value is left unspecified (as 'None'), the default value can be
+ overridden by setting an option.
+ Returns:
+ A dict of currency to the tolerated difference amount to be used for it,
+ e.g. 0.005.
+ """
+ if use_cost is None:
+ use_cost = options_map["infer_tolerance_from_cost"]
+
+ inferred_tolerance_multiplier = options_map["inferred_tolerance_multiplier"]
+
+ default_tolerances = options_map["inferred_tolerance_default"]
+ tolerances = default_tolerances.copy()
+
+ cost_tolerances = collections.defaultdict(D)
+ for posting in postings:
+ # Skip the precision on automatically inferred postings.
+ if posting.meta and AUTOMATIC_META in posting.meta:
+ continue
+ units = posting.units
+ if not (isinstance(units, Amount) and isinstance(units.number, Decimal)):
+ continue
+
+ # Compute bounds on the number.
+ currency = units.currency
+ expo = units.number.as_tuple().exponent
+ if expo < 0:
+ # Note: the exponent is a negative value.
+ tolerance = ONE.scaleb(expo) * inferred_tolerance_multiplier
+ tolerances[currency] = max(tolerance,
+ tolerances.get(currency, -1024))
+
+ if not use_cost:
+ continue
+
+ # Compute bounds on the smallest digit of the number implied as cost.
+ cost = posting.cost
+ if cost is not None:
+ cost_currency = cost.currency
+ if isinstance(cost, Cost):
+ cost_tolerance = min(tolerance * cost.number, MAXIMUM_TOLERANCE)
+ else:
+ assert isinstance(cost, CostSpec)
+ cost_tolerance = MAXIMUM_TOLERANCE
+ for cost_number in cost.number_total, cost.number_per:
+ if cost_number is None or cost_number is MISSING:
+ continue
+ cost_tolerance = min(tolerance * cost_number, cost_tolerance)
+ cost_tolerances[cost_currency] += cost_tolerance
+
+ # Compute bounds on the smallest digit of the number implied as cost.
+ price = posting.price
+ if isinstance(price, Amount) and isinstance(price.number, Decimal):
+ price_currency = price.currency
+ price_tolerance = min(tolerance * price.number, MAXIMUM_TOLERANCE)
+ cost_tolerances[price_currency] += price_tolerance
+
+ for currency, tolerance in cost_tolerances.items():
+ tolerances[currency] = max(tolerance, tolerances.get(currency, -1024))
+
+ default = tolerances.pop('*', ZERO)
+ return defdict.ImmutableDictWithDefault(tolerances, default=default)
+
beancount.core.interpolate.is_tolerance_user_specified(tolerance)
+beancount.core.interpolate.is_tolerance_user_specified(tolerance)
beancount/core/interpolate.py
def is_tolerance_user_specified(tolerance):
- """Return true if the given tolerance number was user-specified.
-
- This would allow the user to provide a tolerance like # 0.1234 but not
- 0.123456. This is used to detect whether a tolerance value # is input by the
- user and not inferred automatically.
-
- Args:
- tolerance: An instance of Decimal.
- Returns:
- A boolean.
- """
- return len(tolerance.as_tuple().digits) < MAX_TOLERANCE_DIGITS
-
def is_tolerance_user_specified(tolerance):
+ """Return true if the given tolerance number was user-specified.
+
+ This would allow the user to provide a tolerance like # 0.1234 but not
+ 0.123456. This is used to detect whether a tolerance value # is input by the
+ user and not inferred automatically.
+
+ Args:
+ tolerance: An instance of Decimal.
+ Returns:
+ A boolean.
+ """
+ return len(tolerance.as_tuple().digits) < MAX_TOLERANCE_DIGITS
+
@@ -14974,7 +12100,7 @@ beancount.core.interpolate.quantize_with_tolerance(tolerances, currency, number)
+beancount.core.interpolate.quantize_with_tolerance(tolerances, currency, number)
beancount/core/interpolate.py
def quantize_with_tolerance(tolerances, currency, number):
- """Quantize the units using the tolerance dict.
-
- Args:
- tolerances: A dict of currency to tolerance Decimalvalues.
- number: A number to quantize.
- currency: A string currency.
- Returns:
- A Decimal, the number possibly quantized.
- """
- # Applying rounding to the default tolerance, if there is one.
- tolerance = tolerances.get(currency)
- if tolerance:
- quantum = (tolerance * 2).normalize()
-
- # If the tolerance is a neat number provided by the user,
- # quantize the inferred numbers. See doc on quantize():
- #
- # Unlike other operations, if the length of the coefficient
- # after the quantize operation would be greater than
- # precision, then an InvalidOperation is signaled. This
- # guarantees that, unless there is an error condition, the
- # quantized exponent is always equal to that of the
- # right-hand operand.
- if is_tolerance_user_specified(quantum):
- number = number.quantize(quantum)
- return number
-
def quantize_with_tolerance(tolerances, currency, number):
+ """Quantize the units using the tolerance dict.
+
+ Args:
+ tolerances: A dict of currency to tolerance Decimalvalues.
+ number: A number to quantize.
+ currency: A string currency.
+ Returns:
+ A Decimal, the number possibly quantized.
+ """
+ # Applying rounding to the default tolerance, if there is one.
+ tolerance = tolerances.get(currency)
+ if tolerance:
+ quantum = (tolerance * 2).normalize()
+
+ # If the tolerance is a neat number provided by the user,
+ # quantize the inferred numbers. See doc on quantize():
+ #
+ # Unlike other operations, if the length of the coefficient
+ # after the quantize operation would be greater than
+ # precision, then an InvalidOperation is signaled. This
+ # guarantees that, unless there is an error condition, the
+ # quantized exponent is always equal to that of the
+ # right-hand operand.
+ if is_tolerance_user_specified(quantum):
+ number = number.quantize(quantum)
+ return number
+
@@ -15093,14 +12219,6 @@ This is meant to accommodate both booked and non-booked amounts. The clever trick that we pull to do this is that for positions which aren't booked, we simply leave the 'cost' as None. This is the case for most of the transactions.
-= Conversions =
-If it often desired to convert this inventory into an equivalent position for -its cost, or to just flatten all the positions with the same currency and count -the number of units, or to compute the market value for the inventory at a -specific date. You do these conversions using the reduce() method:
-inventory.reduce(convert.get_cost) - inventory.reduce(convert.get_units) - inventory.reduce(convert.get_value, price_map, date)
@@ -15115,6 +12233,50 @@
+beancount.core.inventory.Booking (Enum)
+
+
+
+
+Result of booking a new lot to an existing inventory.
+ + + + +An Inventory is a set of positions, indexed for efficiency.
+An Inventory is a set of positions.
+Attributes:
+Name | +Type | +Description | +
---|---|---|
positions |
+ + | A list of Position instances, held in this Inventory object. |
+
beancount.core.inventory.Inventory.__abs__(self)
+beancount.core.inventory.Inventory.__abs__(self)
special
@@ -15180,14 +12359,14 @@ beancount/core/inventory.py
def __abs__(self):
- """Return an inventory with the absolute value of each position.
-
- Returns:
- An instance of Inventory.
- """
- return Inventory({key: abs(pos) for key, pos in self.items()})
-
def __abs__(self):
+ """Return an inventory with the absolute value of each position.
+
+ Returns:
+ An instance of Inventory.
+ """
+ return Inventory({key: abs(pos) for key, pos in self.items()})
+
beancount.core.inventory.Inventory.__add__(self, other)
+beancount.core.inventory.Inventory.__add__(self, other)
special
@@ -15246,18 +12425,18 @@ beancount/core/inventory.py
def __add__(self, other):
- """Add another inventory to this one. This inventory is not modified.
-
- Args:
- other: An instance of Inventory.
- Returns:
- A new instance of Inventory.
- """
- new_inventory = self.__copy__()
- new_inventory.add_inventory(other)
- return new_inventory
-
def __add__(self, other):
+ """Add another inventory to this one. This inventory is not modified.
+
+ Args:
+ other: An instance of Inventory.
+ Returns:
+ A new instance of Inventory.
+ """
+ new_inventory = self.__copy__()
+ new_inventory.add_inventory(other)
+ return new_inventory
+
beancount.core.inventory.Inventory.__copy__(self)
+beancount.core.inventory.Inventory.__copy__(self)
special
@@ -15301,14 +12480,14 @@ beancount/core/inventory.py
def __copy__(self):
- """A shallow copy of this inventory object.
-
- Returns:
- An instance of Inventory, equal to this one.
- """
- return Inventory(self)
-
def __copy__(self):
+ """A shallow copy of this inventory object.
+
+ Returns:
+ An instance of Inventory, equal to this one.
+ """
+ return Inventory(self)
+
beancount.core.inventory.Inventory.__iadd__(self, other)
+beancount.core.inventory.Inventory.__iadd__(self, other)
special
@@ -15367,25 +12546,25 @@ beancount/core/inventory.py
def add_inventory(self, other):
- """Add all the positions of another Inventory instance to this one.
-
- Args:
- other: An instance of Inventory to add to this one.
- Returns:
- This inventory, modified.
- """
- if self.is_empty():
- # Optimization for empty inventories; if the current one is empty,
- # adopt all of the other inventory's positions without running
- # through the full aggregation checks. This should be very cheap. We
- # can do this because the positions are immutable.
- self.update(other)
- else:
- for position in other.get_positions():
- self.add_position(position)
- return self
-
def add_inventory(self, other):
+ """Add all the positions of another Inventory instance to this one.
+
+ Args:
+ other: An instance of Inventory to add to this one.
+ Returns:
+ This inventory, modified.
+ """
+ if self.is_empty():
+ # Optimization for empty inventories; if the current one is empty,
+ # adopt all of the other inventory's positions without running
+ # through the full aggregation checks. This should be very cheap. We
+ # can do this because the positions are immutable.
+ self.update(other)
+ else:
+ for position in other.get_positions():
+ self.add_position(position)
+ return self
+
beancount.core.inventory.Inventory.__init__(self, positions=None)
+beancount.core.inventory.Inventory.__init__(self, positions=None)
special
@@ -15429,22 +12608,22 @@ beancount/core/inventory.py
def __init__(self, positions=None):
- """Create a new inventory using a list of existing positions.
-
- Args:
- positions: A list of Position instances or an existing dict or
- Inventory instance.
- """
- if isinstance(positions, (dict, Inventory)):
- dict.__init__(self, positions)
- else:
- dict.__init__(self)
- if positions:
- assert isinstance(positions, Iterable)
- for position in positions:
- self.add_position(position)
-
def __init__(self, positions=None):
+ """Create a new inventory using a list of existing positions.
+
+ Args:
+ positions: A list of Position instances or an existing dict or
+ Inventory instance.
+ """
+ if isinstance(positions, (dict, Inventory)):
+ dict.__init__(self, positions)
+ else:
+ dict.__init__(self)
+ if positions:
+ assert isinstance(positions, Iterable)
+ for position in positions:
+ self.add_position(position)
+
beancount.core.inventory.Inventory.__iter__(self)
+beancount.core.inventory.Inventory.__iter__(self)
special
@@ -15471,10 +12650,10 @@ beancount/core/inventory.py
def __iter__(self):
- """Iterate over the positions. Note that there is no guaranteed order."""
- return iter(self.values())
-
def __iter__(self):
+ """Iterate over the positions. Note that there is no guaranteed order."""
+ return iter(self.values())
+
beancount.core.inventory.Inventory.__lt__(self, other)
+beancount.core.inventory.Inventory.__lt__(self, other)
special
@@ -15501,10 +12680,10 @@ beancount/core/inventory.py
def __lt__(self, other):
- """Inequality comparison operator."""
- return sorted(self) < sorted(other)
-
def __lt__(self, other):
+ """Inequality comparison operator."""
+ return sorted(self) < sorted(other)
+
beancount.core.inventory.Inventory.__mul__(self, scalar)
+beancount.core.inventory.Inventory.__mul__(self, scalar)
special
@@ -15563,16 +12742,16 @@ beancount/core/inventory.py
def __mul__(self, scalar):
- """Scale/multiply the contents of the inventory.
-
- Args:
- scalar: A Decimal.
- Returns:
- An instance of Inventory.
- """
- return Inventory({key: pos * scalar for key, pos in self.items()})
-
def __mul__(self, scalar):
+ """Scale/multiply the contents of the inventory.
+
+ Args:
+ scalar: A Decimal.
+ Returns:
+ An instance of Inventory.
+ """
+ return Inventory({key: pos * scalar for key, pos in self.items()})
+
beancount.core.inventory.Inventory.__neg__(self)
+beancount.core.inventory.Inventory.__neg__(self)
special
@@ -15615,14 +12794,14 @@ beancount/core/inventory.py
def __neg__(self):
- """Return an inventory with the negative of values of this one.
-
- Returns:
- An instance of Inventory.
- """
- return Inventory({key: -pos for key, pos in self.items()})
-
def __neg__(self):
+ """Return an inventory with the negative of values of this one.
+
+ Returns:
+ An instance of Inventory.
+ """
+ return Inventory({key: -pos for key, pos in self.items()})
+
beancount.core.inventory.Inventory.__repr__(self)
+beancount.core.inventory.Inventory.__repr__(self)
special
@@ -15665,14 +12844,14 @@ beancount/core/inventory.py
def __str__(self):
- """Render as a human-readable string.
-
- Returns:
- A string, for human consumption.
- """
- return self.to_string()
-
def __str__(self):
+ """Render as a human-readable string.
+
+ Returns:
+ A string, for human consumption.
+ """
+ return self.to_string()
+
beancount.core.inventory.Inventory.__str__(self)
+beancount.core.inventory.Inventory.__str__(self)
special
@@ -15715,14 +12894,14 @@ beancount/core/inventory.py
def __str__(self):
- """Render as a human-readable string.
-
- Returns:
- A string, for human consumption.
- """
- return self.to_string()
-
def __str__(self):
+ """Render as a human-readable string.
+
+ Returns:
+ A string, for human consumption.
+ """
+ return self.to_string()
+
beancount.core.inventory.Inventory.add_amount(self, units, cost=None)
+beancount.core.inventory.Inventory.add_amount(self, units, cost=None)
A pair of (position, matched) where 'position' is the position that -that was modified BEFORE it was modified, and where 'matched' is a -MatchResult enum that hints at how the lot was booked to this inventory. +
A pair of (position, booking) where 'position' is the position that +that was modified BEFORE it was modified, and where 'booking' is a +Booking enum that hints at how the lot was booked to this inventory. Position may be None if there is no corresponding Position object, e.g. the position was deleted.
beancount/core/inventory.py
def add_amount(self, units, cost=None):
- """Add to this inventory using amount and cost. This adds with strict lot
- matching, that is, no partial matches are done on the arguments to the
- keys of the inventory.
-
- Args:
- units: An Amount instance to add.
- cost: An instance of Cost or None, as a key to the inventory.
- Returns:
- A pair of (position, matched) where 'position' is the position that
- that was modified BEFORE it was modified, and where 'matched' is a
- MatchResult enum that hints at how the lot was booked to this inventory.
- Position may be None if there is no corresponding Position object,
- e.g. the position was deleted.
- """
- if ASSERTS_TYPES:
- assert isinstance(units, Amount), "Internal error: {!r} (type: {})".format(
- units, type(units).__name__
- )
- assert cost is None or isinstance(
- cost, Cost
- ), "Internal error: {!r} (type: {})".format(cost, type(cost).__name__)
-
- # Find the position.
- key = (units.currency, cost)
- pos = self.get(key, None)
-
- if pos is not None:
- # Note: In order to augment or reduce, all the fields have to match.
-
- # Check if reducing.
- booking = (
- MatchResult.REDUCED
- if not same_sign(pos.units.number, units.number)
- else MatchResult.AUGMENTED
- )
-
- # Compute the new number of units.
- number = pos.units.number + units.number
- if number == ZERO:
- # If empty, delete the position.
- del self[key]
- else:
- # Otherwise update it.
- self[key] = Position(Amount(number, units.currency), cost)
- else:
- # If not found, create a new one.
- if units.number == ZERO:
- booking = MatchResult.IGNORED
- else:
- self[key] = Position(units, cost)
- booking = MatchResult.CREATED
-
- return pos, booking
-
def add_amount(self, units, cost=None):
+ """Add to this inventory using amount and cost. This adds with strict lot
+ matching, that is, no partial matches are done on the arguments to the
+ keys of the inventory.
+
+ Args:
+ units: An Amount instance to add.
+ cost: An instance of Cost or None, as a key to the inventory.
+ Returns:
+ A pair of (position, booking) where 'position' is the position that
+ that was modified BEFORE it was modified, and where 'booking' is a
+ Booking enum that hints at how the lot was booked to this inventory.
+ Position may be None if there is no corresponding Position object,
+ e.g. the position was deleted.
+ """
+ if ASSERTS_TYPES:
+ assert isinstance(units, Amount), (
+ "Internal error: {!r} (type: {})".format(units, type(units).__name__))
+ assert cost is None or isinstance(cost, Cost), (
+ "Internal error: {!r} (type: {})".format(cost, type(cost).__name__))
+
+ # Find the position.
+ key = (units.currency, cost)
+ pos = self.get(key, None)
+
+ if pos is not None:
+ # Note: In order to augment or reduce, all the fields have to match.
+
+ # Check if reducing.
+ booking = (Booking.REDUCED
+ if not same_sign(pos.units.number, units.number)
+ else Booking.AUGMENTED)
+
+ # Compute the new number of units.
+ number = pos.units.number + units.number
+ if number == ZERO:
+ # If empty, delete the position.
+ del self[key]
+ else:
+ # Otherwise update it.
+ self[key] = Position(Amount(number, units.currency), cost)
+ else:
+ # If not found, create a new one.
+ if units.number == ZERO:
+ booking = Booking.IGNORED
+ else:
+ self[key] = Position(units, cost)
+ booking = Booking.CREATED
+
+ return pos, booking
+
beancount.core.inventory.Inventory.add_inventory(self, other)
+beancount.core.inventory.Inventory.add_inventory(self, other)
beancount/core/inventory.py
def add_inventory(self, other):
- """Add all the positions of another Inventory instance to this one.
-
- Args:
- other: An instance of Inventory to add to this one.
- Returns:
- This inventory, modified.
- """
- if self.is_empty():
- # Optimization for empty inventories; if the current one is empty,
- # adopt all of the other inventory's positions without running
- # through the full aggregation checks. This should be very cheap. We
- # can do this because the positions are immutable.
- self.update(other)
- else:
- for position in other.get_positions():
- self.add_position(position)
- return self
-
def add_inventory(self, other):
+ """Add all the positions of another Inventory instance to this one.
+
+ Args:
+ other: An instance of Inventory to add to this one.
+ Returns:
+ This inventory, modified.
+ """
+ if self.is_empty():
+ # Optimization for empty inventories; if the current one is empty,
+ # adopt all of the other inventory's positions without running
+ # through the full aggregation checks. This should be very cheap. We
+ # can do this because the positions are immutable.
+ self.update(other)
+ else:
+ for position in other.get_positions():
+ self.add_position(position)
+ return self
+
@@ -15926,7 +13101,7 @@ beancount.core.inventory.Inventory.add_position(self, position)
+beancount.core.inventory.Inventory.add_position(self, position)
A pair of (position, booking) where 'position' is the position that -that was modified, and where 'matched' is a MatchResult enum that -hints at how the lot was booked to this inventory.
beancount/core/inventory.py
def add_position(self, position):
- """Add using a position (with strict lot matching).
- Return True if this position was booked against and reduced another.
-
- Args:
- position: The Posting or Position to add to this inventory.
- Returns:
- A pair of (position, booking) where 'position' is the position that
- that was modified, and where 'matched' is a MatchResult enum that
- hints at how the lot was booked to this inventory.
- """
- if ASSERTS_TYPES:
- assert hasattr(position, "units") and hasattr(
- position, "cost"
- ), "Invalid type for position: {}".format(position)
- assert isinstance(
- position.cost, (type(None), Cost)
- ), "Invalid type for cost: {}".format(position.cost)
- return self.add_amount(position.units, position.cost)
-
def add_position(self, position):
+ """Add using a position (with strict lot matching).
+ Return True if this position was booked against and reduced another.
+
+ Args:
+ position: The Posting or Position to add to this inventory.
+ Returns:
+ A pair of (position, booking) where 'position' is the position that
+ that was modified, and where 'booking' is a Booking enum that hints at
+ how the lot was booked to this inventory.
+ """
+ if ASSERTS_TYPES:
+ assert hasattr(position, 'units') and hasattr(position, 'cost'), (
+ "Invalid type for position: {}".format(position))
+ assert isinstance(position.cost, (type(None), Cost)), (
+ "Invalid type for cost: {}".format(position.cost))
+ return self.add_amount(position.units, position.cost)
+
beancount.core.inventory.Inventory.average(self)
+beancount.core.inventory.Inventory.average(self)
beancount/core/inventory.py
def average(self):
- """Average all lots of the same currency together.
-
- Use the minimum date from each aggregated set of lots.
-
- Returns:
- An instance of Inventory.
- """
- groups = collections.defaultdict(list)
- for position in self:
- key = (
- position.units.currency,
- position.cost.currency if position.cost else None,
- )
- groups[key].append(position)
-
- average_inventory = Inventory()
- for (currency, cost_currency), positions in groups.items():
- total_units = sum(position.units.number for position in positions)
- # Explicitly skip aggregates when resulting in zero units.
- if total_units == ZERO:
- continue
- units_amount = Amount(total_units, currency)
-
- if cost_currency:
- total_cost = sum(
- convert.get_cost(position).number for position in positions
- )
- cost_number = (
- Decimal("Infinity")
- if total_units == ZERO
- else (total_cost / total_units)
- )
- min_date = None
- for pos in positions:
- pos_date = pos.cost.date if pos.cost else None
- if pos_date is not None:
- min_date = pos_date if min_date is None else min(min_date, pos_date)
- cost = Cost(cost_number, cost_currency, min_date, None)
- else:
- cost = None
-
- average_inventory.add_amount(units_amount, cost)
-
- return average_inventory
-
def average(self):
+ """Average all lots of the same currency together.
+
+ Use the minimum date from each aggregated set of lots.
+
+ Returns:
+ An instance of Inventory.
+ """
+ groups = collections.defaultdict(list)
+ for position in self:
+ key = (position.units.currency,
+ position.cost.currency if position.cost else None)
+ groups[key].append(position)
+
+ average_inventory = Inventory()
+ for (currency, cost_currency), positions in groups.items():
+ total_units = sum(position.units.number
+ for position in positions)
+ # Explicitly skip aggregates when resulting in zero units.
+ if total_units == ZERO:
+ continue
+ units_amount = Amount(total_units, currency)
+
+ if cost_currency:
+ total_cost = sum(convert.get_cost(position).number
+ for position in positions)
+ cost_number = (Decimal('Infinity')
+ if total_units == ZERO
+ else (total_cost / total_units))
+ min_date = None
+ for pos in positions:
+ pos_date = pos.cost.date if pos.cost else None
+ if pos_date is not None:
+ min_date = (pos_date
+ if min_date is None
+ else min(min_date, pos_date))
+ cost = Cost(cost_number, cost_currency, min_date, None)
+ else:
+ cost = None
+
+ average_inventory.add_amount(units_amount, cost)
+
+ return average_inventory
+
beancount.core.inventory.Inventory.cost_currencies(self)
+beancount.core.inventory.Inventory.cost_currencies(self)
beancount/core/inventory.py
def cost_currencies(self):
- """Return the list of unit currencies held in this inventory.
-
- Returns:
- A set of currency strings.
- """
- return set(cost.currency for _, cost in self.keys() if cost is not None)
-
def cost_currencies(self):
+ """Return the list of unit currencies held in this inventory.
+
+ Returns:
+ A set of currency strings.
+ """
+ return set(cost.currency
+ for _, cost in self.keys()
+ if cost is not None)
+
@@ -16137,7 +13310,7 @@ beancount.core.inventory.Inventory.currencies(self)
+beancount.core.inventory.Inventory.currencies(self)
beancount/core/inventory.py
def currencies(self):
- """Return the list of unit currencies held in this inventory.
-
- Returns:
- A list of currency strings.
- """
- return set(currency for currency, _ in self.keys())
-
def currencies(self):
+ """Return the list of unit currencies held in this inventory.
+
+ Returns:
+ A list of currency strings.
+ """
+ return set(currency for currency, _ in self.keys())
+
beancount.core.inventory.Inventory.currency_pairs(self)
+beancount.core.inventory.Inventory.currency_pairs(self)
beancount/core/inventory.py
def currency_pairs(self):
- """Return the commodities held in this inventory.
-
- Returns:
- A set of currency strings.
- """
- return set(position.currency_pair() for position in self)
-
def currency_pairs(self):
+ """Return the commodities held in this inventory.
+
+ Returns:
+ A set of currency strings.
+ """
+ return set(position.currency_pair() for position in self)
+
@@ -16231,7 +13404,7 @@ beancount.core.inventory.Inventory.from_string(string)
+beancount.core.inventory.Inventory.from_string(string)
staticmethod
@@ -16278,26 +13451,25 @@ beancount/core/inventory.py
@staticmethod
-def from_string(string):
- """Create an Inventory from a string. This is useful for writing tests.
-
- Args:
- string: A comma-separated string of <number> <currency> with an
- optional {<number> <currency>} for the cost.
- Returns:
- A new instance of Inventory with the given balances.
- """
- new_inventory = Inventory()
- # We need to split the comma-separated positions but ignore commas
- # occurring within a {...cost...} specification.
- position_strs = re.split(
- r"([-+]?[0-9,.]+\s+[A-Z]+\s*(?:{[^}]*})?)\s*,?\s*", string
- )[1::2]
- for position_str in position_strs:
- new_inventory.add_position(position_from_string(position_str))
- return new_inventory
-
@staticmethod
+def from_string(string):
+ """Create an Inventory from a string. This is useful for writing tests.
+
+ Args:
+ string: A comma-separated string of <number> <currency> with an
+ optional {<number> <currency>} for the cost.
+ Returns:
+ A new instance of Inventory with the given balances.
+ """
+ new_inventory = Inventory()
+ # We need to split the comma-separated positions but ignore commas
+ # occurring within a {...cost...} specification.
+ position_strs = re.split(
+ r'([-+]?[0-9,.]+\s+[A-Z]+\s*(?:{[^}]*})?)\s*,?\s*', string)[1::2]
+ for position_str in position_strs:
+ new_inventory.add_position(position_from_string(position_str))
+ return new_inventory
+
beancount.core.inventory.Inventory.get_currency_units(self, currency)
+beancount.core.inventory.Inventory.get_currency_units(self, currency)
beancount/core/inventory.py
def get_currency_units(self, currency):
- """Fetch the total amount across all the position in the given currency.
- This may sum multiple lots in the same currency denomination.
-
- Args:
- currency: A string, the currency to filter the positions with.
- Returns:
- An instance of Amount, with the given currency.
- """
- total_units = ZERO
- for position in self:
- if position.units.currency == currency:
- total_units += position.units.number
- return Amount(total_units, currency)
-
def get_currency_units(self, currency):
+ """Fetch the total amount across all the position in the given currency.
+ This may sum multiple lots in the same currency denomination.
+
+ Args:
+ currency: A string, the currency to filter the positions with.
+ Returns:
+ An instance of Amount, with the given currency.
+ """
+ total_units = ZERO
+ for position in self:
+ if position.units.currency == currency:
+ total_units += position.units.number
+ return Amount(total_units, currency)
+
@@ -16381,7 +13553,7 @@ beancount.core.inventory.Inventory.get_only_position(self)
+beancount.core.inventory.Inventory.get_only_position(self)
beancount/core/inventory.py
def get_only_position(self):
- """Return the first position and assert there are no more.
- If the inventory is empty, return None.
- """
- if len(self) > 0:
- if len(self) > 1:
- raise AssertionError(
- "Inventory has more than one expected " "position: {}".format(self)
- )
- return next(iter(self))
-
def get_only_position(self):
+ """Return the first position and assert there are no more.
+ If the inventory is empty, return None.
+ """
+ if len(self) > 0:
+ if len(self) > 1:
+ raise AssertionError("Inventory has more than one expected "
+ "position: {}".format(self))
+ return next(iter(self))
+
@@ -16416,7 +13587,7 @@ beancount.core.inventory.Inventory.get_positions(self)
+beancount.core.inventory.Inventory.get_positions(self)
beancount/core/inventory.py
def get_positions(self):
- """Return the positions in this inventory.
-
- Returns:
- A shallow copy of the list of positions.
- """
- return list(iter(self))
-
def get_positions(self):
+ """Return the positions in this inventory.
+
+ Returns:
+ A shallow copy of the list of positions.
+ """
+ return list(iter(self))
+
@@ -16463,7 +13634,7 @@ beancount.core.inventory.Inventory.is_empty(self)
+beancount.core.inventory.Inventory.is_empty(self)
beancount/core/inventory.py
def is_empty(self):
- """Return true if the inventory is empty, that is, has no positions.
-
- Returns:
- A boolean.
- """
- return len(self) == 0
-
def is_empty(self):
+ """Return true if the inventory is empty, that is, has no positions.
+
+ Returns:
+ A boolean.
+ """
+ return len(self) == 0
+
beancount.core.inventory.Inventory.is_mixed(self)
+beancount.core.inventory.Inventory.is_mixed(self)
beancount/core/inventory.py
def is_mixed(self):
- """Return true if the inventory contains a mix of positive and negative lots for
- at least one instrument.
-
- Returns:
- A boolean.
- """
- signs_map = {}
- for position in self:
- sign = position.units.number >= 0
- prev_sign = signs_map.setdefault(position.units.currency, sign)
- if sign != prev_sign:
- return True
- return False
-
def is_mixed(self):
+ """Return true if the inventory contains a mix of positive and negative lots for
+ at least one instrument.
+
+ Returns:
+ A boolean.
+ """
+ signs_map = {}
+ for position in self:
+ sign = position.units.number >= 0
+ prev_sign = signs_map.setdefault(position.units.currency, sign)
+ if sign != prev_sign:
+ return True
+ return False
+
beancount.core.inventory.Inventory.is_reduced_by(self, ramount)
+beancount.core.inventory.Inventory.is_reduced_by(self, ramount)
beancount/core/inventory.py
def is_reduced_by(self, ramount):
- """Return true if the amount could reduce this inventory.
-
- Args:
- ramount: An instance of Amount.
- Returns:
- A boolean.
- """
- if ramount.number == ZERO:
- return False
- for position in self:
- units = position.units
- if ramount.currency == units.currency and not same_sign(
- ramount.number, units.number
- ):
- return True
- return False
-
def is_reduced_by(self, ramount):
+ """Return true if the amount could reduce this inventory.
+
+ Args:
+ ramount: An instance of Amount.
+ Returns:
+ A boolean.
+ """
+ if ramount.number == ZERO:
+ return False
+ for position in self:
+ units = position.units
+ if (ramount.currency == units.currency and
+ not same_sign(ramount.number, units.number)):
+ return True
+ return False
+
@@ -16638,7 +13808,7 @@ beancount.core.inventory.Inventory.is_small(self, tolerances)
+beancount.core.inventory.Inventory.is_small(self, tolerances)
beancount/core/inventory.py
def is_small(self, tolerances):
- """Return true if all the positions in the inventory are small.
-
- Args:
- tolerances: A Decimal, the small number of units under which a position
- is considered small, or a dict of currency to such epsilon precision.
- Returns:
- A boolean.
- """
- if isinstance(tolerances, dict):
- for position in self:
- tolerance = tolerances.get(position.units.currency, ZERO)
- if abs(position.units.number) > tolerance:
- return False
- small = True
- else:
- small = not any(abs(position.units.number) > tolerances for position in self)
- return small
-
def is_small(self, tolerances):
+ """Return true if all the positions in the inventory are small.
+
+ Args:
+ tolerances: A Decimal, the small number of units under which a position
+ is considered small, or a dict of currency to such epsilon precision.
+ Returns:
+ A boolean.
+ """
+ if isinstance(tolerances, dict):
+ for position in self:
+ tolerance = tolerances.get(position.units.currency, ZERO)
+ if abs(position.units.number) > tolerance:
+ return False
+ small = True
+ else:
+ small = not any(abs(position.units.number) > tolerances
+ for position in self)
+ return small
+
beancount.core.inventory.Inventory.reduce(self, reducer, *args)
+beancount.core.inventory.Inventory.reduce(self, reducer, *args)
beancount/core/inventory.py
def reduce(self, reducer, *args):
- """Reduce an inventory using one of the conversion functions.
-
- See functions in beancount.core.convert.
-
- Returns:
- An instance of Inventory.
- """
- inventory = Inventory()
- for position in self:
- inventory.add_amount(reducer(position, *args))
- return inventory
-
def reduce(self, reducer, *args):
+ """Reduce an inventory using one of the conversion functions.
+
+ See functions in beancount.core.convert.
+
+ Returns:
+ An instance of Inventory.
+ """
+ inventory = Inventory()
+ for position in self:
+ inventory.add_amount(reducer(position, *args))
+ return inventory
+
beancount.core.inventory.Inventory.segregate_units(self, currencies)
+beancount.core.inventory.Inventory.segregate_units(self, currencies)
A dict of currency to Inventory instances.
beancount/core/inventory.py
def segregate_units(self, currencies):
- """Split up the list of positions to the given currencies.
-
- Args:
- currencies: A list of currency strings, the currencies to isolate.
- Returns:
- A dict of currency to Inventory instances.
- """
- per_currency_dict = {currency: Inventory() for currency in currencies}
- per_currency_dict[None] = Inventory()
- for position in self:
- currency = position.units.currency
- key = currency if currency in currencies else None
- per_currency_dict[key].add_position(position)
- return per_currency_dict
-
beancount.core.inventory.Inventory.split(self)
-
-
-Split up the list of positions to their corresponding currencies.
- +beancount/core/inventory.py
def split(self):
- """Split up the list of positions to their corresponding currencies.
-
- Returns:
- A dict of currency to Inventory instances.
- """
- per_currency_dict = collections.defaultdict(Inventory)
- for position in self:
- per_currency_dict[position.units.currency].add_position(position)
- return dict(per_currency_dict)
-
def segregate_units(self, currencies):
+ """Split up the list of positions to the given currencies.
+
+ Args:
+ currencies: A list of currency strings, the currencies to isolate.
+ Returns:
+ A dict of currency to Inventory instances.
+ """
+ per_currency_dict = {currency: Inventory()
+ for currency in currencies}
+ per_currency_dict[None] = Inventory()
+ for position in self:
+ currency = position.units.currency
+ key = (currency if currency in currencies else None)
+ per_currency_dict[key].add_position(position)
+ return per_currency_dict
+
beancount.core.inventory.Inventory.to_string(self, dformat=<beancount.core.display_context.DisplayFormatter object at 0x78fcde6b7290>, parens=True)
+beancount.core.inventory.Inventory.to_string(self, dformat=<beancount.core.display_context.DisplayFormatter object at 0x7a32089c1e20>, parens=True)
beancount/core/inventory.py
def to_string(self, dformat=DEFAULT_FORMATTER, parens=True):
- """Convert an Inventory instance to a printable string.
-
- Args:
- dformat: An instance of DisplayFormatter.
- parents: A boolean, true if we should surround the results by parentheses.
- Returns:
- A formatted string of the quantized amount and symbol.
- """
- fmt = "({})" if parens else "{}"
- return fmt.format(", ".join(pos.to_string(dformat) for pos in sorted(self)))
-
def to_string(self, dformat=DEFAULT_FORMATTER, parens=True):
+ """Convert an Inventory instance to a printable string.
+
+ Args:
+ dformat: An instance of DisplayFormatter.
+ parents: A boolean, true if we should surround the results by parentheses.
+ Returns:
+ A formatted string of the quantized amount and symbol.
+ """
+ fmt = '({})' if parens else '{}'
+ return fmt.format(
+ ', '.join(pos.to_string(dformat) for pos in sorted(self)))
+
-beancount.core.inventory.MatchResult (Enum)
-
-
-
-
-Result of booking a new lot to an existing inventory.
- - - - -beancount.core.inventory.check_invariants(inv)
+beancount.core.inventory.check_invariants(inv)
beancount/core/inventory.py
def check_invariants(inv):
- """Check the invariants of the Inventory.
-
- Args:
- inventory: An instance of Inventory.
- Returns:
- True if the invariants are respected.
- """
- # Check that all the keys are unique.
- lots = set((pos.units.currency, pos.cost) for pos in inv)
- assert len(lots) == len(inv), "Invalid inventory: {}".format(inv)
- # Check that none of the amounts is zero.
- for pos in inv:
- assert pos.units.number != ZERO, "Invalid position size: {}".format(pos)
-
def check_invariants(inv):
+ """Check the invariants of the Inventory.
+
+ Args:
+ inventory: An instance of Inventory.
+ Returns:
+ True if the invariants are respected.
+ """
+ # Check that all the keys are unique.
+ lots = set((pos.units.currency, pos.cost) for pos in inv)
+ assert len(lots) == len(inv), "Invalid inventory: {}".format(inv)
+ # Check that none of the amounts is zero.
+ for pos in inv:
+ assert pos.units.number != ZERO, "Invalid position size: {}".format(pos)
+
beancount.core.inventory.from_string(string)
+beancount.core.inventory.from_string(string)
beancount/core/inventory.py
@staticmethod
-def from_string(string):
- """Create an Inventory from a string. This is useful for writing tests.
-
- Args:
- string: A comma-separated string of <number> <currency> with an
- optional {<number> <currency>} for the cost.
- Returns:
- A new instance of Inventory with the given balances.
- """
- new_inventory = Inventory()
- # We need to split the comma-separated positions but ignore commas
- # occurring within a {...cost...} specification.
- position_strs = re.split(
- r"([-+]?[0-9,.]+\s+[A-Z]+\s*(?:{[^}]*})?)\s*,?\s*", string
- )[1::2]
- for position_str in position_strs:
- new_inventory.add_position(position_from_string(position_str))
- return new_inventory
-
@staticmethod
+def from_string(string):
+ """Create an Inventory from a string. This is useful for writing tests.
+
+ Args:
+ string: A comma-separated string of <number> <currency> with an
+ optional {<number> <currency>} for the cost.
+ Returns:
+ A new instance of Inventory with the given balances.
+ """
+ new_inventory = Inventory()
+ # We need to split the comma-separated positions but ignore commas
+ # occurring within a {...cost...} specification.
+ position_strs = re.split(
+ r'([-+]?[0-9,.]+\s+[A-Z]+\s*(?:{[^}]*})?)\s*,?\s*', string)[1::2]
+ for position_str in position_strs:
+ new_inventory.add_position(position_from_string(position_str))
+ return new_inventory
+
beancount.core.number.D(strord=None)
+beancount.core.number.D(strord=None)
beancount/core/number.py
def D(strord=None):
- """Convert a string into a Decimal object.
-
- This is used in parsing amounts from files in the importers. This is the
- main function you should use to build all numbers the system manipulates
- (never use floating-point in an accounting system). Commas are stripped and
- ignored, as they are assumed to be thousands separators (the French comma
- separator as decimal is not supported). This function just returns the
- argument if it is already a Decimal object, for convenience.
-
- Args:
- strord: A string or Decimal instance.
- Returns:
- A Decimal instance.
- """
- try:
- # Note: try a map lookup and optimize performance here.
- if strord is None or strord == "":
- return Decimal()
- elif isinstance(strord, str):
- return Decimal(_CLEAN_NUMBER_RE.sub("", strord))
- elif isinstance(strord, Decimal):
- return strord
- elif isinstance(strord, (int, float)):
- return Decimal(strord)
- else:
- assert strord is None, "Invalid value to convert: {}".format(strord)
- except Exception as exc:
- raise ValueError(
- "Impossible to create Decimal instance from {!s}: {}".format(strord, exc)
- ) from exc
-
beancount.core.number.auto_quantize(number, threshold)
-
-
-Automatically quantize the number at a given threshold.
-For example, with a threshold of 0.01, this will convert:
-20.899999618530273 20.9 - 20.290000000000000000000000000000 20.29 - 110.90 110.9 - 11.0600004196167 11.06 - 10.539999961853027 10.54 - 134.3300018310547 134.33 - 253.920200000000000000000000000000 253.9202
- -beancount/core/number.py
def auto_quantize(number: Decimal, threshold: float) -> Decimal:
- """Automatically quantize the number at a given threshold.
-
- For example, with a threshold of 0.01, this will convert:
-
- 20.899999618530273 20.9
- 20.290000000000000000000000000000 20.29
- 110.90 110.9
- 11.0600004196167 11.06
- 10.539999961853027 10.54
- 134.3300018310547 134.33
- 253.920200000000000000000000000000 253.9202
-
- """
- exponent = auto_quantized_exponent(number, threshold)
- if exponent != number.as_tuple().exponent:
- quant = TEN**exponent
- qnumber = number.quantize(quant).normalize()
- return qnumber
- else:
- return number
-
beancount.core.number.auto_quantized_exponent(number, threshold)
-
-
-Automatically infer the exponent that would be used below a given threshold.
- -beancount/core/number.py
def auto_quantized_exponent(number: Decimal, threshold: float) -> int:
- """Automatically infer the exponent that would be used below a given threshold."""
- dtuple = number.normalize().as_tuple()
- norm = Decimal(dtuple._replace(sign=0, exponent=-len(dtuple.digits)))
- low_threshold = threshold
- high_threshold = 1.0 - low_threshold
- while norm != ZERO:
- if not (low_threshold <= norm <= high_threshold):
- break
- ntuple = norm.scaleb(1).as_tuple()
- norm = Decimal(ntuple._replace(digits=ntuple.digits[ntuple.exponent :]))
- return dtuple.exponent - norm.as_tuple().exponent
-
beancount.core.number.infer_quantum_from_list(numbers, threshold=0.01)
-
-
-Given a list of numbers from floats, infer the common quantization.
-For a series of numbers provided as floats, e.g., prices from a price -source, we'd like to infer what the right quantization that should be used -to avoid rounding errors above some threshold.
-from the numbers. This simple algorithm auto-quantizes all the numbers and -quantizes all of them at the maximum precision that would result in rounding -under the threshold.
- -Parameters: | -
-
|
-
---|
Returns: | -
-
|
-
---|
beancount/core/number.py
def infer_quantum_from_list(
- numbers: List[Decimal], threshold: float = 0.01
-) -> Optional[Decimal]:
- """Given a list of numbers from floats, infer the common quantization.
-
- For a series of numbers provided as floats, e.g., prices from a price
- source, we'd like to infer what the right quantization that should be used
- to avoid rounding errors above some threshold.
-
-
- from the numbers. This simple algorithm auto-quantizes all the numbers and
- quantizes all of them at the maximum precision that would result in rounding
- under the threshold.
-
- Args:
- prices: A list of float or Decimal prices to infer from. If floats are
- provided, conversion is done naively.
- threshold: A fraction, the maximum error to tolerate before stopping the
- search.
- Returns:
- A decimal object to use with decimal.Decimal.quantize().
-
- """
- # Auto quantize all the numbers.
- qnumbers = [auto_quantize(num, threshold) for num in numbers]
- exponent = max(num_fractional_digits(n) for n in qnumbers)
- return -exponent
-
def D(strord=None):
+ """Convert a string into a Decimal object.
+
+ This is used in parsing amounts from files in the importers. This is the
+ main function you should use to build all numbers the system manipulates
+ (never use floating-point in an accounting system). Commas are stripped and
+ ignored, as they are assumed to be thousands separators (the French comma
+ separator as decimal is not supported). This function just returns the
+ argument if it is already a Decimal object, for convenience.
+
+ Args:
+ strord: A string or Decimal instance.
+ Returns:
+ A Decimal instance.
+ """
+ try:
+ # Note: try a map lookup and optimize performance here.
+ if strord is None or strord == '':
+ return Decimal()
+ elif isinstance(strord, str):
+ return Decimal(_CLEAN_NUMBER_RE.sub('', strord))
+ elif isinstance(strord, Decimal):
+ return strord
+ elif isinstance(strord, (int, float)):
+ return Decimal(strord)
+ else:
+ assert strord is None, "Invalid value to convert: {}".format(strord)
+ except Exception as exc:
+ raise ValueError("Impossible to create Decimal instance from {!s}: {}".format(
+ strord, exc))
+
beancount.core.number.num_fractional_digits(number)
+beancount.core.number.is_fast_decimal(decimal_module)
-Return the number of fractional digits.
+Return true if a fast C decimal implementation is installed.
beancount/core/number.py
def num_fractional_digits(number: Decimal) -> int:
- """Return the number of fractional digits."""
- return -number.as_tuple().exponent
-
def is_fast_decimal(decimal_module):
+ "Return true if a fast C decimal implementation is installed."
+ return isinstance(decimal_module.Decimal().sqrt, types.BuiltinFunctionType)
+
beancount.core.number.round_to(number, increment)
+beancount.core.number.round_to(number, increment)
beancount/core/number.py
def round_to(number, increment):
- """Round a number *down* to a particular increment.
-
- Args:
- number: A Decimal, the number to be rounded.
- increment: A Decimal, the size of the increment.
- Returns:
- A Decimal, the rounded number.
- """
- return int((number / increment)) * increment
-
def round_to(number, increment):
+ """Round a number *down* to a particular increment.
+
+ Args:
+ number: A Decimal, the number to be rounded.
+ increment: A Decimal, the size of the increment.
+ Returns:
+ A Decimal, the rounded number.
+ """
+ return int((number / increment)) * increment
+
beancount.core.number.same_sign(number1, number2)
+beancount.core.number.same_sign(number1, number2)
beancount/core/number.py
def same_sign(number1, number2):
- """Return true if both numbers have the same sign.
-
- Args:
- number1: An instance of Decimal.
- number2: An instance of Decimal.
- Returns:
- A boolean.
- """
- return (number1 >= 0) == (number2 >= 0)
-
def same_sign(number1, number2):
+ """Return true if both numbers have the same sign.
+
+ Args:
+ number1: An instance of Decimal.
+ number2: An instance of Decimal.
+ Returns:
+ A boolean.
+ """
+ return (number1 >= 0) == (number2 >= 0)
+
beancount.core.position.Cost.__getnewargs__(self)
+beancount.core.position.Cost.__getnewargs__(self)
special
@@ -17732,10 +14628,10 @@ beancount/core/position.py
def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return _tuple(self)
-
def __getnewargs__(self):
+ 'Return self as a plain tuple. Used by copy and pickle.'
+ return _tuple(self)
+
beancount.core.position.Cost.__new__(_cls, number, currency, date, label)
+beancount.core.position.Cost.__new__(_cls, number, currency, date, label)
special
@@ -17772,7 +14668,7 @@ beancount.core.position.Cost.__repr__(self)
+beancount.core.position.Cost.__repr__(self)
special
@@ -17786,10 +14682,10 @@ beancount/core/position.py
def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + repr_fmt % self
-
def __repr__(self):
+ 'Return a nicely formatted representation string'
+ return self.__class__.__name__ + repr_fmt % self
+
beancount.core.position.CostSpec.__getnewargs__(self)
+beancount.core.position.CostSpec.__getnewargs__(self)
special
@@ -17856,10 +14752,10 @@ beancount/core/position.py
def __getnewargs__(self):
- 'Return self as a plain tuple. Used by copy and pickle.'
- return _tuple(self)
-
def __getnewargs__(self):
+ 'Return self as a plain tuple. Used by copy and pickle.'
+ return _tuple(self)
+
@@ -17872,7 +14768,7 @@ beancount.core.position.CostSpec.__new__(_cls, number_per, number_total, currency, date, label, merge)
+beancount.core.position.CostSpec.__new__(_cls, number_per, number_total, currency, date, label, merge)
special
@@ -17896,7 +14792,7 @@ beancount.core.position.CostSpec.__repr__(self)
+beancount.core.position.CostSpec.__repr__(self)
special
@@ -17910,10 +14806,10 @@ beancount/core/position.py
def __repr__(self):
- 'Return a nicely formatted representation string'
- return self.__class__.__name__ + repr_fmt % self
-
def __repr__(self):
+ 'Return a nicely formatted representation string'
+ return self.__class__.__name__ + repr_fmt % self
+
beancount.core.position.Position.__abs__(self)
+beancount.core.position.Position.__abs__(self)
special
@@ -18021,14 +14917,14 @@ beancount/core/position.py
def __abs__(self):
- """Return the absolute value of the position.
-
- Returns:
- An instance of Position with the absolute units.
- """
- return Position(amount_abs(self.units), self.cost)
-
def __abs__(self):
+ """Return the absolute value of the position.
+
+ Returns:
+ An instance of Position with the absolute units.
+ """
+ return Position(amount_abs(self.units), self.cost)
+
beancount.core.position.Position.__copy__(self)
+beancount.core.position.Position.__copy__(self)
special
@@ -18072,16 +14968,16 @@ beancount/core/position.py
def __copy__(self):
- """Shallow copy, except for the lot, which can be shared. This is important for
- performance reasons; a lot of time is spent here during balancing.
-
- Returns:
- A shallow copy of this position.
- """
- # Note: We use Decimal() for efficiency.
- return Position(copy.copy(self.units), copy.copy(self.cost))
-
def __copy__(self):
+ """Shallow copy, except for the lot, which can be shared. This is important for
+ performance reasons; a lot of time is spent here during balancing.
+
+ Returns:
+ A shallow copy of this position.
+ """
+ # Note: We use Decimal() for efficiency.
+ return Position(copy.copy(self.units), copy.copy(self.cost))
+
beancount.core.position.Position.__eq__(self, other)
+beancount.core.position.Position.__eq__(self, other)
special
@@ -18142,22 +15038,20 @@ beancount/core/position.py
def __eq__(self, other):
- """Equality comparison with another Position. The objects are considered equal
- if both number and lot are matching, and if the number of units is zero
- and the other position is None, that is also okay.
-
- Args:
- other: An instance of Position, or None.
- Returns:
- A boolean, true if the positions are equal.
- """
- return (
- self.units.number == ZERO
- if other is None
- else (self.units == other.units and self.cost == other.cost)
- )
-
def __eq__(self, other):
+ """Equality comparison with another Position. The objects are considered equal
+ if both number and lot are matching, and if the number of units is zero
+ and the other position is None, that is also okay.
+
+ Args:
+ other: An instance of Position, or None.
+ Returns:
+ A boolean, true if the positions are equal.
+ """
+ return (self.units.number == ZERO
+ if other is None
+ else (self.units == other.units and self.cost == other.cost))
+
beancount.core.position.Position.__hash__(self)
+beancount.core.position.Position.__hash__(self)
special
@@ -18200,14 +15094,14 @@ beancount/core/position.py
def __hash__(self):
- """Compute a hash for this position.
-
- Returns:
- A hash of this position object.
- """
- return hash((self.units, self.cost))
-
def __hash__(self):
+ """Compute a hash for this position.
+
+ Returns:
+ A hash of this position object.
+ """
+ return hash((self.units, self.cost))
+
beancount.core.position.Position.__lt__(self, other)
+beancount.core.position.Position.__lt__(self, other)
special
@@ -18266,16 +15160,16 @@ beancount/core/position.py
def __lt__(self, other):
- """A less-than comparison operator for positions.
-
- Args:
- other: Another instance of Position.
- Returns:
- True if this positions is smaller than the other position.
- """
- return self.sortkey() < other.sortkey()
-
def __lt__(self, other):
+ """A less-than comparison operator for positions.
+
+ Args:
+ other: Another instance of Position.
+ Returns:
+ True if this positions is smaller than the other position.
+ """
+ return self.sortkey() < other.sortkey()
+
beancount.core.position.Position.__mul__(self, scalar)
+beancount.core.position.Position.__mul__(self, scalar)
special
@@ -18334,16 +15228,16 @@ beancount/core/position.py
def __mul__(self, scalar):
- """Scale/multiply the contents of the position.
-
- Args:
- scalar: A Decimal.
- Returns:
- An instance of Inventory.
- """
- return Position(amount_mul(self.units, scalar), self.cost)
-
def __mul__(self, scalar):
+ """Scale/multiply the contents of the position.
+
+ Args:
+ scalar: A Decimal.
+ Returns:
+ An instance of Inventory.
+ """
+ return Position(amount_mul(self.units, scalar), self.cost)
+
beancount.core.position.Position.__neg__(self)
+beancount.core.position.Position.__neg__(self)
special
@@ -18386,15 +15280,15 @@ beancount/core/position.py
def get_negative(self):
- """Get a copy of this position but with a negative number.
-
- Returns:
- An instance of Position which represents the inverse of this Position.
- """
- # Note: We use Decimal() for efficiency.
- return Position(-self.units, self.cost)
-
def get_negative(self):
+ """Get a copy of this position but with a negative number.
+
+ Returns:
+ An instance of Position which represents the inverse of this Position.
+ """
+ # Note: We use Decimal() for efficiency.
+ return Position(-self.units, self.cost)
+
beancount.core.position.Position.__new__(cls, units, cost=None)
+beancount.core.position.Position.__new__(cls, units, cost=None)
special
@@ -18422,15 +15316,13 @@ beancount/core/position.py
def __new__(cls, units, cost=None):
- assert isinstance(
- units, Amount
- ), "Expected an Amount for units; received '{}'".format(units)
- assert cost is None or isinstance(
- cost, Position.cost_types
- ), "Expected a Cost for cost; received '{}'".format(cost)
- return _Position.__new__(cls, units, cost)
-
def __new__(cls, units, cost=None):
+ assert isinstance(units, Amount), (
+ "Expected an Amount for units; received '{}'".format(units))
+ assert cost is None or isinstance(cost, Position.cost_types), (
+ "Expected a Cost for cost; received '{}'".format(cost))
+ return _Position.__new__(cls, units, cost)
+
beancount.core.position.Position.__repr__(self)
+beancount.core.position.Position.__repr__(self)
special
@@ -18473,14 +15365,14 @@ beancount/core/position.py
def __str__(self):
- """Return a string representation of the position.
-
- Returns:
- A string, a printable representation of the position.
- """
- return self.to_string()
-
def __str__(self):
+ """Return a string representation of the position.
+
+ Returns:
+ A string, a printable representation of the position.
+ """
+ return self.to_string()
+
beancount.core.position.Position.__str__(self)
+beancount.core.position.Position.__str__(self)
special
@@ -18523,14 +15415,14 @@ beancount/core/position.py
def __str__(self):
- """Return a string representation of the position.
-
- Returns:
- A string, a printable representation of the position.
- """
- return self.to_string()
-
def __str__(self):
+ """Return a string representation of the position.
+
+ Returns:
+ A string, a printable representation of the position.
+ """
+ return self.to_string()
+
beancount.core.position.Position.currency_pair(self)
+beancount.core.position.Position.currency_pair(self)
beancount/core/position.py
def currency_pair(self):
- """Return the currency pair associated with this position.
-
- Returns:
- A pair of a currency string and a cost currency string or None.
- """
- return (self.units.currency, self.cost.currency if self.cost else None)
-
def currency_pair(self):
+ """Return the currency pair associated with this position.
+
+ Returns:
+ A pair of a currency string and a cost currency string or None.
+ """
+ return (self.units.currency, self.cost.currency if self.cost else None)
+
beancount.core.position.Position.from_amounts(units, cost_amount=None)
+beancount.core.position.Position.from_amounts(units, cost_amount=None)
staticmethod
@@ -18637,26 +15529,23 @@ beancount/core/position.py
@staticmethod
-def from_amounts(units, cost_amount=None):
- """Create a position from an amount and a cost.
-
- Args:
- amount: An amount, that represents the number of units and the lot currency.
- cost_amount: If not None, represents the cost amount.
- Returns:
- A Position instance.
- """
- assert cost_amount is None or isinstance(
- cost_amount, Amount
- ), "Invalid type for cost: {}".format(cost_amount)
- cost = (
- Cost(cost_amount.number, cost_amount.currency, None, None)
- if cost_amount
- else None
- )
- return Position(units, cost)
-
@staticmethod
+def from_amounts(units, cost_amount=None):
+ """Create a position from an amount and a cost.
+
+ Args:
+ amount: An amount, that represents the number of units and the lot currency.
+ cost_amount: If not None, represents the cost amount.
+ Returns:
+ A Position instance.
+ """
+ assert cost_amount is None or isinstance(cost_amount, Amount), (
+ "Invalid type for cost: {}".format(cost_amount))
+ cost = (Cost(cost_amount.number, cost_amount.currency, None, None)
+ if cost_amount else
+ None)
+ return Position(units, cost)
+
beancount.core.position.Position.from_string(string)
+beancount.core.position.Position.from_string(string)
staticmethod
@@ -18717,81 +15606,80 @@ beancount/core/position.py
@staticmethod
-def from_string(string):
- """Create a position from a string specification.
-
- This is a miniature parser used for building tests.
-
- Args:
- string: A string of <number> <currency> with an optional {<number>
- <currency>} for the cost, similar to the parser syntax.
- Returns:
- A new instance of Position.
- """
- match = re.match(
- (r"\s*({})\s+({})" r"(?:\s+{{([^}}]*)}})?" r"\s*$").format(
- NUMBER_RE, CURRENCY_RE
- ),
- string,
- )
- if not match:
- raise ValueError("Invalid string for position: '{}'".format(string))
-
- number = D(match.group(1))
- currency = match.group(2)
-
- # Parse a cost expression.
- cost_number = None
- cost_currency = None
- date = None
- label = None
- cost_expression = match.group(3)
- if match.group(3):
- expressions = [expr.strip() for expr in re.split("[,/]", cost_expression)]
- for expr in expressions:
- # Match a compound number.
- match = re.match(
- r"({NUMBER_RE})\s*(?:#\s*({NUMBER_RE}))?\s+({CURRENCY_RE})$".format(
- NUMBER_RE=NUMBER_RE, CURRENCY_RE=CURRENCY_RE
- ),
- expr,
- )
- if match:
- per_number, total_number, cost_currency = match.group(1, 2, 3)
- per_number = D(per_number) if per_number else ZERO
- total_number = D(total_number) if total_number else ZERO
- if total_number:
- # Calculate the per-unit cost.
- total = number * per_number + total_number
- per_number = total / number
- cost_number = per_number
- continue
-
- # Match a date.
- match = re.match(r"(\d\d\d\d)[-/](\d\d)[-/](\d\d)$", expr)
- if match:
- date = datetime.date(*map(int, match.group(1, 2, 3)))
- continue
-
- # Match a label.
- match = re.match(r'"([^"]+)*"$', expr)
- if match:
- label = match.group(1)
- continue
-
- # Match a merge-cost marker.
- match = re.match(r"\*$", expr)
- if match:
- raise ValueError("Merge-code not supported in string constructor.")
-
- raise ValueError("Invalid cost component: '{}'".format(expr))
- cost = Cost(cost_number, cost_currency, date, label)
- else:
- cost = None
-
- return Position(Amount(number, currency), cost)
-
@staticmethod
+def from_string(string):
+ """Create a position from a string specification.
+
+ This is a miniature parser used for building tests.
+
+ Args:
+ string: A string of <number> <currency> with an optional {<number>
+ <currency>} for the cost, similar to the parser syntax.
+ Returns:
+ A new instance of Position.
+ """
+ match = re.match(
+ (r'\s*({})\s+({})'
+ r'(?:\s+{{([^}}]*)}})?'
+ r'\s*$').format(NUMBER_RE, CURRENCY_RE),
+ string)
+ if not match:
+ raise ValueError("Invalid string for position: '{}'".format(string))
+
+ number = D(match.group(1))
+ currency = match.group(2)
+
+ # Parse a cost expression.
+ cost_number = None
+ cost_currency = None
+ date = None
+ label = None
+ cost_expression = match.group(3)
+ if match.group(3):
+ expressions = [expr.strip() for expr in re.split('[,/]', cost_expression)]
+ for expr in expressions:
+
+ # Match a compound number.
+ match = re.match(
+ r'({NUMBER_RE})\s*(?:#\s*({NUMBER_RE}))?\s+({CURRENCY_RE})$'
+ .format(NUMBER_RE=NUMBER_RE, CURRENCY_RE=CURRENCY_RE),
+ expr
+ )
+ if match:
+ per_number, total_number, cost_currency = match.group(1, 2, 3)
+ per_number = D(per_number) if per_number else ZERO
+ total_number = D(total_number) if total_number else ZERO
+ if total_number:
+ # Calculate the per-unit cost.
+ total = number * per_number + total_number
+ per_number = total / number
+ cost_number = per_number
+ continue
+
+ # Match a date.
+ match = re.match(r'(\d\d\d\d)[-/](\d\d)[-/](\d\d)$', expr)
+ if match:
+ date = datetime.date(*map(int, match.group(1, 2, 3)))
+ continue
+
+ # Match a label.
+ match = re.match(r'"([^"]+)*"$', expr)
+ if match:
+ label = match.group(1)
+ continue
+
+ # Match a merge-cost marker.
+ match = re.match(r'\*$', expr)
+ if match:
+ raise ValueError("Merge-code not supported in string constructor.")
+
+ raise ValueError("Invalid cost component: '{}'".format(expr))
+ cost = Cost(cost_number, cost_currency, date, label)
+ else:
+ cost = None
+
+ return Position(Amount(number, currency), cost)
+
beancount.core.position.Position.get_negative(self)
+beancount.core.position.Position.get_negative(self)
beancount/core/position.py
def get_negative(self):
- """Get a copy of this position but with a negative number.
-
- Returns:
- An instance of Position which represents the inverse of this Position.
- """
- # Note: We use Decimal() for efficiency.
- return Position(-self.units, self.cost)
-
def get_negative(self):
+ """Get a copy of this position but with a negative number.
+
+ Returns:
+ An instance of Position which represents the inverse of this Position.
+ """
+ # Note: We use Decimal() for efficiency.
+ return Position(-self.units, self.cost)
+
beancount.core.position.Position.is_negative_at_cost(self)
+beancount.core.position.Position.is_negative_at_cost(self)
beancount/core/position.py
def is_negative_at_cost(self):
- """Return true if the position is held at cost and negative.
-
- Returns:
- A boolean.
- """
- return self.units.number < ZERO and self.cost is not None
-
def is_negative_at_cost(self):
+ """Return true if the position is held at cost and negative.
+
+ Returns:
+ A boolean.
+ """
+ return (self.units.number < ZERO and self.cost is not None)
+
@@ -18899,7 +15787,7 @@ beancount.core.position.Position.sortkey(self)
+beancount.core.position.Position.sortkey(self)
beancount/core/position.py
def sortkey(self):
- """Return a key to sort positions by. This key depends on the order of the
- currency of the lot (we want to order common currencies first) and the
- number of units.
-
- Returns:
- A tuple, used to sort lists of positions.
- """
- currency = self.units.currency
- order_units = CURRENCY_ORDER.get(currency, NCURRENCIES + len(currency))
- if self.cost is not None:
- cost_number = self.cost.number
- cost_currency = self.cost.currency
- else:
- cost_number = ZERO
- cost_currency = ""
-
- return (order_units, cost_number, cost_currency, self.units.number)
-
def sortkey(self):
+ """Return a key to sort positions by. This key depends on the order of the
+ currency of the lot (we want to order common currencies first) and the
+ number of units.
+
+ Returns:
+ A tuple, used to sort lists of positions.
+ """
+ currency = self.units.currency
+ order_units = CURRENCY_ORDER.get(currency, NCURRENCIES + len(currency))
+ if self.cost is not None:
+ cost_number = self.cost.number
+ cost_currency = self.cost.currency
+ else:
+ cost_number = ZERO
+ cost_currency = ''
+
+ return (order_units, cost_number, cost_currency, self.units.number)
+
beancount.core.position.Position.to_string(self, dformat=<beancount.core.display_context.DisplayFormatter object at 0x78fcde6b7290>, detail=True)
+beancount.core.position.Position.to_string(self, dformat=<beancount.core.display_context.DisplayFormatter object at 0x7a32089c1e20>, detail=True)
beancount/core/position.py
def to_string(self, dformat=DEFAULT_FORMATTER, detail=True):
- """Render the position to a string.See to_string() for details."""
- return to_string(self, dformat, detail)
-
def to_string(self, dformat=DEFAULT_FORMATTER, detail=True):
+ """Render the position to a string.See to_string() for details.
+ """
+ return to_string(self, dformat, detail)
+
beancount.core.position.cost_to_str(cost, dformat, detail=True)
+beancount.core.position.cost_to_str(cost, dformat, detail=True)
beancount/core/position.py
def cost_to_str(cost, dformat, detail=True):
- """Format an instance of Cost or a CostSpec to a string.
-
- Args:
- cost: An instance of Cost or CostSpec.
- dformat: A DisplayFormatter object.
- detail: A boolean, true if we should render the non-amount components.
- Returns:
- A string, suitable for formatting.
- """
- strlist = []
-
- if isinstance(cost, Cost):
- if isinstance(cost.number, Decimal):
- strlist.append(Amount(cost.number, cost.currency).to_string(dformat))
- if detail:
- if cost.date:
- strlist.append(cost.date.isoformat())
- if cost.label:
- strlist.append('"{}"'.format(cost.label))
-
- elif isinstance(cost, CostSpec):
- if isinstance(cost.number_per, Decimal) or isinstance(cost.number_total, Decimal):
- amountlist = []
- if isinstance(cost.number_per, Decimal):
- amountlist.append(dformat.format(cost.number_per))
- if isinstance(cost.number_total, Decimal):
- amountlist.append("#")
- amountlist.append(dformat.format(cost.number_total))
- if isinstance(cost.currency, str):
- amountlist.append(cost.currency)
- strlist.append(" ".join(amountlist))
- if detail:
- if cost.date:
- strlist.append(cost.date.isoformat())
- if cost.label:
- strlist.append('"{}"'.format(cost.label))
- if cost.merge:
- strlist.append("*")
-
- return ", ".join(strlist)
-
def cost_to_str(cost, dformat, detail=True):
+ """Format an instance of Cost or a CostSpec to a string.
+
+ Args:
+ cost: An instance of Cost or CostSpec.
+ dformat: A DisplayFormatter object.
+ detail: A boolean, true if we should render the non-amount components.
+ Returns:
+ A string, suitable for formatting.
+ """
+ strlist = []
+
+ if isinstance(cost, Cost):
+ if isinstance(cost.number, Decimal):
+ strlist.append(Amount(cost.number, cost.currency).to_string(dformat))
+ if detail:
+ if cost.date:
+ strlist.append(cost.date.isoformat())
+ if cost.label:
+ strlist.append('"{}"'.format(cost.label))
+
+ elif isinstance(cost, CostSpec):
+ if isinstance(cost.number_per, Decimal) or isinstance(cost.number_total, Decimal):
+ amountlist = []
+ if isinstance(cost.number_per, Decimal):
+ amountlist.append(dformat.format(cost.number_per))
+ if isinstance(cost.number_total, Decimal):
+ amountlist.append('#')
+ amountlist.append(dformat.format(cost.number_total))
+ if isinstance(cost.currency, str):
+ amountlist.append(cost.currency)
+ strlist.append(' '.join(amountlist))
+ if detail:
+ if cost.date:
+ strlist.append(cost.date.isoformat())
+ if cost.label:
+ strlist.append('"{}"'.format(cost.label))
+ if cost.merge:
+ strlist.append('*')
+
+ return ', '.join(strlist)
+
beancount.core.position.from_amounts(units, cost_amount=None)
+beancount.core.position.from_amounts(units, cost_amount=None)
beancount/core/position.py
@staticmethod
-def from_amounts(units, cost_amount=None):
- """Create a position from an amount and a cost.
-
- Args:
- amount: An amount, that represents the number of units and the lot currency.
- cost_amount: If not None, represents the cost amount.
- Returns:
- A Position instance.
- """
- assert cost_amount is None or isinstance(
- cost_amount, Amount
- ), "Invalid type for cost: {}".format(cost_amount)
- cost = (
- Cost(cost_amount.number, cost_amount.currency, None, None)
- if cost_amount
- else None
- )
- return Position(units, cost)
-
@staticmethod
+def from_amounts(units, cost_amount=None):
+ """Create a position from an amount and a cost.
+
+ Args:
+ amount: An amount, that represents the number of units and the lot currency.
+ cost_amount: If not None, represents the cost amount.
+ Returns:
+ A Position instance.
+ """
+ assert cost_amount is None or isinstance(cost_amount, Amount), (
+ "Invalid type for cost: {}".format(cost_amount))
+ cost = (Cost(cost_amount.number, cost_amount.currency, None, None)
+ if cost_amount else
+ None)
+ return Position(units, cost)
+
beancount.core.position.from_string(string)
+beancount.core.position.from_string(string)
beancount/core/position.py
@staticmethod
-def from_string(string):
- """Create a position from a string specification.
-
- This is a miniature parser used for building tests.
-
- Args:
- string: A string of <number> <currency> with an optional {<number>
- <currency>} for the cost, similar to the parser syntax.
- Returns:
- A new instance of Position.
- """
- match = re.match(
- (r"\s*({})\s+({})" r"(?:\s+{{([^}}]*)}})?" r"\s*$").format(
- NUMBER_RE, CURRENCY_RE
- ),
- string,
- )
- if not match:
- raise ValueError("Invalid string for position: '{}'".format(string))
-
- number = D(match.group(1))
- currency = match.group(2)
-
- # Parse a cost expression.
- cost_number = None
- cost_currency = None
- date = None
- label = None
- cost_expression = match.group(3)
- if match.group(3):
- expressions = [expr.strip() for expr in re.split("[,/]", cost_expression)]
- for expr in expressions:
- # Match a compound number.
- match = re.match(
- r"({NUMBER_RE})\s*(?:#\s*({NUMBER_RE}))?\s+({CURRENCY_RE})$".format(
- NUMBER_RE=NUMBER_RE, CURRENCY_RE=CURRENCY_RE
- ),
- expr,
- )
- if match:
- per_number, total_number, cost_currency = match.group(1, 2, 3)
- per_number = D(per_number) if per_number else ZERO
- total_number = D(total_number) if total_number else ZERO
- if total_number:
- # Calculate the per-unit cost.
- total = number * per_number + total_number
- per_number = total / number
- cost_number = per_number
- continue
-
- # Match a date.
- match = re.match(r"(\d\d\d\d)[-/](\d\d)[-/](\d\d)$", expr)
- if match:
- date = datetime.date(*map(int, match.group(1, 2, 3)))
- continue
-
- # Match a label.
- match = re.match(r'"([^"]+)*"$', expr)
- if match:
- label = match.group(1)
- continue
-
- # Match a merge-cost marker.
- match = re.match(r"\*$", expr)
- if match:
- raise ValueError("Merge-code not supported in string constructor.")
-
- raise ValueError("Invalid cost component: '{}'".format(expr))
- cost = Cost(cost_number, cost_currency, date, label)
- else:
- cost = None
-
- return Position(Amount(number, currency), cost)
-
@staticmethod
+def from_string(string):
+ """Create a position from a string specification.
+
+ This is a miniature parser used for building tests.
+
+ Args:
+ string: A string of <number> <currency> with an optional {<number>
+ <currency>} for the cost, similar to the parser syntax.
+ Returns:
+ A new instance of Position.
+ """
+ match = re.match(
+ (r'\s*({})\s+({})'
+ r'(?:\s+{{([^}}]*)}})?'
+ r'\s*$').format(NUMBER_RE, CURRENCY_RE),
+ string)
+ if not match:
+ raise ValueError("Invalid string for position: '{}'".format(string))
+
+ number = D(match.group(1))
+ currency = match.group(2)
+
+ # Parse a cost expression.
+ cost_number = None
+ cost_currency = None
+ date = None
+ label = None
+ cost_expression = match.group(3)
+ if match.group(3):
+ expressions = [expr.strip() for expr in re.split('[,/]', cost_expression)]
+ for expr in expressions:
+
+ # Match a compound number.
+ match = re.match(
+ r'({NUMBER_RE})\s*(?:#\s*({NUMBER_RE}))?\s+({CURRENCY_RE})$'
+ .format(NUMBER_RE=NUMBER_RE, CURRENCY_RE=CURRENCY_RE),
+ expr
+ )
+ if match:
+ per_number, total_number, cost_currency = match.group(1, 2, 3)
+ per_number = D(per_number) if per_number else ZERO
+ total_number = D(total_number) if total_number else ZERO
+ if total_number:
+ # Calculate the per-unit cost.
+ total = number * per_number + total_number
+ per_number = total / number
+ cost_number = per_number
+ continue
+
+ # Match a date.
+ match = re.match(r'(\d\d\d\d)[-/](\d\d)[-/](\d\d)$', expr)
+ if match:
+ date = datetime.date(*map(int, match.group(1, 2, 3)))
+ continue
+
+ # Match a label.
+ match = re.match(r'"([^"]+)*"$', expr)
+ if match:
+ label = match.group(1)
+ continue
+
+ # Match a merge-cost marker.
+ match = re.match(r'\*$', expr)
+ if match:
+ raise ValueError("Merge-code not supported in string constructor.")
+
+ raise ValueError("Invalid cost component: '{}'".format(expr))
+ cost = Cost(cost_number, cost_currency, date, label)
+ else:
+ cost = None
+
+ return Position(Amount(number, currency), cost)
+
beancount.core.position.get_position(posting)
+beancount.core.position.get_position(posting)
beancount/core/position.py
def get_position(posting):
- """Build a Position instance from a Posting instance.
-
- Args:
- posting: An instance of Posting.
- Returns:
- An instance of Position.
- """
- return Position(posting.units, posting.cost)
-
def get_position(posting):
+ """Build a Position instance from a Posting instance.
+
+ Args:
+ posting: An instance of Posting.
+ Returns:
+ An instance of Position.
+ """
+ return Position(posting.units, posting.cost)
+
beancount.core.position.to_string(pos, dformat=<beancount.core.display_context.DisplayFormatter object at 0x78fcde6b7290>, detail=True)
+beancount.core.position.to_string(pos, dformat=<beancount.core.display_context.DisplayFormatter object at 0x7a32089c1e20>, detail=True)
beancount/core/position.py
def to_string(pos, dformat=DEFAULT_FORMATTER, detail=True):
- """Render the Position or Posting instance to a string.
-
- Args:
- pos: An instance of Position or Posting.
- dformat: An instance of DisplayFormatter.
- detail: A boolean, true if we should only render the lot details
- beyond the cost (lot-date, label, etc.). If false, we only render
- the cost, if present.
- Returns:
- A string, the rendered position.
- """
- pos_str = pos.units.to_string(dformat)
- if pos.cost is not None:
- pos_str = "{} {{{}}}".format(pos_str, cost_to_str(pos.cost, dformat, detail))
- return pos_str
-
def to_string(pos, dformat=DEFAULT_FORMATTER, detail=True):
+ """Render the Position or Posting instance to a string.
+
+ Args:
+ pos: An instance of Position or Posting.
+ dformat: An instance of DisplayFormatter.
+ detail: A boolean, true if we should only render the lot details
+ beyond the cost (lot-date, label, etc.). If false, we only render
+ the cost, if present.
+ Returns:
+ A string, the rendered position.
+ """
+ pos_str = pos.units.to_string(dformat)
+ if pos.cost is not None:
+ pos_str = '{} {{{}}}'.format(pos_str, cost_to_str(pos.cost, dformat, detail))
+ return pos_str
+
beancount.core.prices.build_price_map(entries)
+beancount.core.prices.build_price_map(entries)
beancount/core/prices.py
def build_price_map(entries):
- """Build a price map from a list of arbitrary entries.
-
- If multiple prices are found for the same (currency, cost-currency) pair at
- the same date, the latest date is kept and the earlier ones (for that day)
- are discarded.
-
- If inverse price pairs are found, e.g. USD in AUD and AUD in USD, the
- inverse that has the smallest number of price points is converted into the
- one that has the most price points. In that way they are reconciled into a
- single one.
-
- Args:
- entries: A list of directives, hopefully including some Price and/or
- Transaction entries.
- Returns:
- A dict of (currency, cost-currency) keys to sorted lists of (date, number)
- pairs, where 'date' is the date the price occurs at and 'number' a Decimal
- that represents the price, or rate, between these two
- currencies/commodities. Each date occurs only once in the sorted list of
- prices of a particular key. All of the inverses are automatically
- generated in the price map.
- """
- # Fetch a list of all the price entries seen in the ledger.
- price_entries = [entry for entry in entries if isinstance(entry, Price)]
-
- # Build a map of exchange rates between these units.
- # (base-currency, quote-currency) -> List of (date, rate).
- price_map = collections.defaultdict(list)
- for price in price_entries:
- base_quote = (price.currency, price.amount.currency)
- price_map[base_quote].append((price.date, price.amount.number))
-
- # Find pairs of inversed units.
- inversed_units = []
- for base_quote, values in price_map.items():
- base, quote = base_quote
- if (quote, base) in price_map:
- inversed_units.append(base_quote)
-
- # Find pairs of inversed units, and swallow the conversion with the smaller
- # number of rates into the other one.
- for base, quote in inversed_units:
- bq_prices = price_map[(base, quote)]
- qb_prices = price_map[(quote, base)]
- remove = (base, quote) if len(bq_prices) < len(qb_prices) else (quote, base)
- base, quote = remove
-
- remove_list = price_map[remove]
- insert_list = price_map[(quote, base)]
- del price_map[remove]
-
- inverted_list = [(date, ONE / rate) for (date, rate) in remove_list if rate != ZERO]
- insert_list.extend(inverted_list)
-
- # Unzip and sort each of the entries and eliminate duplicates on the date.
- sorted_price_map = PriceMap(
- {
- base_quote: list(
- misc_utils.sorted_uniquify(date_rates, lambda x: x[0], last=True)
- )
- for (base_quote, date_rates) in price_map.items()
- }
- )
-
- # Compute and insert all the inverted rates.
- forward_pairs = list(sorted_price_map.keys())
- for (base, quote), price_list in list(sorted_price_map.items()):
- # Note: You have to filter out zero prices for zero-cost postings, like
- # gifted options.
- sorted_price_map[(quote, base)] = [
- (date, ONE / price) for date, price in price_list if price != ZERO
- ]
-
- sorted_price_map.forward_pairs = forward_pairs
- return sorted_price_map
-
def build_price_map(entries):
+ """Build a price map from a list of arbitrary entries.
+
+ If multiple prices are found for the same (currency, cost-currency) pair at
+ the same date, the latest date is kept and the earlier ones (for that day)
+ are discarded.
+
+ If inverse price pairs are found, e.g. USD in AUD and AUD in USD, the
+ inverse that has the smallest number of price points is converted into the
+ one that has the most price points. In that way they are reconciled into a
+ single one.
+
+ Args:
+ entries: A list of directives, hopefully including some Price and/or
+ Transaction entries.
+ Returns:
+ A dict of (currency, cost-currency) keys to sorted lists of (date, number)
+ pairs, where 'date' is the date the price occurs at and 'number' a Decimal
+ that represents the price, or rate, between these two
+ currencies/commodities. Each date occurs only once in the sorted list of
+ prices of a particular key. All of the inverses are automatically
+ generated in the price map.
+ """
+ # Fetch a list of all the price entries seen in the ledger.
+ price_entries = [entry
+ for entry in entries
+ if isinstance(entry, Price)]
+
+ # Build a map of exchange rates between these units.
+ # (base-currency, quote-currency) -> List of (date, rate).
+ price_map = collections.defaultdict(list)
+ for price in price_entries:
+ base_quote = (price.currency, price.amount.currency)
+ price_map[base_quote].append((price.date, price.amount.number))
+
+ # Find pairs of inversed units.
+ inversed_units = []
+ for base_quote, values in price_map.items():
+ base, quote = base_quote
+ if (quote, base) in price_map:
+ inversed_units.append(base_quote)
+
+ # Find pairs of inversed units, and swallow the conversion with the smaller
+ # number of rates into the other one.
+ for base, quote in inversed_units:
+ bq_prices = price_map[(base, quote)]
+ qb_prices = price_map[(quote, base)]
+ remove = ((base, quote)
+ if len(bq_prices) < len(qb_prices)
+ else (quote, base))
+ base, quote = remove
+
+ remove_list = price_map[remove]
+ insert_list = price_map[(quote, base)]
+ del price_map[remove]
+
+ inverted_list = [(date, ONE/rate)
+ for (date, rate) in remove_list
+ if rate != ZERO]
+ insert_list.extend(inverted_list)
+
+ # Unzip and sort each of the entries and eliminate duplicates on the date.
+ sorted_price_map = PriceMap({
+ base_quote: list(misc_utils.sorted_uniquify(date_rates, lambda x: x[0], last=True))
+ for (base_quote, date_rates) in price_map.items()})
+
+ # Compute and insert all the inverted rates.
+ forward_pairs = list(sorted_price_map.keys())
+ for (base, quote), price_list in list(sorted_price_map.items()):
+ # Note: You have to filter out zero prices for zero-cost postings, like
+ # gifted options.
+ sorted_price_map[(quote, base)] = [
+ (date, ONE/price) for date, price in price_list
+ if price != ZERO]
+
+ sorted_price_map.forward_pairs = forward_pairs
+ return sorted_price_map
+
beancount.core.prices.get_all_prices(price_map, base_quote)
+beancount.core.prices.get_all_prices(price_map, base_quote)
beancount/core/prices.py
def get_all_prices(price_map, base_quote):
- """Return a sorted list of all (date, number) price pairs.
-
- Args:
- price_map: A price map, which is a dict of (base, quote) -> list of (date,
- number) tuples, as created by build_price_map.
- base_quote: A pair of strings, the base currency to lookup, and the quote
- currency to lookup, which expresses which units the base currency is
- denominated in. This may also just be a string, with a '/' separator.
- Returns:
- A list of (date, Decimal) pairs, sorted by date.
- Raises:
- KeyError: If the base/quote could not be found.
- """
- base_quote = normalize_base_quote(base_quote)
- return _lookup_price_and_inverse(price_map, base_quote)
-
def get_all_prices(price_map, base_quote):
+ """Return a sorted list of all (date, number) price pairs.
+
+ Args:
+ price_map: A price map, which is a dict of (base, quote) -> list of (date,
+ number) tuples, as created by build_price_map.
+ base_quote: A pair of strings, the base currency to lookup, and the quote
+ currency to lookup, which expresses which units the base currency is
+ denominated in. This may also just be a string, with a '/' separator.
+ Returns:
+ A list of (date, Decimal) pairs, sorted by date.
+ Raises:
+ KeyError: If the base/quote could not be found.
+ """
+ base_quote = normalize_base_quote(base_quote)
+ return _lookup_price_and_inverse(price_map, base_quote)
+
beancount.core.prices.get_last_price_entries(entries, date)
+beancount.core.prices.get_last_price_entries(entries, date)
beancount/core/prices.py
def get_last_price_entries(entries, date):
- """Run through the entries until the given date and return the last
- Price entry encountered for each (currency, cost-currency) pair.
-
- Args:
- entries: A list of directives.
- date: An instance of datetime.date. If None, the very latest price
- is returned.
- Returns:
- A list of price entries.
- """
- price_entry_map = {}
- for entry in entries:
- if date is not None and entry.date >= date:
- break
- if isinstance(entry, Price):
- base_quote = (entry.currency, entry.amount.currency)
- price_entry_map[base_quote] = entry
- return sorted(price_entry_map.values(), key=data.entry_sortkey)
-
def get_last_price_entries(entries, date):
+ """Run through the entries until the given date and return the last
+ Price entry encountered for each (currency, cost-currency) pair.
+
+ Args:
+ entries: A list of directives.
+ date: An instance of datetime.date. If None, the very latest price
+ is returned.
+ Returns:
+ A list of price entries.
+ """
+ price_entry_map = {}
+ for entry in entries:
+ if date is not None and entry.date >= date:
+ break
+ if isinstance(entry, Price):
+ base_quote = (entry.currency, entry.amount.currency)
+ price_entry_map[base_quote] = entry
+ return sorted(price_entry_map.values(), key=data.entry_sortkey)
+
beancount.core.prices.get_latest_price(price_map, base_quote)
+beancount.core.prices.get_latest_price(price_map, base_quote)
beancount/core/prices.py
def get_latest_price(price_map, base_quote):
- """Return the latest price/rate from a price map for the given base/quote pair.
- This is often used to just get the 'current' price if you're looking at the
- entire set of entries.
-
- Args:
- price_map: A price map, which is a dict of (base, quote) -> list of (date,
- number) tuples, as created by build_price_map.
- Returns:
- A pair of (date, number), where 'date' is a datetime.date instance and
- 'number' is a Decimal of the price, or rate, at that date. The date is the
- latest date which we have an available price for in the price map.
- """
- base_quote = normalize_base_quote(base_quote)
-
- # Handle the degenerate case of a currency priced into its own.
- base, quote = base_quote
- if quote is None or base == quote:
- return (None, ONE)
-
- # Look up the list and return the latest element. The lists are assumed to
- # be sorted.
- try:
- price_list = _lookup_price_and_inverse(price_map, base_quote)
- except KeyError:
- price_list = None
- if price_list:
- return price_list[-1]
- else:
- return None, None
-
def get_latest_price(price_map, base_quote):
+ """Return the latest price/rate from a price map for the given base/quote pair.
+ This is often used to just get the 'current' price if you're looking at the
+ entire set of entries.
+
+ Args:
+ price_map: A price map, which is a dict of (base, quote) -> list of (date,
+ number) tuples, as created by build_price_map.
+ Returns:
+ A pair of (date, number), where 'date' is a datetime.date instance and
+ 'number' is a Decimal of the price, or rate, at that date. The date is the
+ latest date which we have an available price for in the price map.
+ """
+ base_quote = normalize_base_quote(base_quote)
+
+ # Handle the degenerate case of a currency priced into its own.
+ base, quote = base_quote
+ if quote is None or base == quote:
+ return (None, ONE)
+
+ # Look up the list and return the latest element. The lists are assumed to
+ # be sorted.
+ try:
+ price_list = _lookup_price_and_inverse(price_map, base_quote)
+ except KeyError:
+ price_list = None
+ if price_list:
+ return price_list[-1]
+ else:
+ return None, None
+
beancount.core.prices.get_price(price_map, base_quote, date=None)
+beancount.core.prices.get_price(price_map, base_quote, date=None)
beancount/core/prices.py
def get_price(price_map, base_quote, date=None):
- """Return the price as of the given date.
-
- If the date is unspecified, return the latest price.
-
- Args:
- price_map: A price map, which is a dict of (base, quote) -> list of (date,
- number) tuples, as created by build_price_map.
- base_quote: A pair of strings, the base currency to lookup, and the quote
- currency to lookup, which expresses which units the base currency is
- denominated in. This may also just be a string, with a '/' separator.
- date: A datetime.date instance, the date at which we want the conversion
- rate.
- Returns:
- A pair of (datetime.date, Decimal) instance. If no price information could
- be found, return (None, None).
- """
- if date is None:
- return get_latest_price(price_map, base_quote)
-
- base_quote = normalize_base_quote(base_quote)
-
- # Handle the degenerate case of a currency priced into its own.
- base, quote = base_quote
- if quote is None or base == quote:
- return (None, ONE)
-
- try:
- price_list = _lookup_price_and_inverse(price_map, base_quote)
- index = bisect_key.bisect_right_with_key(price_list, date, key=lambda x: x[0])
- if index == 0:
- return None, None
- else:
- return price_list[index - 1]
- except KeyError:
- return None, None
-
def get_price(price_map, base_quote, date=None):
+ """Return the price as of the given date.
+
+ If the date is unspecified, return the latest price.
+
+ Args:
+ price_map: A price map, which is a dict of (base, quote) -> list of (date,
+ number) tuples, as created by build_price_map.
+ base_quote: A pair of strings, the base currency to lookup, and the quote
+ currency to lookup, which expresses which units the base currency is
+ denominated in. This may also just be a string, with a '/' separator.
+ date: A datetime.date instance, the date at which we want the conversion
+ rate.
+ Returns:
+ A pair of (datetime.date, Decimal) instance. If no price information could
+ be found, return (None, None).
+ """
+ if date is None:
+ return get_latest_price(price_map, base_quote)
+
+ base_quote = normalize_base_quote(base_quote)
+
+ # Handle the degenerate case of a currency priced into its own.
+ base, quote = base_quote
+ if quote is None or base == quote:
+ return (None, ONE)
+
+ try:
+ price_list = _lookup_price_and_inverse(price_map, base_quote)
+ index = bisect_key.bisect_right_with_key(price_list, date, key=lambda x: x[0])
+ if index == 0:
+ return None, None
+ else:
+ return price_list[index-1]
+ except KeyError:
+ return None, None
+
beancount.core.prices.normalize_base_quote(base_quote)
+beancount.core.prices.normalize_base_quote(base_quote)
beancount/core/prices.py
def normalize_base_quote(base_quote):
- """Convert a slash-separated string to a pair of strings.
-
- Args:
- base_quote: A pair of strings, the base currency to lookup, and the quote
- currency to lookup, which expresses which units the base currency is
- denominated in. This may also just be a string, with a '/' separator.
- Returns:
- A pair of strings.
- """
- if isinstance(base_quote, str):
- base_quote_norm = tuple(base_quote.split("/"))
- assert len(base_quote_norm) == 2, base_quote
- base_quote = base_quote_norm
- assert isinstance(base_quote, tuple), base_quote
- return base_quote
-
beancount.core.prices.project(orig_price_map, from_currency, to_currency, base_currencies=None)
-
-
-Project all prices with a quote currency to another quote currency.
-Say you have a price for HOOL in USD and you'd like to convert HOOL to CAD. -If there aren't any (HOOL, CAD) price pairs in the database it will remain -unconverted. Projecting from USD to CAD will compute combined rates and -insert corresponding prices over all base currencies (like HOOL). In this -example, each of the (HOOL, USD) prices would see an inserted (HOOL, CAD) -price inserted at the same date.
-It is common to make these projections when reducing inventories in a ledger -that states multiple operating currency pairs, when for example, one wants -to compute total value of a set of accounts in one of those currencies.
-Please note that:
-Even if the target pair has existing entries, projection will still be - applied. For example, is there exist some (HOOL, CAD) prices, the - projection in the example above will still insert some new price points to - it.
-However, projected prices colliding existing ones at the same date will - not override them.
-Projection will fail to insert a new price if the conversion between to - and from currencies has no existing prices (e.g. before its first price - entry).
-Perhaps most importantly, we only insert price points at dates where the - base commodity has a price point. In other words, if we have prices for - dates A and C and the rate changes between these dates at date B, we don't - synthesize a new price at date B. A more accurate method to get projected - prices that takes into account varying rates is to do multiple lookups. - We'll eventually add a method to query it via a specified list of - intermediate pairs. {c1bd24f8d4b7}
-Parameters: | -
-
|
-
---|
Returns: | -
-
|
-
---|
beancount/core/prices.py
def project(
- orig_price_map: PriceMap,
- from_currency: Currency,
- to_currency: Currency,
- base_currencies: Optional[Set[Currency]] = None,
-) -> PriceMap:
- """Project all prices with a quote currency to another quote currency.
-
- Say you have a price for HOOL in USD and you'd like to convert HOOL to CAD.
- If there aren't any (HOOL, CAD) price pairs in the database it will remain
- unconverted. Projecting from USD to CAD will compute combined rates and
- insert corresponding prices over all base currencies (like HOOL). In this
- example, each of the (HOOL, USD) prices would see an inserted (HOOL, CAD)
- price inserted at the same date.
-
- It is common to make these projections when reducing inventories in a ledger
- that states multiple operating currency pairs, when for example, one wants
- to compute total value of a set of accounts in one of those currencies.
-
- Please note that:
-
- - Even if the target pair has existing entries, projection will still be
- applied. For example, is there exist some (HOOL, CAD) prices, the
- projection in the example above will still insert some new price points to
- it.
-
- - However, projected prices colliding existing ones at the same date will
- not override them.
-
- - Projection will fail to insert a new price if the conversion between to
- and from currencies has no existing prices (e.g. before its first price
- entry).
-
- - Perhaps most importantly, we only insert price points at dates where the
- base commodity has a price point. In other words, if we have prices for
- dates A and C and the rate changes between these dates at date B, we don't
- synthesize a new price at date B. A more accurate method to get projected
- prices that takes into account varying rates is to do multiple lookups.
- We'll eventually add a method to query it via a specified list of
- intermediate pairs. {c1bd24f8d4b7}
-
- Args:
- orig_price_map: An existing price map.
- from_currency: The quote currency with existing project points (e.g., USD).
- to_currency: The quote currency to insert price points for (e.g., CAD).
- base_currencies: An optional set of commodities to restrict the
- projections to (e.g., {HOOL}).
- Returns:
- A new price map, with the extra projected prices. The original price map
- is kept intact.
- """
- # If nothing is requested, return the original map.
- if from_currency == to_currency:
- return orig_price_map
-
- # Avoid mutating the input map.
- price_map = {key: list(value) for key, value in orig_price_map.items()}
-
- # Process the entire database (it's not indexed by quote currency).
- currency_pair = (from_currency, to_currency)
- for base_quote, prices in list(price_map.items()):
- # Filter just the currencies to convert.
- base, quote = base_quote
- if quote != from_currency:
- continue
-
- # Skip currencies not requested if a constraint has been provided.
- # {4bb702d82c8a}
- if base_currencies and base not in base_currencies:
- continue
-
- # Create a mapping of existing prices so we can avoid date collisions.
- existing_prices = (
- {date for date, _ in price_map[(base, to_currency)]}
- if (base, to_currency) in price_map
- else set()
- )
-
- # Project over each of the prices.
- new_projected = []
- for date, price in prices:
- rate_date, rate = get_price(price_map, currency_pair, date)
- if rate is None:
- # There is no conversion rate at this time; skip projection.
- # {b2b23353275d}.
- continue
- if rate_date in existing_prices:
- # Skip collisions in date. {97a5703ac517}
- continue
-
- # Append the new rate.
- new_price = price * rate
- new_projected.append((date, new_price))
-
- # Make sure the resulting lists are sorted.
- if new_projected:
- projected = price_map.setdefault((base, to_currency), [])
- projected.extend(new_projected)
- projected.sort()
-
- inverted = price_map.setdefault((to_currency, base), [])
- inverted.extend(
- (date, ZERO if rate == ZERO else ONE / rate) for date, rate in new_projected
- )
- inverted.sort()
-
- return price_map
-
def normalize_base_quote(base_quote):
+ """Convert a slash-separated string to a pair of strings.
+
+ Args:
+ base_quote: A pair of strings, the base currency to lookup, and the quote
+ currency to lookup, which expresses which units the base currency is
+ denominated in. This may also just be a string, with a '/' separator.
+ Returns:
+ A pair of strings.
+ """
+ if isinstance(base_quote, str):
+ base_quote_norm = tuple(base_quote.split('/'))
+ assert len(base_quote_norm) == 2, base_quote
+ base_quote = base_quote_norm
+ assert isinstance(base_quote, tuple), base_quote
+ return base_quote
+
beancount.core.realization.RealAccount.__eq__(self, other)
+beancount.core.realization.RealAccount.__eq__(self, other)
special
@@ -20492,21 +17174,19 @@ beancount/core/realization.py
def __eq__(self, other):
- """Equality predicate. All attributes are compared.
-
- Args:
- other: Another instance of RealAccount.
- Returns:
- A boolean, True if the two real accounts are equal.
- """
- return (
- dict.__eq__(self, other)
- and self.account == other.account
- and self.balance == other.balance
- and self.txn_postings == other.txn_postings
- )
-
def __eq__(self, other):
+ """Equality predicate. All attributes are compared.
+
+ Args:
+ other: Another instance of RealAccount.
+ Returns:
+ A boolean, True if the two real accounts are equal.
+ """
+ return (dict.__eq__(self, other) and
+ self.account == other.account and
+ self.balance == other.balance and
+ self.txn_postings == other.txn_postings)
+
beancount.core.realization.RealAccount.__init__(self, account_name, *args, **kwargs)
+beancount.core.realization.RealAccount.__init__(self, account_name, *args, **kwargs)
special
@@ -20549,18 +17229,18 @@ beancount/core/realization.py
def __init__(self, account_name, *args, **kwargs):
- """Create a RealAccount instance.
-
- Args:
- account_name: a string, the name of the account. Maybe not be None.
- """
- super().__init__(*args, **kwargs)
- assert isinstance(account_name, str)
- self.account = account_name
- self.txn_postings = []
- self.balance = inventory.Inventory()
-
def __init__(self, account_name, *args, **kwargs):
+ """Create a RealAccount instance.
+
+ Args:
+ account_name: a string, the name of the account. Maybe not be None.
+ """
+ super().__init__(*args, **kwargs)
+ assert isinstance(account_name, str)
+ self.account = account_name
+ self.txn_postings = []
+ self.balance = inventory.Inventory()
+
beancount.core.realization.RealAccount.__ne__(self, other)
+beancount.core.realization.RealAccount.__ne__(self, other)
special
@@ -20619,16 +17299,16 @@ beancount/core/realization.py
def __ne__(self, other):
- """Not-equality predicate. See __eq__.
-
- Args:
- other: Another instance of RealAccount.
- Returns:
- A boolean, True if the two real accounts are not equal.
- """
- return not self.__eq__(other)
-
def __ne__(self, other):
+ """Not-equality predicate. See __eq__.
+
+ Args:
+ other: Another instance of RealAccount.
+ Returns:
+ A boolean, True if the two real accounts are not equal.
+ """
+ return not self.__eq__(other)
+
beancount.core.realization.RealAccount.__setitem__(self, key, value)
+beancount.core.realization.RealAccount.__setitem__(self, key, value)
special
@@ -20689,28 +17369,25 @@ beancount/core/realization.py
def __setitem__(self, key, value):
- """Prevent the setting of non-string or non-empty keys on this dict.
-
- Args:
- key: The dictionary key. Must be a string.
- value: The value, must be a RealAccount instance.
- Raises:
- KeyError: If the key is not a string, or is invalid.
- ValueError: If the value is not a RealAccount instance.
- """
- if not isinstance(key, str) or not key:
- raise KeyError("Invalid RealAccount key: '{}'".format(key))
- if not isinstance(value, RealAccount):
- raise ValueError("Invalid RealAccount value: '{}'".format(value))
- if not value.account.endswith(key):
- raise ValueError(
- "RealAccount name '{}' inconsistent with key: '{}'".format(
- value.account, key
- )
- )
- return super().__setitem__(key, value)
-
def __setitem__(self, key, value):
+ """Prevent the setting of non-string or non-empty keys on this dict.
+
+ Args:
+ key: The dictionary key. Must be a string.
+ value: The value, must be a RealAccount instance.
+ Raises:
+ KeyError: If the key is not a string, or is invalid.
+ ValueError: If the value is not a RealAccount instance.
+ """
+ if not isinstance(key, str) or not key:
+ raise KeyError("Invalid RealAccount key: '{}'".format(key))
+ if not isinstance(value, RealAccount):
+ raise ValueError("Invalid RealAccount value: '{}'".format(value))
+ if not value.account.endswith(key):
+ raise ValueError("RealAccount name '{}' inconsistent with key: '{}'".format(
+ value.account, key))
+ return super().__setitem__(key, value)
+
@@ -20723,7 +17400,7 @@ beancount.core.realization.RealAccount.copy(self)
+beancount.core.realization.RealAccount.copy(self)
beancount/core/realization.py
def copy(self):
- """Override dict.copy() to clone a RealAccount.
-
- This is only necessary to correctly implement the copy method.
- Otherwise, calling .copy() on a RealAccount instance invokes the base
- class' method, which return just a dict.
-
- Returns:
- A cloned instance of RealAccount, with all members shallow-copied.
- """
- return copy.copy(self)
-
def copy(self):
+ """Override dict.copy() to clone a RealAccount.
+
+ This is only necessary to correctly implement the copy method.
+ Otherwise, calling .copy() on a RealAccount instance invokes the base
+ class' method, which return just a dict.
+
+ Returns:
+ A cloned instance of RealAccount, with all members shallow-copied.
+ """
+ return copy.copy(self)
+
beancount.core.realization.compute_balance(real_account, leaf_only=False)
+beancount.core.realization.compute_balance(real_account, leaf_only=False)
beancount/core/realization.py
def compute_balance(real_account, leaf_only=False):
- """Compute the total balance of this account and all its subaccounts.
-
- Args:
- real_account: A RealAccount instance.
- leaf_only: A boolean flag, true if we should yield only leaves.
- Returns:
- An Inventory.
- """
- return functools.reduce(
- operator.add, [ra.balance for ra in iter_children(real_account, leaf_only)]
- )
-
def compute_balance(real_account, leaf_only=False):
+ """Compute the total balance of this account and all its subaccounts.
+
+ Args:
+ real_account: A RealAccount instance.
+ leaf_only: A boolean flag, true if we should yield only leaves.
+ Returns:
+ An Inventory.
+ """
+ return functools.reduce(operator.add, [
+ ra.balance for ra in iter_children(real_account, leaf_only)])
+
beancount.core.realization.compute_postings_balance(txn_postings)
+beancount.core.realization.compute_postings_balance(txn_postings)
beancount/core/realization.py
def compute_postings_balance(txn_postings):
- """Compute the balance of a list of Postings's or TxnPosting's positions.
-
- Args:
- postings: A list of Posting instances and other directives (which are
- skipped).
- Returns:
- An Inventory.
- """
- final_balance = inventory.Inventory()
- for txn_posting in txn_postings:
- if isinstance(txn_posting, Posting):
- final_balance.add_position(txn_posting)
- elif isinstance(txn_posting, TxnPosting):
- final_balance.add_position(txn_posting.posting)
- return final_balance
-
def compute_postings_balance(txn_postings):
+ """Compute the balance of a list of Postings's or TxnPosting's positions.
+
+ Args:
+ postings: A list of Posting instances and other directives (which are
+ skipped).
+ Returns:
+ An Inventory.
+ """
+ final_balance = inventory.Inventory()
+ for txn_posting in txn_postings:
+ if isinstance(txn_posting, Posting):
+ final_balance.add_position(txn_posting)
+ elif isinstance(txn_posting, TxnPosting):
+ final_balance.add_position(txn_posting.posting)
+ return final_balance
+
@@ -20930,7 +17606,7 @@ beancount.core.realization.contains(real_account, account_name)
+beancount.core.realization.contains(real_account, account_name)
beancount/core/realization.py
def contains(real_account, account_name):
- """True if the given account node contains the subaccount name.
-
- Args:
- account_name: A string, the name of a direct or indirect subaccount of
- this node.
- Returns:
- A boolean, true the name is a child of this node.
- """
- return get(real_account, account_name) is not None
-
def contains(real_account, account_name):
+ """True if the given account node contains the subaccount name.
+
+ Args:
+ account_name: A string, the name of a direct or indirect subaccount of
+ this node.
+ Returns:
+ A boolean, true the name is a child of this node.
+ """
+ return get(real_account, account_name) is not None
+
beancount.core.realization.dump(root_account)
+beancount.core.realization.dump(root_account)
beancount/core/realization.py
def dump(root_account):
- """Convert a RealAccount node to a line of lines.
-
- Note: this is not meant to be used to produce text reports; the reporting
- code should produce an intermediate object that contains the structure of
- it, which can then be rendered to ASCII, HTML or CSV formats. This is
- intended as a convenient little function for dumping trees of data for
- debugging purposes.
-
- Args:
- root_account: A RealAccount instance.
- Returns:
- A list of tuples of (first_line, continuation_line, real_account) where
- first_line: A string, the first line to render, which includes the
- account name.
- continuation_line: A string, further line to render if necessary.
- real_account: The RealAccount instance which corresponds to this
- line.
- """
- # Compute all the lines ahead of time in order to calculate the width.
- lines = []
-
- # Start with the root node. We push the constant prefix before this node,
- # the account name, and the RealAccount instance. We will maintain a stack
- # of children nodes to render.
- stack = [("", root_account.account, root_account, True)]
- while stack:
- prefix, name, real_account, is_last = stack.pop(-1)
-
- if real_account is root_account:
- # For the root node, we don't want to render any prefix.
- first = cont = ""
- else:
- # Compute the string that precedes the name directly and the one below
- # that for the continuation lines.
- # |
- # @@@ Bank1 <----------------
- # @@@ |
- # | |-- Checking
- if is_last:
- first = prefix + PREFIX_LEAF_1
- cont = prefix + PREFIX_LEAF_C
- else:
- first = prefix + PREFIX_CHILD_1
- cont = prefix + PREFIX_CHILD_C
-
- # Compute the name to render for continuation lines.
- # |
- # |-- Bank1
- # | @@@ <----------------
- # | |-- Checking
- if len(real_account) > 0:
- cont_name = PREFIX_CHILD_C
- else:
- cont_name = PREFIX_LEAF_C
-
- # Add a line for this account.
- if not (real_account is root_account and not name):
- lines.append((first + name, cont + cont_name, real_account))
-
- # Push the children onto the stack, being careful with ordering and
- # marking the last node as such.
- child_items = sorted(real_account.items(), reverse=True)
- if child_items:
- child_iter = iter(child_items)
- child_name, child_account = next(child_iter)
- stack.append((cont, child_name, child_account, True))
- for child_name, child_account in child_iter:
- stack.append((cont, child_name, child_account, False))
-
- if not lines:
- return lines
-
- # Compute the maximum width of the lines and convert all of them to the same
- # maximal width. This makes it easy on the client.
- max_width = max(len(first_line) for first_line, _, __ in lines)
- line_format = "{{:{width}}}".format(width=max_width)
- return [
- (line_format.format(first_line), line_format.format(cont_line), real_node)
- for (first_line, cont_line, real_node) in lines
- ]
-
def dump(root_account):
+ """Convert a RealAccount node to a line of lines.
+
+ Note: this is not meant to be used to produce text reports; the reporting
+ code should produce an intermediate object that contains the structure of
+ it, which can then be rendered to ASCII, HTML or CSV formats. This is
+ intended as a convenient little function for dumping trees of data for
+ debugging purposes.
+
+ Args:
+ root_account: A RealAccount instance.
+ Returns:
+ A list of tuples of (first_line, continuation_line, real_account) where
+ first_line: A string, the first line to render, which includes the
+ account name.
+ continuation_line: A string, further line to render if necessary.
+ real_account: The RealAccount instance which corresponds to this
+ line.
+ """
+ # Compute all the lines ahead of time in order to calculate the width.
+ lines = []
+
+ # Start with the root node. We push the constant prefix before this node,
+ # the account name, and the RealAccount instance. We will maintain a stack
+ # of children nodes to render.
+ stack = [('', root_account.account, root_account, True)]
+ while stack:
+ prefix, name, real_account, is_last = stack.pop(-1)
+
+ if real_account is root_account:
+ # For the root node, we don't want to render any prefix.
+ first = cont = ''
+ else:
+ # Compute the string that precedes the name directly and the one below
+ # that for the continuation lines.
+ # |
+ # @@@ Bank1 <----------------
+ # @@@ |
+ # | |-- Checking
+ if is_last:
+ first = prefix + PREFIX_LEAF_1
+ cont = prefix + PREFIX_LEAF_C
+ else:
+ first = prefix + PREFIX_CHILD_1
+ cont = prefix + PREFIX_CHILD_C
+
+ # Compute the name to render for continuation lines.
+ # |
+ # |-- Bank1
+ # | @@@ <----------------
+ # | |-- Checking
+ if len(real_account) > 0:
+ cont_name = PREFIX_CHILD_C
+ else:
+ cont_name = PREFIX_LEAF_C
+
+ # Add a line for this account.
+ if not (real_account is root_account and not name):
+ lines.append((first + name,
+ cont + cont_name,
+ real_account))
+
+ # Push the children onto the stack, being careful with ordering and
+ # marking the last node as such.
+ child_items = sorted(real_account.items(), reverse=True)
+ if child_items:
+ child_iter = iter(child_items)
+ child_name, child_account = next(child_iter)
+ stack.append((cont, child_name, child_account, True))
+ for child_name, child_account in child_iter:
+ stack.append((cont, child_name, child_account, False))
+
+ if not lines:
+ return lines
+
+ # Compute the maximum width of the lines and convert all of them to the same
+ # maximal width. This makes it easy on the client.
+ max_width = max(len(first_line) for first_line, _, __ in lines)
+ line_format = '{{:{width}}}'.format(width=max_width)
+ return [(line_format.format(first_line),
+ line_format.format(cont_line),
+ real_node)
+ for (first_line, cont_line, real_node) in lines]
+
beancount.core.realization.dump_balances(real_root, dformat, at_cost=False, fullnames=False, file=None)
+beancount.core.realization.dump_balances(real_root, dformat, at_cost=False, fullnames=False, file=None)
beancount/core/realization.py
def dump_balances(real_root, dformat, at_cost=False, fullnames=False, file=None):
- """Dump a realization tree with balances.
-
- Args:
- real_root: An instance of RealAccount.
- dformat: An instance of DisplayFormatter to format the numbers with.
- at_cost: A boolean, if true, render the values at cost.
- fullnames: A boolean, if true, don't render a tree of accounts and
- render the full account names.
- file: A file object to dump the output to. If not specified, we
- return the output as a string.
- Returns:
- A string, the rendered tree, or nothing, if 'file' was provided.
- """
- if fullnames:
- # Compute the maximum account name length;
- maxlen = max(
- len(real_child.account)
- for real_child in iter_children(real_root, leaf_only=True)
- )
- line_format = "{{:{width}}} {{}}\n".format(width=maxlen)
- else:
- line_format = "{} {}\n"
-
- output = file or io.StringIO()
- for first_line, cont_line, real_account in dump(real_root):
- if not real_account.balance.is_empty():
- if at_cost:
- rinv = real_account.balance.reduce(convert.get_cost)
- else:
- rinv = real_account.balance.reduce(convert.get_units)
- amounts = [position.units for position in rinv.get_positions()]
- positions = [
- amount_.to_string(dformat)
- for amount_ in sorted(amounts, key=amount.sortkey)
- ]
- else:
- positions = [""]
-
- if fullnames:
- for position in positions:
- if not position and len(real_account) > 0:
- continue # Skip parent accounts with no position to render.
- output.write(line_format.format(real_account.account, position))
- else:
- line = first_line
- for position in positions:
- output.write(line_format.format(line, position))
- line = cont_line
-
- if file is None:
- return output.getvalue()
-
def dump_balances(real_root, dformat, at_cost=False, fullnames=False, file=None):
+ """Dump a realization tree with balances.
+
+ Args:
+ real_root: An instance of RealAccount.
+ dformat: An instance of DisplayFormatter to format the numbers with.
+ at_cost: A boolean, if true, render the values at cost.
+ fullnames: A boolean, if true, don't render a tree of accounts and
+ render the full account names.
+ file: A file object to dump the output to. If not specified, we
+ return the output as a string.
+ Returns:
+ A string, the rendered tree, or nothing, if 'file' was provided.
+ """
+ if fullnames:
+ # Compute the maximum account name length;
+ maxlen = max(len(real_child.account)
+ for real_child in iter_children(real_root, leaf_only=True))
+ line_format = '{{:{width}}} {{}}\n'.format(width=maxlen)
+ else:
+ line_format = '{} {}\n'
+
+ output = file or io.StringIO()
+ for first_line, cont_line, real_account in dump(real_root):
+ if not real_account.balance.is_empty():
+ if at_cost:
+ rinv = real_account.balance.reduce(convert.get_cost)
+ else:
+ rinv = real_account.balance.reduce(convert.get_units)
+ amounts = [position.units for position in rinv.get_positions()]
+ positions = [amount_.to_string(dformat)
+ for amount_ in sorted(amounts, key=amount.sortkey)]
+ else:
+ positions = ['']
+
+ if fullnames:
+ for position in positions:
+ if not position and len(real_account) > 0:
+ continue # Skip parent accounts with no position to render.
+ output.write(line_format.format(real_account.account, position))
+ else:
+ line = first_line
+ for position in positions:
+ output.write(line_format.format(line, position))
+ line = cont_line
+
+ if file is None:
+ return output.getvalue()
+
beancount.core.realization.filter(real_account, predicate)
+beancount.core.realization.filter(real_account, predicate)