Uploaded Test files
This commit is contained in:
		
							parent
							
								
									f584ad9d97
								
							
						
					
					
						commit
						2e81cb7d99
					
				
					 16627 changed files with 2065359 additions and 102444 deletions
				
			
		| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
from .server import TelnetServer
 | 
			
		||||
 | 
			
		||||
__all__ = [
 | 
			
		||||
    "TelnetServer",
 | 
			
		||||
]
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										10
									
								
								venv/Lib/site-packages/prompt_toolkit/contrib/telnet/log.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								venv/Lib/site-packages/prompt_toolkit/contrib/telnet/log.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
"""
 | 
			
		||||
Python logger for the telnet server.
 | 
			
		||||
"""
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
logger = logging.getLogger(__package__)
 | 
			
		||||
 | 
			
		||||
__all__ = [
 | 
			
		||||
    "logger",
 | 
			
		||||
]
 | 
			
		||||
							
								
								
									
										207
									
								
								venv/Lib/site-packages/prompt_toolkit/contrib/telnet/protocol.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										207
									
								
								venv/Lib/site-packages/prompt_toolkit/contrib/telnet/protocol.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,207 @@
 | 
			
		|||
"""
 | 
			
		||||
Parser for the Telnet protocol. (Not a complete implementation of the telnet
 | 
			
		||||
specification, but sufficient for a command line interface.)
 | 
			
		||||
 | 
			
		||||
Inspired by `Twisted.conch.telnet`.
 | 
			
		||||
"""
 | 
			
		||||
import struct
 | 
			
		||||
from typing import Callable, Generator
 | 
			
		||||
 | 
			
		||||
from .log import logger
 | 
			
		||||
 | 
			
		||||
__all__ = [
 | 
			
		||||
    "TelnetProtocolParser",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def int2byte(number: int) -> bytes:
 | 
			
		||||
    return bytes((number,))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Telnet constants.
 | 
			
		||||
NOP = int2byte(0)
 | 
			
		||||
SGA = int2byte(3)
 | 
			
		||||
 | 
			
		||||
IAC = int2byte(255)
 | 
			
		||||
DO = int2byte(253)
 | 
			
		||||
DONT = int2byte(254)
 | 
			
		||||
LINEMODE = int2byte(34)
 | 
			
		||||
SB = int2byte(250)
 | 
			
		||||
WILL = int2byte(251)
 | 
			
		||||
WONT = int2byte(252)
 | 
			
		||||
MODE = int2byte(1)
 | 
			
		||||
SE = int2byte(240)
 | 
			
		||||
ECHO = int2byte(1)
 | 
			
		||||
NAWS = int2byte(31)
 | 
			
		||||
LINEMODE = int2byte(34)
 | 
			
		||||
SUPPRESS_GO_AHEAD = int2byte(3)
 | 
			
		||||
 | 
			
		||||
TTYPE = int2byte(24)
 | 
			
		||||
SEND = int2byte(1)
 | 
			
		||||
IS = int2byte(0)
 | 
			
		||||
 | 
			
		||||
DM = int2byte(242)
 | 
			
		||||
BRK = int2byte(243)
 | 
			
		||||
IP = int2byte(244)
 | 
			
		||||
AO = int2byte(245)
 | 
			
		||||
AYT = int2byte(246)
 | 
			
		||||
EC = int2byte(247)
 | 
			
		||||
EL = int2byte(248)
 | 
			
		||||
GA = int2byte(249)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TelnetProtocolParser:
 | 
			
		||||
    """
 | 
			
		||||
    Parser for the Telnet protocol.
 | 
			
		||||
    Usage::
 | 
			
		||||
 | 
			
		||||
        def data_received(data):
 | 
			
		||||
            print(data)
 | 
			
		||||
 | 
			
		||||
        def size_received(rows, columns):
 | 
			
		||||
            print(rows, columns)
 | 
			
		||||
 | 
			
		||||
        p = TelnetProtocolParser(data_received, size_received)
 | 
			
		||||
        p.feed(binary_data)
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self,
 | 
			
		||||
        data_received_callback: Callable[[bytes], None],
 | 
			
		||||
        size_received_callback: Callable[[int, int], None],
 | 
			
		||||
        ttype_received_callback: Callable[[str], None],
 | 
			
		||||
    ) -> None:
 | 
			
		||||
 | 
			
		||||
        self.data_received_callback = data_received_callback
 | 
			
		||||
        self.size_received_callback = size_received_callback
 | 
			
		||||
        self.ttype_received_callback = ttype_received_callback
 | 
			
		||||
 | 
			
		||||
        self._parser = self._parse_coroutine()
 | 
			
		||||
        self._parser.send(None)  # type: ignore
 | 
			
		||||
 | 
			
		||||
    def received_data(self, data: bytes) -> None:
 | 
			
		||||
        self.data_received_callback(data)
 | 
			
		||||
 | 
			
		||||
    def do_received(self, data: bytes) -> None:
 | 
			
		||||
        """ Received telnet DO command. """
 | 
			
		||||
        logger.info("DO %r", data)
 | 
			
		||||
 | 
			
		||||
    def dont_received(self, data: bytes) -> None:
 | 
			
		||||
        """ Received telnet DONT command. """
 | 
			
		||||
        logger.info("DONT %r", data)
 | 
			
		||||
 | 
			
		||||
    def will_received(self, data: bytes) -> None:
 | 
			
		||||
        """ Received telnet WILL command. """
 | 
			
		||||
        logger.info("WILL %r", data)
 | 
			
		||||
 | 
			
		||||
    def wont_received(self, data: bytes) -> None:
 | 
			
		||||
        """ Received telnet WONT command. """
 | 
			
		||||
        logger.info("WONT %r", data)
 | 
			
		||||
 | 
			
		||||
    def command_received(self, command: bytes, data: bytes) -> None:
 | 
			
		||||
        if command == DO:
 | 
			
		||||
            self.do_received(data)
 | 
			
		||||
 | 
			
		||||
        elif command == DONT:
 | 
			
		||||
            self.dont_received(data)
 | 
			
		||||
 | 
			
		||||
        elif command == WILL:
 | 
			
		||||
            self.will_received(data)
 | 
			
		||||
 | 
			
		||||
        elif command == WONT:
 | 
			
		||||
            self.wont_received(data)
 | 
			
		||||
 | 
			
		||||
        else:
 | 
			
		||||
            logger.info("command received %r %r", command, data)
 | 
			
		||||
 | 
			
		||||
    def naws(self, data: bytes) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Received NAWS. (Window dimensions.)
 | 
			
		||||
        """
 | 
			
		||||
        if len(data) == 4:
 | 
			
		||||
            # NOTE: the first parameter of struct.unpack should be
 | 
			
		||||
            # a 'str' object. Both on Py2/py3. This crashes on OSX
 | 
			
		||||
            # otherwise.
 | 
			
		||||
            columns, rows = struct.unpack(str("!HH"), data)
 | 
			
		||||
            self.size_received_callback(rows, columns)
 | 
			
		||||
        else:
 | 
			
		||||
            logger.warning("Wrong number of NAWS bytes")
 | 
			
		||||
 | 
			
		||||
    def ttype(self, data: bytes) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Received terminal type.
 | 
			
		||||
        """
 | 
			
		||||
        subcmd, data = data[0:1], data[1:]
 | 
			
		||||
        if subcmd == IS:
 | 
			
		||||
            ttype = data.decode("ascii")
 | 
			
		||||
            self.ttype_received_callback(ttype)
 | 
			
		||||
        else:
 | 
			
		||||
            logger.warning("Received a non-IS terminal type Subnegotiation")
 | 
			
		||||
 | 
			
		||||
    def negotiate(self, data: bytes) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Got negotiate data.
 | 
			
		||||
        """
 | 
			
		||||
        command, payload = data[0:1], data[1:]
 | 
			
		||||
 | 
			
		||||
        if command == NAWS:
 | 
			
		||||
            self.naws(payload)
 | 
			
		||||
        elif command == TTYPE:
 | 
			
		||||
            self.ttype(payload)
 | 
			
		||||
        else:
 | 
			
		||||
            logger.info("Negotiate (%r got bytes)", len(data))
 | 
			
		||||
 | 
			
		||||
    def _parse_coroutine(self) -> Generator[None, bytes, None]:
 | 
			
		||||
        """
 | 
			
		||||
        Parser state machine.
 | 
			
		||||
        Every 'yield' expression returns the next byte.
 | 
			
		||||
        """
 | 
			
		||||
        while True:
 | 
			
		||||
            d = yield
 | 
			
		||||
 | 
			
		||||
            if d == int2byte(0):
 | 
			
		||||
                pass  # NOP
 | 
			
		||||
 | 
			
		||||
            # Go to state escaped.
 | 
			
		||||
            elif d == IAC:
 | 
			
		||||
                d2 = yield
 | 
			
		||||
 | 
			
		||||
                if d2 == IAC:
 | 
			
		||||
                    self.received_data(d2)
 | 
			
		||||
 | 
			
		||||
                # Handle simple commands.
 | 
			
		||||
                elif d2 in (NOP, DM, BRK, IP, AO, AYT, EC, EL, GA):
 | 
			
		||||
                    self.command_received(d2, b"")
 | 
			
		||||
 | 
			
		||||
                # Handle IAC-[DO/DONT/WILL/WONT] commands.
 | 
			
		||||
                elif d2 in (DO, DONT, WILL, WONT):
 | 
			
		||||
                    d3 = yield
 | 
			
		||||
                    self.command_received(d2, d3)
 | 
			
		||||
 | 
			
		||||
                # Subnegotiation
 | 
			
		||||
                elif d2 == SB:
 | 
			
		||||
                    # Consume everything until next IAC-SE
 | 
			
		||||
                    data = []
 | 
			
		||||
 | 
			
		||||
                    while True:
 | 
			
		||||
                        d3 = yield
 | 
			
		||||
 | 
			
		||||
                        if d3 == IAC:
 | 
			
		||||
                            d4 = yield
 | 
			
		||||
                            if d4 == SE:
 | 
			
		||||
                                break
 | 
			
		||||
                            else:
 | 
			
		||||
                                data.append(d4)
 | 
			
		||||
                        else:
 | 
			
		||||
                            data.append(d3)
 | 
			
		||||
 | 
			
		||||
                    self.negotiate(b"".join(data))
 | 
			
		||||
            else:
 | 
			
		||||
                self.received_data(d)
 | 
			
		||||
 | 
			
		||||
    def feed(self, data: bytes) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Feed data to the parser.
 | 
			
		||||
        """
 | 
			
		||||
        for b in data:
 | 
			
		||||
            self._parser.send(int2byte(b))
 | 
			
		||||
							
								
								
									
										349
									
								
								venv/Lib/site-packages/prompt_toolkit/contrib/telnet/server.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										349
									
								
								venv/Lib/site-packages/prompt_toolkit/contrib/telnet/server.py
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,349 @@
 | 
			
		|||
"""
 | 
			
		||||
Telnet server.
 | 
			
		||||
"""
 | 
			
		||||
import asyncio
 | 
			
		||||
import contextvars  # Requires Python3.7!
 | 
			
		||||
import socket
 | 
			
		||||
from asyncio import get_event_loop
 | 
			
		||||
from typing import Awaitable, Callable, List, Optional, Set, TextIO, Tuple, cast
 | 
			
		||||
 | 
			
		||||
from prompt_toolkit.application.current import create_app_session, get_app
 | 
			
		||||
from prompt_toolkit.application.run_in_terminal import run_in_terminal
 | 
			
		||||
from prompt_toolkit.data_structures import Size
 | 
			
		||||
from prompt_toolkit.formatted_text import AnyFormattedText, to_formatted_text
 | 
			
		||||
from prompt_toolkit.input import create_pipe_input
 | 
			
		||||
from prompt_toolkit.output.vt100 import Vt100_Output
 | 
			
		||||
from prompt_toolkit.renderer import print_formatted_text as print_formatted_text
 | 
			
		||||
from prompt_toolkit.styles import BaseStyle, DummyStyle
 | 
			
		||||
 | 
			
		||||
from .log import logger
 | 
			
		||||
from .protocol import (
 | 
			
		||||
    DO,
 | 
			
		||||
    ECHO,
 | 
			
		||||
    IAC,
 | 
			
		||||
    LINEMODE,
 | 
			
		||||
    MODE,
 | 
			
		||||
    NAWS,
 | 
			
		||||
    SB,
 | 
			
		||||
    SE,
 | 
			
		||||
    SEND,
 | 
			
		||||
    SUPPRESS_GO_AHEAD,
 | 
			
		||||
    TTYPE,
 | 
			
		||||
    WILL,
 | 
			
		||||
    TelnetProtocolParser,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
__all__ = [
 | 
			
		||||
    "TelnetServer",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def int2byte(number: int) -> bytes:
 | 
			
		||||
    return bytes((number,))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _initialize_telnet(connection: socket.socket) -> None:
 | 
			
		||||
    logger.info("Initializing telnet connection")
 | 
			
		||||
 | 
			
		||||
    # Iac Do Linemode
 | 
			
		||||
    connection.send(IAC + DO + LINEMODE)
 | 
			
		||||
 | 
			
		||||
    # Suppress Go Ahead. (This seems important for Putty to do correct echoing.)
 | 
			
		||||
    # This will allow bi-directional operation.
 | 
			
		||||
    connection.send(IAC + WILL + SUPPRESS_GO_AHEAD)
 | 
			
		||||
 | 
			
		||||
    # Iac sb
 | 
			
		||||
    connection.send(IAC + SB + LINEMODE + MODE + int2byte(0) + IAC + SE)
 | 
			
		||||
 | 
			
		||||
    # IAC Will Echo
 | 
			
		||||
    connection.send(IAC + WILL + ECHO)
 | 
			
		||||
 | 
			
		||||
    # Negotiate window size
 | 
			
		||||
    connection.send(IAC + DO + NAWS)
 | 
			
		||||
 | 
			
		||||
    # Negotiate terminal type
 | 
			
		||||
    # Assume the client will accept the negociation with `IAC +  WILL + TTYPE`
 | 
			
		||||
    connection.send(IAC + DO + TTYPE)
 | 
			
		||||
 | 
			
		||||
    # We can then select the first terminal type supported by the client,
 | 
			
		||||
    # which is generally the best type the client supports
 | 
			
		||||
    # The client should reply with a `IAC + SB  + TTYPE + IS + ttype + IAC + SE`
 | 
			
		||||
    connection.send(IAC + SB + TTYPE + SEND + IAC + SE)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class _ConnectionStdout:
 | 
			
		||||
    """
 | 
			
		||||
    Wrapper around socket which provides `write` and `flush` methods for the
 | 
			
		||||
    Vt100_Output output.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, connection: socket.socket, encoding: str) -> None:
 | 
			
		||||
        self._encoding = encoding
 | 
			
		||||
        self._connection = connection
 | 
			
		||||
        self._errors = "strict"
 | 
			
		||||
        self._buffer: List[bytes] = []
 | 
			
		||||
 | 
			
		||||
    def write(self, data: str) -> None:
 | 
			
		||||
        data = data.replace("\n", "\r\n")
 | 
			
		||||
        self._buffer.append(data.encode(self._encoding, errors=self._errors))
 | 
			
		||||
        self.flush()
 | 
			
		||||
 | 
			
		||||
    def isatty(self) -> bool:
 | 
			
		||||
        return True
 | 
			
		||||
 | 
			
		||||
    def flush(self) -> None:
 | 
			
		||||
        try:
 | 
			
		||||
            self._connection.send(b"".join(self._buffer))
 | 
			
		||||
        except socket.error as e:
 | 
			
		||||
            logger.warning("Couldn't send data over socket: %s" % e)
 | 
			
		||||
 | 
			
		||||
        self._buffer = []
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def encoding(self) -> str:
 | 
			
		||||
        return self._encoding
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def errors(self) -> str:
 | 
			
		||||
        return self._errors
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TelnetConnection:
 | 
			
		||||
    """
 | 
			
		||||
    Class that represents one Telnet connection.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self,
 | 
			
		||||
        conn: socket.socket,
 | 
			
		||||
        addr: Tuple[str, int],
 | 
			
		||||
        interact: Callable[["TelnetConnection"], Awaitable[None]],
 | 
			
		||||
        server: "TelnetServer",
 | 
			
		||||
        encoding: str,
 | 
			
		||||
        style: Optional[BaseStyle],
 | 
			
		||||
    ) -> None:
 | 
			
		||||
 | 
			
		||||
        self.conn = conn
 | 
			
		||||
        self.addr = addr
 | 
			
		||||
        self.interact = interact
 | 
			
		||||
        self.server = server
 | 
			
		||||
        self.encoding = encoding
 | 
			
		||||
        self.style = style
 | 
			
		||||
        self._closed = False
 | 
			
		||||
        self._ready = asyncio.Event()
 | 
			
		||||
        self.vt100_output = None
 | 
			
		||||
 | 
			
		||||
        # Create "Output" object.
 | 
			
		||||
        self.size = Size(rows=40, columns=79)
 | 
			
		||||
 | 
			
		||||
        # Initialize.
 | 
			
		||||
        _initialize_telnet(conn)
 | 
			
		||||
 | 
			
		||||
        # Create input.
 | 
			
		||||
        self.vt100_input = create_pipe_input()
 | 
			
		||||
 | 
			
		||||
        # Create output.
 | 
			
		||||
        def get_size() -> Size:
 | 
			
		||||
            return self.size
 | 
			
		||||
 | 
			
		||||
        self.stdout = cast(TextIO, _ConnectionStdout(conn, encoding=encoding))
 | 
			
		||||
 | 
			
		||||
        def data_received(data: bytes) -> None:
 | 
			
		||||
            """ TelnetProtocolParser 'data_received' callback """
 | 
			
		||||
            self.vt100_input.send_bytes(data)
 | 
			
		||||
 | 
			
		||||
        def size_received(rows: int, columns: int) -> None:
 | 
			
		||||
            """ TelnetProtocolParser 'size_received' callback """
 | 
			
		||||
            self.size = Size(rows=rows, columns=columns)
 | 
			
		||||
            if self.vt100_output is not None:
 | 
			
		||||
                get_app()._on_resize()
 | 
			
		||||
 | 
			
		||||
        def ttype_received(ttype: str) -> None:
 | 
			
		||||
            """ TelnetProtocolParser 'ttype_received' callback """
 | 
			
		||||
            self.vt100_output = Vt100_Output(
 | 
			
		||||
                self.stdout, get_size, term=ttype, write_binary=False
 | 
			
		||||
            )
 | 
			
		||||
            self._ready.set()
 | 
			
		||||
 | 
			
		||||
        self.parser = TelnetProtocolParser(data_received, size_received, ttype_received)
 | 
			
		||||
        self.context: Optional[contextvars.Context] = None
 | 
			
		||||
 | 
			
		||||
    async def run_application(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Run application.
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        def handle_incoming_data() -> None:
 | 
			
		||||
            data = self.conn.recv(1024)
 | 
			
		||||
            if data:
 | 
			
		||||
                self.feed(data)
 | 
			
		||||
            else:
 | 
			
		||||
                # Connection closed by client.
 | 
			
		||||
                logger.info("Connection closed by client. %r %r" % self.addr)
 | 
			
		||||
                self.close()
 | 
			
		||||
 | 
			
		||||
        # Add reader.
 | 
			
		||||
        loop = get_event_loop()
 | 
			
		||||
        loop.add_reader(self.conn, handle_incoming_data)
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            # Wait for v100_output to be properly instantiated
 | 
			
		||||
            await self._ready.wait()
 | 
			
		||||
            with create_app_session(input=self.vt100_input, output=self.vt100_output):
 | 
			
		||||
                self.context = contextvars.copy_context()
 | 
			
		||||
                await self.interact(self)
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            print("Got %s" % type(e).__name__, e)
 | 
			
		||||
            import traceback
 | 
			
		||||
 | 
			
		||||
            traceback.print_exc()
 | 
			
		||||
            raise
 | 
			
		||||
        finally:
 | 
			
		||||
            self.close()
 | 
			
		||||
 | 
			
		||||
    def feed(self, data: bytes) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Handler for incoming data. (Called by TelnetServer.)
 | 
			
		||||
        """
 | 
			
		||||
        self.parser.feed(data)
 | 
			
		||||
 | 
			
		||||
    def close(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Closed by client.
 | 
			
		||||
        """
 | 
			
		||||
        if not self._closed:
 | 
			
		||||
            self._closed = True
 | 
			
		||||
 | 
			
		||||
            self.vt100_input.close()
 | 
			
		||||
            get_event_loop().remove_reader(self.conn)
 | 
			
		||||
            self.conn.close()
 | 
			
		||||
 | 
			
		||||
    def send(self, formatted_text: AnyFormattedText) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Send text to the client.
 | 
			
		||||
        """
 | 
			
		||||
        if self.vt100_output is None:
 | 
			
		||||
            return
 | 
			
		||||
        formatted_text = to_formatted_text(formatted_text)
 | 
			
		||||
        print_formatted_text(
 | 
			
		||||
            self.vt100_output, formatted_text, self.style or DummyStyle()
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def send_above_prompt(self, formatted_text: AnyFormattedText) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Send text to the client.
 | 
			
		||||
        This is asynchronous, returns a `Future`.
 | 
			
		||||
        """
 | 
			
		||||
        formatted_text = to_formatted_text(formatted_text)
 | 
			
		||||
        return self._run_in_terminal(lambda: self.send(formatted_text))
 | 
			
		||||
 | 
			
		||||
    def _run_in_terminal(self, func: Callable[[], None]) -> None:
 | 
			
		||||
        # Make sure that when an application was active for this connection,
 | 
			
		||||
        # that we print the text above the application.
 | 
			
		||||
        if self.context:
 | 
			
		||||
            self.context.run(run_in_terminal, func)
 | 
			
		||||
        else:
 | 
			
		||||
            raise RuntimeError("Called _run_in_terminal outside `run_application`.")
 | 
			
		||||
 | 
			
		||||
    def erase_screen(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Erase the screen and move the cursor to the top.
 | 
			
		||||
        """
 | 
			
		||||
        if self.vt100_output is None:
 | 
			
		||||
            return
 | 
			
		||||
        self.vt100_output.erase_screen()
 | 
			
		||||
        self.vt100_output.cursor_goto(0, 0)
 | 
			
		||||
        self.vt100_output.flush()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def _dummy_interact(connection: TelnetConnection) -> None:
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TelnetServer:
 | 
			
		||||
    """
 | 
			
		||||
    Telnet server implementation.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(
 | 
			
		||||
        self,
 | 
			
		||||
        host: str = "127.0.0.1",
 | 
			
		||||
        port: int = 23,
 | 
			
		||||
        interact: Callable[[TelnetConnection], Awaitable[None]] = _dummy_interact,
 | 
			
		||||
        encoding: str = "utf-8",
 | 
			
		||||
        style: Optional[BaseStyle] = None,
 | 
			
		||||
    ) -> None:
 | 
			
		||||
 | 
			
		||||
        self.host = host
 | 
			
		||||
        self.port = port
 | 
			
		||||
        self.interact = interact
 | 
			
		||||
        self.encoding = encoding
 | 
			
		||||
        self.style = style
 | 
			
		||||
        self._application_tasks: List[asyncio.Task] = []
 | 
			
		||||
 | 
			
		||||
        self.connections: Set[TelnetConnection] = set()
 | 
			
		||||
        self._listen_socket: Optional[socket.socket] = None
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def _create_socket(cls, host: str, port: int) -> socket.socket:
 | 
			
		||||
        # Create and bind socket
 | 
			
		||||
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 | 
			
		||||
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
 | 
			
		||||
        s.bind((host, port))
 | 
			
		||||
 | 
			
		||||
        s.listen(4)
 | 
			
		||||
        return s
 | 
			
		||||
 | 
			
		||||
    def start(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Start the telnet server.
 | 
			
		||||
        Don't forget to call `loop.run_forever()` after doing this.
 | 
			
		||||
        """
 | 
			
		||||
        self._listen_socket = self._create_socket(self.host, self.port)
 | 
			
		||||
        logger.info(
 | 
			
		||||
            "Listening for telnet connections on %s port %r", self.host, self.port
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        get_event_loop().add_reader(self._listen_socket, self._accept)
 | 
			
		||||
 | 
			
		||||
    async def stop(self) -> None:
 | 
			
		||||
        if self._listen_socket:
 | 
			
		||||
            get_event_loop().remove_reader(self._listen_socket)
 | 
			
		||||
            self._listen_socket.close()
 | 
			
		||||
 | 
			
		||||
        # Wait for all applications to finish.
 | 
			
		||||
        for t in self._application_tasks:
 | 
			
		||||
            t.cancel()
 | 
			
		||||
 | 
			
		||||
        for t in self._application_tasks:
 | 
			
		||||
            await t
 | 
			
		||||
 | 
			
		||||
    def _accept(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Accept new incoming connection.
 | 
			
		||||
        """
 | 
			
		||||
        if self._listen_socket is None:
 | 
			
		||||
            return  # Should not happen. `_accept` is called after `start`.
 | 
			
		||||
 | 
			
		||||
        conn, addr = self._listen_socket.accept()
 | 
			
		||||
        logger.info("New connection %r %r", *addr)
 | 
			
		||||
 | 
			
		||||
        connection = TelnetConnection(
 | 
			
		||||
            conn, addr, self.interact, self, encoding=self.encoding, style=self.style
 | 
			
		||||
        )
 | 
			
		||||
        self.connections.add(connection)
 | 
			
		||||
 | 
			
		||||
        # Run application for this connection.
 | 
			
		||||
        async def run() -> None:
 | 
			
		||||
            logger.info("Starting interaction %r %r", *addr)
 | 
			
		||||
            try:
 | 
			
		||||
                await connection.run_application()
 | 
			
		||||
            except Exception as e:
 | 
			
		||||
                print(e)
 | 
			
		||||
            finally:
 | 
			
		||||
                self.connections.remove(connection)
 | 
			
		||||
                self._application_tasks.remove(task)
 | 
			
		||||
                logger.info("Stopping interaction %r %r", *addr)
 | 
			
		||||
 | 
			
		||||
        task = get_event_loop().create_task(run())
 | 
			
		||||
        self._application_tasks.append(task)
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue