Skip to content

io: TextIOWrapper silently corrupts output when encoder is a no-op #161518

Open

Description

@grgalex

Bug report

Summary

When an encoder returns the input str itself, TextIOWrapper.write treats it as ASCII and writes the str's internal storage to the file. For non-ASCII text the file is silently corrupted.

Expected: TypeError: encoder should return a bytes object, not 'str'.

Reproducer

We use a 44-character string that ends in an emoji, so CPython internally stores it as four bytes per character.

import codecs

class Enc(codecs.IncrementalEncoder):
    def encode(self, text, final=False):
        return text

info = codecs.CodecInfo(None, None, incrementalencoder=Enc,
                        incrementaldecoder=codecs.getincrementaldecoder("utf-8"),
                        name="passthrough")
codecs.register(lambda n: info if n == "passthrough" else None)

with open("out.txt", "w", encoding="passthrough") as f:
    f.write("The quick brown fox jumps over the lazy dog🐍")

print(open("out.txt").read())
The quick b

Only the first 44 bytes (out of 44*4 = 176) were written to the file.

Root cause

Modules/_io/textio.c, in _io_TextIOWrapper_write_impl:

if (b != text && !PyBytes_Check(b)) {

The exemption for b == text is meant for the ASCII fast path, which sets b = text only for ASCII text.

A Python encoder that returns its input also satisfies it, with any text.

Suggested fix

Tie the exemption to what the fast path relies on, that the text is ASCII:

if (!PyBytes_Check(b) && !(b == text && PyUnicode_IS_ASCII(text))) {

With this the reproducer raises the TypeError above.

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Linked PRs

Activity

  1. added
    type-bugAn unexpected behavior, bug, or error
    on Nov 6, 2026
  2. changed the title [-]io: TextIOWrapper silently corrupts its output when the encoder is a no-op[/-] [+]io: TextIOWrapper silently corrupts its output when encoder is a no-op[/+] on Nov 6, 2026
  3. changed the title [-]io: TextIOWrapper silently corrupts its output when encoder is a no-op[/-] [+]io: TextIOWrapper silently corrupts output when encoder is a no-op[/+] on Nov 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions