Python 3.15 was officially released on October 9, 2026. Text input and output that doesn't specify an encoding now uses UTF-8 by default. In earlier versions of Python, the default encoding depended on the region and language settings of the environment where the program ran. As a result, the same program could work correctly on macOS or Linux yet produce garbled text or errors on Windows. This change moves Python toward eliminating those differences between operating systems.
However, the change may cause problems for business scripts that handle old Japanese CSV files and similar data. Whether an existing program keeps working depends on the encoding actually used by the files and external systems it reads and writes. The official Python 3.15 change list also recommends specifying the encoding explicitly to maintain compatibility across versions.
What Changed in Python 3.15: The Default Behavior When No Encoding Is Given
In Python 3.15, if you open a file without specifying an encoding, as in open("data.csv"), UTF-8 is used under the UTF-8 mode that is now enabled by default. If you specify encoding="utf-8" as before, that setting still takes precedence.
In other words, what is new in Python 3.15 is not the ability to handle UTF-8 files, but which encoding is chosen when none is specified.
It also helps to remember that the .py files in which you write Python programs and the CSV or log files those programs read and write are treated differently. Python source files have used UTF-8 as the standard for some time, and UTF-8 mode itself was introduced in Python 3.7.
Up to Python 3.14, on the other hand, ordinary text input and output chose an encoding based on the locale (the region and language settings) whenever UTF-8 mode was disabled. This is explained in the official documentation on Python's UTF-8 mode and the Python 3.14 I/O specification.
Even a simple program that just reads a UTF-8 README file can run into this difference.
If UTF-8 is the default in the developer's macOS or Linux environment, omitting the encoding works fine. But when the program runs on a Windows environment or another system where a different encoding is the default, Japanese text and special symbols may be misinterpreted, leading to garbled characters or errors.
The program and the file are the same, yet the result changes depending on the environment where it runs.
In Python 3.15, the default UTF-8 mode reduces this kind of environment-dependent difference. For example, when reading a UTF-8 CSV file, the following two pieces of code use the same encoding under the default settings of Python 3.15.
with open("data.csv") as f:
text = f.read()
with open("data.csv", encoding="utf-8") as f:
text = f.read()For programs that must also run on older versions of Python, however, it is safer to specify the encoding explicitly, as in the second example. That way the code does not depend on the environment's settings, and the intent to read the file as UTF-8 is clear.
Python 3.15 also introduces lazy imports, which delay loading modules until they are needed, and new profiling features. But making UTF-8 the default affects existing programs even if you don't use any of the new features. Even if you have no plans to adopt the new features, you should review any code that performs text input and output.
Why Making UTF-8 the Default Required a Long Preparation Period
Missing encoding specifications have long been a problem in developing and distributing Python packages.
For example, PEP 597, which proposed a warning feature to detect missing encoding specifications, includes a survey of the 4,000 most downloaded packages on PyPI.
According to that survey, 489 packages used non-ASCII characters in their README files, and 82 of them did not specify an encoding, so they could not be installed from source in environments where UTF-8 was not the default.
This survey was conducted when the PEP was proposed, and it does not mean the same number of packages still have the problem today. Even so, it illustrates well how a README that merely contains an author's name or a copyright symbol can make a package that worked in the developer's environment impossible to install in a user's environment.
On the other hand, there are also programs that have long read and written files in region-specific encodings, relying on Python's previous behavior. If the default encoding changes to UTF-8, such programs may become unable to read data they previously handled correctly.
PEP 686, which made UTF-8 mode the default, also warns that the change could lead not only to exceptions and garbled text but also to data being corrupted without anyone noticing.
For that reason, Python has introduced the mechanisms needed for migration in stages.
Python 3.10 added EncodingWarning, which detects missing encoding specifications, and encoding="locale", which explicitly specifies following the execution environment's locale. EncodingWarning is disabled by default but can be enabled when needed.
In Python 3.11, this was followed by a fix so that specifying encoding="locale" follows the actual locale even when UTF-8 mode is enabled.
After these preparations, UTF-8 mode was enabled by default in Python 3.15.
Be Careful with APIs That Retrieve the Encoding
During migration, you should also pay attention to differences in the APIs used to check the current encoding.
The long-used locale.getpreferredencoding(False) is affected by UTF-8 mode. Under Python 3.15's default settings it returns UTF-8, so calling this function does not necessarily give you the OS locale's native encoding.
To get the encoding of the locale itself, use locale.getencoding(), which was added in Python 3.11. This function is not affected by UTF-8 mode and returns the encoding based on the locale.
If you need the previous behavior, you can disable UTF-8 mode by setting the environment variable PYTHONUTF8=0 or using the startup option -X utf8=0.
This makes it possible to investigate differences from earlier versions or temporarily keep existing processing as it is. However, simply disabling UTF-8 mode does not make it clear in the code which encoding should be used for each file. In the long term, it is preferable to specify the encoding explicitly wherever it matters.
Japanese CP932 Files and External Commands May Cause Problems
In Japanese-language environments, programs that handle files saved in legacy encodings such as CP932 need particular attention.
Suppose, for example, that a program has been reading a CSV file saved in CP932 without specifying an encoding. If it worked because the execution environment's default encoding was CP932, the same code in Python 3.15 will try to read the file as UTF-8.
As a result, decoding errors may occur in the Japanese portions, or the data may not be interpreted correctly.
If you have confirmed that the file is saved in CP932, you can simply specify it explicitly as follows.
with open("data.csv", encoding="cp932") as f:
text = f.read()The official Python codec list lists CP932 as an encoding with aliases such as windows-31j. It is also defined as a separate codec from shift_jis, so the two should not be treated as identical without checking.
The important point is that this change alters the behavior when an encoding is omitted; it does not automatically convert the encoding of existing files.
Summarizing the official specifications for text I/O in Python 3.15, subprocess, and codecs, the main effects are as follows.
| I/O condition | Behavior in Python 3.15 | Recommended action |
|---|---|---|
Calling open() on a UTF-8 file without specifying an encoding |
UTF-8 is used by default | Specify encoding="utf-8" explicitly if compatibility with older versions is needed |
Calling open() on a CP932 file without specifying an encoding |
Read as UTF-8, which may cause errors or garbled text | Check the file's encoding and specify encoding="cp932" |
Specifying encoding="locale" |
Uses the locale's encoding even when UTF-8 mode is enabled | Use when you need to follow the execution environment's encoding |
Running an external command with text=True and no encoding |
UTF-8 is used for text conversion of the pipes | Check the external command's output format and specify the required encoding |
| Reading files or pipes in binary mode | No automatic conversion by encoding is performed | Convert at the necessary stage with the correct encoding specified |
Note: Python 3.15 assumes UTF-8 mode is on by default. The comparison with earlier behavior refers to behavior in a non-UTF-8 locale environment with UTF-8 mode disabled.
In this way, programs that handle UTF-8 data will see fewer environment-dependent differences, while processing that relied on legacy encodings will need explicit specifications.
If a library determines its own encoding, or if UTF-8 mode has been changed through startup settings, you will need to check the specifications for each case.
You Can't Tell the Encoding from a CSV Extension or the OS That Created It
It is worth remembering that a .csv file extension does not determine the encoding.
A CSV created on Windows is not necessarily CP932, and CSVs saved in UTF-8 are also widely used. What matters is checking where the file came from and its output specification.
UTF-8 files may also begin with a BOM (byte order mark), a piece of identifying data.
When reading a UTF-8 file with a BOM, you can use encoding="utf-8-sig" if needed. With this setting, a BOM at the start of the file is removed on reading. With plain encoding="utf-8", on the other hand, the BOM remains as a character.
Even though UTF-8 is now the default in Python 3.15, it does not automatically detect a file's encoding or whether it has a BOM.
Watch for Encoding Mismatches with External Commands
The same kind of problem can occur when working with external programs.
Python's subprocess.run(..., text=True) converts the byte sequence output by an external command into a string. Even if Python tries to interpret it as UTF-8, if the called program outputs CP932, the encodings will not match, leading to errors or garbled text.
For example, when running an older Windows tool that is known to output CP932, you can specify it as follows.
import subprocess
result = subprocess.run(
["legacy-tool.exe"],
capture_output=True,
text=True,
encoding="cp932",
)Setting the environment variable PYTHONIOENCODING will also not solve every encoding problem.
This environment variable changes the encoding of Python's own standard input and output, but it is not a mechanism for changing the encoding of pipes created by subprocess all at once. In fact, PEP 686 considered applying PYTHONIOENCODING to subprocess pipes as well, but the idea was not adopted.
For that reason, the data Python itself writes to standard output and the data it receives from external commands each need to have their encodings checked separately.
What to Check Before Updating to Python 3.15
When migrating to Python 3.15, the first important step is to identify the places in your existing programs where the encoding is omitted.
You can use EncodingWarning, introduced in Python 3.10, for this task.
If you run your programs or tests with the -X warn_default_encoding option, warnings are shown for processing that omits the encoding. The official Python 3.15 I/O documentation also describes how to check using this feature.
However, warnings are shown only for processing that is actually executed, so you need to test features you don't normally use, such as data import functions and integrations with external commands. Also keep in mind that not every library or API can be expected to have missing specifications detected.
PEP 686 outlines a migration procedure: disable UTF-8 mode and enable the warnings to identify problem spots, fix the encoding specifications, then enable UTF-8 mode and test again.
In Python 3.15, you can check both behaviors with the following commands.
python -X utf8=0 -X warn_default_encoding app.py
python -X utf8=1 -X warn_default_encoding app.pyIt is advisable to test with real data that includes Japanese text and special symbols. Even if a program works with alphanumeric-only data, it won't necessarily produce the same result with files containing Japanese.
In particular, check whether the column headings of old CSV files are read correctly and whether exported files open without problems in other business systems.
Simply having no errors is not enough, either. As PEP 686 points out, an encoding mismatch can corrupt data without anyone noticing. You need to compare the strings before and after reading, and even the contents of the files you write out.
If you can standardize newly created files and data exchanged between systems on UTF-8, share that specification with the systems you connect to. If, on the other hand, you must maintain compatibility with legacy systems, it is important to specify the appropriate encoding explicitly for each file and external command.
Making UTF-8 the default in Python 3.15 is a major improvement that reduces bugs caused by differing encoding behavior across operating systems. But that does not mean all existing data has changed to UTF-8.
To use this change safely, you need to understand that code working without an encoding specified and the data's encoding being specified correctly are separate matters. If you review the input and output specifications of your existing code and specify the appropriate encodings explicitly, it will be easier to keep programs running stably even when the Python version or OS changes.
