I have a python script that publishes messages that are read from stdin onto a message queue in the network. However, if there wasn't any message on the pipe for a specified amount of time I have to send a heartbeat.
So I have to distinguish between 3 cases:
There is input on the pipe that can be processed
There was no input for some specified amount of time
The piping process has been closed and we can gracefully terminate.
Currently, my code looks as follow:
import sys
for i, line in enumerate(sys.stdin):
connection.publish(line)
connection.close()
However I need to interrupt the for look if there was any timeout. I.e. sys.stdin has not delivered any data for some time.
This is what I finally came up with
import sys
import select
while True:
try:
if select.select([sys.stdin,],[],[],2.0)[0]:
line = sys.stdin.next()
print "Got:", line
else:
print "No data for 2 secs"
except StopIteration:
print 'EOF!'
break
If anybody is looking for a portable solution, this is what I came up with
after lots of trial and error.
It requires the pywin32 pypi package to work on windows
import os, sys
def is_windows() -> bool:
return os.name == "nt"
if is_windows():
import win32api, win32event, win32file, pywintypes
else:
import select
def stdin_has_content(timeout: float) -> bool:
assert timeout >= 0
if is_windows():
try:
# without this the wait might return despite there not being any input
win32file.FlushFileBuffers(win32api.STD_INPUT_HANDLE)
except pywintypes.error:
# this sometimes fails, but we don't mind
pass
return win32event.WaitForSingleObject(
win32api.STD_INPUT_HANDLE, int(timeout * 1000)
) == win32event.WAIT_OBJECT_0
else:
rlist, _, _ = select.select(
[sys.stdin], [], [], timeout
)
return bool(rlist)
Related
I am trying to capture my keystrokes and return them back to myself. When I run the code I expect to get back what I whichever keys I pressed while the code was running in that order. I don't have an issue there, everything returns fine. But then after the code is ran it returns '-Bash: (myinput): command not found
from __future__ import print_function
import keyboard
keys = []
def keys_in():
try:
while True:
recorded = keyboard.read_key(suppress=True)
keys.append(recorded)
if recorded == 'enter':
print(sorted(set(keys),key=keys.index))
break
keys.append(recorded)
except KeyboardInterrupt:
print('done')
quit()
keys_in()
When I put 1234 in I get :['1', '2', '3', '4', 'enter']
$ 1234
-bash: 1234: command not found
And I'm not sure why it's trying to run my input afterword. Help?
import keyboard
s = set()
def keys_in():
recorded = keyboard.read_key(suppress=True)
while recorded != "enter":
s.add(recorded)
print(list(s))
keys_in()
Try this out. Sets are already sorted. you dont need to add. Does this do it?
I am not familiar with the keyboard module but it appears that, while it does indeed pass the input to the python program, it does it in such a way that the shell gets the input when the python program exits, as if it had been entered on the command line. Here are two options (which do not require installing the keyboard module):
Using sys.stdin.read
from __future__ import print_function
import sys
keys = []
def keys_in():
try:
while True:
recorded = sys.stdin.read(1)
keys.append(recorded)
if recorded == '\n':
print(sorted(set(keys),key=keys.index))
break
keys.append(recorded)
except KeyboardInterrupt:
print('done')
quit()
keys_in()
Using 'input'
from __future__ import print_function
import sys
keys = []
def keys_in():
try:
input_string = input('')
for recorded in input_string:
keys.append(recorded)
except KeyboardInterrupt:
print('done')
quit()
print(sorted(set(keys),key=keys.index))
keys_in()
How can I make a fifo between two python processes, that allow dropping of lines if the reader is not able to handle the input?
If the reader tries to read or readline faster then the writer writes, it should block.
If the reader cannot work as fast as the writer writes, the writer should not block. Lines should not be buffered (except one line at a time) and only the last line written should be received by the reader on its next readline attempt.
Is this possible with a named fifo, or is there any other simple way for achiving this?
The following code uses a named FIFO to allow communication between two scripts.
If the reader tries to read faster than the writer, it blocks.
If the reader cannot keep up with the writer, the writer does not block.
Operations are buffer oriented. Line oriented operations are not currently implemented.
This code should be considered a proof-of-concept. The delays and buffer sizes are arbitrary.
Code
import argparse
import errno
import os
from select import select
import time
class OneFifo(object):
def __init__(self, name):
self.name = name
def __enter__(self):
if os.path.exists(self.name):
os.unlink(self.name)
os.mkfifo(self.name)
return self
def __exit__(self, exc_type, exc_value, exc_traceback):
if os.path.exists(self.name):
os.unlink(self.name)
def write(self, data):
print "Waiting for client to open FIFO..."
try:
server_file = os.open(self.name, os.O_WRONLY | os.O_NONBLOCK)
except OSError as exc:
if exc.errno == errno.ENXIO:
server_file = None
else:
raise
if server_file is not None:
print "Writing line to FIFO..."
try:
os.write(server_file, data)
print "Done."
except OSError as exc:
if exc.errno == errno.EPIPE:
pass
else:
raise
os.close(server_file)
def read_nonblocking(self):
result = None
try:
client_file = os.open(self.name, os.O_RDONLY | os.O_NONBLOCK)
except OSError as exc:
if exc.errno == errno.ENOENT:
client_file = None
else:
raise
if client_file is not None:
try:
rlist = [client_file]
wlist = []
xlist = []
rlist, wlist, xlist = select(rlist, wlist, xlist, 0.01)
if client_file in rlist:
result = os.read(client_file, 1024)
except OSError as exc:
if exc.errno == errno.EAGAIN or exc.errno == errno.EWOULDBLOCK:
result = None
else:
raise
os.close(client_file)
return result
def read(self):
try:
with open(self.name, 'r') as client_file:
result = client_file.read()
except OSError as exc:
if exc.errno == errno.ENOENT:
result = None
else:
raise
if not len(result):
result = None
return result
def parse_argument():
parser = argparse.ArgumentParser()
parser.add_argument('-c', '--client', action='store_true',
help='Set this flag for the client')
parser.add_argument('-n', '--non-blocking', action='store_true',
help='Set this flag to read without blocking')
result = parser.parse_args()
return result
if __name__ == '__main__':
args = parse_argument()
if not args.client:
with OneFifo('known_name') as one_fifo:
while True:
one_fifo.write('one line')
time.sleep(0.1)
else:
one_fifo = OneFifo('known_name')
while True:
if args.non_blocking:
result = one_fifo.read_nonblocking()
else:
result = one_fifo.read()
if result is not None:
print result
The server checks if the client has opened the FIFO. If the client has opened the FIFO, the server writes a line. Otherwise, the server continues running. I have implemented a non-blocking read because the blocking read causes a problem: If the server restarts, most of the time the client stays blocked and never recovers. With a non-blocking client, a server restart is more easily tolerated.
Output
[user#machine:~] python onefifo.py
Waiting for client to open FIFO...
Waiting for client to open FIFO...
Writing line to FIFO...
Done.
Waiting for client to open FIFO...
Writing line to FIFO...
Done.
[user#machine:~] python onefifo.py -c
one line
one line
Notes
On startup, if the server detects that the FIFO already exists, it removes it. This is the easiest way to notify clients that the server has restarted. This notification is usually ignored by the blocking version of the client.
Well, that's not actually a FIFO (queue) as far as I am aware - it's a single variable. I suppose it might be implementable if you set up a queue or pipe with a maximum size of 1, but it seems that it would work better to use a Lock on a single object in one of the processes, which the other process references via a proxy object. The reader would set it to None whenever it reads, and the writer would overwrite the contents every time it writes.
You can get those to the other processes by passing the proxy of the object, and a proxy of the lock, as an argument to all relevant processes. To get it slightly more conveniently, you can use a Manager, which provides a single object with proxy that you can pass in, which contains and provides proxies for whatever other objects (including locks) you want to put in it. This answer provides a useful example of proper use of a Manager to pass objects into a new process.
I am using Windows and am looking for a handler or wrapper using Python for a Minecraft server so that I can automatically enter commands without user input. I have searched through many questions on the website and only found half answers (in my case at least). I believe I will need to use the subprocess module but cannot decide which to use at the moment I am experimenting with the Popen functions. I have found an answer which I modified for my case:
server = Popen("java -jar minecraft_server.jar nogui", stdin=PIPE, stdout=PIPE, stderr=STDOUT)
while True:
print(server.stdout.readline())
server.stdout.flush()
command = input("> ")
if command:
server.stdin.write(bytes(command + "\r\n", "ascii"))
server.stdin.flush()
This does work in some way but only prints a line for every time you enter a command, which cannot work and all my efforts to change this end up with the program unable to execute anything else and instead just read. This is not a duplicate question because none of the answers in similar questions could help me enough.
As you already know, your server.stdout.readline() and input("> ") are blocking your code execution.
You need to make your code non-blocking, by not waiting to actually return what you want, but by checking, if there is anything to read and ignore it, if there isn't and continue to do other things.
On Linux systems you might be able to use select module, but on Windows it only works on sockets.
I was able to make it work on Windows by using threads and queues. (note: it's Python 2 code)
import subprocess, sys
from Queue import Queue, Empty
from threading import Thread
def process_line(line):
if line == "stop\n": # lines have trailing new line characters
print "SERVER SHUTDOWN PREVENTED"
return None
elif line == "quit\n":
return "stop\n"
elif line == "l\n":
return "list\n"
return line
s = subprocess.Popen("java -jar minecraft_server.jar nogui", stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
def read_lines(stream, queue):
while True:
queue.put(stream.readline())
# terminal reading thread
q = Queue()
t = Thread(target=read_lines, args=(sys.stdin, q))
t.daemon = True
t.start()
# server reading thread
qs = Queue()
ts = Thread(target=read_lines, args=(s.stdout, qs))
ts.daemon = True
ts.start()
while s.poll() == None: # loop while the server process is running
# get a user entered line and send it to the server
try:
line = q.get_nowait()
except Empty:
pass
else:
line = process_line(line) # do something with the user entered line
if line != None:
s.stdin.write(line)
s.stdin.flush()
# just pass-through data from the server to the terminal output
try:
line = qs.get_nowait()
except Empty:
pass
else:
sys.stdout.write(line)
sys.stdout.flush()
I'm trying to create a Python notification application. To make it short here is what I wanted to do :
1. Checking my gmail account
2. Display a notification with the number of unread mails
3. Display a button that permits me to open chromium (using a system call)
For now everything looks just fine. The checking mail part was kind of easy. I serialised my unread mail count so that the notification doesn't show up every single minute. It only displays if I have a new mail.
Where I'm blocking is that I don't know how to create the main gtk loop so that I can handle the button signal.
Here is my code :
#!/usr/bin/python
from gi.repository import Notify, Gtk, GLib
from urllib.request import FancyURLopener
from datetime import datetime, date, time, timedelta
import os.path, sys, getopt
from subprocess import call
serialisedvalue=0;
serialiseddate=0;
def callback():
call(["chromium", "gmail.com"])
def serialise(unread):
try:
f = open("mailcount", "w")
try:
f.write(unread+"\n") # Write a string to a file
f.write(datetime.now().strftime('%b %d %Y %I:%M%p'))
finally:
f.close()
except IOError:
pass
def deserialise():
global serialisedvalue
global serialiseddate
try:
f = open("mailcount", "r")
try:
serialisedvalue = f.readline().rstrip()
serialiseddate = datetime.strptime(f.readline(), '%b %d %Y %I:%M%p')
finally:
f.close()
except IOError:
pass
def notif(unread):
Notify.init ("New Mail")
if unread != "1":
Hello=Notify.Notification.new ("New mail","You have "+unread+" unread mails","/usr/share/icons/Faenza/actions/96/mail-forward.png")
else :
Hello=Notify.Notification.new ("New mail","You have "+unread+" unread mails","/usr/share/icons/Faenza/actions/96/mail-forward.png")
Hello.add_action('action', 'Read', callback, None, None)
Hello.show ()
def main(argv):
notify=0
forced=0
try:
opts, args = getopt.getopt(argv,"nf",['notify','force-notify'])
except getopt.GetoptError:
print("unreadgmail.py [-n --notify] [-f --force-notify")
sys.exit(2)
for opt,args in opts:
if opt in ("-n", "--notify"):
notify=1
elif opt in ("-f","--force-notify"):
forced=1
url = 'https://%s:%s#mail.google.com/mail/feed/atom' % ("myaccount", "mypassword")
opener = FancyURLopener()
page = opener.open(url)
contents = page.read().decode('utf-8')
ifrom = contents.index('<fullcount>') + 11
ito = contents.index('</fullcount>')
unread = contents[ifrom:ito]
print("Unread messages : "+unread)
if notify==1 and forced==0:
if os.path.exists("mailcount"):
deserialise()
else:
serialise(unread)
deserialise()
if unread != "0":
if unread != serialisedvalue:
notif(unread)
serialise(unread)
elif ((datetime.now() - serialiseddate) > timedelta(hours=1)):
notif(unread)
if forced==1:
notif(unread)
GLib.MainLoop().run()
if __name__ == "__main__":
main(sys.argv[1:])
I remember that my notifications used to work fine with pygtk and pynotify. Though I want to update my code and since I lost the last code, I don't have a clue on that. Calling Gtk.main() in my main just block the program until I kill it.
I'm using Gnome3.6, Archlinux and python3.3.
So does anyone know how to "wait" for the signal handler to be clicked before the program ends ? In fact it runs fine, but the script just end when the notification is displayed and it doesn't wait for the signal.
Thanks a lot :)
EDIT : A bit more details of my problem :
As you can see, the program already ended and is not waiting for a signal. That's what I'm trying to solve right now.
If you're not using GTK+--which as far as I can tell you aren't--you could probably call GLib.MainLoop().run() at the end of your main function to keep your program running.
Normally, you process a file line by line in Python using a loop like:
import sys
for s in sys.stdin:
# do something with the line in s
or
import sys
while True:
line = sys,stdin.readline()
if len(line) == 0: break
# process input line
Of course, you can also use raw_input() in soemthing like this:
try:
while True:
s = raw_input()
# process input line
except EOFError:
# there's EOF.
Of course in all these cases, if there's no input ready to be read, the underlying read() operation suspends waiting for I/O.
What I want to do is see if there is input pending without suspending, so I can read until input is exhausted and then go do something else. That is, I'd like to be able to do something like
while "there is input pending":
#get the input
but when no more input is pending, break the loop.
If you are using some variant of Unix, and your standard input is a pipe and not a file, you can use the select module to check to see whether there is waiting input. At a minimum, the code might look like:
import select
rlist, wlist, elist = select.select([sys.stdin], [], [])
if rlist:
s = raw_input()
else:
pass # no input ready right now
Okay, here's something that works well on UNIX:
import sys
import select
import tty
import termios
def isData():
return select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], [])
old_settings = termios.tcgetattr(sys.stdin)
try:
tty.setcbreak(sys.stdin.fileno())
i = 0
while 1:
print i
i += 1
if isData():
c = sys.stdin.read(1)
if c == '\x1b': # x1b is ESC
break
finally:
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)
I'll modify/extend this answer when I have a chance to make a somewhat better test program. I'm (so far) unclear on how well tty and termios work on Windows.
Update: Grmph. This depends on select. There are reasons I don't like Windows.