I am building application with interactive console interface (line htop, atop utilities) using urwid library, so my trouble is: as interface takes all the space in console window - I could not see python's errors, I tried to do that:
import sys
f = open("test_err", "w")
original_stderr = sys.stderr
sys.stderr = f
print a #a is undefined
sys.stderr = original_stderr
f.close()
It works when I dont use urwid, but not when I use it...
you could try redirecting errors to a file. after each time you run the program, you will need to refresh the file. most editors let you easily do this by pushing f5
def main():
#your code here
print someError #raises an error
try: #run main function
main()
except BaseException as err: #catch all errors
with open('errors.txt','a') as errors: #open a file to write the errors to
errors.write(err.message+'\n')#write the error
change the 'a' to 'w' in the open function if you only want to see one error in the file at a time (instead of having multiple error over a long period of time in one file).
if you want to see the error right when it happens, you can make the error catcher open a window that has the error on it.
def main():
#your code here
print someErr
try: #run main function
main()
except BaseException as err: #catch all errors
import Tkinter as tk #imports the ui module
root = tk.Tk() #creates the root of the window
#creates the text and attaches it to the root
window = tk.Label(root, text=err.message)
window.pack()
#runs the window
root.mainloop()
if you want to build your own window to catch errors, you can learn about Tkinter here. (it is built into python, you don't have to install anything)
Here's what I came up with. I'm taking advantage of unicode-rxvt (urxvt) feature to be passed in a file descriptor. Of course this means you need to be developing this in an X environment, and not a console.
from __future__ import print_function
import os
from datetime import datetime
_debugfile = None
def _close_debug(fo):
fo.close()
def DEBUG(*obj):
"""Open a terminal emulator and write messages to it for debugging."""
global _debugfile
if _debugfile is None:
import atexit
masterfd, slavefd = os.openpty()
pid = os.fork()
if pid: # parent
os.close(masterfd)
_debugfile = os.fdopen(slavefd, "w+", 0)
atexit.register(_close_debug, _debugfile)
else: # child
os.close(slavefd)
os.execlp("urxvt", "urxvt", "-pty-fd", str(masterfd))
print(datetime.now(), ":", ", ".join(map(repr, obj)), file=_debugfile)
This will open a new terminal window automatically when you call DEBUG for the first time and close it at exit. Then any messages passed to it are shown in this new window. This is your "debug window". So your main app works normally, without cluttering it up with messages, but you can still see debug output in this new terminal.
Related
I made an App to process and display some data in a Tkinter window. I now want to send the contents of the window to a printer for printing on actual paper. However I don't really see any libraries in tkinter or Python to do this. I have to confess I am very new to tkinter and Python.....
Can anyone point me in the right direction?
Thanks.
tkinter is for a graphics user interface and so is all about display. To print data in a tkinter widget you'd have to retrieve that data depending on what the widget is and put it on the clipboard (can do this in tkinter) or file it (in a separate function) and use a separate printing app to print.
(edited as per comment)
This code does much the same as the subprocess module in a minimalist fashion since you have a specific task on Windows. To test it needs a pdf filepath inserted so I've put in an example that brings up notepad just so it can run as it is.
This has an alternative to checking for print status by use of a manual pause. Then Adobe (or any executable that has the appropriate print facility) can be closed automatically. I'd expect the manual pause to be replaced by an automatic timer set for an estimate time for a document to print. Probably need to consider only the time needed to transfer the document into the printing buffer - but that is up to how you want to operate with your system.
"""Process to start external executable.
. default is to let process run until parent pause is complete then process is terminated in parent,
.. this allows time for a process started im the child like printing to finish.
. option to wait until process has finished before returning to parent.
(proc.py)
"""
import _winapi as win
from os import waitpid
from sys import exc_info as ei
def startproc(exe,cmd, wait=False):
try:
ph, th, pid, tid = win.CreateProcess(exe,cmd,None,None,1,0,None,None,None)
win.CloseHandle(th)
except:
print(ei()[1])
ph = 0
return (ph,'error')
if ph > 0:
if not wait:
return (ph,'process still going')
else:
pid, exitstatus = waitpid(ph,0)
return (0,'process done')
#exe = "C:\\Program Files\\Adobe\\Acrobat DC\\Acrobat\\Acrobat.exe"
#cmd = "open <pdf filepath>"
exe = "C:\Windows\System32\\notepad.exe"
cmd = None
print(__doc__)
proc,msg = startproc(exe,cmd)
print(msg)
if 'done' not in msg: # manual pause for printing
input('\n-- carry on --\n') # could be automatic timer
if msg != 'error' and proc != 0:
if win.GetExitCodeProcess(proc) == win.STILL_ACTIVE:
win.TerminateProcess(proc,0)
if 'done' not in msg: print('process closed')
#can delete pdf here
input('\n--- finish ---\n')
I want to remote control a python application which uses urwid for the user interface.
My idea was to create a file, pass it's name as command line argument to the application and whenever I write to the file the application reads from that file.
Urwid's event loop has a method watch_file(fd, callback).
This method is described as "Call callback() when fd has some data to read."
This sounds exactly like what I want to have, but it causes an infinite loop.
callback is executed as often as possible, despite the fact that the file is empty.
Even if I delete the file, callback is still called.
#!/usr/bin/env python3
import urwid
import atexit
def onkeypress(key, size=None):
if key == 'q':
raise urwid.ExitMainLoop()
text.set_text(key)
def onfilechange():
text.set_text(cmdfile.read())
# clear file so that I don't read already executed commands again
# and don't run into an infinite loop - but I am doing that anyway
with open(cmdfile.name, 'w') as f:
pass
cmdfile = open('/tmp/cmd', 'rt')
atexit.register(cmdfile.close)
text = urwid.Text("hello world")
filler = urwid.Filler(text)
loop = urwid.MainLoop(filler, unhandled_input=onkeypress)
loop.watch_file(cmdfile, onfilechange)
if __name__ == '__main__':
loop.run()
(My initial idea was to open the file only for reading instead of keeping it open all the time but fd has to be a file object, not a path.)
Urwid offers several different event loops.
By default, SelectEventLoop is used.
GLibEventLoop has the same behaviour, it runs into an infinite loop.
AsyncioEventLoop instead throws an "operation not permitted" exception.
TwistedEventLoop and TornadoEventLoop would need additional software to be installed.
I have considered using the independent watchdog library but it seems accessing the user interface from another thread would require to write a new loop, see this stack overflow question.
The answer to that question recommends polling instead which I would prefer to avoid.
If urwid specifically provides a method to watch a file I cannot believe that it does not work in any implementation.
So what am I doing wrong?
How do I react to a file change in a python/urwid application?
EDIT:
I have tried using named pipes (and removed the code to clear the file) but visually it has the same behaviour: the app does not start.
Audibly, however, there is a great difference: It does not go into the infinite loop until I write to the file.
Before I write to the file callback is not called but the app is not started either, it just does nothing.
After I write to the file, it behaves as described above for regular files.
I have found the following work around: read a named pipe in another thread, safe each line in a queue and poll in the UI thread to see if something is in the queue.
Create the named pipe with mkfifo /tmp/mypipe.
Then write to it with echo >>/tmp/mypipe "some text".
#!/usr/bin/env python3
import os
import threading
import queue
import urwid
class App:
POLL_TIME_S = .5
def __init__(self):
self.text = urwid.Text("hello world")
self.filler = urwid.Filler(self.text)
self.loop = urwid.MainLoop(self.filler, unhandled_input=self.onkeypress)
def watch_pipe(self, path):
self._cmd_pipe = path
self.queue = queue.Queue()
threading.Thread(target=self._read_pipe_thread, args=(path,)).start()
self.loop.set_alarm_in(0, self._poll_queue)
def _read_pipe_thread(self, path):
while self._cmd_pipe:
with open(path, 'rt') as pipe:
for ln in pipe:
self.queue.put(ln)
self.queue.put("!! EOF !!")
def _poll_queue(self, loop, args):
while not self.queue.empty():
ln = self.queue.get()
self.text.set_text(ln)
self.loop.set_alarm_in(self.POLL_TIME_S, self._poll_queue)
def close(self):
path = self._cmd_pipe
# stop reading
self._cmd_pipe = None
with open(path, 'wt') as pipe:
pipe.write("")
os.remove(path)
def run(self):
self.loop.run()
def onkeypress(self, key, size=None):
if key == 'q':
raise urwid.ExitMainLoop()
self.text.set_text(key)
if __name__ == '__main__':
a = App()
a.watch_pipe('/tmp/mypipe')
a.run()
a.close()
Given this code:
from time import sleep
class TemporaryFileCreator(object):
def __init__(self):
print 'create temporary file'
# create_temp_file('temp.txt')
def watch(self):
try:
print 'watching tempoary file'
while True:
# add_a_line_in_temp_file('temp.txt', 'new line')
sleep(4)
except (KeyboardInterrupt, SystemExit), e:
print 'deleting the temporary file..'
# delete_temporary_file('temp.txt')
sleep(3)
print str(e)
t = TemporaryFileCreator()
t.watch()
during the t.watch(), I want to close this application in the console..
I tried using CTRL+C and it works:
However, if I click the exit button:
it doesn't work.. I checked many related questions about this but it seems that I cannot find the right answer..
What I want to do:
The console can be exited while the program is still running.. to handle that, when the exit button is pressed, I want to make a cleanup of the objects (deleting of created temporary files), rollback of temporary changes, etc..
Question:
how can I handle console exit?
how can I integrate it on object destructors (__exit__())
Is it even possible? (how about py2exe?)
Note: code will be compiled on py2exe.. "hopes that the effect is the same"
You may want to have a look at signals. When a *nix terminal is closed with a running process, this process receives a couple signals. For instance this code waits for the SIGHUB hangup signal and writes a final message. This codes works under OSX and Linux. I know you are specifically asking for Windows but you might want to give it a shot or investigate what signals a Windows command prompt is emitting during shutdown.
import signal
import sys
def signal_handler(signal, frame):
with open('./log.log', 'w') as f:
f.write('event received!')
signal.signal(signal.SIGHUP, signal_handler)
print('Waiting for the final blow...')
#signal.pause() # does not work under windows
sleep(10) # so let us just wait here
Quote from the documentation:
On Windows, signal() can only be called with SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, or SIGTERM. A ValueError will be raised in any other case.
Update:
Actually, the closest thing in Windows is win32api.setConsoleCtrlHandler (doc). This was already discussed here:
When using win32api.setConsoleCtrlHandler(), I'm able to receive shutdown/logoff/etc events from Windows, and cleanly shut down my app.
And if Daniel's code still works, this might be a nice way to use both (signals and CtrlHandler) for cross-platform purposes:
import os, sys
def set_exit_handler(func):
if os.name == "nt":
try:
import win32api
win32api.SetConsoleCtrlHandler(func, True)
except ImportError:
version = “.”.join(map(str, sys.version_info[:2]))
raise Exception(”pywin32 not installed for Python ” + version)
else:
import signal
signal.signal(signal.SIGTERM, func)
if __name__ == "__main__":
def on_exit(sig, func=None):
print "exit handler triggered"
import time
time.sleep(5)
set_exit_handler(on_exit)
print "Press to quit"
raw_input()
print "quit!"
If you use tempfile to create your temporary file, it will be automatically deleted when the Python process is killed.
Try it with:
>>> foo = tempfile.NamedTemporaryFile()
>>> foo.name
'c:\\users\\blah\\appdata\\local\\temp\\tmpxxxxxx'
Now check that the named file is there. You can write to and read from this file like any other.
Now kill the Python window and check that file is gone (it should be)
You can simply call foo.close() to delete it manually in your code.
I'm trying to create a minimal python script that calls the winsparkle C library using ctypes. The code only works if I run it line-by-line, win_sparkle_check_update_without_ui() pops up a window to download an update as expected. But running the script normally this function just gets called and skipped instantly, it doesn't remain open, unless I add the hacky time.sleep option.
What is the proper pythonic way to run this function and leave it open until the user closes the popup?
from ctypes import CDLL
import logging, time
class UpdateAgent(object):
def __init__(self, appcast_url):
self.appcast_url = appcast_url
def check_for_update(self):
winsparkle = CDLL("WinSparkle.dll")
url = self.appcast_url
winsparkle.win_sparkle_set_appcast_url(url.encode('ascii', 'ignore'))
winsparkle.win_sparkle_set_app_details(
unicode('Company'),
unicode('myapp'),
unicode(9))
winsparkle.win_sparkle_init()
winsparkle.win_sparkle_check_update_without_ui()
#time.sleep(10)
def run(self):
try:
self.check_for_update()
except Exception as e:
logging.info('%s' % (e))
I am using file locking in python script (to control single instance of it execution).
http://code.google.com/p/pylockfile/
I release lock in finally code block.
But if script closed, for example, closing the terminal running it, the finally block doesn't execute and the file stays locked.
How to catch python script destructor event in any case?
See this blog post regarding this subject. It uses the win32api when under Windows, while under Linux the SIGTERM signal is caught. To verify its working, it might be helpful to write something to a file in the on_exit handler like as done below. As the snippet is quite brief, I'll just include it (full props to the blog author):
import os, sys
def set_exit_handler(func):
if os.name == "nt":
try:
import win32api
win32api.SetConsoleCtrlHandler(func, True)
except ImportError:
version = '.'.join(map(str, sys.version_info[:2]))
raise Exception('pywin32 not installed for Python ' + version)
else:
import signal
signal.signal(signal.SIGTERM, func)
if __name__ == '__main__':
def on_exit(sig, func=None):
f = open('log.txt', 'w')
f.write('shutdown...')
f.close()
sys.exit()
set_exit_handler(on_exit)
print 'Press to quit'
raw_input()
print 'quit!'
If you will close the terminal running that program, it will create a file to verify the callback functionality.