Skip to main content

Bursar for Python

Python 3.12 and 3.13 are supported.

pip install bursar[postgres]
DATABASE_URL=postgresql://... bursar migrate

To install trusted host-owned database objects after Bursar, pass one or more repeatable integration files:

DATABASE_URL=postgresql://... \
  bursar migrate --post-migrate-sql ./host-integration.sql

The files run in order, after Bursar's pending migrations and in the same transaction. They run on every invocation, so they must be idempotent.

Create the reusable facade after migrations have run:

from bursar import Bursar, PostgresStore

store = PostgresStore(database_url, tenant_id=tenant_id)
bursar = Bursar.create(credit_store=store)

bursar.credits owns account operations:

grant = bursar.credits.add_credits(
    user_id,
    500,
    entry_type="purchase",
    idempotency_key="checkout:42",
)
charge = bursar.credits.deduct_credits(
    user_id,
    20,
    idempotency_key="job:42",
)
refund = bursar.credits.refund_credits(charge.entry_id)

page = bursar.credits.list_ledger_entries(user_id, limit=25)
while page.next_cursor is not None:
    page = bursar.credits.list_ledger_entries(
        user_id, limit=25, cursor=page.next_cursor
    )

LedgerEntry, LedgerCursor, and LedgerPage are exported from bursar. Pagination is cursor-only. Usage history is available through list_usage_entries; one entry is available through get_ledger_entry.

PostgresStore is the only store, and both it and the CreditStore base class are imported directly from the top-level package:

  • from bursar import PostgresStore — the production, tenant-scoped store
  • from bursar import CreditStore — the abstract base for custom implementations

Use bursar.catalog.publish_and_activate(config) for a canonical document with pricing, credits, entitlements, admission, plans, commerce, and catalog. The optional billing service and auto-recharge policy read that same active document.

Stores and Bursar do not install database objects. Deployment is:

DATABASE_URL -> bursar migrate -> publish canonical config -> start app

Optional S3 and ClickHouse storage

create_bursar_runtime is the Python composition root when Bursar should manage optional storage projections. PostgreSQL remains authoritative for balances, leases, billing state, and the transactional outbox.

With no extra infrastructure, it creates no background worker and analytics continue to query PostgreSQL:

import os

from bursar.storage import BursarRuntimeOptions, create_bursar_runtime

runtime = create_bursar_runtime(
    BursarRuntimeOptions(
        postgres=os.environ["DATABASE_URL"],
        tenant_id=os.environ["BURSAR_TENANT_ID"],
    )
)
runtime.start()

bursar = runtime.bursar

Install bursar[postgres,s3] and add S3 connection settings when an archive is needed. ClickHouse remains structurally injected:

import os

import clickhouse_connect

from bursar.storage import (
    BursarRuntimeOptions,
    ClickHouseUsageStoreOptions,
    S3BillingArchiveOptions,
    S3Credentials,
    create_bursar_runtime,
)

clickhouse_client = clickhouse_connect.get_client(
    dsn=os.environ["CLICKHOUSE_URL"]
)

runtime = create_bursar_runtime(
    BursarRuntimeOptions(
        postgres=os.environ["DATABASE_URL"],
        s3=S3BillingArchiveOptions(
            bucket=os.environ["BURSAR_S3_BUCKET"],
            region=os.environ["BURSAR_S3_REGION"],
            endpoint=os.getenv("BURSAR_S3_ENDPOINT"),
            force_path_style=os.getenv("BURSAR_S3_FORCE_PATH_STYLE") == "true",
            credentials=S3Credentials(
                access_key_id=os.environ["BURSAR_S3_ACCESS_KEY_ID"],
                secret_access_key=os.environ["BURSAR_S3_SECRET_ACCESS_KEY"],
            ),
        ),
        clickhouse=ClickHouseUsageStoreOptions(
            client=clickhouse_client,
            # Optional; omit to retain the projection indefinitely.
            retention_days=730,
        ),
    )
)

runtime.start()
# Use runtime.bursar in the application.
# On graceful shutdown:
runtime.close()

External writes happen through a leased PostgreSQL outbox, never in a customer request. S3 object keys are deterministic and the ClickHouse projection is replay-safe. ClickHouse analytics are therefore eventually consistent. PostgreSQL payload retention should be at least as long as the outbox retry horizon, which is enforced by the SQL storage configuration.

Set outbox=False only when a separate process consumes the Bursar outbox. Database retention maintenance remains independent: schedule bursar.maybe_run_storage_maintenance() and partition maintenance with pg_cron as described in the SQL README.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

bursar-1.0.0.tar.gz (339.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

bursar-1.0.0-py3-none-any.whl (330.6 kB view details)

Uploaded Python 3

File details

Details for the file bursar-1.0.0.tar.gz.

File metadata

  • Download URL: bursar-1.0.0.tar.gz
  • Upload date:
  • Size: 339.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for bursar-1.0.0.tar.gz
Algorithm Hash digest
SHA256 b66a08fa2733316dcf6be1494a15464c75deee6f388c46d6a53d3a04676cfad1
MD5 1686e30ea0dc248dbf122a252e78e010
BLAKE2b-256 8098a79ec8474af1547490299427a065febc13a27d6aaf791e76f2aca2dcad55

See more details on using hashes here.

File details

Details for the file bursar-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: bursar-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 330.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for bursar-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 cd105179248ea7f0c8f088a439b867de169a97f13123a1fee4f92b5324e97464
MD5 c691478cd5665e69b06b3d06a7a34105
BLAKE2b-256 67a7255c0b48df4719aa36977d5e7bd6c5b0aa012ed46ab87deaf809d858d579

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page