Issue #9290: In IDLE the sys.std* streams now implement io.TextIOBase

interface and support all mandatory methods and properties.
This commit is contained in:
Serhiy Storchaka 2013-01-25 15:35:28 +02:00
commit 4c9bae3d31
3 changed files with 83 additions and 73 deletions

View File

@ -417,10 +417,8 @@ class ModifiedInterpreter(InteractiveInterpreter):
except socket.timeout as err: except socket.timeout as err:
self.display_no_subprocess_error() self.display_no_subprocess_error()
return None return None
# Can't regiter self.tkconsole.stdin, since run.py wants to self.rpcclt.register("console", self.tkconsole)
# call non-TextIO methods on it (such as getvar) self.rpcclt.register("stdin", self.tkconsole.stdin)
# XXX should be renamed to "console"
self.rpcclt.register("stdin", self.tkconsole)
self.rpcclt.register("stdout", self.tkconsole.stdout) self.rpcclt.register("stdout", self.tkconsole.stdout)
self.rpcclt.register("stderr", self.tkconsole.stderr) self.rpcclt.register("stderr", self.tkconsole.stderr)
self.rpcclt.register("flist", self.tkconsole.flist) self.rpcclt.register("flist", self.tkconsole.flist)
@ -864,10 +862,10 @@ class PyShell(OutputWindow):
self.save_stderr = sys.stderr self.save_stderr = sys.stderr
self.save_stdin = sys.stdin self.save_stdin = sys.stdin
from idlelib import IOBinding from idlelib import IOBinding
self.stdin = PseudoInputFile(self) self.stdin = PseudoInputFile(self, "stdin", IOBinding.encoding)
self.stdout = PseudoFile(self, "stdout", IOBinding.encoding) self.stdout = PseudoOutputFile(self, "stdout", IOBinding.encoding)
self.stderr = PseudoFile(self, "stderr", IOBinding.encoding) self.stderr = PseudoOutputFile(self, "stderr", IOBinding.encoding)
self.console = PseudoFile(self, "console", IOBinding.encoding) self.console = PseudoOutputFile(self, "console", IOBinding.encoding)
if not use_subprocess: if not use_subprocess:
sys.stdout = self.stdout sys.stdout = self.stdout
sys.stderr = self.stderr sys.stderr = self.stderr
@ -1278,36 +1276,82 @@ class PyShell(OutputWindow):
return 'disabled' return 'disabled'
return super().rmenu_check_paste() return super().rmenu_check_paste()
class PseudoFile(object): class PseudoFile(io.TextIOBase):
def __init__(self, shell, tags, encoding=None): def __init__(self, shell, tags, encoding=None):
self.shell = shell self.shell = shell
self.tags = tags self.tags = tags
self.encoding = encoding self._encoding = encoding
def write(self, s): @property
if not isinstance(s, str): def encoding(self):
raise TypeError('must be str, not ' + type(s).__name__) return self._encoding
return self.shell.write(s, self.tags)
def writelines(self, lines): @property
for line in lines: def name(self):
self.write(line) return '<%s>' % self.tags
def flush(self):
pass
def isatty(self): def isatty(self):
return True return True
class PseudoInputFile(object):
def __init__(self, shell): class PseudoOutputFile(PseudoFile):
self.readline = shell.readline
self.isatty = shell.isatty def writable(self):
return True
def write(self, s): def write(self, s):
raise io.UnsupportedOperation("not writable") if self.closed:
writelines = write raise ValueError("write to closed file")
if not isinstance(s, str):
raise TypeError('must be str, not ' + type(s).__name__)
return self.shell.write(s, self.tags)
class PseudoInputFile(PseudoFile):
def __init__(self, shell, tags, encoding=None):
PseudoFile.__init__(self, shell, tags, encoding)
self._line_buffer = ''
def readable(self):
return True
def read(self, size=-1):
if self.closed:
raise ValueError("read from closed file")
if size is None:
size = -1
elif not isinstance(size, int):
raise TypeError('must be int, not ' + type(size).__name__)
result = self._line_buffer
self._line_buffer = ''
if size < 0:
while True:
line = self.shell.readline()
if not line: break
result += line
else:
while len(result) < size:
line = self.shell.readline()
if not line: break
result += line
self._line_buffer = result[size:]
result = result[:size]
return result
def readline(self, size=-1):
if self.closed:
raise ValueError("read from closed file")
if size is None:
size = -1
elif not isinstance(size, int):
raise TypeError('must be int, not ' + type(size).__name__)
line = self._line_buffer or self.shell.readline()
if size < 0:
size = len(line)
self._line_buffer = line[size:]
return line[:size]
usage_msg = """\ usage_msg = """\

View File

@ -16,6 +16,8 @@ from idlelib import RemoteDebugger
from idlelib import RemoteObjectBrowser from idlelib import RemoteObjectBrowser
from idlelib import StackViewer from idlelib import StackViewer
from idlelib import rpc from idlelib import rpc
from idlelib import PyShell
from idlelib import IOBinding
import __main__ import __main__
@ -277,63 +279,24 @@ class MyRPCServer(rpc.RPCServer):
quitting = True quitting = True
thread.interrupt_main() thread.interrupt_main()
class _RPCFile(io.TextIOBase):
"""Wrapper class for the RPC proxy to typecheck arguments
that may not support pickling. The base class is there only
to support type tests; all implementations come from the remote
object."""
def __init__(self, rpc):
super.__setattr__(self, 'rpc', rpc)
def __getattribute__(self, name):
# When accessing the 'rpc' attribute, or 'write', use ours
if name in ('rpc', 'write', 'writelines'):
return io.TextIOBase.__getattribute__(self, name)
# Else only look into the remote object only
return getattr(self.rpc, name)
def __setattr__(self, name, value):
return setattr(self.rpc, name, value)
@staticmethod
def _ensure_string(func):
def f(self, s):
if not isinstance(s, str):
raise TypeError('must be str, not ' + type(s).__name__)
return func(self, s)
return f
class _RPCOutputFile(_RPCFile):
@_RPCFile._ensure_string
def write(self, s):
if not isinstance(s, str):
raise TypeError('must be str, not ' + type(s).__name__)
return self.rpc.write(s)
class _RPCInputFile(_RPCFile):
@_RPCFile._ensure_string
def write(self, s):
raise io.UnsupportedOperation("not writable")
writelines = write
class MyHandler(rpc.RPCHandler): class MyHandler(rpc.RPCHandler):
def handle(self): def handle(self):
"""Override base method""" """Override base method"""
executive = Executive(self) executive = Executive(self)
self.register("exec", executive) self.register("exec", executive)
self.console = self.get_remote_proxy("stdin") self.console = self.get_remote_proxy("console")
sys.stdin = _RPCInputFile(self.console) sys.stdin = PyShell.PseudoInputFile(self.console, "stdin",
sys.stdout = _RPCOutputFile(self.get_remote_proxy("stdout")) IOBinding.encoding)
sys.stderr = _RPCOutputFile(self.get_remote_proxy("stderr")) sys.stdout = PyShell.PseudoOutputFile(self.console, "stdout",
IOBinding.encoding)
sys.stderr = PyShell.PseudoOutputFile(self.console, "stderr",
IOBinding.encoding)
sys.displayhook = rpc.displayhook sys.displayhook = rpc.displayhook
# page help() text to shell. # page help() text to shell.
import pydoc # import must be done here to capture i/o binding import pydoc # import must be done here to capture i/o binding
pydoc.pager = pydoc.plainpager pydoc.pager = pydoc.plainpager
from idlelib import IOBinding
sys.stdin.encoding = sys.stdout.encoding = \
sys.stderr.encoding = IOBinding.encoding
self.interp = self.get_remote_proxy("interp") self.interp = self.get_remote_proxy("interp")
rpc.RPCHandler.getresponse(self, myseq=None, wait=0.05) rpc.RPCHandler.getresponse(self, myseq=None, wait=0.05)

View File

@ -218,6 +218,9 @@ Core and Builtins
Library Library
------- -------
- Issue #9290: In IDLE the sys.std* streams now implement io.TextIOBase
interface and support all mandatory methods and properties.
- Issue #13454: Fix a crash when deleting an iterator created by itertools.tee() - Issue #13454: Fix a crash when deleting an iterator created by itertools.tee()
if all other iterators were very advanced before. if all other iterators were very advanced before.