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]

Reply via email to