Skip to main content

Call COBOL programs as Python functions — file I/O handled for you

Project description

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. Compilecobc -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

Project details


Download files

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

Source Distribution

pobol-0.1.1.tar.gz (15.8 kB view details)

Uploaded Source

Built Distribution

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

pobol-0.1.1-py3-none-any.whl (18.9 kB view details)

Uploaded Python 3

File details

Details for the file pobol-0.1.1.tar.gz.

File metadata

  • Download URL: pobol-0.1.1.tar.gz
  • Upload date:
  • Size: 15.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pobol-0.1.1.tar.gz
Algorithm Hash digest
SHA256 f910af1191f034378a84877bbb6ac961ca730390475cd7e3df3c64078e72ee18
MD5 089e00a2b0f3f8483935b38a3c5258ed
BLAKE2b-256 abbfc47150a82913fed89818be07cde7d1203b11c0e37f97242af3ac907540c5

See more details on using hashes here.

Provenance

The following attestation bundles were made for pobol-0.1.1.tar.gz:

Publisher: ci.yml on andyreagan/pobol

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pobol-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: pobol-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 18.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pobol-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 55e80e0436f640e9209ee06a2f38ebde1a02e30d0547bf2669a246cd99f5ca1c
MD5 12f73057c5153ae2d8aaa3ccfde65b6b
BLAKE2b-256 0d537e6cd2168197b1bd8e2fd44d949fe5bee68976dbd297b7f0911919494a39

See more details on using hashes here.

Provenance

The following attestation bundles were made for pobol-0.1.1-py3-none-any.whl:

Publisher: ci.yml on andyreagan/pobol

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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