5

I am working on a script with user input in Py 3.6.

In the script, the user is asked to enter a text section - potentially containing new lines - into the shell. The entered text will then be saved to a Python variable for further processing.

Since the user input may contain newlines, I think I cannot use input() but am using sys.stdin.read() (as suggested here).

Problem

Reading in the input works fine, but to end the user input, the user has to hit Return and then use the key combination CTRL + d (see here). (See Current Procedure below)

Question

  • I would prefer that the user can just end their input to sys.stdin.read by hitting the Return key (cf Expected Procedure below)

EDIT: Any other simplification to the current process with CTRL + d is appreciated as well.

  • Is this doable?

  • There are some hacks here but I thought maybe there is a better way

Current code

    # display text on screen
    print("Review this text\n" + text)
    # user will copy and paste relevant items from text displayed into Terminal
    user_input =  sys.stdin.read() 
    print ("hit ctrl + d to continue")
    # process `user_input`

Current procedure

With the current code reproduced below, the user has to

1) paste the text 2) hit RETURN to end input 3) hit Ctrl+d to move to next file

Expected procedure

I would like to reduce this to:

1) paste the text 2) hit RETURN to end input and move to next file

Running Python 3.5.6 on MacOSX, using Terminal for text input. Any help is much appreciated!

patrick
  • 4,455
  • 6
  • 44
  • 61
  • You'll probably want to `print ("hit ctrl + d to continue")` *before* the read operation. – Mad Physicist Jan 10 '19 at 16:43
  • What operating system is this? – Mad Physicist Jan 10 '19 at 16:44
  • If the input may contain multiple lines, then how are you imagining it to be possible to distinguish the returns between lines from the return that ends the input? – jasonharper Jan 10 '19 at 16:46
  • How do you feel about switching to a GUI? You might be better off with a text field and a submit button. – Kevin Jan 10 '19 at 16:47
  • @MadPhysicist This is MacOS High Sierra 10.13.6 Also good call on the `print` operation, still WIP – patrick Jan 10 '19 at 16:57
  • @jasonharper yes that is a good question, but I was thinking there might be a way. I willl specify that any other 1 key input is acceptable as well if need be. The multiline text will also be copied & pasted by the user, if that makes any difference. – patrick Jan 10 '19 at 16:58
  • You may be looking for unbuffered read input, the problem is that the terminal may buffer input before passing it to python process, see this: https://stackoverflow.com/questions/2408560/python-nonblocking-console-input/2409034#2409034 – geckos Jan 10 '19 at 17:11
  • Copy/pasted makes no difference as you cannot (really; well, you could turn buffering off and read one byte at at time and see input frequency I guess) distinguish timing of input sequence with `read` from `stdin`. The only other option that immediately comes to mind, can you have anywhere else in the input empty lines (just a newline)? If not, that would be reasonably simple to terminate the input, otherwise, not that trivial (and possibly not very fool proof). – Ondrej K. Jan 10 '19 at 21:20
  • @OndrejK. this is a very good point, I am implementing a logic that returns once a newline by itself is input. I will post here. Thanks for all the input! – patrick Jan 10 '19 at 21:57

1 Answers1

2

Based on your reply in the comment, if terminating by empty line is acceptable (i.e. your input text cannot contain newline on its own except to terminate input), that is quote trivial:

user_input = ''          # User input we'll be adding to
for line in sys.stdin:   # Iterator loops over readline() for file-like object
    if line == '\n':     # Got a line that was just a newline
        break            # Break out of the input loop
    user_input += line   # Keep adding input to variable

The other option I've been mentioning, even though I do not really like the assumptions underpinning this approach. You can read your input and record time of each entered. You can define a time limit after which you are comfortable to assume it was not part of copy paste as a single block. And following newline on its own is then assumed to be end of user input. For instance:

import sys
import time

COPY_PASTE_LIMIT = 0.5  # For instance 500ms
                        # Presuming platform time precision
                        # greater then whole seconds.

user_input = ''
last = 0  # We'll also later terminate when input was nothing
          # but an initial empty line.
for line in sys.stdin:
    now = time.time()
    if line == '\n' and (now - last > COPY_PASTE_LIMIT or last == 0):
        break
    last = now
    user_input += line

print(user_input)
Ondrej K.
  • 8,841
  • 11
  • 24
  • 39