Essentially, i want what is in this thread: Output to console while preserving user input in ruby, but in Python. I have googled for quite a while, and found an ALMOST working solution, except that it blocked the main thread, as long as i wasn't typing anything in and pressing enter.
Some output of what i don't want to happen is here:
/raw:jtv!jtv#jtv.tmi.twitch.tv PRIVMSG #cobaltstreak :USERCOLOR ullr_son_of_sif #DAA520
Some example input of what i want is:
:jtv!jtv#jtv.tmi.twitch.tv PRIVMSG #cobaltstreak :USERCOLOR ullr_son_of_sif #DAA520
/raw
PRIV:jtv!jtv#jtv.tmi.twitch.tv PRIVMSG #cobaltstreak :SPECIALUSER nightbot subscriber
MSG #cobaltstreak :This shouldn't be here, but on the same line with /raw
This meaning, i want the bottom line of the console to preserve input, while outputting everything happening in the main thread without affecting input.
My current code is:
def console(q, m, lock):
while 1:
raw_input() # After pressing Enter you'll be in "input mode"
with lock:
i = raw_input('> ')
cmd = i.split(' ')[0]
msg = i.strip(cmd + ' ')
q.put(cmd)
m.put(msg)
if cmd == 'quit':
break
as well has:
cmd = cmd_queue.get()
msg = msg_queue.get()
action = cmd_actions.get(cmd)
if action is not None:
action(stdout_lock, msg)
Note the code above is the very first couple of lines in my while loop.
I am on Windows and using python 2.7.6
Related
I want my program to stop executing when a ctrl-c is entered in the terminal window (that has focus) where the program is executing. Every google hit tells me this should work but it doesn't.
First I tried putting the try block in a class method my main invoked:
try:
for row in csvInput:
<process the current row...>
except KeyboardInterrupt:
print '\nTerminating program!\n'
exit()
and then I tried putting the try block in my main program and that didn't work:
if __name__ == '__main__':
try:
programArg = ProgramArgs(argparse.ArgumentParser)
args = programArg.processArgs()
currentDir = os.getcwd()
product = Product(currentDir, args.directory[0], programArg.outputDir)
product.verify()
except KeyboardInterrupt:
print '\nTerminating program!\n'
exit()
I recently (May 2, 2020) hit this same issue in Windows-10 using Anaconda2-Spyder(Python2.7). I am new to using Spyder. I tried multiple ways to get [break] or [ctrl-c] to work as expected by trying several suggestions listed in stackoverflow. Nothing seemed to work. However, what I eventually noticed is that the program stops on the line found after the "KeyboardInterrupt" catch.
[Solution]: select [Run current line] or [continue execution] from the debugger tools (either menu item or icon functions) and the rest of the program executes and the program properly exits. I built the following to experiment with keyboard input.
def Test(a=0,b=0):
#Simple program to test Try/Catch or in Python try/except.
#[break] using [Ctrl-C] seemed to hang the machine.
#Yes, upon [Ctrl-C] the program stopped accepting User #
Inputs but execution was still "hung".
def Add(x,y):
result = x+y
return result
def getValue(x,label="first"):
while not x:
try:
x=input("Enter {} value:".format(label))
x = float(x)
continue
except KeyboardInterrupt:
print("\n\nUser initiated [Break] detected." +
"Stopping Program now....")
#use the following without <import sys>
raise SystemExit
#otherwise, the following requires <import sys>
#sys.exit("User Initiated [Break]!")
except Exception:
x=""
print("Invalid entry, please retry using a " +
"numeric entry value (real or integer #)")
continue
return x
print ("I am an adding machine program.\n" +
"Just feed me two numbers using "Test(x,y) format\n" +
"to add x and y. Invalid entries will cause a \n" +
"prompt for User entries from the keyboard.")
if not a:
a = getValue(a,"first")
if not b:
b = getValue(b,"second")
return Add(a,b)
I made a program that checks a secret key. and another program that supposed to find the secret key. I managed to open a child process in the second program but couldn't send it an input. Here's the program that checks a secret key, secret_key.py:
SECRET_KEY = "hi"
current_key = 0
while not current_key == "exit":
wrong_key = False
current_key = raw_input("Enter the key or enter 'exit' to exit.\n")
for i in range(len(current_key)):
if i < len(SECRET_KEY):
if not current_key[i] == SECRET_KEY[i]:
wrong_key = True
else:
wrong_key = True
if not wrong_key:
print("the key is right!\n")
current_key = "exit"
raw_input("Press ENTER to exit\n")
exit()
Now i made a .sh file to be able to run it in a new terminal as a child process, program.sh:
#! /bin/bash
python Desktop/school/secret_key.py
And here's where i got stuck, find_key.py:
import subprocess
program = subprocess.Popen("./program.sh")
now I could't find a way to send secret_key.py the input it asks for.
Is there anyway I can send a string input to secret_key.py from find_key.py?
To send input to and read output from a process opened via Popen, you can read from and write to the process using the process' stdin and stdout fields. You do need to tell Popen to set up pipes, though:
process = subprocess.Popen([SPIM_BIN] + extra_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
pin = process.stdin
pout = process.stdout
And now you can write to pin and read from pout as you would with any old file descriptor.
Note that this will probably not allow you to connect to the gnome-terminal. But it will allow you to connect to program.sh.
I'm writing a script (Python 2.7.10) to log in to networking devices and gather diagnostics information that the vendor can potentially ask for. It's fairly straightforward, but I've run into an interesting problem (at least for me).
I've exhausted my limited knowledge on this.
This is the piece of the code that calls the functions to run:
elif args.hostname and args.username and args.jtac and args.commands and args.shell:
print("RUN FOR SINGLE HOST w/ SHELL AND CLI COMMANDS")
open_ssh_session(args.hostname, args.username, password)
commands_and_iterations_cli(args.hostname, args.jtac, args.iterations, float(args.interval))
commands_and_iterations_shell(args.username, args.hostname, args.jtac, args.iterations, float(args.interval))
single_core_dump(args.hostname, args.username, password, args.jtac)
pull_files_from_juniper_device(args.hostname, args.username, password, args.jtac)
push_files_to_juniper_sftp(args.hostname, args.username, password, args.jtac)
So I have 2 functions:
def commands_and_iterations_cli(hostname, jtac, iterations, interval):
print("Enter each JUNOS CLI command on separate lines, press CTRL+D when finished.\n"
"NOTE: Valid CLI commands only, DO NOT input shell commands here!\n")
# Take user input, enter a command on each new line, send EOF to indicate that you're done.
cli_commands = sys.stdin.readlines()
# Instantiate user shell.
channel = client.invoke_shell()
# Take each line from given input, and iterate over hostname.
for line in cli_commands:
line = line.strip()
iter_counter = 0
print("Running {}, {} times, every {} seconds.".format(line, str(iterations), interval))
while iter_counter < iterations:
iter_counter = iter_counter + 1
channel.send(line +' | save "{}-{}-{}"\n'.format(hostname, jtac, line))
time.sleep(interval)
def commands_and_iterations_shell(username, hostname, jtac, iterations, interval):
print("Enter each shell command on separate lines, press CTRL+D when finished.\n"
"NOTE: Valid shell commands only, DO NOT input JUNOS CLI commands here!\n"
"** ENSURE COMMANDS ARE SAFE TO RUN IN PRODUCTION IF DOING SO! **\n")
# Take user input, enter a command on each new line, send EOF to indicate that you're done.
shell_commands = sys.stdin.readlines()
# Prompt for hostname's root password to enter root shell.
print("Enter root password for {}:\n".format(hostname))
rootpass = getpass.getpass()
# Instantiate user shell.
channel = client.invoke_shell()
# Start root shell.
channel.send("start shell user root\n")
# Let the prompt become available.
time.sleep(1)
# Send the password and let the root prompt return.
channel.send(rootpass+"\n")
time.sleep(1)
# Take each line from given input, and iterate over hostname.
for line in shell_commands:
line = line.strip()
print("Running {}, {} times, every {} seconds.".format(line, str(iterations), interval))
iter_counter = 0
while iter_counter != iterations:
channel.send(line +' >> "/var/home/{}/{}-{}-{}" \n'.format(username, hostname, jtac, line))
time.sleep(interval)
iter_counter = iter_counter + 1
print(iter_counter)
The code runs perfectly if I run commands_and_iterations_cli() or commands_and_iterations_shell() independently. However when I try both (the example given), the CLI function will run correctly, then when it comes time to run the shell function, the shell function will print the text in it, and prompt for the root password, and then immediately skip to the next function after it without prompting for commands. I've even tried giving it a 30 second sleep before the shell function runs, and the behavior just follows.
Thanks, all.
It doesn't work because you have to use CTRL+D to exit sys.stdin.readlines() which closes the stream and further calls to it do nothing (i.e. return empty list).
I was asked to simulate CLI with Python.
This is what I did
def somefunction(a,b):
//codes here
//consider some other functions too
print "--- StackOverFlow Shell ---"
while True:
user_input = raw_input("#> ")
splitit = user_input.split(" ")
if splitit[0] == "add":
firstNum = splitit[1]
sNum = splitit[2]
result = somefunction(firstNum, sNum)
print result
//consider some other elif blocks with "sub", "div", etc
else:
print "Invalid Command"
I do also check the length of the list, here "splitit" I will allow only 3 argumets, first will be the operation, and second and third are the arguments with which some functions are to be performed, in case the argument is more than 3, for that i do put a check.
Though Somehow I manage to make it work, but is there a better way to achieve the same?
Use python CMD Module:
Check few examples given on the below pages
http://docs.python.org/library/cmd.html # Support for line-oriented command interpreters
http://www.doughellmann.com/PyMOTW/cmd - # Create line-oriented command processors
prompt can be set to a string to be printed each time the user is asked for a new command.
intro is the “welcome” message printed at the start of the program.
eg:
import cmd
class HelloWorld(cmd.Cmd):
"""Simple command processor example."""
prompt = 'prompt: '
intro = "Simple command processor example."
You should check out the VTE lib:
http://earobinson.wordpress.com/2007/09/10/python-vteterminal-example/
It works really well and you can very easily customize its look. This is how easy it is:
# make terminal
terminal = vte.Terminal()
terminal.connect ("child-exited", lambda term: gtk.main_quit())
terminal.fork_command()
# put the terminal in a scrollable window
terminal_window = gtk.ScrolledWindow()
terminal_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
terminal_window.add(terminal)
I'm using Python/Pexpect to spawn an SSH session to multiple routers. The code will work for one router but then the output of session.before will get out of sync with some routers so that it will return the output from a previous sendline. This seems particularly the case when sending a blank line (sendline()). Anyone got any ideas? Any insight would be really appreciated.
Below is a sample of what I'm seeing:
ssh_session.sendline('sh version')
while (iresult==2):
iresult = ssh_session.expect(['>','#','--More--'],timeout=SESSION_TIMEOUT)
debug_print("execute_1 " + str(iresult))
debug_print("execute_bef " + ssh_session.before)
debug_print("execute_af " + ssh_session.after)
thisoutput = ssh_session.before
output += thisoutput
if(iresult==2):
debug_print("exec MORE")
ssh_session.send(" ")
else:
debug_print("exec: end loop")
for cmd in config_commands:
debug_print("------------------------------------------------\n")
debug_print ("running command " + cmd.strip() + "\n")
iresult=2
ssh_session.sendline(cmd.strip())
while (iresult==2):
iresult = ssh_session.expect([prompt+">",prompt+"#"," --More-- "],timeout=SESSION_TIMEOUT)
thisoutput = ssh_session.before
debug_print("execute_1 " + str(iresult))
debug_print("execute_af " + ssh_session.after)
debug_print("execute_bef " + thisoutput)
thisoutput = ssh_session.before
output += thisoutput
if(iresult==2):
debug_print("exec MORE")
ssh_session.send(" ")
else:
debug_print("exec: end loop")
I get this:
logged in
exec: sh version
execute_1 1
execute_bef
R9
execute_af #
exec: end loop
------------------------------------------------
running command config t
execute_1 1
execute_af #
execute_bef sh version
Cisco IOS Software, 1841 Software (C1841-IPBASEK9-M), Version 15.1(4)M4, RELEASE SOFTWARE (fc1)
Technical Support: http://www.cisco.com/techsupport...
I've run into this before with pexpect (and I'm trying to remember how I worked around it).
You can re-synchronize with the terminal session by sending a return and then expecting for the prompt in a loop. When the expect times out then you know that you are synchronized.
The root cause is probably that you are either:
Calling send without a match expect (because you don't care about the output)
Running a command that produces output but expecting for a pattern in the middle of that output and then not to next prompt that is at end of the output. One way to deal with this is to change your expect pattern to "(.+)PROMPT" - this will expect until the next prompt and capture all the output of the command sent (which you can parse in the next step).
I faced a similar problem. I tried waiting for the command to be printed on the screen and the sending enter.
I you want to execute say command 'cmd', then you do:
session.send(cmd)
index = session.expect([cmd, pexpect.TIMEOUT], 1)
session.send('\n')
index = session.expect([whatever you expect])
Worked for me.
I'm not sure this is the root of your problem, but it may be worth a try.
Something I've run into is that when you spawn a session that starts with or lands you in a shell, you have to deal with quirks of the TERM type (vt220, color-xterm, etc.). You will see characters used to move the cursor or change colors. The problem is almost guaranteed to show up with the prompt; the string you are looking for to identify the prompt appears twice because of how color changes are handled (the prompt is sent, then codes to backspace, change the color, then the prompt is sent again... but expect sees both instances of the prompt).
Here's something that handles this, guaranteed to be ugly, hacky, not very Pythonic, and functional:
import pexpect
# wait_for_prompt: handle terminal prompt craziness
# returns either the pexpect.before contents that occurred before the
# first sighting of the prompt, or returns False if we had a timeout
#
def wait_for_prompt(session, wait_for_this, wait_timeout=30):
status = session.expect([wait_for_this, pexpect.TIMEOUT, pexpect.EOF], timeout=wait_timeout)
if status != 0:
print 'ERROR : timeout waiting for "' + wait_for_this + '"'
return False
before = session.before # this is what we will want to return
# now look for and handle any additional sightings of the prompt
while True:
try:
session.expect(wait_for_this, timeout=0.1)
except:
# we expect a timeout here. All is normal. Move along, Citizen.
break # get out of the while loop
return before
s = pexpect.spawn('ssh me#myserver.local')
s.expect('password') # yes, we assume that the SSH key is already there
# and that we will successfully connect. I'm bad.
s.sendline('mypasswordisverysecure') # Also assuming the right password
prompt = 'me$'
wait_for_prompt(s, prompt)
s.sendline('df -h') # how full are my disks?
results = wait_for_prompt(s, prompt)
if results:
print results
sys.exit(0)
else:
print 'Misery. You lose.'
sys.exit(1)
I know this is an old thread, but I didn't find much about this online and I just got through making my own quick-and-dirty workaround for this. I'm also using pexpect to run through a list of network devices and record statistics and so forth, and my pexpect.spawn.before will also get out of sync sometimes. This happens very often on the faster, more modern devices for some reason.
My solution was to write an empty carriage return between each command, and check the len() of the .before variable. If it's too small, it means it only captured the prompt, which means it must be at least one command behind the actual ssh session. If that's the case, the program sends another empty line to move the actual data that I want into the .before variable:
def new_line(this, iteration):
if iteration > 4:
return data
else:
iteration+=1
this.expect(":")
this.sendline(" \r")
data = this.before
if len(data) < 50:
# The numer 50 was chosen because it should be longer than just the hostname and prompt of the device, but shorter than any actual output
data = new_line(this, iteration)
return data
def login(hostname):
this = pexpect.spawn("ssh %s" % hostname)
stop = this.expect([pexpect.TIMEOUT,pexpect.EOF,":"], timeout=20)
if stop == 2:
try:
this.sendline("\r")
this.expect(":")
this.sendline("show version\r")
version = new_line(this,0)
this.expect(":")
this.sendline("quit\r")
return version
except:
print 'failed to execute commands'
this.kill(0)
else:
print 'failed to login'
this.kill(0)
I accomplish this by a recursive command that will call itself until the .before variable finally captures the command's output, or until it calls itself 5 times, in which case it simply gives up.