Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

input() recognize Carriage return (\r, 0x0D) but not Line Feed (\n, 0x0A) #9217

Open
DeqingSun opened this issue Apr 30, 2024 · 3 comments
Open

Comments

@DeqingSun
Copy link

DeqingSun commented Apr 30, 2024

CircuitPython version

Adafruit CircuitPython 9.0.4 on 2024-04-16; Adafruit KB2040 with rp2040

Code/REPL

if supervisor.runtime.serial_bytes_available:
    data = input()
    print(data)

Behavior

On windows, enter will be encoded as \r\n (0x0D, 0x0A)
However, the input seems only recognize the \r, and the 0x0A cause the code to freeze at the input()

I've tried to send 0x0D only, the input has response.
Then I tried 0x0A, the input has no response.

Description

I tried to search why this happen on github. It seems the input is linked with mp_builtin_input in modbuiltins.c.
And it leads to readline_process_char.
It processes \r

} else if (c == '\r') {

but not \n

Additional information

It seems circuit python uses the same code as readline_process_char

The Unix port of Circuit python uses fgets to read line. And fgets actually look for \n

https://github.com/lattera/freebsd/blob/401a161083850a9a4ce916f37520c084cff1543b/lib/libc/stdio/fgets.c#L90

And python version of input() will take care \n or \r\n

https://github.com/python/cpython/blob/19d468a3ab1d57c07a4283d67d439908574aa0cc/Python/bltinmodule.c#L2317

@DeqingSun DeqingSun added the bug label Apr 30, 2024
@dhalbert dhalbert added this to the Support milestone Apr 30, 2024
@dhalbert
Copy link
Collaborator

The situation about what constitutes an end of line for input is complicated. CircuitPython is reading characters either from a host computer or a UART port. Unlike CPython ("regular" Python) which knows what it's running on, we cannot know which kind of host computer (Windows, Linux, macOS, etc.) we are receiving input from. Or, we might be receiving input from a UART, or a raw keyboard.

We inherited the input() behavior from MicroPython. So I believe the current behavior is deliberate: input is triggered by a \r, and \n is discarded. This covers what gets sent from the different operating systems. If you don't try to see what's in the buffer before trying an input, then it behaves more or less as you'd expect: the Enter key causes the input to finish.

What is your use case, and what do you want the behavior to be? Note that there may be other ways to get what you want, such as using stdin.read(), or using the secondary USB CDC channel to exchange data between the board and the host.

@DeqingSun
Copy link
Author

DeqingSun commented May 1, 2024

Thanks for your reply. The problem I had today was solved by using stdin.read(1) and managing my buffer to deal with newline. It works well for my job and I'm done with it at least for now.

More thought about this. If I use a serial monitor or my own code, as long as I can fully control the data, it can be fine to just send \r. However, here is a case that is worth some improvement for other users:

I have a code similar to this:

while True:
    if time.monotonic() - previousLightSensorSampleTime > 0.1:
        previousLightSensorSampleTime = time.monotonic()
        // print some data peridocally
        
    while supervisor.runtime.serial_bytes_available:
        print(input())

I used Thonny to run the script and I believe other users can use other IDE and have similar issue.
At the beginning the console can print the sensor data. no problem.
Then I type something in console, and press enter. My input got printed. But my data print also paused. Every time I press enter, I got one sensor print.

So here comes a problem. For IDE like Thonny, I do not have any choice on how the "Enter" is encoded. And it got encoded as \r\n in Windows. the \n became a blocking character that made serial_bytes_available to be true, but input() got waiting. For experienced user like me it is fine. But for a user with no idea about line ending, this is frustrating. (Also frustrating for me for a couple of minutes )

Not only Windows, Arduino also do \r\n

https://github.com/arduino/ArduinoCore-avr/blob/63092126a406402022f943ac048fa195ed7e944b/cores/arduino/Print.cpp#L126-L129

Maybe the input function can peek if there is \n after \r and just remove it? At least, I feel, the \n should be left in the buffer to trigger serial_bytes_available

@dhalbert
Copy link
Collaborator

dhalbert commented May 1, 2024

We'll think about this, thanks. I'm not sure it can be perfect, in case the \n is delayed. But if it's there, we could discard it.

For your own purposes, sending data, as I mentioned, there is a secondary CDC channel which you can enable which transmitsand receives bytes, and has no extra printing or REPL handling. See https://learn.adafruit.com/customizing-usb-devices-in-circuitpython/circuitpy-midi-serial#usb-serial-console-repl-and-data-3096590

@dhalbert dhalbert modified the milestones: Support, Long term May 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants