If you have worked with IBM Mainframe, you probably know how to read datasets using ISPF, SORT utilities, COBOL programs, or DB2 unload processes.
But have you ever wondered:
Can we read a Mainframe dataset directly using Python?
Yes! And the interesting part is that IBM provides its own Python package to interact with Mainframe datasets.
In this article, we will explore how to read Mainframe datasets using Python on IBM z/OS with the help of zoautil_py, an IBM-maintained Python package available through ZOAU.
We will cover:
- What is
zoautil_py? - How to read a Mainframe dataset using Python?
- Bulk reading vs stream reading
- Reading datasets in text mode
- Reading datasets in binary mode
- Understanding EBCDIC encoding
- Which approach should you use?
Let’s get started!
1. What is zoautil_py?
zoautil_py is an IBM-provided Python package that allows developers to interact with z/OS resources.
It is part of ZOAU (z/OS Open Automation Utilities).
Using this package, Python programs can perform operations such as:
- Reading Mainframe datasets
- Writing Mainframe datasets
- Creating datasets
- Listing datasets
- Listing dataset members
- Searching dataset contents
- Performing other dataset-related operations
For this article, we will focus specifically on reading dataset contents.
The package provides two important modules:
| Module | Purpose |
|---|---|
zoautil_py.datasets |
Convenient dataset operations, including bulk reading |
zoautil_py.zoau_io |
Stream-based dataset access, including record-by-record reading |
Official IBM documentation:
2. Prerequisites
Before writing Python code, make sure the following requirements are satisfied.
- Python is installed and configured on your IBM z/OS system.
- ZOAU is installed and available in your environment.
- The
zoautil_pypackage is accessible from your Python runtime. - Your user ID has permission to access the required dataset.
- You are executing Python within the appropriate z/OS environment.
For example, let’s assume we have the following Mainframe dataset:
ANKIT.TEST.INPUT
And it contains:
101|ANKIT|DATA ENGINEER
102|RAHUL|DEVELOPER
103|PRIYA|ANALYST
104|AMIT|TESTER
Our goal is to read these records using Python.
3. Method 1: Read a Mainframe Dataset Using Bulk Mode
The first approach is to read the dataset contents in bulk.
In simple terms:
Bulk mode means reading the dataset contents into Python in one operation instead of processing each record individually as it is read.
This is convenient when the dataset is relatively small and you want to work with its complete contents.
3.1 Reading Dataset in Text Mode
Let’s start with the simplest example.
from zoautil_py import datasets
dataset_name = "ANKIT.TEST.INPUT"
data = datasets.read(dataset_name)
print(data)
Output:
101|ANKIT|DATA ENGINEER
102|RAHUL|DEVELOPER
103|PRIYA|ANALYST
104|AMIT|TESTER
That’s it!
We have successfully read a Mainframe dataset using Python.
How Does This Work?
Let’s understand the code.
from zoautil_py import datasets
This imports the dataset-related functionality from IBM’s Python package.
data = datasets.read(dataset_name)
This reads the contents of the dataset and returns the result as a Python string.
You can then perform operations such as:
- Printing the data
- Splitting records
- Applying transformations
- Searching for specific values
- Processing the data further
3.2 Read Only a Few Records
Sometimes we don’t need the complete dataset.
For example, we may want to read only the first 5 records.
from zoautil_py import datasets
dataset_name = "ANKIT.TEST.INPUT"
data = datasets.read_head(
dataset_name,
nlines=5
)
print(data)
This is useful when you want to inspect a dataset before processing it.
3.3 Read a Dataset Using Bulk Mode in Binary
What if we don’t want Python to decode the dataset contents into text?
For example:
- The dataset contains binary data.
- You want to inspect the actual bytes.
- You want to perform byte-level processing.
- You want to control the encoding yourself.
In such situations, we can use <code>read_as_bytes()</code>.
from zoautil_py import datasets dataset_name = "ANKIT.TEST.INPUT" data = datasets.read_as_bytes(dataset_name) print(data)
Output will be similar to:
[
b'101|ANKIT|DATA ENGINEER',
b'102|RAHUL|DEVELOPER',
b'103|PRIYA|ANALYST',
b'104|AMIT|TESTER'
]
Notice something interesting!
The output is a list of Python byte objects rather than a normal string.
Each item represents the bytes of a dataset record.
How to Decode the Data?
If you know that the dataset contains EBCDIC data encoded using code page 1142, you can decode individual records.
from zoautil_py import datasets
dataset_name = "ANKIT.TEST.INPUT"
data = datasets.read_as_bytes(dataset_name)
for record in data:
decoded_record = record.decode("cp1142")
print(decoded_record)
Output:
101|ANKIT|DATA ENGINEER
102|RAHUL|DEVELOPER
103|PRIYA|ANALYST
104|AMIT|TESTER
Important: Use the correct code page for your dataset. cp1142 is only an example and may not match every Mainframe environment.
4. Method 2: Read a Mainframe Dataset Using Stream Mode
Now let’s understand another approach.
Instead of reading the complete dataset into memory, we can read it record by record.
This approach is useful when:
- The dataset contains millions of records.
- You want to process records individually.
- You don’t want to load the entire dataset into memory.
- You want to perform transformations while reading.
For this approach, we use:
zoautil_py.zoau_io
The important function is:
zopen()
It allows us to open a Mainframe dataset as a record stream.
4.1 Read Dataset Record by Record in Binary Mode
Let’s start with binary reading.
from zoautil_py.zoau_io import zopen
dataset_name = "ANKIT.TEST.INPUT"
with zopen(f"//'{dataset_name}'", "rb") as dataset:
for record in dataset:
print(record)
or
with zopen(f"//'{dataset_name}'", "rb") as dataset:lrecl = dataset.maxreclenwhile True:raw_line = dataset.read(lrecl)print(raw_line)
Output:
b'101|ANKIT|DATA ENGINEER'
b'102|RAHUL|DEVELOPER'
b'103|PRIYA|ANALYST'
b'104|AMIT|TESTER'
Notice that every record is returned as a byte object.
Understanding the Code
zopen(f"//'{dataset_name}'", "rb")
This opens the Mainframe dataset for reading.
The dataset name is specified using the // prefix and quoted dataset name.
for record in dataset:
This iterates over the dataset records one by one.
You don’t need to read the entire dataset into memory before starting the processing.
Why Is This Useful?
Imagine your dataset contains 10 million records.
Instead of storing all 10 million records in a Python list, you can process each record individually. Here we will also use the noseek parameter of this zopen method.
noseek (bool) – Indicates that the stream may not use repositioning functions.
-
- If true, the system tries to open the file in QSAM (queued sequential access method). This access method generally provides the best performance.
- If the parameter is omitted or false, the BSAM (basic sequential access method) is used.
For example:
from zoautil_py.zoau_io import zopen
dataset_name = "ANKIT.TEST.INPUT"
record_count = 0
with zopen(f"//'{dataset_name}'", "rb", noseek=True) as dataset:
for record in dataset:
record_count += 1
print(record)
print("Total Records:", record_count)
or
with zopen(f"//'{dataset_name}'", "rb", noseek=True) as dataset:lrecl = dataset.maxreclenwhile True:raw_line = dataset.read(lrecl)record_count += 1print("Total Records:", record_count)
This approach can be much more memory-efficient than bulk reading.
5. Read Dataset Record by Record in Text Mode
So far, we have read records in binary mode.
But what if we want normal Python strings instead of bytes?
We can specify the encoding while opening the dataset.
For example:
from zoautil_py.zoau_io import zopen
dataset_name = "ANKIT.TEST.INPUT"
with zopen(
f"//'{dataset_name}'",
"r",
"cp1142"
) as dataset:
for record in dataset:
print(record)
Output:
101|ANKIT|DATA ENGINEER
102|RAHUL|DEVELOPER
103|PRIYA|ANALYST
104|AMIT|TESTER
Now each record is returned as a Python string.
What Is Happening Internally?
The text wrapper performs the required decoding operation.
Conceptually:
Mainframe Dataset
|
v
EBCDIC Bytes
|
v
Decode Using cp1142
|
v
Python String
This makes it much easier to perform normal string operations.
For example:
from zoautil_py.zoau_io import zopen
dataset_name = "ANKIT.TEST.INPUT"
with zopen(
f"//'{dataset_name}'",
"r",
"cp1142"
) as dataset:
for record in dataset:
if "ANKIT" in record:
print(record)
Output:
101|ANKIT|DATA ENGINEER
This is a simple example of filtering dataset records while reading them.
6. Bulk Mode vs Stream Mode
Let’s compare both approaches.
| Feature | Bulk Mode | Stream Mode |
|---|---|---|
| API | datasets.read() / read_as_bytes() |
zoau_io.zopen() |
| Reading approach | Read complete content | Read record by record |
| Memory usage | Can be higher | Usually lower for sequential processing |
| Processing | After loading content | While reading |
| Suitable for | Small datasets | Large datasets |
| Text mode | Supported | Supported |
| Binary mode | Supported | Supported |
| Record-by-record processing | Can be done after loading | Natural approach |
Which One Should You Use?
If the dataset is small and you need the complete content for processing, bulk reading is convenient.
If the dataset is large and you want to process records individually, stream reading is generally a better choice.
For large Mainframe datasets, I would usually prefer stream-based processing when the transformation can be performed record by record.
7. Text Mode vs Binary Mode
This is another important concept when working with Mainframe datasets.
| Mode | Result | Use Case |
|---|---|---|
| Text Mode | Python strings | Normal character data processing |
| Binary Mode | Python bytes | Byte-level processing and encoding control |
Text Mode
Example:
data = datasets.read("ANKIT.TEST.INPUT")
Or:
with zopen("//'ANKIT.TEST.INPUT'", "r", "cp1142") as dataset:
for record in dataset:
print(record)
Text mode is useful when you want to:
- Split fields
- Compare strings
- Apply transformations
- Search values
- Process character-based records
Binary Mode
Example:
data = datasets.read_as_bytes("ANKIT.TEST.INPUT")
Or:
with zopen("//'ANKIT.TEST.INPUT'", "rb") as dataset:
for record in dataset:
print(record)
Binary mode is useful when you want to:
- Inspect raw bytes
- Preserve original byte representations
- Handle encoding manually
- Process binary or non-text data
8. A Practical Example: Read and Transform Mainframe Dataset Records
Let’s combine everything into one practical example.
Suppose we have the following dataset:
ANKIT.TEST.INPUT
Records:
101|ankit|data engineer
102|rahul|developer
103|priya|analyst
Our requirement is to read the dataset and convert the names and job titles to uppercase.
Using Stream Mode
from zoautil_py.zoau_io import zopen
dataset_name = "ANKIT.TEST.INPUT"
with zopen(
f"//'{dataset_name}'",
"r",
"cp1142"
) as dataset:
for record in dataset:
fields = record.split("|")
employee_id = fields[0]
employee_name = fields[1].upper()
job_title = fields[2].upper()
transformed_record = (
f"{employee_id}|"
f"{employee_name}|"
f"{job_title}"
)
print(transformed_record)
Output:
101|ANKIT|DATA ENGINEER
102|RAHUL|DEVELOPER
103|PRIYA|ANALYST
This example demonstrates how you can read Mainframe dataset records and perform transformations directly using Python.
The same logic can later be extended to write transformed records into another Mainframe dataset.
9. Important Things to Remember
Before reading Mainframe datasets using Python, remember these points:
zoautil_pyis provided through ZOAU and is designed for z/OS interactions.datasets.read()returns text content.datasets.read_as_bytes()returns record contents as bytes.zoau_io.zopen()supports stream-based dataset access.- Stream reading allows you to process records individually.
- Specify the correct EBCDIC code page when reading text.
- Binary mode does not automatically convert data into normal Python strings.
- Dataset access permissions are controlled by Mainframe security.
- Dataset organization and record format matter.
- The examples here focus on supported non-VSAM datasets such as sequential datasets and partitioned datasets.
- VSAM datasets require different handling depending on the supported interface.
- Large datasets should be processed carefully to avoid unnecessary memory consumption.
Conclusion
Reading Mainframe datasets using Python is possible with the help of IBM’s zoautil_py package.
In this article, we explored two important approaches:
Bulk Mode
datasets.read()datasets.read_as_bytes()
Stream Mode
zoautil_py.zoau_io.zopen()
We also explored how to read data in text mode and binary mode, how EBCDIC decoding works, and how to perform simple record transformations.
For developers working on Mainframe modernization, Python integration, or data processing on z/OS, understanding these APIs can be extremely useful.
And the best part?
You can process Mainframe datasets directly from Python without necessarily transferring them to another platform first.
Happy Learning!
References
- IBM ZOAU Python API – Datasets: https://www.ibm.com/docs/en/zoau/1.4.x?topic=apis-datasets
- IBM ZOAU Python API –
zoau_io: https://www.ibm.com/docs/en/zoau/1.3.x?topic=apis-zoau-io - IBM Example – Read a Dataset Record by Record: https://www.ibm.com/docs/en/zoau/1.4.x?topic=examples-read-dataset-record-by-record
- IBM ZOAU Documentation: https://www.ibm.com/docs/en/zoau