Skip to main content

pobol

CI PyPI version

Call COBOL programs as Python functions. Like maturin for Rust, but for GnuCOBOL.

Motivation: You're migrating COBOL off a mainframe. The programs do real work you want to keep, but they expect VSAM files, DD names, and JCL — none of which exist on Linux/macOS. pobol wraps cobc (GnuCOBOL) so you can:

  1. Point it at a .cbl file — it parses the source and discovers everything
  2. Pass Python dicts as input records
  3. Get Python dicts back from the output files

No copybook transcription, no temp-file juggling, no batch scripts.

Quick Start

uv add pobol          # or: pip install pobol

Zero-config mode (auto-discovery)

from pobol import load

# pobol parses the COBOL source, discovers SELECT/FD clauses,
# extracts record layouts, strips mainframe artifacts, compiles, and
# handles all file I/O automatically.
report = load("customer_report.cob")

print(report.file_info)
# COBOL Program: customer_report.cob
#
# INPUTS:
#   INPUT-FILE (45 bytes): IN-CUST-ID, IN-CUST-NAME, IN-BALANCE
# OUTPUTS:
#   OUTPUT-FILE (54 bytes): OUT-CUST-ID, OUT-CUST-NAME, OUT-BALANCE, OUT-DISCOUNT

result = report(
    input_file=[
        {"IN-CUST-ID": 1, "IN-CUST-NAME": "Alice", "IN-BALANCE": 2500.00},
        {"IN-CUST-ID": 2, "IN-CUST-NAME": "Bob", "IN-BALANCE": 800.00},
    ]
)

for rec in result.output_file:
    print(rec)
# {'out_cust_id': 1, 'out_cust_name': 'Alice', 'out_balance': 2500.0, 'out_discount': 250.0}
# {'out_cust_id': 2, 'out_cust_name': 'Bob',   'out_balance':  800.0, 'out_discount':   0.0}

Mainframe source — works directly

# Point at raw mainframe COBOL with sequence numbers, LABEL RECORDS,
# IBM-370, DD-name assigns — pobol handles it all:
prog = load("check_disbursement.cbl")

print(prog.file_info)
# Discovers all 6 files, 4 record layouts under one FD, 60+ fields...
# Strips sequence numbers, rewrites ASSIGN TO for env-var mapping,
# removes LABEL RECORDS/RECORDING MODE/BLOCK CONTAINS.

Explicit copybooks (optional override)

from pobol import load, parse_copybook

input_cb = parse_copybook("""
    01  INPUT-RECORD.
        05  CUST-ID     PIC 9(6).
        05  CUST-NAME   PIC X(30).
        05  BALANCE     PIC 9(7)V9(2).
""")
output_cb = parse_copybook("""
    01  OUTPUT-RECORD.
        05  CUST-ID     PIC 9(6).
        05  CUST-NAME   PIC X(30).
        05  BALANCE     PIC 9(7)V9(2).
        05  DISCOUNT    PIC 9(7)V9(2).
""")

report = load(
    "customer_report.cob",
    inputs={"INPUT-FILE": input_cb},
    outputs={"OUTPUT-FILE": output_cb},
)

How It Works

                                                        ┌──────────────────┐
  Python dict ──▶ Copybook.encode() ──▶ temp file ──▶  │                  │
                                                        │  cobc-compiled   │
  Python dict ◀── Copybook.decode() ◀── temp file ◀──  │  COBOL program   │
                                                        │                  │
                                                        └──────────────────┘
  1. Parse source — discovers SELECT/FD/OPEN clauses, extracts PIC field layouts, detects input vs output files
  2. Strip mainframe — removes sequence numbers (cols 1-6), identification (cols 73-80), LABEL RECORDS, RECORDING MODE, BLOCK CONTAINS, fixes SOURCE-COMPUTER
  3. Rewrite assigns — converts ASSIGN TO DATAIN (DD names) to working-storage paths loaded from DD_* environment variables
  4. Compile — cobc -x compiles to a native executable (cached by content hash)
  5. Write inputs — your Python dicts are encoded as fixed-width records to temp files
  6. Run — the executable runs with DD_* env vars pointing to temp files
  7. Read outputs — output temp files are decoded back into Python dicts

Handling Multiple Record Types

Real mainframe COBOL often has multiple 01-level records under one FD (header, detail, trailer). pobol discovers all of them:

prog = load("check_disbursement.cbl")

# See all discovered layouts
for file_name, layouts in prog.record_layouts.items():
    for rec_name, copybook in layouts.items():
        print(f"{file_name}/{rec_name}: {len(copybook.fields)} fields")
# TXN-DATA-FILE/TXN-DATA-RECORD: 2 fields
# TXN-DATA-FILE/TXN-DATA-HDR-RECORD: 5 fields
# TXN-DATA-FILE/TXN-DATA-DETAIL-RECORD: 60 fields
# TXN-DATA-FILE/TXN-DATA-TRLR-RECORD: 5 fields

# For multi-record files, use raw_files with pre-encoded bytes:
header_bytes = header_copybook.encode(header_dict)
detail_bytes = detail_copybook.encode_many(detail_dicts)
trailer_bytes = trailer_copybook.encode(trailer_dict)

result = prog(
    raw_files={
        "TXN-DATA-FILE": header_bytes + detail_bytes + trailer_bytes,
    }
)

API Reference

load(source, **kwargs) → CobolProgram

Compile a COBOL source file and return a callable.

Parameter Type Default Description
source str | Path required Path to .cob/.cbl file
inputs dict[str, Copybook] None Override auto-discovered input layouts
outputs dict[str, Copybook] None Override auto-discovered output layouts
dialect str None GnuCOBOL -std= dialect
extra_flags list[str] None Extra cobc flags
strip_mainframe bool True Strip mainframe format artifacts
rewrite_assigns bool True Rewrite ASSIGN TO for env-var mapping

CobolProgram.__call__(**kwargs) → CobolResult

Parameter Type Description
stdin str Data for ACCEPT/stdin
env dict Extra environment variables
timeout float Execution timeout (default 30s)
raw_files dict[str, bytes] Pre-encoded file data by SELECT name
**file_inputs list[dict] Input data as list of dicts, keyed by SELECT name

CobolResult

Attribute Type Description
.stdout str Program stdout (DISPLAY output)
.stderr str Program stderr
.return_code int Exit code
.outputs dict[str, list[dict]] Decoded output files
.output_file list[dict] Shorthand for .outputs["OUTPUT-FILE"]

parse_cobol_source(source, **kwargs) → ParsedSource

Parse without compiling. Returns discovered files, record layouts, and cleaned source.

Supported PIC Clauses

PIC Python type Notes
X(n), XX str Left-justified, space-padded
9(n), 999 int Zero-padded
S9(n) int Trailing sign overpunch
9(n)V9(m), 9(5)V99 float Implied decimal (mixed forms supported)
S9(n)V9(m) float Signed implied decimal

Development

uv sync
uv run pytest -v
uv run python examples/demo.py

Prerequisites

  • GnuCOBOL (cobc) — brew install gnucobol / apt install gnucobol
  • Python 3.11+

License

MIT

Metadata

Release files for pobol 0.1.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pobol 0.1.3
File Size Uploaded Provenance
pobol-0.1.3.tar.gz 16.0 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for pobol 0.1.3
File Interpreter ABI Platform
pobol-0.1.3-py3-none-any.whl Python 3 none any Details

Total release size: 34.6 kB

Release files / pobol-0.1.3.tar.gz

Download URL pobol-0.1.3.tar.gz
Size 16.0 kB
Tags Source
SHA-256 checksum
How to use checksums
5934be4e9376a5e35a3f454da096e317b17c1b6229c77cee84c39d1d52f43555
BLAKE2b-256 checksum
How to use checksums
7f85ed5af36bd26d9262f724c594ce590c30a020c361fd82b87e7dfa16edac2f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.

Transparency log

Release files / pobol-0.1.3-py3-none-any.whl

Download URL pobol-0.1.3-py3-none-any.whl
Size 18.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4925233858ec077246afe631cb3454c5f40293933ec31078882aa5fd86d977d8
BLAKE2b-256 checksum
How to use checksums
22ac46d743dd335f8e39012ae3fca9ec51d2d60ad12a1c9e0d36973ee2c330de
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.

Transparency log

Security for pobol 0.1.3

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for pobol-0.1.3.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 29, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page