input() after readlines() from sys.stdin? - python

I have a case where my script calls readlines() on sys.stdin followed by a call to input(), but that last call won't work.
Here's my script:
import click
#click.command()
#click.argument('data', type=click.File())
def inp(data):
lines = data.readlines()
print('Read {} lines. Continue?'.format(len(lines)))
choice = input().lower()
print("You said '{}'.".format(choice))
if __name__ == '__main__':
inp()
The script works fine if the user specified an actual file argument on the command line, but not if they pipe input data and use the -:
head -n10 data.txt | python3 script.py -
Read 10 lines. Continue?
Aborted!
I need to support the - reading followed by input() -- how can I make it work?

to save our time, u are free to jump to the bottom to see the conclusion
Start
I am searching for the answer to the question as u did.
At First, I think the following will solve the problem...
multiple piped inputs without EOF
How to use `input()` after reading a file from `stdin`?
however, it does not succeed in my computer,
and thus, I try to figure out how to exactly clear the EOF mark...
Terminate/Break/Abort Python Console/sys.stdin readline()
How do you terminate/break/abort a Python console/sys.stdin readline()?
flush
How to flush the input stream in python?
In C Language, we use fflush(stdin), ..etc to clear the buffer
In C++ Language, we use std::cin.get(), ..etc to clear the buffer
But How About Python Language ??
After Searching For 2 hours, I realize Python so far does not provide this function, clear the buffer/EOF in the stdin
And the answers available online which succeed like this one
How to use `input()` after reading a file from `stdin`? are only suitable for Linux System
After Reading This
https://www.twblogs.net/a/5c0ac824bd9eee6fb21399d4
*I am so sure(i think) that Python in Windows cannot use input after stdin is taken by file
However, does that mean, we should batter the sys.stdin method?
No, Obviously, or I won't spend my time here typing...
And Here Is My Final Method To Solve The Problem
re-Create Your Own Input as follow
def new_input(): ## version 1
import msvcrt
str_=''
c=msvcrt.getche()
while ord(c)!=3 and ord(c)!=4 and ord(c)!=26 and ord(c)!=13:
# print(ord(c))
str_ = str_+str(c)[2:-1]
c=msvcrt.getche()
return str_
def new_input(interact_string_): ## version 2
import msvcrt
print(interact_string_, end ="")
str_=''
c=msvcrt.getche()
while ord(c)!=3 and ord(c)!=4 and ord(c)!=26 and ord(c)!=13:
# print(ord(c))
str_ = str_+str(c)[2:-1]
c=msvcrt.getche()
return str_
def new_input(interact_string_): ## version 3
import msvcrt
import os
print(interact_string_, end ="")
str_=''
print('')
c=msvcrt.getche()
while ord(c)!=3 and ord(c)!=4 and ord(c)!=26 and ord(c)!=13:
os.system('cls')
print(interact_string_, end ="")
# print(ord(c))
if ord(c)==8:
str_ = str_[0:-1]
print(str_)
else:
str_ = str_+str(c)[2:-1]
print(str_)
c=msvcrt.getche()
# print(str_)
return str_
Here, are the meanings of numbers
ord(c)==3 will be the key of Ctrl+C
ord(c)==4 will be the key of Ctrl+D
ord(c)==26 will be the key of Ctrl+Z
ord(c)==13 will be the key of Enter
ord(c)==22 will be the key of Ctrl+C
ord(c)==8 will be the key of Backspace
And it will still be convenient to using if-structure to react with, and returns the final string
Conclusion:
after using stdin to readlines()/readline()/read() to read a file you cannot ever using stdin to input/output in Windows System
while, in the same case, the Same Problem on Linux System is solvable, and the answer will be up there...
In my opinion and under my research, The Best Answer to Windows will be mine (of course will I say that..) . ++==> re-Create your input()
Thanks For Your Reading, constructive advice and polite comment is welcomed. Anyone With a Better Idea or Solve is welcome too.
like it if u enjoy the journey with me while reading my adventure to finding answer

Related

print string to terminal over and over again without flickering in python

I wrote a little python3 script, that runs another program with Popen and processes its output to create a little dashboard for it. The script generates a long string with information about the other program, clears the terminal and prints it. Everytime the screen refreshes, the whole terminal flickers.
here are the important parts of my script:
def screen_clear():
if os.name == 'posix':
os.system('clear')
else:
os.system('cls')
def display(lines):
# lines as a list of, well, lines i guess
s=''
for line in lines:
s=s + '\n' + str(line)
screen_clear()
print(s)
I bet theres a more elegant way without flickering to this, right?
Thanks for any help in advance!
the only solution to try out I can think of would be using print(s, end='\r') instead of clearing the screen first and printing again. The \r marker tells the console to override the last line.
In the end I'm sorry to say that consoles are simply not made for using them as a dashboard with permanently changing values. If the aforementioned solution doesn't work, maybe try implementing your dashboard in another way, python offers lots of solutions for that.

Mapping Window Drives Python: How to handle when the Win cmd Line needs input

Good Afternoon,
I used a version of this method to map a dozen drive letters:
# Drive letter: M
# Shared drive path: \\shared\folder
# Username: user123
# Password: password
import subprocess
# Disconnect anything on M
subprocess.call(r'net use * /del', shell=True)
# Connect to shared drive, use drive letter M
subprocess.call(r'net use m: \\shared\folder /user:user123 password', shell=True)
The above code works great as long as I do not have a folder with a file in use by a program.
If I run the same command just in a cmd window and a file is in use when I try to disconnect the drive it returns the Are you sure? Y/N.
How can I pass this question back to the user via the Py script (or if nothing else, force the disconnect so that the code can continue to run?
To force disconnecting try it with /yes like so
subprocess.call(r'net use * /del /yes', shell=True)
In order to 'redirect' the question to the user you have (at least) 2 possible approaches:
Read and write to the standard input / output stream of the sub process
Work with exit codes and start the sub process a second time if necessary
The first approach is very fragile as you have to read the standard output and interpret it which is specific to your current locale as well as answering later the question which is also specific to your current locale (e.g. confirming would be done with 'Y' in English but with 'J' in German etc.)
The second approach is more stable as it relies on more or less static return codes. I did a quick test and in case of cancelling the question the return code is 2; in case of success of course just 0. So with the following code you should be able to handle the question and act on user input:
import subprocess
exitcode = subprocess.call(r'net use * /del /no', shell=True)
if exitcode == 2:
choice = input("Probably something bad happens ... still continue? (Y/N)")
if choice == "Y":
subprocess.call(r'net use * /del /yes', shell=True)
else:
print("Cancelled")
else:
print("Worked on first try")

Python: time.sleep functions unexpectedly?

I'm trying to make a simple function that will type out a string letter by letter, such as in a game, where text scrolls.
Here's what my code looks like:
import time
def ScrollingText(s):
s=str(s)
for letter in s:
print(letter, end="")
time.sleep(.05)
print("") # newline at the end
if __name__=='__main__':
ScrollingText("Hello World!")
However when I run, it waits, then dumps out the whole string at once. I'm new to python (and this forum as well) so if anyone can point me in the right direction and show me what I'm missing here in time.sleep I'd greatly appreciate it. Thanks!
As you do not have a newline in your string, python will buffer it. You have to explicitly flush the output after each character like this:
import sys
import time
def ScrollingText(s):
s=str(s)
for letter in s:
print(letter, end="")
sys.stdout.flush()
time.sleep(.05)
print("") # newline at the end
For better performance, I/O is usually buffered, that is, python will collect the data you print until it can send it out as a large block (this also goes for file I/O by the way). By calling flush() on sys.stdout (which is the file object where print writes to by default), you force python to send your data to the operating system (and that will send it to your terminal).

Python: how to modify/edit the string printed to screen and read it back?

I'd like to print a string to command line / terminal in Windows and then edit / change the string and read it back. Anyone knows how to do it? Thanks
print "Hell"
Hello! <---Edit it on the screen
s = raw_input()
print s
Hello!
You could do some ANSI trickery to make it look like you are editing on screen. Check out this link (also similar to this SO post on colors).
This would only work on certain terminals and configurations. ymmv.
This python script worked in my Cygwin terminal on Win7:
print 'hell'
print '\033[1A\033[4CO!'
Ends up printing hellO! on one line. The 2nd print moves the cursor up one line (Esc[1A) then over 4 characters (Esc[4C]) and then prints the 'O!'.
It wouldn't let you read it back though... only a 1/2 answer.
I had this same use-case for a command-line application.
Finally found a hack to do this.
# pip install pyautogui gnureadline
import pyautogui
import readline
from threading import Thread
def editable_input(text):
Thread(target=pyautogui.write, args=(text,)).start()
modified_input = input()
return modified_input
a = editable_input("This is a random text")
print("Received input : ", a)
The trick here is use pyautogui to send the text from keyboard. But we want to do this immediately after the input(). Since input() is a blocking call, we can run the pyautogui command in a different thread. And have an input function immediately after that in the main thread.
gnureadline is for making sure we can press left and right arrow keys to move the cursor in a terminal without printing escape characters.
Tested this on Ubuntu 20, python 3.7
raw_input accepts a parameter for a "prompt message", so use that to output the message, and then prepend it to what you get back. However, this won't allow you to backspace into the prompt, because it's a prompt and not really part of the input.
s = "Hell" + raw_input("Hell")
print s
os.sys.stdout is write only, but you can erase some characters of the last line with \b or the whole line with \r, as long as you did not write a carriage return.
(however, see also my question about limitations to the standard python console/terminal)
I once made some output exercise (including a status bar) to write,erase or animate if you will, perhaps it is helpfull:
from __future__ import print_function
import sys, time
# status generator
def range_with_status(total):
n=0
while n<total:
done = '#'*(n+1)
todo = '-'*(total-n-1)
s = '<{0}>'.format(done+todo)
if not todo:
s+='\n'
if n>0:
s = '\r'+s
sys.stdout.write(s)
sys.stdout.flush()
yield n
n+=1
print ('doing something ...')
for i in range_with_status(10):
time.sleep(0.1)
print('ready')
time.sleep(0.4)
print ('And now for something completely different ...')
time.sleep(0.5)
msg = 'I am going to erase this line from the console window.'
sys.stdout.write(msg); sys.stdout.flush()
time.sleep(1)
sys.stdout.write('\r' + ' '*len(msg))
sys.stdout.flush()
time.sleep(0.5)
print('\rdid I succeed?')
time.sleep(4)
If it's for your own purposes, then here's a dirty wee hack using the clipboard without losing what was there before:
def edit_text_at_terminal(text_to_edit):
import pyperclip
# Save old clipboard contents so user doesn't lose them
old_clipboard_contents = pyperclip.paste()
#place text you want to edit in the clipboard
pyperclip.copy(text_to_edit)
# If you're on Windows, and ctrl+v works, you can do this:
import win32com.client
shell = win32com.client.Dispatch("WScript.Shell")
shell.SendKeys("^v")
# Otherwise you should tell the user to type ctrl+v
msg = "Type ctrl+v (your old clipboard contents will be restored):\n"
# Get the new value, the old value will have been pasted
new_value= str(raw_input(msg))
# restore the old clipboard contents before returning new value
pyperclip.copy(old_clipboard_contents )
return new_value
Note that ctrl+v doesn't work in all terminals, notably the Windows default (there are ways to make it work, though I recommend using ConEmu instead).
Automating the keystrokes for other OSs will involve a different process.
Please remember this is a quick hack and not a "proper" solution. I will not be held responsible for loss of entire PhD dissertations momentarily stored on your clipboard.
For a proper solution there are better approaches such as curses for Linux, and on Windows it's worth looking into AutHotKey (perhaps throw up an input box, or do some keystrokes/clipboard wizardry).

How to implement a python REPL that nicely handles asynchronous output?

I have a Python-based app that can accept a few commands in a simple read-eval-print-loop. I'm using raw_input('> ') to get the input. On Unix-based systems, I also import readline to make things behave a little better. All this is working fine.
The problem is that there are asynchronous events coming in, and I'd like to print output as soon as they happen. Unfortunately, this makes things look ugly. The "> " string doesn't show up again after the output, and if the user is halfway through typing something, it chops their text in half. It should probably redraw the user's text-in-progress after printing something.
This seems like it must be a solved problem. What's the proper way to do this?
Also note that some of my users are Windows-based.
TIA
Edit: The accepted answer works under Unixy platforms (when the readline module is available), but if anyone knows how to make this work under Windows, it would be much appreciated!
Maybe something like this will do the trick:
#!/usr/bin/env python2.6
from __future__ import print_function
import readline
import threading
PROMPT = '> '
def interrupt():
print() # Don't want to end up on the same line the user is typing on.
print('Interrupting cow -- moo!')
print(PROMPT, readline.get_line_buffer(), sep='', end='')
def cli():
while True:
cli = str(raw_input(PROMPT))
if __name__ == '__main__':
threading.Thread(target=cli).start()
threading.Timer(2, interrupt).start()
I don't think that stdin is thread-safe, so you can end up losing characters to the interrupting thread (that the user will have to retype at the end of the interrupt). I exaggerated the amount of interrupt time with the time.sleep call. The readline.get_line_buffer call won't display the characters that get lost, so it all turns out alright.
Note that stdout itself isn't thread safe, so if you've got multiple interrupting threads of execution, this can still end up looking gross.
Why are you writing your own REPL using raw_input()? Have you looked at the cmd.Cmd class? Edit: I just found the sclapp library, which may also be useful.
Note: the cmd.Cmd class (and sclapp) may or may not directly support your original goal; you may have to subclass it and modify it as needed to provide that feature.
run this:
python -m twisted.conch.stdio
You'll get a nice, colored, async REPL, without using threads. While you type in the prompt, the event loop is running.
look into the code module, it lets you create objects for interpreting python code also (shameless plug) https://github.com/iridium172/PyTerm lets you create interactive command line programs that handle raw keyboard input (like ^C will raise a KeyboardInterrupt).
It's kind of a non-answer, but I would look at IPython's code to see how they're doing it.
I think you have 2 basic options:
Synchronize your output (i.e. block until it comes back)
Separate your input and your (asyncronous) output, perhaps in two separate columns.

Categories

Resources