Jump to content

Python Concepts/Console Input

From Wikiversity

Objective

[edit | edit source]

Lesson

[edit | edit source]

In the context of this lesson, the word "console" implies the visual display which you see in front of you when you sit at your desk, and also the attached keyboard. The keyboard provides "console input" while the visual display provides console output.

Other input devices such as mice, joysticks, game controllers and touch screens are not covered in this lesson.

Python reserves one file object for console input, standard input:

>>> import io
>>> import sys
>>>
>>> sys.stdin
<_io.TextIOWrapper name='<stdin>' mode='r' encoding='UTF-8'>
>>> isinstance(sys.stdin, io.TextIOWrapper)
True
>>>

The above file object is always open for reading in text mode. It has its associated file descriptor:

>>> sys.stdin.fileno()
0
>>>

More information about sys.stdin:

>>> sys.stdin.isatty()
True # When input is keyboard.
>>> sys.stdin.closed
False
>>> sys.stdin.readable()
True
>>> sys.stdin.seekable()
True
>>> sys.stdin.tell()
5296
>>> sys.stdin.encoding
'UTF-8'
>>> 
>>> os.device_encoding(0)
'UTF-8'
>>> os.isatty(0)
True
>>> os.ttyname(0)
'/dev/ttys001'
>>> os.get_terminal_size(0)
os.terminal_size(columns=122, lines=47)
>>> >>> os.get_terminal_size(0)
os.terminal_size(columns=115, lines=42) # You can change the size of your terminal window.
>>> os.stat(0)
os.stat_result(st_mode=8592, st_ino=697, st_dev=202182376, st_nlink=1, st_uid=501, st_gid=4, st_size=0, ....)
>>>

Because sys.stdin is a file object, it responds to methods applicable to file objects. However it is recommended that you do not attempt such methods or operations as these on sys.stdin: sys.stdin.close(), sys.stdin.flush(), sys.stdin.detach(), os.close(0)

Reading from stdin

[edit | edit source]

File descriptor 0 usually means standard input. When your python script reads from standard input, this usually means that it is reading data which you are typing on your keyboard. Because file descriptor 0 reads text, the new line character at the end of each line becomes significant.

>>> a = sys.stdin.readline() # The readline method waits until you type '\n'.
The quick, brown fox ...
>>> a
'The quick, brown fox ...\n'
>>> 
>>> a = sys.stdin.read(5)
ABCD EFGH # You entered 10 characters.
>>> a
'ABCD ' # a contains 5 characters. The remaining characters are waiting to be read.
>>> b = sys.stdin.read(5)
>>> b
'EFGH\n' # All input has been read.
>>> 
>>> a = sys.stdin.read(5) # Read 5 characters from stdin.
ABCD EFGH IJKL # You entered 15.
>>> a
'ABCD '
>>> b = sys.stdin.readline() # There are still 10 characters waiting to be read.
>>> b
'EFGH IJKL\n' # b contains all remaining characters.
>>>

In the real world your python script will probably read from stdin after supplying a prompt for your response.

import sys
import os

print ('Enter date-of-birth [mm/dd/yyyy]: ', end='', flush=True)
a = sys.stdin.readline()
print ('You entered', a, end='') # str a has '\n' at end.
$ python3.6 t4.py
Enter date-of-birth [mm/dd/yyyy]: 12/31/1967
You entered 12/31/1967
$ 

python's built-in function input(....) simplifies prompting and reading from stdin:

import sys
import readline

s = input ('Enter date-of-birth [mm/dd/yyyy]: ')

print ('You entered', s)
$ python3.6 t4.py
Enter date-of-birth [mm/dd/yyyy]: 12/31/1967
You entered 12/31/1967
$ 

If the readline module was loaded, then input() will use it to provide elaborate line editing and history features similar to command line editing with emacs.

Detecting timeout on stdin

[edit | edit source]

The python script above sends a prompt to stdout and expects a response on stdin. What should happen if the person at the keyboard is called away and no response is forthcoming?

You decide to terminate the session after 30 seconds of inactivity. Here is one way to do it:

$ cat t6.py
import signal
import sys
import readline

def handler(signum, frame):
    print ('\nOperation timed out.')
    exit (99)

# Set the signal handler and a 30-second alarm                                                                         
signal.signal(signal.SIGALRM, handler)
signal.alarm(30)

status = 0

try :
    s = input ('Enter date-of-birth [mm/dd/yyyy]: ')
except KeyboardInterrupt :
    print ('\nKeyboardInterrupt detected.' )
    status = 98
except SystemExit :
    print ('SystemExit detected.' )
    status = 97
except :
    print ('\nException detected: ', str(sys.exc_info()[0]) )
    status = 96
else :
    print ('You entered', s)

signal.alarm(0)          # Disable the alarm.

exit (status)
$ python3.6 t6.py ; echo $?
Enter date-of-birth [mm/dd/yyyy]: 12/31/1999 # Normal input.
You entered 12/31/1999
0 # Normal exit 0.
$
$ python3.6 t6.py ; echo $?
Enter date-of-birth [mm/dd/yyyy]: # No input.
Operation timed out.
SystemExit detected.
97 # Exit with error status 97.
$
$ python3.6 t6.py ; echo $?
Enter date-of-birth [mm/dd/yyyy]: # Entered ^C.
KeyboardInterrupt detected.
98 # Exit with error status 98.
$ 

Input stream redirected

[edit | edit source]

Usually stdin is connected to the keyboard so that a command like sys.stdin.read() reads data from the keyboard until end-of-line is detected. However, input can come from a pipe.

$ cat t4.py
import sys
import readline

print ('sys.stdin.isatty():', sys.stdin.isatty())

s = input ('Enter date-of-birth [mm/dd/yyyy]: ')

if not sys.stdin.isatty() :
    print ()

print ('You entered', s)
$ python3.6 t4.py
sys.stdin.isatty(): True                     # This is interactive session.
Enter date-of-birth [mm/dd/yyyy]: 12/31/1967 # Input from keyboard.
You entered 12/31/1967
$ 
$ echo '12/31/1988' | python3.6 t4.py # Input from a pipe.
sys.stdin.isatty(): False             # Not interactive.
Enter date-of-birth [mm/dd/yyyy]:     # No input from keyboard.
You entered 12/31/1988
$ 

Console input from terminal

[edit | edit source]

The terminal can provide console input. On Unix the device name of the terminal is available and it behaves like a file:

$ cat t5.py
import os

print ('Name of my terminal is:', os.ttyname(0))

f = open(os.ttyname(0))	# for reading                                                                               

print ('File object opened for reading from my terminal is:', f)

print ('Enter name of your favorite pet: ', end='',flush=True)

pet = f.read()

f.close()

print ('Input from terminal was:', pet, end='')
print ('len(pet) =', len(pet))
$ python3.6 t5.py
Name of my terminal is: /dev/ttys003
File object opened for reading from my terminal is: <_io.TextIOWrapper name='/dev/ttys003' mode='r' encoding='UTF-8'>
Enter name of your favorite pet: Fido # Enter 'Fido\n' then ^D for end-of-file.
Input from terminal was: Fido
len(pet) = 5 # String pet contains 5 characters (including the new-line) and without the ^D.
$ 

Assignments

[edit | edit source]
  • How do you know if stdin contains data waiting to be read?
  • If stdin contains unwanted data, how do you clear the buffer?

Further Reading or Review

[edit | edit source]

References

[edit | edit source]

1. Python's documentation:

29.1. sys — System-specific parameters and functions: "sys.stdin"

"16.1.4. File Descriptor Operations," "18.8.3. Example (of signal.alarm(....))"


2. Python's methods:

"16.2.3.1. I/O Base Classes," "16.2.3.4. Text I/O"


3. Python's built-in functions:

"os.get_terminal_size(fd=STDOUT_FILENO)," "input(....)," "print(*objects, ....)"