python pexpect sendcontrol key characters - python

I am working with pythons pexpect module to automate tasks, I need help in figuring out key characters to use with sendcontrol. how could one send the controlkey ENTER ? and for future reference how can we find the key characters?
here is the code i am working on.
#!/usr/bin/env python
import pexpect
id = pexpect.spawn ('ftp 192.168.3.140')
id.expect_exact('Name')
id.sendline ('anonymous')
id.expect_exact ('Password')
*# Not sure how to send the enter control key
id.sendcontrol ('???')*
id.expect_exact ('ftp')
id.sendline ('dir')
id.expect_exact ('ftp')
lines = id.before.split ('\n')
for line in lines :
print line

pexpect has no sendcontrol() method. In your example you appear to be trying to send an empty line. To do that, use:
id.sendline('')
If you need to send real control characters then you can send() a string that contains the appropriate character value. For instance, to send a control-C you would:
id.send('\003')
or:
id.send(chr(3))
Responses to comment #2:
Sorry, I typo'ed the module name -- now fixed. More importantly, I was looking at old documentation on noah.org instead of the latest documentation at SourceForge. The newer documentation does show a sendcontrol() method. It takes an argument that is either a letter (for instance, sendcontrol('c') sends a control-C) or one of a variety of punctuation characters representing the control characters that don't correspond to letters. But really sendcontrol() is just a convenient wrapper around the send() method, which is what sendcontrol() calls after after it has calculated the actual value that you want to send. You can read the source for yourself at line 973 of this file.
I don't understand why id.sendline('') does not work, especially given that it apparently works for sending the user name to the spawned ftp program. If you want to try using sendcontrol() instead then that would be either:
id.sendcontrol('j')
to send a Linefeed character (which is control-j, or decimal 10) or:
id.sendcontrol('m')
to send a Carriage Return (which is control-m, or decimal 13).
If those don't work then please explain exactly what does happen, and how that differs from what you wanted or expected to happen.

If you're just looking to "press enter", you can send a newline:
id.send("\n")
As for other characters that you might want to use sendcontrol() with, I found this useful: https://condor.depaul.edu/sjost/lsp121/documents/ascii-npr.htm
For instance, I was interested in Ctrl+v. Looking it up in the table shows this line:
control character
python & java
decimal
description
^v
\x16
22
synchronous idle
So if I want to send that character, I can do any of these:
id.send('\x16')
id.send(chr(22))
id.sendcontrol('v')
sendcontrol() just looks up the correct character to send and then sends it like any other text
For keys not listed in that table, you can run this script: https://github.com/pexpect/pexpect/blob/master/tests/getch.py (ctrl space to exit)
For instance, ran that script and pressed F4 and it said:
27<STOP>
79<STOP>
83<STOP>
So then to press F4 via pexpect:
id.send(chr(27) + chr(79) + chr(83))

Related

Line terminator adds dot at the end of a line in npm test of Python code

I wanted to learn command line programming using Python.
I saw a to-do challenge on the internet and started to work on it by learning from the web. The challenge is to create a command line interface of a to-do app.
The challenge is titled CoronaSafe Engineering Fellowship Test Problem. Here is the challenge material on Google Drive: https://drive.google.com/drive/folders/1SyLcxnEBNRecIyFAuL5kZqSg8Dw4xnTG?usp=sharing
and there is a GitHub project at https://github.com/nseadlc-2020/package-todo-cli-task/
In the README.md I was instructed to create symbolic link for the batch file todo.bat with the name todo. Now, my first condition is that, when the symbolic link is called from the command prompt without any arguments, it must print some usage tips for the program. Finally, I have to use the npm test command to test the execution.
At the very beginning I got this trouble, whenever I use a print statement, I see a dot • at the end of every string which ends with a new line. For instance,
import sys
import random
args = sys.argv[1:]
if len(args) == 0:
print('Usage :-', end='\n')
print('$ ./todo help # Show usage', end='')
The above statements when executed without arguments gives the output,
Usage :-.
$ ./todo help # Show usage
Here, I noticed that for the first print statement ends with a newline, the string ends with what looks like a middle dot (•). Whereas, for the second print statement since I override the end parameter with an empty string, no newline character was output, and so the dot is not printed. See the screen shot:
What's wrong, and how can I pass the test? My program does not print a middle dot at all.
The problem seems to be squarely inside the todo.test.js file.
In brief, Windows and Unix-like platforms have different line ending conventions (printing a line in Windows adds two control characters at the end, whilst on Unix-like systems only one is printed) and it looks like the test suite is only prepared to cope with results from Unix-like systems.
Try forcing your Python to only print Unix line feeds, or switch to a free Unix-like system for running the tests.
Alternatively, rename todo.test.js and replace it with a copy with DOS line feeds. In many Windows text editors, you should be able to simply open the file as a Unix text file, then "Save As..." and select Windows text file (maybe select "ANSI" if it offers that, though the term is horribly wrong and they should know better); see e.g. Windows command to convert Unix line endings? for many alternative solutions (many of which vividly illustrate some of the other issues with Windows; proceed with caution).
This seems to be a known issue, as noted in the README.md you shared: https://github.com/nseadlc-2020/package-todo-cli-task/issues/12 (though it imprecisely labels this as "newline UTF encoding issues"; the problem has nothing to do with UTF-8 or UTF-16).
See also the proposed duplicate Line endings (also known as Newlines) in JS strings
I had exactly the same problem.
I replaced:
print(variable_name) # Or print("Your text here")
With:
sys.stdout.buffer.write(variable_name.encode('utf-8')) # To sys.stdout.buffer.write("Your text here".encode('utf-8'))
Now it worked fine in windows.
First write your help string like this
help_string='Usage :-\n$ ./task add 2 hello world # Add a new item with priority 2 and text "hello world" to the list\n$ ./task ls # Show incomplete priority list items sorted by priority in ascending order\n$ ./task del INDEX # Delete the incomplete item with the given index\n$ ./task done INDEX # Mark the incomplete item with the given index as complete\n$ ./task help # Show usage\n$ ./task report # Statistics'
Then print it on the console using
sys.stdout.buffer.write(help_string.encode('utf8'))
This problem occurs due to differences in encoding type of windows and npm tests. Also make sure to avoid any spaces after or before "\n".
Why have multiple prints,when python prints can incorporate new line without having to declare separately, follow example below:
print("Usage :- \n$ ./todo help #Show usage")
Output:
Usage :-
$ ./todo help #Show usage

Python 2.7 VSCODE doesn't get the correct length of input string

in my class we are studying python 2.7. I am using vscode to test the exercises.
exercise 1: read user input and print the length. If the user write
exit the program finish.
My code is follow:
myexit=False
while (myexit!=True):
#read user input
txt=raw_input("write a string or write exit to go out: ")
#print the user input string
print txt
if (str(txt)=='exit'):
myexit=True#exit from while
else:
print len(txt) #print the string length
print "finish"
when i test the code i get always the length of the string +1
example: if i write foo the output is 4 and no 3. When i write exit i
don't go out from the while and the output is 5.
Where i wrong ?
I have missed a module?
Thanks for your help
I am not sure exactly why this is happening, and I don't have access to a windows machine to test/verify but based on the comments above, it appears that on the version of python you are using that raw_input is only stripping the newline(\n) and not the carriage return(\r). Windows uses \r\n while unix uses \n. When raw input returns the \r is still on the string, hence the extra char. A useful debugging technique at the cli is to use the function repr() on the value to see exactly how it is represented. This is helpful to locate any stray control or invisible chars in strings.
The function rstrip() will remove all whitespace from the right side of the string, which in this case should safely remove the stray \r. It should also be safe if this code is running on a *nix like system as rstrip() will only remove the whitespace if it is present. You can also specify a set of char to strip, so if you would would like to be pedantic, you could use rstrip("\r").
txt=raw_input("write a string or write exit to go out: ").rstrip("\r")
Should fix the issue while still maintaining compatibility on different versions.

Advanced input in python

I want to receive some information from a user in a next way:
My score is of 10 - is already printed
Between 'is' and 'of' there is an empty place for user's input so he doesn't enter his information at the end( if using simple input() ) but in the middle. While user is entering some information it appears between 'is' and 'of'
Is there any possible way to do it?
One way to get something close to what you want is if you terminal supports ANSI escape codes:
x = input("My score is \x1b[s of 10\x1b[u")
\x1b is the escape character. Neither escape character is dipsplayed on the screen; instead, they introduce byte sequences that the terminal interprets as an instruction of some kind. ESC[s tells the terminal to remember where the cursor is at the moment. ESC[u tells the terminal to move the cursor to the last-remembered position.
(The rectangle is the cursor in an unfocused window.)
Using a library that abstracts away the exact terminal you are using is preferable, but this gives you an idea of how such libraries interact with your terminal: it's all just bytes written to standard output.
If you use console then consider importing curses library. It works on both linux and windows. Download it for windows from http://www.lfd.uci.edu/~gohlke/pythonlibs/#curses
With this library you have a total control over console. Here is the answer to your question.
How to input a word in ncurses screen?

sys.stdin.readline() reads without prompt, returning 'nothing in between'

I have a function that executes the following (among other things):
userinput = stdin.readline()
betAmount = int(userinput)
Is supposed to take input integer from stdin as a string and convert it to an integer.
When I call the function, however, it returns a single newline character (it doesn't even wait for me to input anything).
Earlier in the program I get some input in the form of:
stdin.read(1)
to capture a single character.
Could this have something to do with it? Am I somehow writing a newline character to the next line of stdin?
How can I fix this?
stdin.read(1) reads one character from stdin. If there was more than one character to be read at that point (e.g. the newline that followed the one character that was read in) then that character or characters will still be in the buffer waiting for the next read() or readline().
As an example, given rd.py:
from sys import stdin
x = stdin.read(1)
userinput = stdin.readline()
betAmount = int(userinput)
print ("x=",x)
print ("userinput=",userinput)
print ("betAmount=",betAmount)
... if I run this script as follows (I've typed in the 234):
C:\>python rd.py
234
x= 2
userinput= 34
betAmount= 34
... so the 2 is being picked up first, leaving the 34 and the trailing newline character to be picked up by the readline().
I'd suggest fixing the problem by using readline() rather than read() under most circumstances.
Simon's answer and Volcano's together explain what you're doing wrong, and Simon explains how you can fix it by redesigning your interface.
But if you really need to read 1 character, and then later read 1 line, you can do that. It's not trivial, and it's different on Windows vs. everything else.
There are actually three cases: a Unix tty, a Windows DOS prompt, or a regular file (redirected file/pipe) on either platform. And you have to handle them differently.
First, to check if stdin is a tty (both Windows and Unix varieties), you just call sys.stdin.isatty(). That part is cross-platform.
For the non-tty case, it's easy. It may actually just work. If it doesn't, you can just read from the unbuffered object underneath sys.stdin. In Python 3, this just means sys.stdin.buffer.raw.read(1) and sys.stdin.buffer.raw.readline(). However, this will get you encoded bytes, rather than strings, so you will need to call .decode(sys.stdin.decoding) on the results; you can wrap that all up in a function.
For the tty case on Windows, however, input will still be line buffered even on the raw buffer. The only way around this is to use the Console I/O functions instead of normal file I/O. So, instead of stdin.read(1), you do msvcrt.getwch().
For the tty case on Unix, you have to set the terminal to raw mode instead of the usual line-discipline mode. Once you do that, you can use the same sys.stdin.buffer.read(1), etc., and it will just work. If you're willing to do that permanently (until the end of your script), it's easy, with the tty.setraw function. If you want to return to line-discipline mode later, you'll need to use the termios module. This looks scary, but if you just stash the results of termios.tcgetattr(sys.stdin.fileno()) before calling setraw, then do termios.tcsetattr(sys.stdin.fileno(), TCSAFLUSH, stash), you don't have to learn what all those fiddly bits mean.
On both platforms, mixing console I/O and raw terminal mode is painful. You definitely can't use the sys.stdin buffer if you've ever done any console/raw reading; you can only use sys.stdin.buffer.raw. You could always replace readline by reading character by character until you get a newline… but if the user tries to edit his entry by using backspace, arrows, emacs-style command keys, etc., you're going to get all those as raw keypresses, which you don't want to deal with.
stdin.read(1)
will not return when you press one character - it will wait for '\n'. The problem is that the second character is buffered in standard input, and the moment you call another input - it will return immediately because it gets its input from buffer.
If you need just one character and you don't want to keep things in the buffer, you can simply read a whole line and drop everything that isn't needed.
Replace:
stdin.read(1)
with
stdin.readline().strip()[:1]
This will read a line, remove spaces and newlines and just keep the first character.
Try this ...
import sys
buffer = []
while True:
userinput = sys.stdin.readline().rstrip('\n')
if userinput == 'quit':
break
else:
buffer.append(userinput)
import sys
userinput = sys.stdin.readline()
betAmount = int(userinput)
print betAmount
This works on my system. I checked int('23\n') would result in 23.

How to query an input in Python without outputting a new line

The title describes the question pretty much.
The input function, which does the query, does not emit a newline:
>>> input('tell me: ')
tell me: what?
'what?'
>>>
as you see, the prompt is output without any newline, and what the user types after that appears on the same line as the prompt. Of course, the user is also typing a newline, and (like everything else the user types), that newline is echoed (so further results are on following lines). Is that your problem?
If so, then you need to switch to platform-specific approaches, such as curses on just about any machine except Windows, and msvcrt on Windows (or, you could look for a curses port on Windows, but I don't know if there's one for Python 3). The two modules are very different, and you haven't clarified your platform (or your exact needs -- my previous paragraph is an attempt at an educated guess;-), so I'll just wait for you to clarify needs and platforms rather than launching into long essays that may not prove helpful.
If you use raw_input it does not insert a new line automatically.
>>> name = raw_input ("What...is your name? ")
What...is your name? Arthur, King of the Britons!

Categories