Python

 View Only

Best practices for encodings with Python

By Kiarash Alirezaei posted 07/30/26 02:07 PM

  

Encoding issues are among the most frequently reported challenges when working with Python on z/OS. Understanding how to properly handle character encodings and file tagging is essential for ensuring scripts and data behave consistently across environments. 

Understanding encodings on z/OS 

On z/OS, you'll encounter three primary encoding schemes: 

  • EBCDIC (IBM-1047): Native z/OS encoding, used by traditional mainframe applications and datasets. The default for z/OS is EBCDIC. On z/OS UNIX, it's generally IBM-1047, though people working from TSO generally use IBM-037. 

  • UTF-8: Universal encoding standard, default for most modern applications and Python 3 

  • ASCII (ISO8859-1): Commonly encountered in z/OS UNIX files and open-source projects 

Recommendation: Appropriately tag files with their expected encoding. UTF-8 is not typically recommended as a default since not all z/OS components support it. For Python source files on z/OS, ISO8859-1 is the standard tagging used by IBM Open Enterprise SDK for Python, while maintaining EBCDIC compatibility where required for legacy integration. 

File tagging 

z/OS UNIX uses extended file attributes (tags) to indicate a file's encoding. Tags tell the system how to interpret file contents. Whether automatic conversion should occur is determined jointly by the file's tag and the relevant environment variables (such as _BPXK_AUTOCVT). 

 

You can check a file’s tag with the following command: 

ls -T myfile.py

Output example: 

t ISO8859-1   T=on  myfile.py 

Where: 

  • t = text file 

  • ISO8859-1 = encoding (could be UTF-8, IBM-1047, etc.) 

  • T=on = auto-conversion enabled 

 

Note: For Python source files on z/OS, the recommended tagging is ISO8859-1, as this is the standard used by IBM Open Enterprise SDK for Python for z/OS. 

Tagging Python source files 

Recommendation: Tag all Python source files (.py) with ISO8859-1. Only deviate from this if you have a specific need for EBCDIC (IBM-1047) or explicit Unicode content (UTF-8). 

# Tag a single file 
chtag -tc ISO8859-1 script.py 
 
# Tag all Python files in a directory 
find . -name "*.py" -exec chtag -tc ISO8859-1 {} \ ; 

#Tag recursively 
find /path/to/project -name "*.py" -exec chtag -tc ISO8859-1 {}  \; 

IBM Open Enterprise SDK for Python supports both IBM-1047 and ASCII source files. When you create new Python files, they should be tagged with their correct encoding to ensure proper handling by the Python interpreter. 

How Python handles file encoding:

During the open operation, Python handles files based on their tagging: 

  • Untagged files: Python attempts to automatically determine the encoding in read mode. In binary mode, no encoding detection is performed. 

  • Tagged files: Python decodes the file using the tagged encoding. In binary mode, the tag is ignored. 

  • Explicit encoding parameter: When you specify an encoding parameter in the open() function, Python ignores the file's tag and uses the specified encoding. 

 

Note: While source files might be EBCDIC, all text within the Python interpreter is represented as UTF-8. All I/O continues to be in UTF-8 unless explicit conversions are performed using the encoding parameter. 

Example - Converting file encoding if necessary:

If you need to convert a file from one encoding to another, you can use the iconv utility: 

# Convert from EBCDIC (IBM-1047) to ISO8859-1 
iconv -f IBM-1047 -t ISO8859-1 input.py > output.py  
chtag -tc ISO8859-1 output.py 

ISO8859-1 tagging: ISO8859-1 (also known as Latin-1) is a superset of ASCII and can represent all characters in the 0-255 range. This is the standard tagging used throughout the IBM Python for z/OS distribution. For Python source code that only uses ASCII characters (which is the vast majority of code), ISO8859-1 and UTF-8 are functionally equivalent for the ASCII subset. 
 
UTF-8 for Unicode content: If you need to use Unicode characters beyond the Latin-1 range in your source code (such as emoji or non-Latin scripts), you should: 

  1. Use UTF-8 encoding declarations at the top of your file:
    -- coding: utf-8 -- 
  2. Tag the file as UTF-8: 
    chtag -tc UTF-8 script.py 

For most Python development on z/OS, ISO8859-1 tagging is the recommended standard and aligns with IBM Open Enterprise SDK for Python for z/OS conventions.

Tagging configuration files 

Recommendation: Tag configuration files based on their content. 

For configuration files that contain only ASCII characters: 

chtag -tc ISO8859-1 config.json 
chtag -tc ISO8859-1 settings.yaml 
chtag -tc ISO8859-1 pyproject.toml 

For configuration files that may contain Unicode characters (international text, emoji, etc.) or come from distributed platforms: 

chtag -tc UTF-8 config.json 
chtag -tc UTF-8 settings.yaml 

Detecting file encoding: If you're unsure of a file's encoding, you can use the file command to get information about the file, though it may not always accurately detect the encoding. The most reliable method is to check the file's CCSID (Coded Character Set Identifier) using ls -T: 

# Check current file tag 
ls -T config.json 

You can also use Python's chardet library (if installed) to detect encoding: 

python3 -c "import chardet; data=open('config.json','rb').read(); print(chardet.detect(data))" 

Best practice: ISO8859-1 is the encoding used by default when Python creates files on z/OS. If you're unsure of the appropriate encoding, ISO8859-1 is a safe default for files that will be processed primarily on z/OS. Use UTF-8 when you need explicit Unicode support or when sharing files with other platforms. 

Tagging shell scripts

Recommendation: Tag shell scripts as ISO8859-1. 

chtag -tc ISO8859-1 setup.sh 
chtag -tc ISO8859-1 deploy.sh 

Shell scripts on z/OS typically use EBCDIC encoding. However, if your shell scripts are managed in Git or developed on distributed platforms, they may also use ASCII (ISO8859-1) encoding and should be tagged accordingly. 

Auto-conversion (Enhanced vs. Unenhanced ASCII) 

The T=on flag enables automatic conversion between encodings: 

# Enable auto-conversion (Enhanced ASCII) 
chtag -tc ISO8859-1 file.txt
 
# Disable auto-conversion (Unenhanced ASCII)   
chtag -tcu ISO8859-1 file.txt 

Use Enhanced ASCII (T=on) for text files that require automatic conversion between encodings when accessed by different applications. 

Use Unenhanced ASCII (T=off) for binary data, files where byte-for-byte integrity is critical, and performance-critical applications (avoids conversion overhead). 

Python file I/O best practices

Always specify encoding explicitly to avoid platform-dependent behaviour: 

with open('data.txt', 'r', encoding='iso8859-1') as f: content = f.read() 

Reading ISO8859-1 files 

# Standard ISO8859-1 file  
with open('script.py', 'r', encoding='iso8859-1') as f: 
    content = f.read() 

Reading UTF-8 files 

# UTF-8 file from distributed systems 
with open('api_data.json', 'r', encoding='utf-8') as f: 
    data = json.load(f) 

Reading IBM-1047 files 

# EBCDIC IBM-1047 file 
with open('ebcdic_file.txt', 'r', encoding='ibm1047') as f: 
    content = f.read() 

Writing ISO8859-1 files 

# Write ISO8859-1 and tag the file
import os 

filename = 'output.txt' 
with open(filename, 'w', encoding='iso8859-1') as f: 
    f.write('Processing complete\n') 

Writing UTF-8 files

# Write UTF-8 with Unicode characters and tag the file 
import os

filename = 'output.txt' 
with open(filename, 'w', encoding='utf-8') as f: 
    f.write('Hello, World! \n') 

Writing IBM-1047 files

# Write IBM-1047 (EBCDIC) file 
import os

filename = 'output.txt' 
with open(filename, 'w', encoding='ibm1047') as f: 
    f.write('Processing complete\n') 

Binary files

# Binary mode - no encoding conversion 
with open('data.bin', 'rb') as f: 
    binary_data = f.read() 

with open('output.bin', 'wb') as f: 
    f.write(binary_data) 

Environment Variables

_BPXK_AUTOCVT 

Controls automatic conversion for untagged files: 

# Enable auto-conversion (default: ON) 
export _BPXK_AUTOCVT=ON 

# Disable auto-conversion (strict mode) 
export _BPXK_AUTOCVT=OFF 

_BPXK_AUTOCVT=ON is required for using Python on z/OS. It enables automatic conversion between encodings for untagged files. 

Important: It is not recommended to set _BPXK_AUTOCVT in your ~/.profile or ~/.bashrc as it has a performance impact if you enable it and run various applications. Instead, set it only when running Python

# Set only when running Python 
export _BPXK_AUTOCVT=ON python3 script.py 

For production environments, consider _BPXK_AUTOCVT=OFF to enforce proper tagging discipline, but note that Python requires it to be ON for proper operation. 

Note: Python internally sets the _TAG_REDIR_IN, _TAG_REDIR_OUT, and _TAG_REDIR_ERR variables, so you do not need to set them manually. 

Common issues and solutions 

UnicodeDecodeError 

Error: 

UnicodeDecodeError: 'iso8859-1' codec can't decode byte 0x85 in position 10 

Cause: File is being read as ISO8859-1, but the content within it is not in that encoding (likely EBCDIC). 

Solution: 

# Check file tag first 
import subprocess  
result = subprocess.run(['ls', '-T', 'file.txt'], capture_output=True, text=True)  
print(result.stdout) 

# Read with correct encoding 
with open('file.txt', 'r', encoding='ibm1047') as f:  
     content = f.read() 

Note: The file tag indicates what encoding the file claims to be, but the actual content may be in a different encoding. Always verify that the file's tag matches its actual content encoding. 

Corrupted output 

Symptom: Text appears as strange characters. 

Cause: Encoding mismatch between writer and reader. 

Solution: 

# Ensure consistent encoding throughout pipeline 
# Writer: 
with open('output.txt', 'w', encoding='iso8859-1') as f:  
     f.write('Processing complete\n')  

# Reader: 
with open('output.txt', 'r', encoding='iso8859-1') as f:  
     print(f.read()) 

Mixing ISO8859-1 and UTF-8 files 

Symptom: Some files work, others fail with encoding errors. 

Cause: Inconsistent tagging across project files. 

Solution: 

# Standardize on ISO8859-1 for Python files 
find /path/to/project -name "*.py" -exec chtag -tc ISO8859-1 {}  \; 
 
# Use UTF-8 only for files with explicit Unicode content 
chtag -tc UTF-8 /path/to/unicode_data.json 

Quick reference 

Python encoding patterns:

# ISO8859-1 read (IBM Python standard) 
with open('file.txt', 'r', encoding='iso8859-1') as f:  
     data = f.read() 

# UTF-8 read (for Unicode content) 
with open('file.txt', 'r', encoding='utf-8') as f:  
     data = f.read() 

# EBCDIC read 
with open('file.txt', 'r', encoding='ibm1047') as f:  
     data = f.read() 

# Binary (no encoding) 
with open('file.bin', 'rb') as f:  
     data = f.read() 

Want to learn more about using Python on z/OS? Visit our Python Portal for a convenient way to access more resources.

0 comments
14 views

Permalink