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:30:35 +02:00
parent 4e63fbe04d
commit 9abc830c6a
3 changed files with 82 additions and 71 deletions

View file

@ -428,10 +428,8 @@ class ModifiedInterpreter(InteractiveInterpreter):
except socket.timeout, err: except socket.timeout, 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)
@ -884,10 +882,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
@ -1279,37 +1277,83 @@ class PyShell(OutputWindow):
return 'disabled' return 'disabled'
return super(PyShell, self).rmenu_check_paste() return super(PyShell, self).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.softspace = 0 self.softspace = 0
self.encoding = encoding self._encoding = encoding
def write(self, s): @property
if not isinstance(s, (basestring, bytearray)): def encoding(self):
raise TypeError('must be string, not ' + type(s).__name__) return self._encoding
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, (basestring, bytearray)):
raise TypeError('must be string, 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

@ -15,6 +15,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__
@ -249,57 +251,19 @@ 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, basestring):
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):
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",
from idlelib import IOBinding IOBinding.encoding)
sys.stdin.encoding = sys.stdout.encoding = \ sys.stderr = PyShell.PseudoOutputFile(self.console, "stderr",
sys.stderr.encoding = IOBinding.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

@ -189,6 +189,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.