Python 3.15 makes UTF-8 the default encoding
Python 3.15, due as a release candidate on August 4, 2026, ships PEP 686 enabled by default. Every text-mode open() call and every subprocess.run(text=True) without an explicit encoding= argument will use UTF-8 instead of the system locale encoding. On Windows, redirected sys.stdin, sys.stdout, and sys.stderr are also affected; streams attached to a console already used UTF-8 before this change.
On macOS and Linux, UTF-8 locales are already standard, so almost nothing changes in practice. On Windows, where the system code page is typically cp1252, cp932, or a regional variant, any code that omits encoding= will behave differently after the upgrade.
Find code that depends on the locale encoding
Python 3.10 added EncodingWarning to identify calls that omit an explicit encoding. Run your test suite or a script with the warning enabled:
python -X warn_default_encoding your_script.pyOr set the environment variable before running:
PYTHONWARNDEFAULTENCODING=1 python -m pytestEvery text-mode open() call that omits encoding= raises this warning; binary open() calls and standard-stream access are not flagged. On macOS and Linux, warnings still appear for locale-dependent calls even though the locale is already UTF-8, which makes the output useful for writing cross-platform code.
Fix the callsites
Each warning points to a choice between two explicit values:
# Correct for config files, source code, JSON, and network data
with open("data.json", encoding="utf-8") as f:
content = f.read()
# Correct for files that must match the user's system encoding
with open("report.txt", encoding="locale") as f:
content = f.read()The encoding="locale" option was added in Python 3.10 and correctly preserves locale behavior in UTF-8 mode as of Python 3.11 (an earlier bug was fixed in that release). It works in subprocess.run() as well:
# Preserve old locale behavior in subprocess calls
result = subprocess.run(["tool"], capture_output=True, text=True, encoding="locale")If your code calls locale.getpreferredencoding() to discover the system encoding, replace it with locale.getencoding(). In UTF-8 mode, getpreferredencoding() returns 'UTF-8' regardless of the locale; getencoding(), added in Python 3.11, always returns the locale encoding.
Opt out during migration
PYTHONUTF8=0 disables UTF-8 mode and restores locale encoding as the default. Add it to your deployment environment while auditing your callsites:
PYTHONUTF8=0 python your_script.pyThe -X utf8=0 command-line flag does the same thing inline. Both work as a stopgap during migration; the long-term fix is explicit encoding= arguments throughout the code.
Learn more
- PEP 686 – Make UTF-8 Mode default specifies every interface affected by UTF-8 mode
- What’s New in Python 3.15 covers this change alongside explicit lazy imports for faster CLI startup
- Try free-threaded Python with uv covers free-threaded Python, which shipped experimentally in 3.13, became officially supported in 3.14, and continues to mature in 3.15