`codecs.StreamReader.read()` silently drops an incomplete character at the end of the stream
Los mantenedores suelen responder en 1 día
@saiyam-jain-dev ya está trabajando en esto.
Desde el 25/10/2026.
- #160642 de @saiyam-jain-dev — abierto
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 56/100
Línea de trabajo
Start with Lib/codecs.py around the StreamReader.read() implementation and the linked expected-failure test StreamReaderTruncationBug.test_read_reports_truncated_input from PR #160155. Compare its end-of-stream behavior with IncrementalDecoder.decode(b'', final=True) and check the reported UTF-8, UTF-16, UTF-32, and escape-codec cases. Done means truncated input raises or uses the selected error handler instead of being silently discarded.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
Bug description
StreamReader.read() decodes with the stateless codec function and keeps any undecoded trailing bytes for the next call. When the stream is exhausted there is no next call, so an incomplete final character is neither reported nor replaced; it just disappears:
>>> import codecs, io
>>> reader = codecs.getreader('utf-8')(io.BytesIO(b'ab\xe2'))
>>> reader.read()
'ab'
>>> reader.read()
''
Every other way of decoding the same bytes either raises or applies the error handler:
>>> b'ab\xe2'.decode('utf-8')
Traceback (most recent call last):
File "<python-input-3>", line 1, in <module>
b'ab\xe2'.decode('utf-8')
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe2 in position 2: unexpected end of data
>>> io.TextIOWrapper(io.BytesIO(b'ab\xe2'), encoding='utf-8').read()
Traceback (most recent call last):
...
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe2 in position 2: unexpected end of data
>>> codecs.getreader('utf-8')(io.BytesIO(b'ab\xe2'), 'replace').read()
'ab'
>>> b'ab\xe2'.decode('utf-8', 'replace')
'ab�'
So with errors='strict' a truncated file reads as if it were well-formed, and with 'replace' the replacement character that would mark the damage is missing. The same happens with utf-16, utf-32 and the escape codecs (unicode_escape with a trailing backslash); the CJK codecs are not affected, because their stateless decoders raise on an incomplete sequence themselves.
The cause is in read(): self.decode is the codec's stateless function with its default final=False (Lib/codecs.py line 512), and the loop exits on empty newdata without ever telling the decoder that the input is complete; self.bytebuffer keeps the fragment forever. Fixing it would mean a final decode pass once the underlying stream returns nothing, and raising (or applying the handler) if bytes remain, which is what IncrementalDecoder.decode(b'', final=True) does. The incremental decoders did not exist when StreamReader was written, which I assume is how this came about.
I realise codecs.open() is deprecated since 3.14 and StreamReader is on its way out with it, so feel free to say this is not worth touching; but it is silent data loss in the strict mode, which seemed worth having on record.
This is the same mechanism as #56717 (bpo-12508, 2011), which describes it for UTF-8 with errors='replace' and has had no activity since; I am filing it anyway because it applies to every codec with multi-byte characters and the strict-mode data loss is the part that matters, but feel free to fold this into that one.
Found by the Hypothesis tests in #160155, where it is pinned as StreamReaderTruncationBug.test_read_reports_truncated_input under expectedFailure.
CPython versions tested on
CPython main (6e1bd24e4d8)
Operating systems tested on
Linux
- Lenguaje dominante
- Python
- Estrellas
- 77.8k
- Forks
- 37.7k
- Merge medio
- 15 h 24 min
- PR fusionados (30 d)
- 728
Preparar el entorno
Inicia el contenedor de desarrollo del proyecto en tu navegador, con tu propia cuenta de GitHub.
- Sin Dockerfile ni archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de python/cpython
-
What's New in 3.12 credits the unstable C API tier to PEP 697 instead of PEP 689Posiblemente ocupada Un pull request vinculado a esta issue está abierto o ya se fusionó. Abiertodocs
Dificultad 1/5 Menos de una hora Aptitud para principiantes 82/100
python/cpython#161078 · 3 comentarios ·
Los mantenedores suelen responder en 1 día
-
OS-windows type-bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
Los mantenedores suelen responder en 1 día
-
docs
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
python/cpython#161007 · 2 comentarios ·
Los mantenedores suelen responder en 1 día
-
Null dereferencing of a pointer in initconfig.cPosiblemente ocupada @lazerg la tomó hace 3 días. Abiertointerpreter-core topic-C-API type-bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
python/cpython#160962 · 4 comentarios ·
Los mantenedores suelen responder en 1 día
-
tests topic-profiling type-bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
python/cpython#160881 · 1 comentario ·
Los mantenedores suelen responder en 1 día
Todos los issues de python/cpython
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
pytest-dev/pyfakefs#1367 · 1 comentario ·
Los mantenedores suelen responder en 2 días
-
IFEvalG exclude_word_harder misses the keyword next to punctuation, at the start or end of the text, and in other letter casePosiblemente ocupada @damienen la tomó hoy. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
allenai/open-instruct#2151 ·
Los mantenedores suelen responder en 1 día
-
feat(plan): support convertorLabels and convertorAffinity in Plan resourcePosiblemente ocupada @AmenB la tomó hoy. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
RedHatQE/openshift-python-wrapper#2849 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100
Los mantenedores suelen responder en 25 días
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
libnativeapi/nativeapi#21 ·