Giter Club home page Giter Club logo

telnetlib3's Introduction

Coveralls Code Coverage

Latest Version

Downloads

Introduction

telnetlib3 is a Telnet Client and Server library for python. This project requires python 3.7 and later, using the asyncio module.

Quick Example

Authoring a Telnet Server using Streams interface that offers a basic war game:

import asyncio, telnetlib3

async def shell(reader, writer):
    writer.write('\r\nWould you like to play a game? ')
    inp = await reader.read(1)
    if inp:
        writer.echo(inp)
        writer.write('\r\nThey say the only way to win '
                     'is to not play at all.\r\n')
        await writer.drain()
    writer.close()

loop = asyncio.get_event_loop()
coro = telnetlib3.create_server(port=6023, shell=shell)
server = loop.run_until_complete(coro)
loop.run_until_complete(server.wait_closed())

Authoring a Telnet Client that plays the war game with this server:

import asyncio, telnetlib3

async def shell(reader, writer):
    while True:
        # read stream until '?' mark is found
        outp = await reader.read(1024)
        if not outp:
            # End of File
            break
        elif '?' in outp:
            # reply all questions with 'y'.
            writer.write('y')

        # display all server output
        print(outp, flush=True)

    # EOF
    print()

loop = asyncio.get_event_loop()
coro = telnetlib3.open_connection('localhost', 6023, shell=shell)
reader, writer = loop.run_until_complete(coro)
loop.run_until_complete(writer.protocol.waiter_closed)

Command-line

Two command-line scripts are distributed with this package.

telnetlib3-client

Small terminal telnet client. Some example destinations and options:

telnetlib3-client nethack.alt.org
telnetlib3-client --encoding=cp437 --force-binary blackflag.acid.org
telnetlib3-client htc.zapto.org

telnetlib3-server

Telnet server providing the default debugging shell. This provides a simple shell server that allows introspection of the session's values, for example:

tel:sh> help
quit, writer, slc, toggle [option|all], reader, proto

tel:sh> writer
<TelnetWriter server mode:kludge +lineflow -xon_any +slc_sim server-will:BINARY,ECHO,SGA client-will:BINARY,NAWS,NEW_ENVIRON,TTYPE>

tel:sh> reader
<TelnetReaderUnicode encoding='utf8' limit=65536 buflen=0 eof=False>

tel:sh> toggle all
wont echo.
wont suppress go-ahead.
wont outbinary.
dont inbinary.
xon-any enabled.
lineflow disabled.

tel:sh> reader
<TelnetReaderUnicode encoding='US-ASCII' limit=65536 buflen=1 eof=False>

tel:sh> writer
<TelnetWriter server mode:local -lineflow +xon_any +slc_sim client-will:NAWS,NEW_ENVIRON,TTYPE>

Both command-line scripts accept argument --shell=my_module.fn_shell describing a python module path to a coroutine of signature shell(reader, writer), just as the above examples.

Features

The following RFC specifications are implemented:

  • rfc-727, "Telnet Logout Option," Apr 1977.
  • rfc-779, "Telnet Send-Location Option", Apr 1981.
  • rfc-854, "Telnet Protocol Specification", May 1983.
  • rfc-855, "Telnet Option Specifications", May 1983.
  • rfc-856, "Telnet Binary Transmission", May 1983.
  • rfc-857, "Telnet Echo Option", May 1983.
  • rfc-858, "Telnet Suppress Go Ahead Option", May 1983.
  • rfc-859, "Telnet Status Option", May 1983.
  • rfc-860, "Telnet Timing mark Option", May 1983.
  • rfc-885, "Telnet End of Record Option", Dec 1983.
  • rfc-1073, "Telnet Window Size Option", Oct 1988.
  • rfc-1079, "Telnet Terminal Speed Option", Dec 1988.
  • rfc-1091, "Telnet Terminal-Type Option", Feb 1989.
  • rfc-1096, "Telnet X Display Location Option", Mar 1989.
  • rfc-1123, "Requirements for Internet Hosts", Oct 1989.
  • rfc-1184, "Telnet Linemode Option (extended options)", Oct 1990.
  • rfc-1372, "Telnet Remote Flow Control Option", Oct 1992.
  • rfc-1408, "Telnet Environment Option", Jan 1993.
  • rfc-1571, "Telnet Environment Option Interoperability Issues", Jan 1994.
  • rfc-1572, "Telnet Environment Option", Jan 1994.
  • rfc-2066, "Telnet Charset Option", Jan 1997.

Further Reading

Further documentation available at https://telnetlib3.readthedocs.org/

telnetlib3's People

Contributors

aidanpb avatar albireox avatar andrewnelis avatar fried avatar gtaylor avatar hughpyle avatar joel-hall avatar jquast avatar sethb0 avatar tarkatronic avatar tehmaze avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

telnetlib3's Issues

installtion fails with pip > 1.5

With pip 1.5.6, package installation runs smoothly:

$ bin/pip-1.5.6 install telnetlib3 
Downloading/unpacking telnetlib3
  Downloading telnetlib3-0.2.3.tar.gz (96kB): 96kB downloaded
  Running setup.py (path:/home/ckauhaus/code/backy/build/telnetlib3/setup.py) egg_info for package telnetlib3

Requirement already satisfied (use --upgrade to upgrade): asyncio in ./lib/python3.4/site-packages (from telnetlib3)
Installing collected packages: telnetlib3
  Running setup.py install for telnetlib3
    changing mode of build/scripts-3.4/telnet-client from 644 to 755
    changing mode of build/scripts-3.4/telnet-server from 644 to 755
    changing mode of build/scripts-3.4/telnet-talker from 644 to 755

    changing mode of /home/ckauhaus/code/backy/bin/telnet-client to 755
    changing mode of /home/ckauhaus/code/backy/bin/telnet-server to 755
    changing mode of /home/ckauhaus/code/backy/bin/telnet-talker to 755
Successfully installed telnetlib3

However, using newer pip versions just spit out installation errors due to an missing keyword argument in parse_requirements():

$ bin/pip install telnetlib3
Collecting telnetlib3
  Using cached telnetlib3-0.2.3.tar.gz
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):
      File "<string>", line 20, in <module>
      File "/tmp/pip-build-rtq64401/telnetlib3/setup.py", line 28, in <module>
        install_requires = [str(req.req) for req in requirements]
      File "/tmp/pip-build-rtq64401/telnetlib3/setup.py", line 28, in <listcomp>
        install_requires = [str(req.req) for req in requirements]
      File "/home/ckauhaus/code/backy/lib/python3.4/site-packages/pip/req/req_file.py", line 72, in parse_requirements
        "parse_requirements() missing 1 required keyword argument: "
    TypeError: parse_requirements() missing 1 required keyword argument: 'session'

    ----------------------------------------
Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-rtq64401/telnetlib3

I'm using Python 3.4.1.

server.TelnetServer.connection_made ValueError with ipv6 connections

  File "site-packages/telnetlib3/server.py", line 146, in connection_made
    transport.get_extra_info('peername'))
ValueError: too many values to unpack (expected 2)

So on ipv6 peername is a 4 element tuple, on ipv4 its a two element tuple.

# The offending piece of code
        self._client_ip, self._client_port = (
            transport.get_extra_info('peername'))

A simple fix is to just splice the tuple down to two elements and throw the extra v6 info out

        self._client_ip, self._client_port = (
            transport.get_extra_info('peername')[:2])

docs/rfcs.rst with global links

we should be able to use phrase, 'rfc1779_', for example, in any docstring or sphinx .rst document which appropriately links to a stable url.

Reversed roles with CHARSET negotiation

It seems that telnetlib3 sends the CHARSET subnegotiation upon receiving a WILL CHARSET (and waits for the subnegotiation after receiving a DO CHARSET).

RFC 2066 says about the IAC SB CHARSET REQUEST:

This message initiates a new CHARSET subnegotiation. It can only be sent by a side that has received a DO CHARSET message and sent a WILL CHARSET message (in either order).

This is exactly the other way around.

And it also seems that telnetlib3 doesn't respond to a WILL CHARSET with a DO CHARSET, which I think it should.

Problem with COM-PORT-CONTROL subnegotiation

I have a server that sends a COM-PORT-CONTROL subnegotiation with every message. In particular I always receive bytes

[255 250 44 106 96 255 240]

before the actual message. My understanding is that this correspond to the server communicating the linestate. I've tried several things to disable the sending of that subnegotiation but either I'm not doing it correctly or the server ignores those commands.

The problem is that telnetlib3 detects the subnegotiation but because it's not one of the ones that it knows how to handle it raises

ValueError: SB unhandled: cmd=COM_PORT_OPTION, buf=deque([b',', b'j', b'`'])

This doesn't actually crash the code (I still get the message) but I'm unable to catch the exception or disable it, which means it's triggered every time I receive a message.

Would it be possible to either:

a) Handle the COM-PORT-CONTROL subnegotiation (even if it's with an empty fn_call).
b) Provide a method to ignore the exception, or maybe make it a warning that can be ignored.

I can provide a PR for either option if I'm told what's preferred.

Timeout crashes the telnetlib server

Hello,
I've encountered a server crash after the timeout occurred. I think the issue was introduced after he circular reference was broken: 782c677

Let's start a simple telnetlib sever with timeout set to 5 seconds.

import asyncio, telnetlib3

@asyncio.coroutine
def shell(reader, writer):
    writer.write('\r\nWould you like to play a game? ')
    inp = yield from reader.read(1)
    if inp:
        writer.echo(inp)
        writer.write('\r\nThey say the only way to win '
                     'is to not play at all.\r\n')
        yield from writer.drain()
    writer.close()


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    coro = telnetlib3.create_server(port=8023, shell=shell, timeout=5)
    server = loop.run_until_complete(coro)
    loop.run_until_complete(server.wait_closed())

After the server is started and client makes a connection (telnet localhost 8023). After 5 seconds, the server crashes with:

Task exception was never retrieved
future: <Task finished name='Task-5' coro=<shell() done, defined at ex.py:3> exception=TypeError("'NoneType' object is not callable")>
Traceback (most recent call last):
  ...
 <home>/.local/lib/python3.8/site-packages/telnetlib3/stream_reader.py", line 125, in decode
    encoding = self.fn_encoding(incoming=True)
TypeError: 'NoneType' object is not callable

Could you verify that this is a bug?
IMHO the TelnetReaderUnicode.read() should be able to take the timeout into account. What do you think?

How to close connection properly?

If I get client_reader/writer with:

client_reader, client_writer = telnetlib3.open_connection(host=self._host, port=self._port)

How to close the connection properly?
I expected this to work:

client_writer.close()
client_writer.wait_closed()

The first command passes with no exceptions, but on the second one I get:

...
/opt/python/miniconda3/envs/jlab/lib/python3.8/asyncio/streams.py in wait_closed(self)
    358     async def wait_closed(self):
--> 359         await self._protocol._get_close_waiter(self)
    360 

AttributeError: 'NoneType' object has no attribute '_get_close_waiter'

What is the proper way of closing the connection?
Is client_writer.close() enough?

Or maybe I should:

client_writer.write('exit" + '\r\n')
await asyncio.wait_for(client_writer.drain(), timeout=timeout_secs)

Regards

AttributeError on wait_closed() after close()

Hi,

thanks for telnetlib3.

I found this problem was already reported in #55 and #62, but since they were closed issues, I opened a new one:

    try:
        reader, writer = await telnetlib3.open_connection(host, port)
    ...
    ...
    except asyncio.TimeoutError:
        print("Connection or operation timed out")

    except Exception as e:
        print(f"An error occurred: {str(e)}")

    finally:
        if not writer.transport.is_closing():
            writer.close()
            await writer.wait_closed()

Throws this error:

Traceback (most recent call last):
  File "d:\Google Drive\Home Assistant\Dev\ha-elios4you\custom_components\elios4you\test\e4u.py", line 58, in <module>
    asyncio.run(main())
  File "C:\Users\aless\AppData\Local\Programs\Python\Python310\lib\asyncio\runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "C:\Users\aless\AppData\Local\Programs\Python\Python310\lib\asyncio\base_events.py", line 649, in run_until_complete
    return future.result()
  File "d:\Google Drive\Home Assistant\Dev\ha-elios4you\custom_components\elios4you\test\e4u.py", line 55, in main
    await writer.wait_closed()
AttributeError: 'TelnetWriterUnicode' object has no attribute 'wait_closed'

bin/telnet-client should warn when stdout is ascii codec ?

:$$$: .,g

2014-03-03 11:03:12,205 ERROR base_events.py:677 Exception in callback <bound method _SelectorSocketTransport._read_ready of <asyncio.selector_events._SelectorSocketTransport object at 0x7f9578c1bc10>>()
handle: Handle(<bound method _SelectorSocketTransport._read_ready of <asyncio.selector_events._SelectorSocketTransport object at 0x7f9578c1bc10>>, ())
Traceback (most recent call last):
File "/usr/lib/python3.3/site-packages/asyncio/events.py", line 39, in _run
self._callback(*self._args)
File "/usr/lib/python3.3/site-packages/asyncio/selector_events.py", line 458, in _read_ready
self._protocol.data_received(data)
File "/usr/lib/python3.3/site-packages/telnetlib3/client.py", line 424, in data_received
self.shell.feed_byte(byte)
File "/usr/lib/python3.3/site-packages/telnetlib3/conio.py", line 83, in feed_byte
self.write(ucs)
File "/usr/lib/python3.3/site-packages/telnetlib3/conio.py", line 130, in write
self.stream_out.write(string)
UnicodeEncodeError: 'ascii' codec can't encode character '\ufffd' in position 0: ordinal not in range(128)

Am I doing something wrong? (Can't use in GUI application)

I've got the following Python code but for some reason telnetlib3 is causing asyncio errors (that is, saying that no asyncio runtime is available when there is one). I'm using Toga, which is inherently compatible with asyncio, yet I receive this error:

Exception in callback TelnetClient.connection_made(<_ProactorSoc...sport fd=1900>)
handle: <Handle TelnetClient.connection_made(<_ProactorSoc...sport fd=1900>)>
Traceback (most recent call last):
  File "C:\Users\ethin\AppData\Local\Programs\Python\Python312\Lib\asyncio\events.py", line 84, in _run
    self._context.run(self._callback, *self._args)
  File "C:\Users\ethin\source\venvs\mudclient\Lib\site-packages\telnetlib3\client.py", line 74, in connection_made
    super().connection_made(transport)
  File "C:\Users\ethin\source\venvs\mudclient\Lib\site-packages\telnetlib3\client_base.py", line 137, in connection_made
    self.reader = reader_factory(**reader_kwds)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\ethin\source\venvs\mudclient\Lib\site-packages\telnetlib3\stream_reader.py", line 497, in __init__
    super().__init__(limit=limit)
  File "C:\Users\ethin\source\venvs\mudclient\Lib\site-packages\telnetlib3\stream_reader.py", line 39, in __init__
    self._loop = asyncio.get_event_loop_policy().get_event_loop()
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\ethin\AppData\Local\Programs\Python\Python312\Lib\asyncio\events.py", line 698, in get_event_loop
    raise RuntimeError('There is no current event loop in thread %r.'
RuntimeError: There is no current event loop in thread 'Dummy-1'.
  File "C:\Users\ethin\source\venvs\mudclient\Lib\site-packages\telnetlib3\client_base.py", line 185, in data_received
    recv_inband = self.writer.feed_byte(bytes([byte]))
                  ^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'feed_byte'
  File "C:\Users\ethin\source\venvs\mudclient\Lib\site-packages\telnetlib3\client_base.py", line 185, in data_received
    recv_inband = self.writer.feed_byte(bytes([byte]))
                  ^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'feed_byte'
# Infinite more AttributeError exceptions

I'm unsure what I'm doing wrong. I set the shell to None, specifically because I handle the shell differently, but other than that I'm unsure what the problem is. The full code follows:

import asyncio
from toga.style import Pack
from toga.style.pack import COLUMN, ROW
import toga
import telnetlib3
from cytolk import tolk

class TelnetApp(toga.App):
    def startup(self):
        self.writer = None
        self.main_window = toga.MainWindow(title=self.formal_name)
        self.command_input = toga.TextInput(placeholder='Enter command here...', on_confirm =self.on_confirm)
        self.session_output = toga.MultilineTextInput(readonly=True, style=Pack(flex=1))
        self.close_button = toga.Button('Close', on_press=self.close_app, style=Pack(padding=5))
        self.command_box = toga.Box(style=Pack(direction=ROW, padding=5), children=[self.command_input])
        self.output_box = toga.Box(style=Pack(direction=COLUMN, padding=5), children=[self.session_output])
        self.button_box = toga.Box(style=Pack(direction=ROW, padding=5), children=[self.close_button])
        self.main_container = toga.Box(
            style=Pack(direction=COLUMN, padding=10),
            children=[self.command_box, self.output_box, self.button_box]
        )
        self.main_window.content = self.main_container
        self.main_window.show()
        self.loop.create_task(self.start_telnet())

    async def start_telnet(self):
        if self.writer is None:
            reader, self.writer = await telnetlib3.open_connection("localhost", 4000, shell=None)
            while True:
                data = await reader.read(1024)
                if not data:
                    break
                if data:
                    self.session_output.value += data
                    tolk.speak(data)

    async def on_confirm(self, event, **kwargs):
        if self.command_input.value:
            command = self.command_input.value.strip()
            await writer.write(command.encode('UTF-8', errors='replace') + b"\r\n")
            self.command_input.value = ""

    def close_app(self, widget, **kwargs):
        tolk.unload()
        self.exit()

def main():
    tolk.load()
    return TelnetApp('Mud Client', 'mudclient')

if __name__ == '__main__':
    main().main_loop()

Do I need to explicitly set the shell to a callback?

Script telnetlib3-client not working

Whenever I'm trying to run the telnetlib3-client script, e.g. telnetlib3-client 127.0.0.1, I get the following error:

Traceback (most recent call last):
  File "/Users/timus/Coding/envs/py3.11/bin/telnetlib3-client", line 8, in <module>
    sys.exit(main())
             ^^^^^^
  File "/Users/timus/Coding/envs/py3.11/lib/python3.11/site-packages/telnetlib3/client.py", line 450, in main
    asyncio.run(run_client())
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/asyncio/runners.py", line 190, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/asyncio/runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/asyncio/base_events.py", line 650, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "/Users/timus/Coding/envs/py3.11/lib/python3.11/site-packages/telnetlib3/client.py", line 370, in run_client
    key_values=accessories.repr_mapping(kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/timus/Coding/envs/py3.11/lib/python3.11/site-packages/telnetlib3/accessories.py", line 92, in repr_mapping
    "=".join(f"{key}={shlex.quote(value)}" for key, value in mapping.items())
  File "/Users/timus/Coding/envs/py3.11/lib/python3.11/site-packages/telnetlib3/accessories.py", line 92, in <genexpr>
    "=".join(f"{key}={shlex.quote(value)}" for key, value in mapping.items())
                      ^^^^^^^^^^^^^^^^^^
  File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/shlex.py", line 329, in quote
    if _find_unsafe(s) is None:
       ^^^^^^^^^^^^^^^
TypeError: expected string or bytes-like object, got 'int'

This happens on Linux as well as macOS, for CPython 3.8 - 3.11.

Documentation missing for TelnetStreamReader and TelnetStreamWriter

At the bottom of this page there is:

Return (reader, writer):
    The reader is aย TelnetStreamReaderย instance, the writer is aย TelnetStreamWriterinstance.

Both TelnetStreamReader and TelnetStreamWriter are not clickable. When i search for TelnetStreamReader i get client, stream_reader and server. The stream_reader page has class TelnetReader and the other two pages also don't contain the class definition of TelnetStreamReader

TelnetReader returns incorrect class string, instead of bytes

Hi,
I've noticed that the TelnetReader is inheriting from asyncio.StreamReader, however its read method returns str, instead of bytes like its parent.
https://github.com/jquast/telnetlib3/blob/master/telnetlib3/stream_reader.py#L147
https://github.com/python/typeshed/blob/5d553c9584eb86793cfa61019ddee5c7b62bc286/stdlib/3/asyncio/streams.pyi#L101
This causes issues when we try to type check the code, but also adds confusion, since classes which implement asyncio.StreamReader interface now follow different rules.

Missing setuptools in requirements list

When running a unit test that imports telnetlib3, the unit test runs fine when ran directly under Pytest (./venv/bin/pytest), but fails when ran under Pants (./pants test). Importing telnetlib3 within Pants errors outs saying that there's no module named pkg_resources. Adding the below snippet to the Pants build file:

        "telnetlib3": {
            "dependencies": [
                ":reqs#setuptools",
            ],
        },

is the only way to successfully import telnetlib3 within Pants. This seems to indicate missing dependencies in the telnetlib3 requirements list. Namely, it seems that setuptools is missing in the requirements list.

7Import does not work on python 3.6

Hello,

I want to test your library, but I'm not able to import it.

Python 3.7.0 (default, Oct 16 2018, 19:43:19)
[Clang 10.0.0 (clang-1000.11.45.2)] on darwin
Type "help", "copyright", "credits" or "license" for more information.

import asyncio, telnetlib3
Traceback (most recent call last):
File "", line 1, in
File "/usr/local/lib/python3.7/site-packages/telnetlib3/init.py", line 3, in
from .server_base import * # noqa
File "/usr/local/lib/python3.7/site-packages/telnetlib3/server_base.py", line 8, in
from .stream_writer import (TelnetWriter, TelnetWriterUnicode)
File "/usr/local/lib/python3.7/site-packages/telnetlib3/stream_writer.py", line 9, in
from . import slc
File "/usr/local/lib/python3.7/site-packages/telnetlib3/slc.py", line 4, in
from .accessories import eightbits, name_unicode
File "/usr/local/lib/python3.7/site-packages/telnetlib3/accessories.py", line 94
task = asyncio.async

^

I'm running python 3.7.0 on MacOS Mojave

cannot recv WILL ECHO on server end

I'm currently debugging an issue when a telnet connection is established form a (external) java client to our python telnet server. For this I've tried to reproduce this issue by compiling a minimal java telnet client (https://commons.apache.org/proper/commons-net/examples/telnet/TelnetClientExample.java) and testing it on our server. In the logs of our server I can see an exception when a connection to the server is established:

[11 May 2023 16:01:23.182] telnetlib3.server_base|     INFO | Connection from <Peer 127.0.0.1 41170>
[11 May 2023 16:01:23.182] telnetlib3.stream_writer|    DEBUG | pending_option[DO + TTYPE] = True
[11 May 2023 16:01:23.182] telnetlib3.stream_writer|    DEBUG | send IAC DO TTYPE
[11 May 2023 16:01:23.182] telnetlib3.stream_writer|    DEBUG | recv IAC WILL ECHO
[11 May 2023 16:01:23.182] telnetlib3.stream_writer|    DEBUG | WILL ECHO unsolicited
[11 May 2023 16:01:23.182] telnetlib3.stream_writer|    DEBUG | handle_will(ECHO)
[11 May 2023 16:01:23.183] telnetlib3.server_base|     WARN |   File "/home/thomashisch/miniconda3/envs/py311_2/lib/python3.11/site-packages/telnetlib3/server_base.py", line 179, in data_received
[11 May 2023 16:01:23.183] telnetlib3.server_base|     WARN |     recv_inband = self.writer.feed_byte(bytes([byte]))
[11 May 2023 16:01:23.183] telnetlib3.server_base|     WARN |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[11 May 2023 16:01:23.183] telnetlib3.server_base|     WARN |   File "/home/thomashisch/miniconda3/envs/py311_2/lib/python3.11/site-packages/telnetlib3/stream_writer.py", line 453, in feed_byte
[11 May 2023 16:01:23.183] telnetlib3.server_base|     WARN |     self.handle_will(opt)
[11 May 2023 16:01:23.183] telnetlib3.server_base|     WARN |   File "/home/thomashisch/miniconda3/envs/py311_2/lib/python3.11/site-packages/telnetlib3/stream_writer.py", line 1503, in handle_will
[11 May 2023 16:01:23.183] telnetlib3.server_base|     WARN |     raise ValueError("cannot recv WILL ECHO on server end")
[11 May 2023 16:01:23.183] telnetlib3.server_base|     WARN | ValueError: cannot recv WILL ECHO on server end

In this ticket I want to report that the exception is unexpected, because we don't get this exception with other telnet clients.

With a GNU telnet command from (Ubuntu host) the following is output:

[11 May 2023 16:13:06.323] telnetlib3.server_base|     INFO | Connection from <Peer 127.0.0.1 46088>
[11 May 2023 16:13:06.324] telnetlib3.stream_writer|    DEBUG | pending_option[DO + TTYPE] = True
[11 May 2023 16:13:06.324] telnetlib3.stream_writer|    DEBUG | send IAC DO TTYPE
[11 May 2023 16:13:06.324] telnetlib3.stream_writer|    DEBUG | recv IAC WILL TTYPE
[11 May 2023 16:13:06.324] telnetlib3.stream_writer|    DEBUG | handle_will(TTYPE)
[11 May 2023 16:13:06.325] telnetlib3.stream_writer|    DEBUG | remote_option[TTYPE] = True
[11 May 2023 16:13:06.325] telnetlib3.stream_writer|    DEBUG | send IAC SB TTYPE SEND IAC SE
[11 May 2023 16:13:06.325] telnetlib3.stream_writer|    DEBUG | pending_option[SB + TTYPE] = True
[11 May 2023 16:13:06.325] telnetlib3.stream_writer|    DEBUG | pending_option[DO + TTYPE] = False
[11 May 2023 16:13:06.325] telnetlib3.server_base|    DEBUG | begin advanced negotiation
[11 May 2023 16:13:06.325] telnetlib3.stream_writer|    DEBUG | pending_option[WILL + SGA] = True
[11 May 2023 16:13:06.325] telnetlib3.stream_writer|    DEBUG | send IAC WILL SGA
[11 May 2023 16:13:06.325] telnetlib3.stream_writer|    DEBUG | pending_option[WILL + ECHO] = True
[11 May 2023 16:13:06.325] telnetlib3.stream_writer|    DEBUG | send IAC WILL ECHO
[11 May 2023 16:13:06.326] telnetlib3.stream_writer|    DEBUG | pending_option[WILL + BINARY] = True
[11 May 2023 16:13:06.326] telnetlib3.stream_writer|    DEBUG | send IAC WILL BINARY
[11 May 2023 16:13:06.326] telnetlib3.stream_writer|    DEBUG | pending_option[DO + NEW_ENVIRON] = True
[11 May 2023 16:13:06.326] telnetlib3.stream_writer|    DEBUG | send IAC DO NEW_ENVIRON
[11 May 2023 16:13:06.326] telnetlib3.stream_writer|    DEBUG | pending_option[DO + NAWS] = True
[11 May 2023 16:13:06.326] telnetlib3.stream_writer|    DEBUG | send IAC DO NAWS
[11 May 2023 16:13:06.326] telnetlib3.stream_writer|    DEBUG | pending_option[DO + CHARSET] = True
[11 May 2023 16:13:06.326] telnetlib3.stream_writer|    DEBUG | send IAC DO CHARSET
[11 May 2023 16:13:06.326] telnetlib3.stream_writer|    DEBUG | sub-negotiation cmd TTYPE SE completion byte
[11 May 2023 16:13:06.326] telnetlib3.stream_writer|    DEBUG | pending_option[SB + TTYPE] = False
[11 May 2023 16:13:06.326] telnetlib3.stream_writer|    DEBUG | recv TTYPE IS: b'XTERM-256COLOR'
[11 May 2023 16:13:06.326] telnetlib3.stream_writer|    DEBUG | recv IAC SB TTYPE IS 'XTERM-256COLOR'
[11 May 2023 16:13:06.326] telnetlib3.server|    DEBUG | ttype cycle cont at ttype1: XTERM-256COLOR.
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | send IAC SB TTYPE SEND IAC SE
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | pending_option[SB + TTYPE] = True
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | recv IAC DO SGA
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | handle_do(SGA)
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | skip WILL SGA; pending_option = True
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | local_option[SGA] = True
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | pending_option[WILL + SGA] = False
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | recv IAC DO ECHO
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | handle_do(ECHO)
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | skip WILL ECHO; pending_option = True
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | local_option[ECHO] = True
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | pending_option[WILL + ECHO] = False
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | recv IAC DO BINARY
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | handle_do(BINARY)
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | skip WILL BINARY; pending_option = True
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | local_option[BINARY] = True
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | pending_option[WILL + BINARY] = False
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | recv IAC WILL NEW_ENVIRON
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | handle_will(NEW_ENVIRON)
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | remote_option[NEW_ENVIRON] = True
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | request_environ: b"\xff\xfa'\x01\x00LANG\x00TERM\x00COLUMNS\x00LINES\x00DISPLAY\x00COLORTERM\x00\x03\xff\xf0"
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | pending_option[SB + NEW_ENVIRON] = True
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | pending_option[DO + NEW_ENVIRON] = False
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | recv IAC WILL NAWS
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | handle_will(NAWS)
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | skip DO NAWS; pending_option = True
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | remote_option[NAWS] = True
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | pending_option[SB + NAWS] = True
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | pending_option[DO + NAWS] = False
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | sub-negotiation cmd NAWS SE completion byte
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | pending_option[SB + NAWS] = False
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | recv IAC SB NAWS (cols=211, rows=50) IAC SE
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | recv IAC WONT CHARSET
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | handle_wont(CHARSET)
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | remote_option[CHARSET] = False
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | pending_option[DO + CHARSET] = False
[11 May 2023 16:13:06.327] telnetlib3.server|    DEBUG | BINARY in: direction request.
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | pending_option[DO + BINARY] = True
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | send IAC DO BINARY
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | sub-negotiation cmd TTYPE SE completion byte
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | pending_option[SB + TTYPE] = False
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | recv TTYPE IS: b'XTERM-256COLOR'
[11 May 2023 16:13:06.327] telnetlib3.stream_writer|    DEBUG | recv IAC SB TTYPE IS 'XTERM-256COLOR'
[11 May 2023 16:13:06.327] telnetlib3.server|    DEBUG | ttype cycle stop at ttype2: XTERM-256COLOR, looped.
[11 May 2023 16:13:06.328] telnetlib3.stream_writer|    DEBUG | sub-negotiation cmd NEW_ENVIRON SE completion byte
[11 May 2023 16:13:06.328] telnetlib3.stream_writer|    DEBUG | pending_option[SB + NEW_ENVIRON] = False
[11 May 2023 16:13:06.328] telnetlib3.stream_writer|    DEBUG | recv NEW_ENVIRON IS: b'\x03LANG\x01en_US.UTF-8\x03TERM\x01XTERM-256COLOR\x03COLUMNS\x03LINES\x00DISPLAY\x01PF3885SS:1\x03COLORTERM\x01truecolor\x00DISPLAY\x01PF3885SS:1\x00DISPLAY\x01PF3885SS:1'
[11 May 2023 16:13:06.328] telnetlib3.stream_writer|    DEBUG | NEW_ENVIRON IS unsolicited
[11 May 2023 16:13:06.328] telnetlib3.server|    DEBUG | on_environ received: {'LANG': 'en_US.UTF-8', 'TERM': 'XTERM-256COLOR', 'DISPLAY': 'PF3885SS:1', 'COLORTERM': 'truecolor'}
[11 May 2023 16:13:06.328] telnetlib3.stream_writer|    DEBUG | recv IAC WILL BINARY
[11 May 2023 16:13:06.328] telnetlib3.stream_writer|    DEBUG | handle_will(BINARY)
[11 May 2023 16:13:06.328] telnetlib3.stream_writer|    DEBUG | skip DO BINARY; pending_option = True
[11 May 2023 16:13:06.328] telnetlib3.stream_writer|    DEBUG | remote_option[BINARY] = True
[11 May 2023 16:13:06.328] telnetlib3.stream_writer|    DEBUG | pending_option[DO + BINARY] = False
[11 May 2023 16:13:06.328] telnetlib3.server|    DEBUG | encoding complete: 'UTF-8'
[11 May 2023 16:13:06.328] telnetlib3.server_base|    DEBUG | negotiation complete after 0.01s.

Prevent terminal type negotiation

How can i disable the server wanting to negotiate terminal type with the client? I saw this line of code but i'm not sure where begin_negotiation is called. At the moment i see the server sending FF FD 18 IAC DO TERMINAL-TYPE and the client responding FF FC 18 IAC WON'T TERMINAL-TYPE. Since i always connect to the same client i don't need this functionality.

IBM AS400 Telnet characters lose or missing

Hello, i hope you are ok.

I am having a issue with chars lose after doing a fwpage using telnetlib3, I donโ€™t know if it is a bug or is not supported with CP1145

this is the program:

import asyncio, telnetlib3, nest_asyncio

nest_asyncio.apply()

host = "as400host"
async def shell(reader, writer):
    while True:
        # read stream until 'User' mark is found
        outp = await reader.read(1024)
        if not outp :
            # End of File
            break
        elif 'User  . . . . . . . . . . . . . .' in outp:
            writer.write('afteruserandpassword\r\r\r\r\rXOP\r2\r\x1B\x5B\x36\x7e') #  
        # display all server output
        print(outp, flush=True)
        
        
    # EOF
    print("Finalizado")

loop = asyncio.get_event_loop()
coro = telnetlib3.open_connection(host, 23, shell=shell,encoding="utf-8", encoding_errors='replace')
reader, writer = loop.run_until_complete(coro)
loop.run_until_complete(writer.protocol.waiter_closed)

I am having this issue with telnetlib and telnetlib3

Can you help me?

1.0 api client shell interface does not return EOF ('')

The server shell interface, which uses the same Streams API and pattern detects EOF just fine. I think this has to do with the wonky way we require to read from stdin and telnet both:

stdin_task = stdin_task or asyncio.ensure_future(stdin.read(2**12))
telnet_task = telnet_task or asyncio.ensure_future(reader.read(2**12))
done, pending = yield from asyncio.wait([stdin_task, telnet_task], return_when=asyncio.FIRST_COMPLETED)

Its a bit peculiar. Help.

$ PYTHONASYNCIODEBUG=1 telnetlib3-client localhost 6023
2016-01-13 12:17:13,850 INFO client_base.py:109 Connected to <Peer ::1 6023>
Ready.
tel:sh> quit2016-01-13 12:17:14,823 INFO client_base.py:72 Connection closed to <Peer ::1 6023>
2016-01-13 12:17:14,836 ERROR base_events.py:1090 Task was destroyed but it is pending!
source_traceback: Object created at (most recent call last):
  File "/Users/jquast/.pyenv/versions/telnetlib3/bin/telnetlib3-client", line 9, in <module>
    load_entry_point('telnetlib3==1.0.0', 'console_scripts', 'telnetlib3-client')()
  File "/Users/jquast/Code/telnetlib3/telnetlib3/client.py", line 269, in main
    start_client(host, port, log, **kwargs))
  File "/Users/jquast/.pyenv/versions/3.5.1/lib/python3.5/asyncio/base_events.py", line 325, in run_until_complete
    self.run_forever()
  File "/Users/jquast/.pyenv/versions/3.5.1/lib/python3.5/asyncio/base_events.py", line 295, in run_forever
    self._run_once()
  File "/Users/jquast/.pyenv/versions/3.5.1/lib/python3.5/asyncio/base_events.py", line 1246, in _run_once
    handle._run()
  File "/Users/jquast/.pyenv/versions/3.5.1/lib/python3.5/asyncio/events.py", line 125, in _run
    self._callback(*self._args)
  File "/Users/jquast/Code/telnetlib3/telnetlib3/client_base.py", line 118, in begin_shell
    self._loop.create_task(res)
task: <Task pending coro=<telnet_client_shell() running at /Users/jquast/Code/telnetlib3/telnetlib3/client_shell.py:90> wait_for=<Future finished result=None created at /Users/jquast/.pyenv/versions/3.5.1/lib/python3.5/asyncio/tasks.py:402> created at /Users/jquast/Code/telnetlib3/telnetlib3/client_base.py:118>--- Logging error ---
Exception ignored in: 2016-01-13 12:17:14,836 ERROR base_events.py:1090 Task was destroyed but it is pending!
source_traceback: Object created at (most recent call last):
  File "/Users/jquast/.pyenv/versions/telnetlib3/bin/telnetlib3-client", line 9, in <module>
    load_entry_point('telnetlib3==1.0.0', 'console_scripts', 'telnetlib3-client')()
  File "/Users/jquast/Code/telnetlib3/telnetlib3/client.py", line 272, in main
    loop.run_until_complete(writer._protocol.waiter_closed)
  File "/Users/jquast/.pyenv/versions/3.5.1/lib/python3.5/asyncio/base_events.py", line 325, in run_until_complete
    self.run_forever()
  File "/Users/jquast/.pyenv/versions/3.5.1/lib/python3.5/asyncio/base_events.py", line 295, in run_forever
    self._run_once()
  File "/Users/jquast/.pyenv/versions/3.5.1/lib/python3.5/asyncio/base_events.py", line 1246, in _run_once
    handle._run()
  File "/Users/jquast/.pyenv/versions/3.5.1/lib/python3.5/asyncio/events.py", line 125, in _run
    self._callback(*self._args)
  File "/Users/jquast/.pyenv/versions/3.5.1/lib/python3.5/asyncio/tasks.py", line 301, in _wakeup
    self._step()
  File "/Users/jquast/.pyenv/versions/3.5.1/lib/python3.5/asyncio/tasks.py", line 239, in _step
    result = coro.send(None)
  File "/Users/jquast/.pyenv/versions/3.5.1/lib/python3.5/asyncio/coroutines.py", line 121, in send
    return self.gen.send(value)
  File "/Users/jquast/Code/telnetlib3/telnetlib3/client_shell.py", line 85, in telnet_client_shell
    stdin.read(2**12))
task: <Task pending coro=<StreamReader.read() running at /Users/jquast/.pyenv/versions/3.5.1/lib/python3.(telnetlib3) C:\Users\jquast\Code\telnetlib3>

Handle Server Disconnects

First, this library is great ๐Ÿ˜„!

I've got a few serial devices that work quite well with it, but I've got one device that is a bit flakey. Sometimes I get bad values back (I suspect this can be solved with some asyncio.sleep(1) calls to ensure commands aren't colliding), other times it just hard disconnects. I'd like to abstract away all of these issues by automatically opening a new telnet connection but I can't seem to figure out what the right way to do that is.

It appears that I can catch a RuntimeError to find a broken StreamWriter, but what's the proper way to close the broken one and open a new one? Ideally I'd like this to happen transparently so that the user doesn't even realize a new connection has been opened. This case is a bit simpler as the device's state is held independently of the telnet connection, so no initialization code is required, just reopening the connection.

I do not have an error stack trace handy at the moment, I'll make sure to copy it next time I see an error.

Ouput bytearray data back to telnet client

Hi there,
due to the necessary of job I'm building a honeypot for Mirai (IoT malware) based on Telnet.

image

I made a simple telnet shell for Mirai sample to connect, but there'll be a trouble when Mirai sample send command "cat /bin/echo". It seem like bad ascii series for stream-writer. (like following picture)

image

image

Encoding failed when using java telnet client

My telnet server implementation is affected by an encoding failed error that causes a delay of connect_maxwait=4.0s until the connection is established. This is a problem for me, since my telnet server should react to telnet commands from the client as fast as possible (<100ms).

[15 May 2023 17:53:59.127] asyncio|    DEBUG | Using selector: EpollSelector
[15 May 2023 17:54:02.549] telnetlib3.server_base|     INFO | Connection from <Peer 127.0.0.1 37092>
[15 May 2023 17:54:02.549] telnetlib3.stream_writer|    DEBUG | pending_option[DO + TTYPE] = True
[15 May 2023 17:54:02.549] telnetlib3.stream_writer|    DEBUG | send IAC DO TTYPE
[15 May 2023 17:54:02.549] telnetlib3.stream_writer|    DEBUG | recv IAC DO ECHO
[15 May 2023 17:54:02.549] telnetlib3.stream_writer|    DEBUG | handle_do(ECHO)
[15 May 2023 17:54:02.549] telnetlib3.stream_writer|    DEBUG | pending_option[WILL + ECHO] = True
[15 May 2023 17:54:02.549] telnetlib3.stream_writer|    DEBUG | send IAC WILL ECHO
[15 May 2023 17:54:02.549] telnetlib3.stream_writer|    DEBUG | local_option[ECHO] = True
[15 May 2023 17:54:02.550] telnetlib3.stream_writer|    DEBUG | pending_option[WILL + ECHO] = False
[15 May 2023 17:54:02.550] telnetlib3.stream_writer|    DEBUG | recv IAC WILL SGA
[15 May 2023 17:54:02.550] telnetlib3.stream_writer|    DEBUG | WILL SGA unsolicited
[15 May 2023 17:54:02.550] telnetlib3.stream_writer|    DEBUG | handle_will(SGA)
[15 May 2023 17:54:02.550] telnetlib3.stream_writer|    DEBUG | pending_option[DO + SGA] = True
[15 May 2023 17:54:02.550] telnetlib3.stream_writer|    DEBUG | send IAC DO SGA
[15 May 2023 17:54:02.550] telnetlib3.stream_writer|    DEBUG | remote_option[SGA] = True
[15 May 2023 17:54:02.550] telnetlib3.stream_writer|    DEBUG | pending_option[DO + SGA] = False
[15 May 2023 17:54:02.550] telnetlib3.stream_writer|    DEBUG | recv IAC DO SGA
[15 May 2023 17:54:02.550] telnetlib3.stream_writer|    DEBUG | handle_do(SGA)
[15 May 2023 17:54:02.550] telnetlib3.stream_writer|    DEBUG | pending_option[WILL + SGA] = True
[15 May 2023 17:54:02.551] telnetlib3.stream_writer|    DEBUG | send IAC WILL SGA
[15 May 2023 17:54:02.551] telnetlib3.stream_writer|    DEBUG | local_option[SGA] = True
[15 May 2023 17:54:02.551] telnetlib3.stream_writer|    DEBUG | pending_option[WILL + SGA] = False
[15 May 2023 17:54:02.551] telnetlib3.server_base|    DEBUG | begin advanced negotiation
[15 May 2023 17:54:02.551] telnetlib3.stream_writer|    DEBUG | pending_option[WILL + SGA] = True
[15 May 2023 17:54:02.551] telnetlib3.stream_writer|    DEBUG | skip WILL SGA; local_option = True
[15 May 2023 17:54:02.551] telnetlib3.stream_writer|    DEBUG | pending_option[WILL + SGA] = False
[15 May 2023 17:54:02.551] telnetlib3.stream_writer|    DEBUG | pending_option[WILL + ECHO] = True
[15 May 2023 17:54:02.551] telnetlib3.stream_writer|    DEBUG | skip WILL ECHO; local_option = True
[15 May 2023 17:54:02.551] telnetlib3.stream_writer|    DEBUG | pending_option[WILL + ECHO] = False
[15 May 2023 17:54:02.551] telnetlib3.stream_writer|    DEBUG | pending_option[WILL + BINARY] = True
[15 May 2023 17:54:02.551] telnetlib3.stream_writer|    DEBUG | send IAC WILL BINARY
[15 May 2023 17:54:02.551] telnetlib3.stream_writer|    DEBUG | pending_option[DO + NEW_ENVIRON] = True
[15 May 2023 17:54:02.551] telnetlib3.stream_writer|    DEBUG | send IAC DO NEW_ENVIRON
[15 May 2023 17:54:02.552] telnetlib3.stream_writer|    DEBUG | pending_option[DO + NAWS] = True
[15 May 2023 17:54:02.552] telnetlib3.stream_writer|    DEBUG | send IAC DO NAWS
[15 May 2023 17:54:02.552] telnetlib3.stream_writer|    DEBUG | pending_option[DO + CHARSET] = True
[15 May 2023 17:54:02.552] telnetlib3.stream_writer|    DEBUG | send IAC DO CHARSET
[15 May 2023 17:54:02.564] telnetlib3.stream_writer|    DEBUG | recv IAC WILL TTYPE
[15 May 2023 17:54:02.564] telnetlib3.stream_writer|    DEBUG | handle_will(TTYPE)
[15 May 2023 17:54:02.564] telnetlib3.stream_writer|    DEBUG | remote_option[TTYPE] = True
[15 May 2023 17:54:02.564] telnetlib3.stream_writer|    DEBUG | send IAC SB TTYPE SEND IAC SE
[15 May 2023 17:54:02.564] telnetlib3.stream_writer|    DEBUG | pending_option[SB + TTYPE] = True
[15 May 2023 17:54:02.564] telnetlib3.stream_writer|    DEBUG | pending_option[DO + TTYPE] = False
[15 May 2023 17:54:02.565] telnetlib3.stream_writer|    DEBUG | recv IAC DONT BINARY
[15 May 2023 17:54:02.565] telnetlib3.stream_writer|    DEBUG | handle_dont(BINARY)
[15 May 2023 17:54:02.565] telnetlib3.stream_writer|    DEBUG | pending_option[WILL + BINARY] = False
[15 May 2023 17:54:02.565] telnetlib3.stream_writer|    DEBUG | local_option[BINARY] = False
[15 May 2023 17:54:02.565] telnetlib3.stream_writer|    DEBUG | recv IAC WONT NEW_ENVIRON
[15 May 2023 17:54:02.565] telnetlib3.stream_writer|    DEBUG | handle_wont(NEW_ENVIRON)
[15 May 2023 17:54:02.565] telnetlib3.stream_writer|    DEBUG | remote_option[NEW_ENVIRON] = False
[15 May 2023 17:54:02.565] telnetlib3.stream_writer|    DEBUG | pending_option[DO + NEW_ENVIRON] = False
[15 May 2023 17:54:02.566] telnetlib3.stream_writer|    DEBUG | recv IAC WONT NAWS
[15 May 2023 17:54:02.566] telnetlib3.stream_writer|    DEBUG | handle_wont(NAWS)
[15 May 2023 17:54:02.566] telnetlib3.stream_writer|    DEBUG | remote_option[NAWS] = False
[15 May 2023 17:54:02.566] telnetlib3.stream_writer|    DEBUG | pending_option[DO + NAWS] = False
[15 May 2023 17:54:02.566] telnetlib3.stream_writer|    DEBUG | recv IAC WONT CHARSET
[15 May 2023 17:54:02.566] telnetlib3.stream_writer|    DEBUG | handle_wont(CHARSET)
[15 May 2023 17:54:02.566] telnetlib3.stream_writer|    DEBUG | remote_option[CHARSET] = False
[15 May 2023 17:54:02.566] telnetlib3.stream_writer|    DEBUG | pending_option[DO + CHARSET] = False
[15 May 2023 17:54:02.566] telnetlib3.stream_writer|    DEBUG | sub-negotiation cmd TTYPE SE completion byte
[15 May 2023 17:54:02.566] telnetlib3.stream_writer|    DEBUG | pending_option[SB + TTYPE] = False
[15 May 2023 17:54:02.566] telnetlib3.stream_writer|    DEBUG | recv TTYPE IS: b'VT100'
[15 May 2023 17:54:02.566] telnetlib3.stream_writer|    DEBUG | recv IAC SB TTYPE IS 'VT100'
[15 May 2023 17:54:02.566] telnetlib3.server|    DEBUG | ttype cycle cont at ttype1: VT100.
[15 May 2023 17:54:02.566] telnetlib3.stream_writer|    DEBUG | send IAC SB TTYPE SEND IAC SE
[15 May 2023 17:54:02.567] telnetlib3.stream_writer|    DEBUG | pending_option[SB + TTYPE] = True
[15 May 2023 17:54:02.567] telnetlib3.stream_writer|    DEBUG | sub-negotiation cmd TTYPE SE completion byte
[15 May 2023 17:54:02.567] telnetlib3.stream_writer|    DEBUG | pending_option[SB + TTYPE] = False
[15 May 2023 17:54:02.567] telnetlib3.stream_writer|    DEBUG | recv TTYPE IS: b'VT100'
[15 May 2023 17:54:02.567] telnetlib3.stream_writer|    DEBUG | recv IAC SB TTYPE IS 'VT100'
[15 May 2023 17:54:02.567] telnetlib3.server|    DEBUG | ttype cycle stop at ttype2: VT100, looped.
[15 May 2023 17:54:06.552] telnetlib3.server|    DEBUG | encoding failed after 4.00s: US-ASCII
[15 May 2023 17:54:06.552] telnetlib3.server_base|    DEBUG | negotiation complete after 4.00s.

The code of the client application is available from https://commons.apache.org/proper/commons-net/examples/telnet/TelnetClientExample.java with the exception that the echohandler is initialized slightly differently (to avoid the problem mentioned in #73)

image

telnetlib3 with debian

I don't know where to go with the following :

import telnetlib give warning message :
DeprecationWarning: 'telnetlib' is deprecated and slated for removal in Python 3.13
import telnetlib

After installing telnetlib3 with pipx and modify telnetlib to telnetlib3 in script then I get error :
import telnetlib3
ModuleNotFoundError: No module named 'telnetlib3'

How can I then install module telnetlib3 ?

Tab,Backspace,Arrow not working

I am running telnet server using telnetlib3 and trying to connect it from another server, when i press tab/backspace , it doesn't do anything. I have used the same code as the war game.

Do I have add some functions to handle the key press like tab/delete?

Remove support for Python 3.6?

Python 3.6 is EOL. How would you feel about removing support for it in order to reduce support burden? That'd still leave 3.7, 3.8, 3.9, 3.10, and 3.11, which is quite the handful!

Use new-style asyncio (python 3.8 DeprecationWarning)

This library started a long time ago, many of the patterns used are now marked deprecated in 3.8 and will be removed in future python versions

telnetlib3/stream_reader.py:13
  /Users/jq/Code/telnetlib3/telnetlib3/stream_reader.py:13: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
    def readline(self):

telnetlib3/stream_reader.py:136
  /Users/jq/Code/telnetlib3/telnetlib3/stream_reader.py:136: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
    def readline(self):

telnetlib3/stream_reader.py:148
  /Users/jq/Code/telnetlib3/telnetlib3/stream_reader.py:148: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
    def read(self, n=-1):

telnetlib3/stream_reader.py:198
  /Users/jq/Code/telnetlib3/telnetlib3/stream_reader.py:198: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
    def readexactly(self, n):

telnetlib3/server_shell.py:12
  /Users/jq/Code/telnetlib3/telnetlib3/server_shell.py:12: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
    def telnet_server_shell(reader, writer):

telnetlib3/server_shell.py:64
  /Users/jq/Code/telnetlib3/telnetlib3/server_shell.py:64: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
    def readline(reader, writer):

telnetlib3/server.py:379
  /Users/jq/Code/telnetlib3/telnetlib3/server.py:379: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
    def create_server(host=None, port=23, protocol_factory=TelnetServer, **kwds):

telnetlib3/server.py:442
  /Users/jq/Code/telnetlib3/telnetlib3/server.py:442: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
    def _sigterm_handler(server, log):

telnetlib3/client_shell.py:125
  /Users/jq/Code/telnetlib3/telnetlib3/client_shell.py:125: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
    def make_stdio(self):

telnetlib3/client_shell.py:160
  /Users/jq/Code/telnetlib3/telnetlib3/client_shell.py:160: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
    def telnet_client_shell(telnet_reader, telnet_writer):

telnetlib3/client.py:239
  /Users/jq/Code/telnetlib3/telnetlib3/client.py:239: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
    def open_connection(host=None, port=23, *, client_factory=None, loop=None,

telnetlib3/tests/test_charset.py::test_telnet_server_on_charset[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_charset.py:39: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_core.py::test_create_server_on_connect[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_core.py:74: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_core.py::test_telnet_server_open_close[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_core.py:94: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_core.py::test_telnet_server_advanced_negotiation[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_core.py:160: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_core.py::test_telnet_server_closed_by_client[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_core.py:196: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_core.py::test_telnet_server_eof_by_client[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_core.py:224: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_core.py::test_telnet_server_closed_by_server[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_core.py:251: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_core.py::test_telnet_server_idle_duration[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_core.py:291: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_core.py::test_telnet_server_closed_by_error[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_core.py:344: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_core.py::test_telnet_server_negotiation_fail[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_core.py:393: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_core.py::test_telnet_server_cmdline[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_core.py:464: DeprecationWarning: The loop argument is deprecated since Python 3.8 and scheduled for removal in Python 3.10.
    proc = await asyncio.create_subprocess_exec(

telnetlib3/tests/test_core.py::test_telnet_server_cmdline[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_core.py:476: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_core.py::test_telnet_client_cmdline[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_core.py:535: DeprecationWarning: The loop argument is deprecated since Python 3.8 and scheduled for removal in Python 3.10.
    proc = await asyncio.create_subprocess_exec(

telnetlib3/tests/test_core.py::test_telnet_client_tty_cmdline[127.0.0.1]
  /Users/jq/Code/telnetlib3/.tox/py38/lib/python3.8/site-packages/pexpect/_async.py:8: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
    def expect_async(expecter, timeout=None):

telnetlib3/tests/test_core.py::test_telnet_client_tty_cmdline[127.0.0.1]
  /Users/jq/Code/telnetlib3/.tox/py38/lib/python3.8/site-packages/pexpect/_async.py:31: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
    def repl_run_command_async(repl, cmdlines, timeout=-1):

telnetlib3/tests/test_core.py::test_telnet_client_cmdline_stdin_pipe[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_core.py:593: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
    def shell(reader, writer):

telnetlib3/tests/test_core.py::test_telnet_client_cmdline_stdin_pipe[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_core.py:609: DeprecationWarning: The loop argument is deprecated since Python 3.8 and scheduled for removal in Python 3.10.
    proc = await asyncio.create_subprocess_exec(

telnetlib3/tests/test_core.py::test_telnet_server_as_module
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_core.py:446: DeprecationWarning: The loop argument is deprecated since Python 3.8 and scheduled for removal in Python 3.10.
    proc = await asyncio.create_subprocess_exec(

telnetlib3/tests/test_core.py::test_telnet_client_as_module
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_core.py:504: DeprecationWarning: The loop argument is deprecated since Python 3.8 and scheduled for removal in Python 3.10.
    proc = await asyncio.create_subprocess_exec(

telnetlib3/tests/test_encoding.py::test_telnet_server_encoding_default[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_encoding.py:31: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_encoding.py::test_telnet_server_encoding_client_will[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_encoding.py:85: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_encoding.py::test_telnet_server_encoding_server_do[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_encoding.py:112: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_encoding.py::test_telnet_server_encoding_bidirectional[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_encoding.py:139: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_encoding.py::test_telnet_server_encoding_by_LANG[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_encoding.py:194: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_encoding.py::test_telnet_server_binary_mode[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_encoding.py:224: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
    def binary_shell(reader, writer):

telnetlib3/tests/test_encoding.py::test_telnet_server_binary_mode[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_encoding.py:241: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_environ.py::test_telnet_server_on_environ[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_environ.py:38: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_linemode.py::test_server_demands_remote_linemode_client_agrees[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_linemode.py:37: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    client_reader, client_writer = await asyncio.open_connection(

telnetlib3/tests/test_linemode.py::test_server_demands_remote_linemode_client_demands_local[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_linemode.py:91: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    client_reader, client_writer = await asyncio.open_connection(

telnetlib3/tests/test_naws.py::test_telnet_server_on_naws[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_naws.py:38: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_shell.py::test_telnet_server_shell_as_coroutine[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_shell.py:33: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
    def shell(reader, writer):

telnetlib3/tests/test_shell.py::test_telnet_server_shell_as_coroutine[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_shell.py:48: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_shell.py::test_telnet_client_shell_as_coroutine[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_shell.py:82: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
    def shell(reader, writer):

telnetlib3/tests/test_shell.py::test_telnet_server_shell_make_coro_by_function[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_shell.py:112: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_shell.py::test_telnet_server_no_shell[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_shell.py:136: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    client_reader, client_writer = await asyncio.open_connection(

telnetlib3/tests/test_shell.py::test_telnet_server_given_shell[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_shell.py:162: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_shell.py::test_telnet_server_shell_eof[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_shell.py:305: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_timeout.py::test_telnet_server_default_timeout[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_timeout.py:33: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_timeout.py::test_telnet_server_set_timeout[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_timeout.py:60: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_timeout.py::test_telnet_server_waitfor_timeout[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_timeout.py:89: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_tspeed.py::test_telnet_server_on_tspeed[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_tspeed.py:35: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_ttype.py::test_telnet_server_on_ttype[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_ttype.py:35: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_ttype.py::test_telnet_server_on_ttype_beyond_max[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_ttype.py:79: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_ttype.py::test_telnet_server_on_ttype_empty[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_ttype.py:124: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_ttype.py::test_telnet_server_on_ttype_looped[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_ttype.py:164: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_ttype.py::test_telnet_server_on_ttype_repeated[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_ttype.py:206: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_ttype.py::test_telnet_server_on_ttype_mud[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_ttype.py:248: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

telnetlib3/tests/test_writer.py::test_iac_do_twice_replies_once[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_writer.py:125: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
    def shell(reader, writer):

telnetlib3/tests/test_writer.py::test_iac_do_twice_replies_once[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_writer.py:136: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    client_reader, client_writer = await asyncio.open_connection(

telnetlib3/tests/test_writer.py::test_iac_dont_dont[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_writer.py:154: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
    def shell(reader, writer):

telnetlib3/tests/test_writer.py::test_iac_dont_dont[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_writer.py:165: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    client_reader, client_writer = await asyncio.open_connection(

telnetlib3/tests/test_writer.py::test_slc_simul[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_writer.py:223: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
    def shell(reader, writer):

telnetlib3/tests/test_writer.py::test_slc_simul[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_writer.py:254: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    client_reader, client_writer = await asyncio.open_connection(

telnetlib3/tests/test_writer.py::test_unhandled_do_sends_wont[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_writer.py:284: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    client_reader, client_writer = await asyncio.open_connection(

telnetlib3/tests/test_writer.py::test_writelines_bytes[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_writer.py:301: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
    def shell(reader, writer):

telnetlib3/tests/test_writer.py::test_writelines_bytes[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_writer.py:310: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    client_reader, client_writer = await asyncio.open_connection(

telnetlib3/tests/test_writer.py::test_writelines_unicode[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_writer.py:326: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
    def shell(reader, writer):

telnetlib3/tests/test_writer.py::test_writelines_unicode[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_writer.py:335: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    client_reader, client_writer = await asyncio.open_connection(

telnetlib3/tests/test_writer.py::test_send_ga[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_writer.py:361: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
    def shell(reader, writer):

telnetlib3/tests/test_writer.py::test_send_ga[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_writer.py:370: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    client_reader, client_writer = await asyncio.open_connection(

telnetlib3/tests/test_writer.py::test_not_send_ga[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_writer.py:390: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
    def shell(reader, writer):

telnetlib3/tests/test_writer.py::test_not_send_ga[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_writer.py:399: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    client_reader, client_writer = await asyncio.open_connection(

telnetlib3/tests/test_writer.py::test_not_send_eor[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_writer.py:415: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
    def shell(reader, writer):

telnetlib3/tests/test_writer.py::test_not_send_eor[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_writer.py:424: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    client_reader, client_writer = await asyncio.open_connection(

telnetlib3/tests/test_writer.py::test_send_eor[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_writer.py:445: DeprecationWarning: "@coroutine" decorator is deprecated since Python 3.8, use "async def" instead
    def shell(reader, writer):

telnetlib3/tests/test_writer.py::test_send_eor[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_writer.py:456: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    client_reader, client_writer = await asyncio.open_connection(

telnetlib3/tests/test_xdisploc.py::test_telnet_server_on_xdisploc[127.0.0.1]
  /Users/jq/Code/telnetlib3/telnetlib3/tests/test_xdisploc.py:39: DeprecationWarning: The loop argument is deprecated since Python 3.8, and scheduled for removal in Python 3.10.
    reader, writer = await asyncio.open_connection(

Telnetlib3 not windows compatible

Telnetlib3 is not importable on windows. This seems to be mainly because of the termios package which is linux only. There might be other factors that need to be changed, but for now telnetlib3 is completely incompatible on windows. If it could be ported, it would be cool-i'm going to host a server on linux, of course, but for local testing I would like compatibility.

Example code for client doesn't work

Python 3.6.3

Traceback (most recent call last):
  File "/home/flip111/prj/venv/bin/adev", line 11, in <module>
    sys.exit(cli())
  File "/home/flip111/prj/venv/lib64/python3.6/site-packages/click/core.py", line 722, in __call__
    return self.main(*args, **kwargs)
  File "/home/flip111/prj/venv/lib64/python3.6/site-packages/click/core.py", line 697, in main
    rv = self.invoke(ctx)
  File "/home/flip111/prj/venv/lib64/python3.6/site-packages/click/core.py", line 1066, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/flip111/prj/venv/lib64/python3.6/site-packages/click/core.py", line 895, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/flip111/prj/venv/lib64/python3.6/site-packages/click/core.py", line 535, in invoke
    return callback(*args, **kwargs)
  File "/home/flip111/prj/venv/lib64/python3.6/site-packages/aiohttp_devtools/cli.py", line 88, in runserver
    run_app(*_runserver(**active_config))
  File "/home/flip111/prj/venv/lib64/python3.6/site-packages/aiohttp_devtools/runserver/main.py", line 46, in runserver
    config.check()
  File "/home/flip111/prj/venv/lib64/python3.6/site-packages/aiohttp_devtools/runserver/config.py", line 171, in check
    app_factory = self.import_app_factory()
  File "/home/flip111/prj/venv/lib64/python3.6/site-packages/aiohttp_devtools/runserver/config.py", line 138, in import_app_factory
    module = import_module(module_path)
  File "/usr/lib64/python3.6/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 994, in _gcd_import
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 678, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/home/flip111/prj/src/server/app.py", line 106, in <module>
    loop.run_until_complete(reader.protocol.waiter_closed)
AttributeError: 'TelnetReaderUnicode' object has no attribute 'protocol'

was also wondering why there is buf = '' in the example code

Add support for TN3270

Hi @jquast

Have you ever looked at TN3270?
Could you imagine to add support for it?
Relevant RFCs are:

TN3270 Current Practices
https://datatracker.ietf.org/doc/html/rfc1576 Jan 1994

TN3270 Extensions for LUname and Printer Selection
https://datatracker.ietf.org/doc/html/rfc1646 Jul 1994

TN3270 Enhancements
https://datatracker.ietf.org/doc/html/rfc2355 Jun 1998

TN3270E Service Location and Session Balancing
https://datatracker.ietf.org/doc/html/rfc3049 Jan 2001

There are a few projects floating around that implement TN3270 servers in various languages, including python, though they all are rather nonchalant regarding telnet negotiation and focus on the application part.
It would be great to do TN3270 by the book.

Deprecation error for `reader.close` and no documentation available

When using telnetlib3 I tried to close an open connection using

reader.close()
writer.close()

which triggered a deprecation warning

Python.3.10_qbz5n2kfra8p0\LocalCache\local-packages\Python310\site-packages\telnetlib3\stream_reader.py:393: DeprecationWarning: connection_closed deprecated, use feed_eof() instead

However, the documentation on close and feed_eof does not seem to exist in the documentation. That makes it a bit hard to understand why not use the usual close method to close a connection, and what feed_eof does and how this can be properly used.

  • Python: 3.10.11
  • Windows 10
  • telnetlib3: 2.0.2

telnetlib3 without asyncio?

Hello, would it be possible to create a telnetlib module without this asyncio overhead? In my case this makes the code EXTREMLY more cumbersome, unreadable and complicated. Thanks

Telnet server example doesn't work

the overall lack of documentation for the server part led me to try the example code verbatim. The snippet completes, instead of waiting for a connection.

import asyncio, telnetlib3

async def shell(reader, writer):
    writer.write('\r\nWould you like to play a game? ')
    inp = await reader.read(1)
    if inp:
        writer.echo(inp)
        writer.write('\r\nThey say the only way to win '
                     'is to not play at all.\r\n')
        await writer.drain()
    writer.close()

loop = asyncio.get_event_loop()
coro = telnetlib3.create_server('127.0.0.1', 6023, shell=shell)
server = loop.run_until_complete(coro)
loop.run_until_complete(server.wait_closed())

results in

/home/anton/PycharmProjects/XO/venv/PycharmProjects/bin/python /home/anton/.config/JetBrains/PyCharmCE2023.2/scratches/scratch.py 
/home/anton/.config/JetBrains/PyCharmCE2023.2/scratches/scratch.py:13: DeprecationWarning: There is no current event loop
  loop = asyncio.get_event_loop()

Process finished with exit code 0

The internet is full of telnet client examples of usage of this library, but no telnet server.

Adding tests to the sdist

Would you mind adding tests to the sdist uploaded to PyPI, so they can be used in distro packaging?

This can be mostly automated by using check-manifest which can update MANIFEST.in to ensure it is a complete archive.

Tests are not Python 3.7 compatible

[   49s] /usr/lib/python3.7/site-packages/_pytest/python.py:507: in _importtestmodule
[   49s]     mod = self.fspath.pyimport(ensuresyspath=importmode)
[   49s] /usr/lib/python3.7/site-packages/py/_path/local.py:701: in pyimport
[   49s]     __import__(modname)
[   49s] E     File "/home/abuild/rpmbuild/BUILD/telnetlib3-1.0.2/archived_tests/test_bsdtelnet.py", line 49
[   49s] E       yield from child.expect("telnet> ", async=True)
[   49s] E                                               ^
[   49s] E   SyntaxError: invalid syntax

These cause pytest without arguments to fail, as it tries to find tests.

One solution for that is to include pytest configuration in setup.cfg which tests pytest where to look for the tests, i.e. not archived_tests

__repr__ logging problem

I am using this excellent library as a dependency and the parent library seems to be calling the __repr__ of BaseClient defined in client_base.py:

def __repr__(self):
hostport = self.get_extra_info('peername')[:2]
return '<Peer {0} {1}>'.format(*hostport)
def get_extra_info(self, name, default=None):
"""Get optional client protocol or transport information."""
return self._extra.get(name, self._transport._extra.get(name, default))

The trouble is that it seems to be calling for this __repr__ after disconnect and self._transport has been redefined as None:
# break circular references.
self._transport = None

Which causes an AttributeError when trying to get _extra from _transport:

--- Logging error ---
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/logging/__init__.py", line 1081, in emit
    msg = self.format(record)
  File "/usr/local/lib/python3.8/logging/__init__.py", line 925, in format
    return fmt.format(record)
  File "/usr/local/lib/python3.8/site-packages/colorlog/colorlog.py", line 123, in format
    message = super(ColoredFormatter, self).format(record)
  File "/usr/local/lib/python3.8/logging/__init__.py", line 664, in format
    record.message = record.getMessage()
  File "/usr/local/lib/python3.8/logging/__init__.py", line 369, in getMessage
    msg = msg % self.args
  File "/usr/local/lib/python3.8/site-packages/telnetlib3/client_base.py", line 202, in __repr__
    hostport = self.get_extra_info('peername')[:2]
  File "/usr/local/lib/python3.8/site-packages/telnetlib3/client_base.py", line 207, in get_extra_info
    return self._extra.get(name, self._transport._extra.get(name, default))
AttributeError: 'NoneType' object has no attribute '_extra'
Call stack:
  File "/usr/local/lib/python3.8/threading.py", line 890, in _bootstrap
    self._bootstrap_inner()
  File "/usr/local/lib/python3.8/threading.py", line 932, in _bootstrap_inner
    self.run()
  File "/usr/local/lib/python3.8/threading.py", line 870, in run
    self._target(*self._args, **self._kwargs)
Unable to print the message and arguments - possible formatting error.
Use the traceback above to help find the error.

I can resolve this simply by:

def get_extra_info(self, name, default=None): 
    """Get optional client protocol or transport information.""" 
    if self._transport is None:
        return ("", "")
    return self._extra.get(name, self._transport._extra.get(name, default)) 

So I would love to know if we can merge contributors preferred solution or if I should just handle this upstream or by monkey patching the repr.

'TelnetReaderUnicode' object has no attribute '_wait_for_data'

I tried to run simple telnet server:

import asyncio, telnetlib3

@asyncio.coroutine
def shell(reader, writer):
    writer.write('\r\nWould you like to play a game\r\n')
    inp = yield from reader.read(1)
    if inp:
        writer.echo(inp)
        writer.write('\r\nThey say the only way to win '
                     'is to not play at all.\r\n')
        yield from writer.drain()
    writer.close()

loop = asyncio.get_event_loop()
coro = telnetlib3.create_server(port=6023, shell=shell)
server = loop.run_until_complete(coro)
loop.run_until_complete(server.wait_closed())

Now when I connect using default telnet client on ubuntu. I get the error message:
Task exception was never retrieved

future: <Task finished coro=<shell() done, defined at /home/roshanmk/workspace/tall-tsif-testbed/tall/simulators/camera/pickCamera.py:3> exception=AttributeError("'TelnetReaderUnicode' object has no attribute '_wait_for_data'",)>
Traceback (most recent call last):
  File "/usr/lib/python3.4/asyncio/tasks.py", line 237, in _step
    result = next(coro)
  File "/home/roshanmk/workspace/tall-tsif-testbed/tall/simulators/camera/pickCamera.py", line 6, in shell
    inp = yield from reader.read(1)
  File "/usr/local/lib/python3.4/dist-packages/telnetlib3/stream_reader.py", line 183, in read
    yield from self._wait_for_data('read')
AttributeError: 'TelnetReaderUnicode' object has no attribute '_wait_for_data'

I am on Python 3.4.2 and telnetlib3-1.0.1

What is the replacement for TerminalShell ?

Hi folks

I was using release 0.5, and was mostly doing stuff like

import telnetlib3

my_client = telnetlib3.TelnetClient(shell=telnetlib3.TerminalShell)

However after a careless upgrade I ended up with release 1.0 and out of the blue my app crashed with message like this:
AttributeError: module 'telnetlib3' has no attribute 'TerminalShell'

How should this be rewritten to obtain the previous behaviour ?

Thanks

`TelnetWriter.is_closing()` raises `AttributeError` after `TelnetWriter.close()` was called.

Hi @jquast, thanks for telnetlib3!

It looks like calling TelnetWriter.close() sets TelnetWriter._protocol to None.
Because of this, calling TelnetWriter.is_closing() after this raises an AttributeError.

Traceback (most recent call last):
  ...
  File "/home/jonathan/git/project/project/src/project/pty_stream/telnet.py", line 122, in ...
    if not instance.is_closing():
  File "/home/jonathan/git/project/project/src/project/pty_stream/telnet.py", line 161, in is_closing
    result: bool = self.stream_writer.is_closing()
  File "/home/jonathan/.pyenv/versions/3.9.13/lib/python3.9/asyncio/streams.py", line 356, in is_closing
    return self._transport.is_closing()
AttributeError: 'NoneType' object has no attribute 'is_closing'

According to the Python docs, is_closing() should return True after close() was called. It should not raise an AttributeError: https://docs.python.org/3/library/asyncio-stream.html#asyncio.StreamWriter.is_closing

Return True if the stream is closed or in the process of being closed.

What I don't understand is the need here to break circular refs. Asyncio's StreamWriter doesn't do it either. Changing the type of a field from the base class from protocols.BaseProtocol to Optional[protocols.BaseProtocol] is a Liskov violation.

edit:
Similar, .wait_closed() can fail with an AttributeError too:

  ...
  File "/home/jonathan/git/radkit/project/.../telnet.py", line 128, in create_acm
    await instance.stream_writer.wait_closed()
  File "/home/jonathan/.pyenv/versions/3.9.13/lib/python3.9/asyncio/streams.py", line 359, in wait_closed
    await self._protocol._get_close_waiter(self)
AttributeError: 'NoneType' object has no attribute '_get_close_waiter'

Cannot install with pip 6.0.8, python 3.4.3, OS X 10.10.2

Traceback (most recent call last):
  File "<string>", line 20, in <module>
  File "/private/var/folders/tp/sf8pkx4j49j9gf3c1bxntffr0000gn/T/pip-build-r1bjhsa2/telnetlib3/setup.py", line 28, in <module>
    install_requires = [str(req.req) for req in requirements]
  File "/private/var/folders/tp/sf8pkx4j49j9gf3c1bxntffr0000gn/T/pip-build-r1bjhsa2/telnetlib3/setup.py", line 28, in <listcomp>
    install_requires = [str(req.req) for req in requirements]
  File "/Users/sethb/.virtualenvs/eight/lib/python3.4/site-packages/pip/req/req_file.py", line 19, in parse_requirements
    "parse_requirements() missing 1 required keyword argument: "
TypeError: parse_requirements() missing 1 required keyword argument: 'session'
Complete output from command python setup.py egg_info:
Traceback (most recent call last):

  File "<string>", line 20, in <module>

  File "/private/var/folders/tp/sf8pkx4j49j9gf3c1bxntffr0000gn/T/pip-build-r1bjhsa2/telnetlib3/setup.py", line 28, in <module>

    install_requires = [str(req.req) for req in requirements]

  File "/private/var/folders/tp/sf8pkx4j49j9gf3c1bxntffr0000gn/T/pip-build-r1bjhsa2/telnetlib3/setup.py", line 28, in <listcomp>

    install_requires = [str(req.req) for req in requirements]

  File "/Users/sethb/.virtualenvs/eight/lib/python3.4/site-packages/pip/req/req_file.py", line 19, in parse_requirements

    "parse_requirements() missing 1 required keyword argument: "

TypeError: parse_requirements() missing 1 required keyword argument: 'session'

----------------------------------------
Command "python setup.py egg_info" failed with error code 1 in /private/var/folders/tp/sf8pkx4j49j9gf3c1bxntffr0000gn/T/pip-build-r1bjhsa2/telnetlib3

develop bug: SLC buffer wrong size: expect multiple of 3

2016-04-05 17:48:55,653 INFO server_base.py:124 Connection from <Peer 173.227.7.2 50188>
2016-04-05 17:48:55,655 WARNING stream_writer.py:1383 Unhandled: WILL AUTHENTICATION.
2016-04-05 17:48:56,013 WARNING server_base.py:298 File "/home/dingo/telnetlib3/telnetlib3/server_base.py", line 149, in data_received
2016-04-05 17:48:56,013 WARNING server_base.py:298 recv_inband = self.writer.feed_byte(bytes([byte]))
2016-04-05 17:48:56,013 WARNING server_base.py:298 File "/home/dingo/telnetlib3/telnetlib3/stream_writer.py", line 275, in feed_byte
2016-04-05 17:48:56,013 WARNING server_base.py:298 self.handle_subnegotiation(self._sb_buffer)
2016-04-05 17:48:56,013 WARNING server_base.py:298 File "/home/dingo/telnetlib3/telnetlib3/stream_writer.py", line 1436, in handle_subnegotiatio
n
2016-04-05 17:48:56,014 WARNING server_base.py:298 self._handle_sb_linemode(buf)
2016-04-05 17:48:56,014 WARNING server_base.py:298 File "/home/dingo/telnetlib3/telnetlib3/stream_writer.py", line 1876, in _handle_sb_linemode
2016-04-05 17:48:56,014 WARNING server_base.py:298 self._handle_sb_linemode_slc(buf)
2016-04-05 17:48:56,014 WARNING server_base.py:298 File "/home/dingo/telnetlib3/telnetlib3/stream_writer.py", line 1957, in handle_sb_linemode
slc
2016-04-05 17:48:56,014 WARNING server_base.py:298 raise ValueError('SLC buffer wrong size: expect multiple of 3')
2016-04-05 17:48:56,014 WARNING server_base.py:298 ValueError: SLC buffer wrong size: expect multiple of 3

When clients disconnect protocol objects remain on the server side

circular references.

The protocol references the transport which references the protocol.
The stream_writer has a bunch of references to the protocol which references the stream_writer
the stream_reader has a reference to the protocol which references the stream_reader.

I used weakref.finalize to print when the writer or the protocol get deleted and they don't get deleted until python is shutdown.

    weakref.finalize(writer, print, "writer was deleted.")
    weakref.finalize(writer._protocol, print, "protocol deleted.")

Let me organize my changes into a pull request for a fix

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.