Brijesh-Thakkar opened a new pull request, #20663: URL: https://github.com/apache/nuttx/pull/20663
*Note: Please adhere to [Contributing Guidelines](https://github.com/apache/nuttx/blob/master/CONTRIBUTING.md).* ## Summary Fixes #17727. `tools/flash_writer.py` imports `telnetlib` at the top level. `telnetlib` was removed in Python 3.13 ([PEP 594](https://peps.python.org/pep-0594/)), so on 3.13+ the script fails at import time, before argument parsing. This breaks **all** users, including the documented serial flow (`-s -c /dev/ttyUSB0`), not only telnet (`-t`) users: ``` $ python3.13 tools/flash_writer.py --help Traceback (most recent call last): File ".../tools/flash_writer.py", line 27, in <module> import telnetlib ModuleNotFoundError: No module named 'telnetlib' ``` This PR replaces `telnetlib` with a small in-file, synchronous helper class, `_TelnetConn` (stdlib `socket` + `select` only), implementing just the three calls `TelnetDev` uses: `Telnet(host, port, timeout)`, `write()` and `read_eager()`. It mirrors CPython 3.12 `telnetlib` behavior on the wire: - `write()` doubles IAC (`0xFF`) bytes, which is required so XMODEM payload bytes are not interpreted as telnet commands. - Incoming data: `IAC IAC` → `0xFF`; `DO`/`DONT` refused with `WONT`, `WILL`/`WONT` with `DONT`; `SB … SE` content discarded; NUL and XON dropped (parity with telnetlib). - `read_eager()` never blocks, returns `b""` when nothing is ready, raises `EOFError` at EOF. - The socket timeout is kept after connect; the socket is closed in `__del__`. Only the `TelnetDev` constructor call changes; the rest of the script is untouched. Alternatives considered: - **Wrapping the import in try/except** (#18414) fixes serial but leaves telnet broken; reviewers asked for telnet to keep working. - **Dropping telnet mode** removes a feature (serial-to-telnet proxies such as ser2net). - **Falling back to the PyPI `telnetlib-313-and-up` package** adds a third-party dependency to a firmware-flashing tool. ## Impact - **Users:** `flash_writer.py` works again on Python 3.13+ in both serial and telnet modes. On Python ≤3.12, behavior is unchanged; the only visible difference is that the `DeprecationWarning` is gone. - **Dependencies:** none added (stdlib only, Python 3.8+ syntax). - **Build / boards / docs:** no impact; only `tools/flash_writer.py` changes. Documented usage (`Documentation/platforms/arm/cxd56xx/boards/spresense/index.rst`) is unchanged. - **Security / compatibility:** no new network behavior; on-the-wire bytes are identical to `telnetlib` (see Testing). Pre-existing issues noticed but **not** addressed here (happy to follow up separately): - `-t` combined with `-b` raises `AttributeError` (`TelnetDev` has no `setBaudrate`). - A port taken from `CXD56_TELNETSRV_PORT` stays a `str`, so the `"%d"` in the connect-error message would raise `TypeError` and hide the real error. ## Testing **Host:** Ubuntu 26.04 LTS; Python 3.12.14, 3.13.15 and 3.14.4 (separate venvs with only `pyserial` + `xmodem`), plus a Python 3.8.20 syntax/run check. **Hardware:** I don't have a Spresense board. @cederom offered on the issue to test on real hardware. Below are emulated end-to-end tests. ### Test setup - A small emulator of the CXD56 bootloader updater ("Welcome to nash", `updater#` prompt, `install`, `save_file -x`, `rm`, `chmod`, `set bootable`, `sync`, `reboot`), receiving files with `xmodem` (1K). - Two transports: - serial over a `socat` pty pair (`-s -c <pty>`); - telnet via a TCP listener that behaves like ser2net in telnet mode: sends its option negotiation on connect (also an RFC 2217 variant) and IAC-escapes in both directions (`-t -i 127.0.0.1 -p <port>`). - Payloads: random binary, a payload dense in `FF/00/11/13/0D/0A/18/06/15/FD/FB`, and all 256 byte values with long `0xFF`/`0x00` runs. - Pass = SHA-256 of every received file matches the source (ignoring XMODEM `0x1A` padding) and the command sequence matches. - Harness scripts and full logs available on request. ### Results | Case | 3.12 master | 3.13 master | 3.14 master | 3.12 PR | 3.13 PR | 3.14 PR | |---|---|---|---|---|---|---| | `--help` (`-W error`) | DeprecationWarning | ModuleNotFoundError | ModuleNotFoundError | ✅ | ✅ | ✅ | | serial `-N` | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | | telnet `-N` | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | | telnet `-d` (ROM + autoboot cancel) | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | | telnet, RFC 2217 negotiation | ✅ | ❌ | ❌ | ✅ | ✅ | ✅ | Additional checks: - **Wire capture:** raw bytes in both directions on 3.12/3.13/3.14 with this PR are byte-identical to master on 3.12 (18/18 captures), including the telnet option replies. - **Differential test vs CPython 3.12 `telnetlib`:** 537 byte-stream scenarios (random and IAC-heavy data, every negotiation verb, SB blocks, `IAC IAC`, sequences split across reads, EOF mid-sequence). Identical `read_eager()` output, reply bytes and `EOFError` timing; 0 mismatches. Three deliberately broken variants of the helper were detected (403 / 330 / 265 mismatches), so the test is sensitive. - **Negative control:** with IAC escaping disabled in the helper, the end-to-end telnet test fails (transfer hangs), confirming the harness catches escaping bugs. - **Connect failure:** same message and exit code (111) as master, no extra warnings under `-W default`. - `./tools/checkpatch.sh -g HEAD~1..HEAD` passes. - Not testable on a pty: serial `-d` (DTR reset; `setDTR` raises `ENOTTY`). This path is unchanged by this PR. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
