This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Documentation Recommends Broken Pattern
Type: behavior Stage: resolved
Components: IO Versions: Python 3.4, Python 3.5
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: aronacher, benjamin.peterson, flox, hynek, martin.panter, pitrou, python-dev, r.david.murray, stutzbach
Priority: normal Keywords:

Created on 2014-04-27 15:03 by aronacher, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Messages (14)
msg217270 - (view) Author: Armin Ronacher (aronacher) * (Python committer) Date: 2014-04-27 15:03
The documentation recommends replacing sys.stdin with a binary stream currently: https://docs.python.org/3/library/sys.html#sys.stdin

This sounds like a bad idea because it will break pretty much everything in Python in the process.

As example:

>>> import sys
>>> sys.stdin = sys.stdin.detach()
>>> input('Test: ')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: '_io.BufferedReader' object has no attribute 'errors'

>>> sys.stdout = sys.stdout.detach()
>>> print('Hello World!')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' does not support the buffer interface
msg217286 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2014-04-27 17:00
Initial introduction is 59cb9c074e09.
msg217741 - (view) Author: Martin Panter (martin.panter) * (Python committer) Date: 2014-05-02 03:39
That particular revision isn’t sound so bad; I think the next revision, 78fb7f8cd349, which adds the make_streams_binary() function that replaces the variables is a worry though.

This is the kind of code I use when I want to write binary data to stdout:

output = sys.stdout.detach()
sys.stdout = None  # Avoid error message during shutdown, due to output being closed or garbage-collected
output.write(...)

This raises two issues:

1. Is the practice of setting sys.stdout to None documented or recommended anywhere? If not, could it be, or is there another way to do this?

2. The error message I am avoiding looks more broken than it could be:

$ python3 -c 'from sys import stdout; stdout.detach().close()'
Exception ignored in: $

The trailing dollar ($) sign just indicates that there was no newline printed.
msg218580 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2014-05-14 21:54
The issue of (mixed) string and binary input/output on the standard streams is still a bit of a work in progress, I think, both documentation wise and code wise.  So I'm not sure we know yet what the best practice is to recommend here.

I think I agree with Armin, though.  I think mentioning reading binary from sys.stdin.buffer is fine, but suggesting replacing the streams is bad.  If someone figures that out on their own, it's on their own head, but I don't think we should suggest it.

That said, that shutdown error message is probably a bug of one sort or another.
msg218581 - (view) Author: Armin Ronacher (aronacher) * (Python committer) Date: 2014-05-14 22:05
Sidestepping:  The shutdown message is a related issue.  TextIOWrapper tends to internally log errors apparently which is super annoying and probably should be fixed.  I encountered the same problem with sockets disconnecting wrapped in TextIOWrapper always writing some dummy traceback to stderr which I can't silence.
msg218626 - (view) Author: Roundup Robot (python-dev) (Python triager) Date: 2014-05-15 20:39
New changeset 4621bb82ceec by Antoine Pitrou in branch '3.4':
Issue #21364: remove recommendation of broken pattern.
http://hg.python.org/cpython/rev/4621bb82ceec

New changeset dbf728f9a2f0 by Antoine Pitrou in branch 'default':
Issue #21364: remove recommendation of broken pattern.
http://hg.python.org/cpython/rev/dbf728f9a2f0
msg218627 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2014-05-15 20:40
Thanks for the report, Armin. I've removed that recommendation and changed the surrounding wording to insist that standard streams are always text streams.
msg218628 - (view) Author: Armin Ronacher (aronacher) * (Python committer) Date: 2014-05-15 20:44
To avoid further problems may I also recommend documenting how exactly people are supposed to wrap sys.stdout and so forth.  Clearly putting a StringIO there is insufficient as StringIO does not have a buffer.

Something like this maybe?

import io
buf = io.BytesIO()
sys.stdout = io.TextIOWrapper(buf,
    encoding='utf-8',
    errors='strict', # or surrogate-escape as this is the default for stdout now? not sure
    line_buffering=True
)
msg218629 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2014-05-15 20:47
> To avoid further problems may I also recommend documenting how exactly
> people are supposed to wrap sys.stdout and so forth.  Clearly putting
> a StringIO there is insufficient as StringIO does not have a buffer.

I would like to know of some situations where you want to write some
code that accesses standard streams as binary *and* don't control the
application setup (i.e. library code rather than application code). It
seems to me that a library should take the binary streams as parameters
rather than force the use of stdin/stdout.
msg218631 - (view) Author: Armin Ronacher (aronacher) * (Python committer) Date: 2014-05-15 21:40
> I would like to know of some situations where you want to write some
> code that accesses standard streams as binary *and* don't control the
> application setup (i.e. library code rather than application code). It
> seems to me that a library should take the binary streams as parameters
> rather than force the use of stdin/stdout.

The same situations people wrapped streams before on python 2:

* code.py users.  Werkzeug's traceback system implements a remote python 
  shell through it.
* any system that wants to unittest shell scripts on a high level.
* any system that wants to execute arbitrary python code and then
  capture whatever output it did.  This is for instance what I see
  Sphinx users frequently do (or doctests)

In fact, the reasons people wrap sys.stdout/sys.stderr on 2.x are the same reasons why people would do it on 3.x: they have arbitrary code they did not write and they want to capture what it does.

Since you do not know if that is binary or text you need a stream object that behaves the same way as the default one.
msg218632 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2014-05-15 21:50
> The same situations people wrapped streams before on python 2:
> 
> * code.py users.  Werkzeug's traceback system implements a remote python 
>   shell through it.
> * any system that wants to unittest shell scripts on a high level.
> * any system that wants to execute arbitrary python code and then
>   capture whatever output it did.  This is for instance what I see
>   Sphinx users frequently do (or doctests)

I see, I misunderstood you. You actually want to get back the bytes
output of e.g. stdout, right?
You could indeed use a TextIOWrapper wrapping a BytesIO object. Or of
course another possibility is to do the encoding yourself, e.g.
sys.stdout.getvalue().encode('utf-8', 'surrogateescape').
msg218633 - (view) Author: Armin Ronacher (aronacher) * (Python committer) Date: 2014-05-15 21:52
Pretty much, yes.  Just that you probably want 'replace' instead.  
surrogate-escape does not do anything useful here I think.
msg218634 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2014-05-15 21:53
Note that in 3.4 we have contextlib.replace_stdout, but it doesn't give any examples of how to construct file-like objects that will work well with it.
msg218635 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2014-05-15 21:55
I mean redirect_stdout.
History
Date User Action Args
2022-04-11 14:58:02adminsetgithub: 65563
2014-05-15 21:55:06r.david.murraysetmessages: + msg218635
2014-05-15 21:53:05r.david.murraysetmessages: + msg218634
2014-05-15 21:52:30aronachersetmessages: + msg218633
2014-05-15 21:50:57pitrousetmessages: + msg218632
2014-05-15 21:40:11aronachersetmessages: + msg218631
2014-05-15 20:47:13pitrousetmessages: + msg218629
2014-05-15 20:44:56aronachersetmessages: + msg218628
2014-05-15 20:40:42pitrousetstatus: open -> closed
resolution: fixed
messages: + msg218627

stage: resolved
2014-05-15 20:39:47python-devsetnosy: + python-dev
messages: + msg218626
2014-05-14 22:05:20aronachersetmessages: + msg218581
2014-05-14 21:54:27r.david.murraysetnosy: + r.david.murray

messages: + msg218580
versions: - Python 3.3
2014-05-02 03:39:36martin.pantersetnosy: + martin.panter
messages: + msg217741
2014-04-27 17:00:10pitrousetmessages: + msg217286
2014-04-27 16:52:52floxsetnosy: + pitrou, benjamin.peterson, stutzbach, flox, hynek

type: behavior
components: + IO
versions: + Python 3.3, Python 3.4, Python 3.5
2014-04-27 15:03:53aronachercreate