QNetworkReply not being deleted - python

I'm doing something similar to this question but have a more subtle problem.
I have an API client class that makes HTTP requests; I store the QNetworkReply in the object, so as to access its data from the slot connected to the "finished" signal. On the next request, this is replaced by the next QNetworkReply, so Python should be able to free the previous request object and thus the underlying network resources. Instead, the old reply objects seem to get stuck somewhere, causing a memory leak, and if the app runs long enough, a delay on quit, presumably because all the requests ever issued are finally being deleted.
Simplified but complete example:
import sys, signal
from PySide import QtCore, QtNetwork
class Poller(QtCore.QObject):
url = QtCore.QUrl("http://localhost:5000/")
def __init__(self, parent=None):
super(Poller,self).__init__(parent)
self.manager = QtNetwork.QNetworkAccessManager()
def start(self):
request = QtNetwork.QNetworkRequest(self.url)
self.reply = self.manager.get(request)
self.reply.finished.connect(self.readReply)
self.reply.error.connect(self.error)
def readReply(self):
text = self.reply.readAll()
self.reply.close() # doesn't help
self.reply.deleteLater() # doesn't help
QtCore.QTimer.singleShot(10, self.start)
#QtCore.Slot(QtNetwork.QNetworkReply.NetworkError)
def error(self, err):
self.reply.finished.disconnect()
sys.stderr.write('Error: ' + str(err) + '\n')
QtCore.QTimer.singleShot(10, self.start)
signal.signal(signal.SIGINT, signal.SIG_DFL)
app = QtCore.QCoreApplication(sys.argv)
p = Poller()
p.start()
app.exec_()
The http server being polled doesn't much matter; for this test I'm using the Flask hello world. In fact, since this doesn't do connection keepalive, if I check "netstat" I see a steadily increasing number of zombie TCP connections in a TIME_WAIT state, and eventually start getting PySide.QtNetwork.QNetworkReply.NetworkError.UnknownNetworkError once 30,000+ ports have been used up; further evidence that the QNetworkReply is not being properly freed.
Same problem happens with PySide or PyQt4. What am I doing wrong, or could this be a bug?

First, turns out it's normal for TCP connections to stick around for a while in TIME_WAIT after closing. I was only hitting the limit because I had set the singleShot timer to 0ms for testing.
I rewrote the example in C++. deleteLater worked as expected, and I could reproduce both the memory growth and slow quit by omitting it. (Since the memory is managed by Qt, all the reply objects had to be deleted by the QNetworkAccessManager destructor).
Interestingly, on closer examination, the slow quit does not happen in Python when deleteLater is used, but the memory growth does. So I guess the C++ object is getting deleted but there are still resources being used somewhere. This is still mysterious to me.
The fix, though, is to call setParent(None) on the QNetworkReply. This can be done at any time, even when it is first returned from the QNetworkAccessManager. It is initially parented to the manager; changing the parent to null means Qt isn't responsible for memory management, and it will be properly handled by Python, even without using deleteLater.
(I found this hint somewhere online; unfortunately I can't find it now, or I would link it.)
Edit: I thought this worked in my testing, but my app is still leaking memory.
Edit 2: I have no leak with PyQt4 on Python 2, but I do with PySide, and with both on Python 3.

Related

Guaranteeing calling to destruction on process termination

After reading A LOT of data on the subject I still couldn't find any actual solution to my problem (there might not be any).
My problem is as following:
In my project I have multiple drivers working with various hardware's (IO managers, programmable loads, power supplies and more).
Initializing connection to these hardware's is costly (in time), and I cant open and then close the connection for every communication iteration between us.
Meaning I cant do this (Assuming programmable load implements enter / exit):
start of code...
with programmable_load(args) as program_instance:
programmable_load_instance.do_something()
rest of code...
So I went for a different solution :
class programmable_load():
def __init__(self):
self.handler = handler_creator()
def close_connection(self):
self.handler.close_connection()
self.handler = None
def __del__(self):
if (self.handler != None):
self.close_connection()
For obvious reasons I dont 'trust' the destructor to actually get called so I explicitly call close_connection() when I want to end my program (for all drivers).
The problem happens when I abruptly terminate the process, for example when I run via debug mode and quit debugging.
In these cases the process terminates without running through any destructors.
I understand that the OS will clear all memory unused at this point, but is there any way to clear the memory in an organized manner?
and if not, is there a way to make the quit debugging function pass through a certain set of functions? Does the python process know it got a quite debugging event or does it treat it as a normal termination?
Operating system: Windows
According to this documentation:
If a process is terminated by TerminateProcess, all threads of the
process are terminated immediately with no chance to run additional
code.
(Emphasis mine.) This implies that there is nothing you can do in this case.
As detailed here, signals don't work very well on ms-windows.
As was mentioned in a comment, you could use atexit to do the cleanup. But that only works if the process is asked to close (e.g. QUIT signal on Linux) and not just killed (as is likely the case when stopping the debugging session). Similarily if you force your computer to turn off (e.g. long press power button or remove power) then it won't be called either. There is no 'solution' to that for obvious reasons. Your program can't expect to be called when the power suddenly goes off or when it is forcefully killed. The point of forcefully killing is to definitely kill the process now. If it first called your clean-up code then you could delay that which defeats the purpose. That is why there are signals such as to ask your process to stop. This is not Python specific. The same concept also applies across operating systems.
Bonus (design suggestion, not a solution): I would argue that you can still make use of the context manager (using with). Your problem is not unique. Database connections are usually kept alive for longer as well. It is a question of the scope. Move the context further up to the application level. Then it is clear what the boundary is and you don't need any magic (you are probably also aware of #contextmanager to make that a breeze).
I haven't tested properly as I don't have wingide installed over here so I can't grant you this will work but what about using setconsolectrlhandler? For instance, try something like this:
import os
import sys
import win32api
if __name__ == "__main__":
def callback(sig, func=None):
print("Exit handler called!")
try:
win32api.SetConsoleCtrlHandler(callback, True)
except Exception as e:
print("Captured exception", e)
sys.exit(1)
print("Press to quit")
input()
print("Bye!")
It'll be able to handle CTRL+C and CTRL+BREAK signals:

Multithread call inside Twisted _delayedRender of request

I have simple Twisted webserver application serving my math requests. Everything working fine (I hide big code pieces which is not related to my question):
#import section ...
class PlsPage(Resource):
isLeaf = True
def render_POST(self, request):
reactor.callLater(0, self._delayedRender, request)
return NOT_DONE_YET
def _delayedRender(self, request):
#some actions before
crossval_scores = cross_validation.cross_val_score(pls1, X, y=numpy.asarray(Y), scoring=my_custom_scorer, cv=KFold(700, n_folds=700))
#some actions after
request.finish()
reactor.listenTCP(12000, server.Site(PlsPage()))
reactor.run()
When I try to speed up cross_validation calculation by setting n_jobs for example to 3.
crossval_scores = cross_validation.cross_val_score(pls1, X, y=numpy.asarray(Y), scoring=my_custom_scorer, cv=KFold(700, n_folds=700), n_jobs=3)
and after that I got exactly 3 exceptions:
twisted.internet.error.CannotListenError: Couldn't listen on any:12000: [Errno 10048] Only one usage of each socket address (protocol/network address/port) is normally permitted.
For some reasons I can't call cross_val_score with n_jobs > 1 inside _delayedRender.
Here is a traceback of exception, for some reasons reactor.listenTCP trying to start 3 times too.
Any ideas how to get it work?
UPD1. I create file PLS.py and moved all the code here, except last 2 lines:
from twisted.web import server
from twisted.internet import reactor, threads
import PLS
reactor.listenTCP(12000, server.Site(PLS.PlsPage()))
reactor.run()
But the problem still persists. I also found that this problem persists only on Windows. My Linux machine run this scripts well.
scikit_learn apparently uses the multiprocessing module in order to achieve concurrency. The multiprocessing transmits data between processes using pickle, which, among other... idiosyncratic problems that it causes, will cause some of the modules imported in your parent process to be imported in your worker processes.
Your PLS_web.py "module", however, is not actually a module, it's a script; since you have put reactor.listenTCP and reactor.run at the bottom of it, it actually does stuff when you import it rather than just loading its code.
This particular error is because since your web server is being run 4 times (once for the controller process, once for each of the three jobs), each of the 3 times beyond the first encounter an error because the first server is already listening on port 12000.
You should remove the reactor.run/reactor.listenTCP lines elsewhere, into a top level script. A good rule of thumb is that these lines should never appear in the same file as a class or def statement; define your code in one place and start it up in another. Once you've moved it to a file that doesn't get imported (and you might want to even put it in a file whose name isn't a legal module identifier, like run-my-server.py) then multiprocessing might be able to import all the code it needs and do its job.
Better yet, don't write those lines at all, write a twisted application plugin and run your program with twistd. If you don't have to put the reactor.run statement in any place, you can't put it in the wrong place :).

Creating a CherryPy with ws4py socket heartbeat

I have spent about 5 hours searching for how to do this to no avail.
We are using ws4py on top of cherrypy. At current, when a connection is physically lost (say you turn off your WiFi) the connection will not be terminated untill a message is sent from the server, at which point it detects the dropped line and cleans up the socket.
This is causing us issues, and we need to know sooner if the socket is gone.
the file "websocket.py" in ws4py has a class called "Heartbeat" which looks like exactly what I want, and I believe that an instance is created inside the "WebSocket" if its has a "heartbeat_freq" parameter passed in;
class WebSocket(object):
""" Represents a websocket endpoint and provides a high level interface to drive the endpoint. """
def __init__(self, sock, protocols=None, extensions=None, environ=None, heartbeat_freq=None):
Above is the ws4py ctor, but I cannot find where this code is called from. What I do know is that it is tied up in a CherryPy callback system. Here is what I found;
The above ctor is called from "cherrypyserver.py" in the function;
def upgrade(self, protocols=None, extensions=None, version=WS_VERSION, handler_cls=WebSocket, heartbeat_freq=None):
This function seems to be a callback, as it is called from _cprequest.py in a function
def __call__(self):
"""Run self.callback(**self.kwargs)."""
return self.callback(**self.kwargs)
Now there is a bit more stuff floating around but in honesty I'm kinda lost, and think I am going about this wrong.
From what I can figure out, I need to set the "heartbeat_freq" parameter of the callback, but am not sure where I would set this parameter. The code below is where I specify the "WebSocket" handler class (websocket2.Handler inherits from "WebSocket") that the callback creates an instance of.
rootmap2={
'wsgi.pipeline': [
('validator1', validator),
('validator2', validator),
] ,
'tools.websocket.on': True,
'tools.websocket.handler_cls': websocket2.Handler,
}
I believe that somewhere in this "rootmap" I have to specify the parameter. Does anybody know how to do this.
To clarify, I want my server to create a heartbeat for each peer. I believe this is done by passing in a "heartbeat_freq" value.
At current I am just broadcasting a heartbeat to all, which I dont like the sound of personally
The heartbeat is only activated when the websocket object has its run method executed (usually in a thread as it's blocking). However the CherryPy plugin does not use that code path anymore for various reasons. I'm not yet decided as to what to do with the heartbeat yet because, as it stands, it'd have to be run in its own thread for each websocket. I find that expensive memory-wise. However, if you do need that feature, create an instance of the Heartbeat class with the websocket instance. Simply hold a reference to each Heartbeat instance.

Python-Twisted Reactor Starting too Early

I have an application that uses PyQt4 and python-twisted to maintain a connection to another program. I am using "qt4reactor.py" as found here. This is all packaged up using py2exe. The application works wonderfully for 99% of users, but one user has reported that networking is failing completely on his Windows system. No other users report the issue, and I cannot replicate it on my own Windows VM. The user reports no abnormal configuration.
The debugging logs show that the reactor.connectTCP() call is executing immediately, even though the reactor hasn't been started yet! There's no mistaking run order because this is a single-threaded process with 60 sec of computation and multiple log messages between this line and when the reactor is supposed to start.
There's a lot of code, so I am only putting in pseudo-code, hoping that there is a general solution for this issue. I will link to the actual code below it.
import qt4reactor
qt4reactor.install()
# Start setting up main window
# ...
from twisted.internet import reactor
# Separate listener for detecting/processing multiple instances
self.InstanceListener = ListenerFactory(...)
reactor.listenTCP(LISTEN_PORT, self.InstanceListener)
# The active/main connection
self.NetworkingFactory = ClientFactory(...)
reactor.connectTCP(ACTIVE_IP, ACTIVE_PORT, self.NetworkingFactory)
# Finish setting up main window
# ...
from twisted.internet import reactor
reactor.runReturn()
The code is nested throughout the Armory project files. ArmoryQt.py (containing the above code) and armoryengine.py (containing the ReconnectingClientFactory subclass used for this connection).
So, the reactor.connectTCP() call executes immediately. The client code executes the send command and then immediately connectionLost() gets called. It does not appear to try to reconnect. It also doesn't throw any errors other than connectionLost(). Even more mysteriously, it receives messages from the remote node later on, and this app even processes them! But it believes it's not connected (and handshake never finished, so the remote node shouldn't be sending messages, but might be a bug/oversight in that program).
What on earth is going on!? How could the reactor get started before I tell it to start? I searched the code and found no other code that (I believe) could start the reactor.
The API that you're looking for is twisted.internet.reactor.callWhenRunning.
However, it wouldn't hurt to have less than 60 seconds of computation at startup, either :). Perhaps you should spread that out, or delegate it to a thread, if it's relatively independent?

Getting erratic "Invalid Signal signature" errors in PySide QWebPage

I am creating an application which automates loading webpages and creating screenshots (no, I cannot use one of the existing solutions) using PySide. One part of the app gets a list of URLs and loads each URL in turn using a new QWebPage object. After the page is loaded a screenshot is taken and the QWebPage object is deleted.
Every now and then, given enough runs, I get the following error from PySide, as a RuntimeError exception:
Invalid Signal signature: loadStarted()
Failed to connect signal loadStarted().
The first line is printed to STDERR (probably by Qt?) and the second line is the Python exception.
loadStarted() is a built-in QWebPage signal, not something I created. This works 90% of the time, and I could not figure out what makes it fail occasionally.
To be honest, this app is quite unusual in design, as it hooks PySide/Qt into a uWSGI served web app - this means that for example I am not using the QApplication event loop but rather a local event loop for each page load. I am also not experienced in either Qt or Python so it's possible I'm making a lot of mistakes, but I can't figure out what those are.
I am thinking this post might have something to do with it, but I'm not sure.
Any suggestions on where to look next?
UPDATE: the signal is connected through the following code:
class MyWebPage(QWebPage):
def __init__(self, parent=None):
super(MyWebPage, self).__init__(parent)
self.loadStarted.connect(self.started)
self.loadFinished[bool].connect(self.finished)
MyWebPage objects are created as children of a different single QObject instance which is not deleted until the process shuts down. They are deleted by calling page.deleteLater() once I am done with them. Since I am running a local event loop, I trigger deferred deletions to happen after exiting local event loop by calling:
# self.eventLoop is the local event loop, which at this stage is not running
self.eventLoop.processEvents()
# self.app is the QApplication instance
self.app.sendPostedEvents(None, QEvent.DeferredDelete)
I was having the same problem (I'd get these errors every once in a while, but I couldn't consistently reproduce it). I think you may be right that it has something to do with methods not existing when you try to connect the signals to them - just to test that, I put the .connect calls in a separate method, and the errors went away. For example:
EDIT:
(a few hours later) I guess I spoke too soon - I just got the error again.
UPDATE:
(a few weeks later)
I played around with the syntax a lot and occasionally even got a RuntimeError (possibly this bug in PySide?). I'm still not completely sure why, but as the error happens inconsistently, you're probably safe in forcing it like this:
class MyWebPage(QWebPage):
def __init__(self, parent=None):
super(MyWebPage, self).__init__(parent)
success = False
while not success:
try:
success = self.loadStarted.connect(self.started)
except RuntimeError:
success = False
success = False
while not success:
try:
success = self.loadFinished[bool].connect(self.finished)
except RuntimeError:
success = False
If you really want to be safe, you could maybe keep a loop counter and just crash the program if the signal doesn't connect correctly before some threshold.
For what its worth, this and other issues were finally solved in a decent way after I upgraded to PySide 1.2.1.

Categories