GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — io: TextIOWrapper silently corrupts output when encoder is a no-op · Issue #161518 · python/cpython

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

New issue
Copy link
New issue
Copy link
Open
Open
io: TextIOWrapper silently corrupts output when encoder is a no-op#161518
Copy link
Labels
extension-modulesC modules in the Modules dirC modules in the Modules dirtopic-IOtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

Description

@grgalex
grgalex
opened on Nov 6, 2026
Issue body actions

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:

cpython/Modules/_io/textio.c

Line 1834 in d900c8f

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

  • gh-161518: Reject a non-bytes encoder result unless it is the ASCII fast path #161531

Activity

  1. grgalex
    added
    type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
    on Nov 6, 2026
  2. grgalex
    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. grgalex
    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
  4. aisk
    added
    extension-modulesC modules in the Modules dirC modules in the Modules dir
    topic-IO
    on Nov 7, 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

    extension-modulesC modules in the Modules dirC modules in the Modules dirtopic-IOtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      You are visitor 01402503 — GeezerVison does not store the pages you look up.
      Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.