Skip to content

Introduction

The bdantic Python package provides an interface between Beancount and Pydantic. It accomplishes this by providing models which can be parsed directly from native Beancount types. In addition to this, all models can be exported back into their native Beancount types for full compatibility.

Installation

pip install bdantic

Usage

Parsing

A handful of functions are provided for parsing Beancount types, but the primary method supports parsing most core types:

import bdantic

from beancount.core import amount
from decimal import Decimal

amt = amount.Amount(number=Decimal(1.50), currency="USD"))
model = bdantic.parse(amt) # Produces a bdantic.models.Amount

Alternatively, you can call the parse method on the model directly:

from bdantic.models import Amount
from beancount.core import amount
from decimal import Decimal

amt = amount.Amount(number=Decimal(1.50), currency="USD"))
model = Amount.parse(amt)

Exporting

All models can be directly exported back to their native Beancount types by using their bult-in export method:

amt_export = model.export()
assert amt == amt_export

Ingesting

Functions are available for parsing common responses from interacting with the Beancount package. You can parse an entire Beancount file with the following:

import bdantic

from beancount import loader

# A bdantic.models.BeancountFile instance
bfile = bdantic.parse_loader(*loader.load_file("ledger.beancount"))
print(len(bfile.entries))

You can also parse the response from executing a query:

import bdantic

from beancount import loader
from beancount.query import query

entries, _, options = loader.load_file("ledger.beancount")

query = "SELECT date, narration, account, position"
result = query.run_query(entries, options, query)
parsed_result = bdantic.parse_query(result)

Or the result of running a realization:

import bdantic

from beancount.core import realization

entries, _, options = loader.load_file("ledger.beancount")

real = realization.realize(entries)
parsed_real = bdantic.parse(real)

Rendering

Perhaps the most powerful usage of bdantic is rendering beancount data into a more universal format like JSON. Since all models inherit from Pydantic they include full support for rendering their contents as JSON:

import bdantic

from beancount import loader

bfile = bdantic.parse_loader(*loader.load_file("ledger.beancount"))
js = bfile.json()
print(js) # Look ma, my beancount data in JSON!

The rendered JSON can be parsed back into the Beancount model that generated it:

from bdantic.models import BeancountFile

bfile = BeancountFile.parse_raw(js)

In additiona to JSON, the directive models can be rendered as valid Beancount syntax using the built-in syntax method:

from bdantic.models import Amount, Posting, Transaction
from datetime import date
from decimal import Decimal

txn = Transaction(
    date=date.today(),
    meta={},
    flag="*",
    payee="Home Depot",
    narration="Tools n stuff",
    tags=None,
    links=None,
    postings=[
        Posting(
            account="Assets:Bank:Cash",
            units=Amount(number=Decimal(-142.32), currency="USD"),
            cost=None,
            CostSpec=None,
            flag=None,
            meta={},
        ),
        Posting(
            account="Expenses:HomeDepot",
            units=Amount(number=Decimal(142.32), currency="USD"),
            cost=None,
            CostSpec=None,
            flag=None,
            meta={},
        ),
    ],
)

print(txn.syntax())

Advanced

For more documentation, see the navigation sections to the left.