Merge lp:~nataliabidart/ubuntu-sso-client/dont-let-it-go into lp:ubuntu-sso-client

Proposed by Natalia Bidart
Status: Merged
Approved by: Natalia Bidart
Approved revision: 858
Merged at revision: 855
Proposed branch: lp:~nataliabidart/ubuntu-sso-client/dont-let-it-go
Merge into: lp:ubuntu-sso-client
Diff against target: 137 lines (+68/-4)
3 files modified
ubuntu_sso/utils/runner/glib.py (+10/-1)
ubuntu_sso/utils/runner/qt.py (+14/-2)
ubuntu_sso/utils/runner/tests/test_qt.py (+44/-1)
To merge this branch: bzr merge lp:~nataliabidart/ubuntu-sso-client/dont-let-it-go
Reviewer Review Type Date Requested Status
dobey (community) Approve
Roberto Alsina (community) Approve
Review via email: mp+92485@code.launchpad.net

Commit message

- Hold on to the Qprocess instance to avoid garbage collection (LP: #930140).

To post a comment you must log in.
Revision history for this message
Roberto Alsina (ralsina) wrote :

+1 except for style nitpick discussed on IRC

review: Approve
857. By Natalia Bidart

- Better reading.

858. By Natalia Bidart

Also handle removing the process on error.

Revision history for this message
dobey (dobey) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ubuntu_sso/utils/runner/glib.py'
2--- ubuntu_sso/utils/runner/glib.py 2012-02-07 20:57:15 +0000
3+++ ubuntu_sso/utils/runner/glib.py 2012-02-10 15:52:57 +0000
4@@ -16,6 +16,8 @@
5
6 """Utility to spawn another program from a GLib mainloop."""
7
8+import os
9+
10 # pylint: disable=E0611,F0401
11 try:
12 from shlex import quote
13@@ -49,7 +51,14 @@
14 # pylint: disable=E1103
15 GLib.spawn_close_pid(pid)
16 # pylint: enable=E1103
17- reply_handler(status)
18+
19+ if os.WIFEXITED(status):
20+ status = os.WEXITSTATUS(status)
21+ reply_handler(status)
22+ else:
23+ msg = 'Child terminated abnormally, '\
24+ 'status from waitpid is %r' % status
25+ error_handler(msg=msg, failed_to_start=False)
26
27 def handle_error(gerror):
28 """Handle error when spawning the process."""
29
30=== modified file 'ubuntu_sso/utils/runner/qt.py'
31--- ubuntu_sso/utils/runner/qt.py 2012-01-26 18:58:58 +0000
32+++ ubuntu_sso/utils/runner/qt.py 2012-02-10 15:52:57 +0000
33@@ -23,6 +23,11 @@
34
35 logger = setup_logging("ubuntu_sso.utils.runner.qt")
36
37+# pylint: disable=C0103
38+# the set below is a workaround to hold to object references to avoid
39+# garbage collection before the processes finish executing
40+_processes = set()
41+
42
43 def spawn_program(args, reply_handler, error_handler):
44 """Spawn the program specified by 'args' using the Qt mainloop.
45@@ -36,6 +41,7 @@
46 """
47
48 process = QtCore.QProcess()
49+ _processes.add(process)
50
51 def print_pid():
52 """Add a debug log message."""
53@@ -43,14 +49,20 @@
54 logger.debug('Spawning the program %r with the qt mainloop '
55 '(returned pid is %r).', args, pid)
56
57+ def child_watch(status):
58+ """Handle child termination."""
59+ reply_handler(status)
60+ _processes.remove(process)
61+
62 def handle_error(process_error):
63 """Handle error when spawning the process."""
64- failed_to_start = process_error == QtCore.QProcess.FailedToStart
65+ failed_to_start = (process_error == process.FailedToStart)
66 msg = 'ProcessError is %r' % process_error
67 error_handler(msg=msg, failed_to_start=failed_to_start)
68+ _processes.remove(process)
69
70 process.started.connect(print_pid)
71- process.finished.connect(reply_handler)
72+ process.finished.connect(child_watch)
73 process.error.connect(handle_error)
74
75 args = list(args)
76
77=== modified file 'ubuntu_sso/utils/runner/tests/test_qt.py'
78--- ubuntu_sso/utils/runner/tests/test_qt.py 2012-02-08 16:35:50 +0000
79+++ ubuntu_sso/utils/runner/tests/test_qt.py 2012-02-10 15:52:57 +0000
80@@ -22,6 +22,7 @@
81 from twisted.internet import defer
82
83 from ubuntu_sso.utils import runner
84+from ubuntu_sso.utils.runner import qt
85 from ubuntu_sso.utils.runner.tests.test_runner import SpawnProgramTestCase
86
87
88@@ -88,6 +89,48 @@
89 yield super(QtSpawnProgramTestCase, self).setUp()
90 # Since we can't mix plan qt runner and the qt4reactor, we patch
91 # QProcess and fake the conditions so the qt runner is chosen
92- self.patch(QtCore, 'QProcess', FakedProcess)
93+ self.process = FakedProcess()
94+ self.patch(QtCore, 'QProcess', lambda: self.process)
95 self.patch(runner, 'is_twisted_reactor_installed', lambda: False)
96 self.patch(runner, 'is_qt4_main_loop_installed', lambda: True)
97+
98+ @defer.inlineCallbacks
99+ def test_dont_let_it_go_success(self):
100+ """The QProcess instance is stored (and removed) to avoid GC."""
101+ # pylint: disable=W0212
102+
103+ d = defer.Deferred()
104+
105+ def check(status):
106+ """Do the check."""
107+ self.assertIn(self.process, qt._processes)
108+ d.callback(status)
109+
110+ runner.qt.spawn_program(self.args,
111+ reply_handler=check, error_handler=lambda **kw: d.errback(kw))
112+
113+ yield d
114+
115+ self.assertNotIn(self.process, qt._processes)
116+
117+ @defer.inlineCallbacks
118+ def test_dont_let_it_go_error(self):
119+ """The QProcess instance is stored (and removed) to avoid GC."""
120+ # pylint: disable=W0212
121+
122+ d = defer.Deferred()
123+
124+ def check(msg, failed_to_start):
125+ """Do the check."""
126+ self.assertIn(self.process, qt._processes)
127+ d.callback(msg)
128+
129+ self.process._error = 42 # this will make the process emit 'error'
130+
131+ runner.qt.spawn_program(self.args,
132+ reply_handler=lambda _: d.errback(AssertionError(_)),
133+ error_handler=check)
134+
135+ yield d
136+
137+ self.assertNotIn(self.process, qt._processes)

Subscribers

People subscribed via source and target branches