You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ORPA-pyOpenRPA/Resources/WPy64-3720/python-3.7.2.amd64/Lib/site-packages/ptpython/contrib/asyncssh_repl.py

134 lines
4.2 KiB

"""
Tool for embedding a REPL inside a Python 3 asyncio process.
See ./examples/asyncio-ssh-python-embed.py for a demo.
Note that the code in this file is Python 3 only. However, we
should make sure not to use Python 3-only syntax, because this
package should be installable in Python 2 as well!
"""
from __future__ import unicode_literals
import asyncio
import asyncssh
from prompt_toolkit.input import PipeInput
from prompt_toolkit.interface import CommandLineInterface
from prompt_toolkit.layout.screen import Size
from prompt_toolkit.shortcuts import create_asyncio_eventloop
from prompt_toolkit.terminal.vt100_output import Vt100_Output
from ptpython.repl import PythonRepl
__all__ = (
'ReplSSHServerSession',
)
class ReplSSHServerSession(asyncssh.SSHServerSession):
"""
SSH server session that runs a Python REPL.
:param get_globals: callable that returns the current globals.
:param get_locals: (optional) callable that returns the current locals.
"""
def __init__(self, get_globals, get_locals=None):
assert callable(get_globals)
assert get_locals is None or callable(get_locals)
self._chan = None
def _globals():
data = get_globals()
data.setdefault('print', self._print)
return data
repl = PythonRepl(get_globals=_globals,
get_locals=get_locals or _globals)
# Disable open-in-editor and system prompt. Because it would run and
# display these commands on the server side, rather than in the SSH
# client.
repl.enable_open_in_editor = False
repl.enable_system_bindings = False
# PipInput object, for sending input in the CLI.
# (This is something that we can use in the prompt_toolkit event loop,
# but still write date in manually.)
self._input_pipe = PipeInput()
# Output object. Don't render to the real stdout, but write everything
# in the SSH channel.
class Stdout(object):
def write(s, data):
if self._chan is not None:
self._chan.write(data.replace('\n', '\r\n'))
def flush(s):
pass
# Create command line interface.
self.cli = CommandLineInterface(
application=repl.create_application(),
eventloop=create_asyncio_eventloop(),
input=self._input_pipe,
output=Vt100_Output(Stdout(), self._get_size))
self._callbacks = self.cli.create_eventloop_callbacks()
def _get_size(self):
"""
Callable that returns the current `Size`, required by Vt100_Output.
"""
if self._chan is None:
return Size(rows=20, columns=79)
else:
width, height, pixwidth, pixheight = self._chan.get_terminal_size()
return Size(rows=height, columns=width)
def connection_made(self, chan):
"""
Client connected, run repl in coroutine.
"""
self._chan = chan
# Run REPL interface.
f = asyncio.ensure_future(self.cli.run_async())
# Close channel when done.
def done(_):
chan.close()
self._chan = None
f.add_done_callback(done)
def shell_requested(self):
return True
def terminal_size_changed(self, width, height, pixwidth, pixheight):
"""
When the terminal size changes, report back to CLI.
"""
self._callbacks.terminal_size_changed()
def data_received(self, data, datatype):
"""
When data is received, send to inputstream of the CLI and repaint.
"""
self._input_pipe.send(data)
def _print(self, *data, **kw):
"""
_print(self, *data, sep=' ', end='\n', file=None)
Alternative 'print' function that prints back into the SSH channel.
"""
# Pop keyword-only arguments. (We cannot use the syntax from the
# signature. Otherwise, Python2 will give a syntax error message when
# installing.)
sep = kw.pop('sep', ' ')
end = kw.pop('end', '\n')
_ = kw.pop('file', None)
assert not kw, 'Too many keyword-only arguments'
data = sep.join(map(str, data))
self._chan.write(data + end)