A person places the third of three identical Café cards onto a numbered rack beside a machine with gauges, a Python logo, and panels labeled UTF-8 and SAFE.

Python 3.15 Preview: UTF-8 by Default

Python 3.15 makes UTF-8 the default text encoding. A plain open("notes.txt") call now decodes the same way on every platform, including Windows. Earlier Python versions picked an encoding from your locale, so on Windows the same code that worked on Linux and macOS could produce garbage.

By the end of this tutorial, you’ll understand that:

  • Python 3.15 enables UTF-8 as its default text encoding, so text I/O uses it when you omit the encoding argument.
  • The previous encoding default came from your locale, which often meant cp1252 on Windows and utf-8 on Linux and macOS.
  • Passing encoding="utf-8" keeps your file I/O portable and safe across every Python version and platform.
  • The flake8-encodings tool and the -X warn_default_encoding flag help you find code that leans on the implicit default.
  • Setting PYTHONUTF8=0 or passing encoding="locale" restores the locale-based behavior when you need it.

Here’s what the change looks like. Say that you save the text "Café ☕" to a file and read it back with a bare call to open() on Windows:

Language: Windows PowerShell
PS> py -3.14 -c "print(open('cafe.txt').read())"
Café ☕

In this example, the default decoder—often cp1252—misinterprets the UTF-8 bytes, so the code prints mojibake. Now look at how the same code behaves on Python 3.15:

Language: Windows PowerShell
PS> py -3.15 -c "print(open('cafe.txt').read())"
Café ☕

Same code, no encoding argument to open(), and the mojibake is gone because of the consistent UTF-8 default. In this tutorial, you’ll first see exactly what changes, then try the new default on a Python 3.15 pre-release. After that, you’ll learn how to keep your own code working the same way on every Python version and platform.

Take the Quiz: Test your knowledge with our interactive “Python 3.15 Preview: UTF-8 by Default” quiz. You’ll receive a score upon completion to help you track your learning progress:


Interactive Quiz

Python 3.15 Preview: UTF-8 by Default

Test your understanding of Python 3.15's UTF-8 default, from the origins of the old locale-based encoding to passing explicit encodings in your code.

Meet Python 3.15’s UTF-8 Default

It helps to understand why the default text encoding behavior was a problem in Python versions older than 3.15. Take the built-in open() function as a baseline. Whenever you omitted the encoding argument when opening a text file, Python fell back to whatever locale.getencoding() returned on your operating system.

On Windows, that fallback was the ANSI code page—the legacy character set that Windows selects from your system’s regional settings. Common examples are cp1252 in the Americas and Western Europe, cp1251 in Cyrillic-script regions, and cp932 in Japan.

On Unix, it was the codeset from your LC_CTYPE locale, which was utf-8 on a modern Linux or macOS system but plain ascii under a bare C or POSIX locale. The latter two are the minimal default locales that a system uses when nothing else is configured, which is often the case in containers and CI runners. You end up with the same code but different text encoding or decoding behaviors depending on where the code runs.

This inconsistent behavior can raise UnicodeDecodeError or UnicodeEncodeError when reading or writing text files, respectively. It can also produce mojibake, as you saw earlier, with no error at all.

UTF-8 became the de facto standard everywhere long ago, except in Python’s own default. It’s the standard for source code files, JSON, TOML, and YAML, as well as on the web and in other programming languages like Go, Rust, and Java. On top of that, every mainstream editor, such as Visual Studio Code, uses it out of the box.

Python 3.15 closes that gap through PEP 686. Text input/output (I/O) without an explicit encoding now uses UTF-8 no matter which operating system or locale you’re using.

The table below summarizes the default text encoding that open() picks before and after Python 3.15:

Scenario Default up to 3.14 Default in 3.15+
Windows ANSI code page (cp1252) utf-8
Linux/macOS (UTF-8 locale) utf-8 utf-8
Any OS under a C/POSIX locale ascii utf-8
encoding="<your_preferred_encoding>" as given as given

The rows that change are the ones that used to depend on the operating system or the locale. On a UTF-8 Linux or macOS system, your everyday runs look the same, but the C/POSIX row still applies to you, since containers, cron jobs, and CI runners often start under a bare C locale. Your code also has to decode correctly on other people’s machines. On Windows, this change removes a whole class of bugs.

If you want to go deeper into how character encodings work under the hood, Real Python’s video course on Unicode in Python: Working With Character Encodings covers code points, byte representations, and the built-in functions for converting between them.

Now that you understand the problem, it’s time to try out the new text encoding default on your own.

Locked learning resources

Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Article

Already a member? Sign-In

Locked learning resources

The full article is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Article

Already a member? Sign-In

About Leodanis Pozo Ramos

Leodanis is a self-taught Python developer, educator, and technical writer with over 10 years of experience.

» More about Leodanis

Each tutorial at Real Python is created by a team of developers so that it meets our high quality standards. The team members who worked on this tutorial are:

What Do You Think?

What’s your #1 takeaway or favorite thing you learned? How are you going to put your newfound skills to use? Leave a comment below and let us know.

Commenting Tips: The most useful comments are those written with the goal of learning from or helping out other students. Get tips for asking good questions and get answers to common questions in our support portal.


Looking for a real-time conversation? Visit the Real Python Community Chat or join the next “Office Hours” Live Q&A Session. Happy Pythoning!

Become a Member to join the conversation.

Keep Learning

Related Topics: intermediate python