Commit 39e70a4e authored by Serhiy Storchaka's avatar Serhiy Storchaka

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

interface and support all mandatory methods and properties.
parent 3e6e2ac3
...@@ -417,10 +417,8 @@ class ModifiedInterpreter(InteractiveInterpreter): ...@@ -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)
...@@ -860,10 +858,10 @@ class PyShell(OutputWindow): ...@@ -860,10 +858,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
...@@ -1259,36 +1257,82 @@ class PyShell(OutputWindow): ...@@ -1259,36 +1257,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
@property
def encoding(self):
return self._encoding
@property
def name(self):
return '<%s>' % self.tags
def isatty(self):
return True
class PseudoOutputFile(PseudoFile):
def writable(self):
return True
def write(self, s): def write(self, s):
if self.closed:
raise ValueError("write to closed file")
if not isinstance(s, str): if not isinstance(s, str):
raise TypeError('must be str, not ' + type(s).__name__) raise TypeError('must be str, not ' + type(s).__name__)
return self.shell.write(s, self.tags) return self.shell.write(s, self.tags)
def writelines(self, lines):
for line in lines:
self.write(line)
def flush(self): class PseudoInputFile(PseudoFile):
pass
def isatty(self): def __init__(self, shell, tags, encoding=None):
return True PseudoFile.__init__(self, shell, tags, encoding)
self._line_buffer = ''
class PseudoInputFile(object): def readable(self):
def __init__(self, shell): return True
self.readline = shell.readline
self.isatty = shell.isatty
def write(self, s): def read(self, size=-1):
raise io.UnsupportedOperation("not writable") if self.closed:
writelines = write 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 = """\
......
...@@ -15,6 +15,8 @@ from idlelib import RemoteDebugger ...@@ -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__
...@@ -262,62 +264,23 @@ class MyRPCServer(rpc.RPCServer): ...@@ -262,62 +264,23 @@ 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)
# 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)
......
...@@ -202,6 +202,9 @@ Core and Builtins ...@@ -202,6 +202,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.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment