Merge lp:~mvo/software-center/dbus-idle-timeout into lp:software-center
- dbus-idle-timeout
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 3217 |
Proposed branch: | lp:~mvo/software-center/dbus-idle-timeout |
Merge into: | lp:software-center |
Diff against target: |
219 lines (+113/-8) 3 files modified
software-center-dbus (+3/-1) softwarecenter/db/dataprovider.py (+63/-6) tests/test_dataprovider.py (+47/-1) |
To merge this branch: | bzr merge lp:~mvo/software-center/dbus-idle-timeout |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Gary Lasker (community) | Approve | ||
Review via email: mp+127208@code.launchpad.net |
Commit message
Description of the change
This branch addresses bug #1058567 to ensure that the data provider
exits after a certain amount of time.
Gary Lasker (gary-lasker) wrote : | # |
- 3212. By Michael Vogt
-
move implementation of the dbus methods into a seperate function so that we can add decorators - the dbus.service.method decorator seems to not like additional decroators
Michael Vogt (mvo) wrote : | # |
Thanks, indeed, it looks like the dbus decorator does not like other decorators at the same time. I fixed this now and the tests work now on my box.
Michael Vogt (mvo) wrote : | # |
Anything holding this one back?
Gary Lasker (gary-lasker) wrote : | # |
Hi Michael, I'm sorry for the delay on this one! Ok, I just tried it and I'm still getting an error in the test. It seems that SoftwareCenterD
=======
ERROR: test_idle_timeout (__main_
-------
Traceback (most recent call last):
File "tests/
self.bus_name, main_loop=
File "/home/
self.
TypeError: an integer is required
-------
Ran 15 tests in 8.076s
FAILED (errors=1)
Gary Lasker (gary-lasker) wrote : | # |
Note that the above is when testing on Precise. I'll try this on a Quantal machine and see if I still get the error.
Gary Lasker (gary-lasker) wrote : | # |
Meanwhile I will approve this as the only issue is the failing test case. Michael, please feel free to merge this branch when the test is ready.
Thanks!
Michael Vogt (mvo) wrote : | # |
Thanks for the review, I just double checked, it fails on precise for me as well but works on quantal.
Preview Diff
1 | === modified file 'software-center-dbus' | |||
2 | --- software-center-dbus 2012-07-18 12:45:55 +0000 | |||
3 | +++ software-center-dbus 2012-10-02 07:44:21 +0000 | |||
4 | @@ -18,9 +18,11 @@ | |||
5 | 18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 18 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
6 | 19 | 19 | ||
7 | 20 | import logging | 20 | import logging |
8 | 21 | import sys | ||
9 | 21 | from softwarecenter.db.dataprovider import dbus_main | 22 | from softwarecenter.db.dataprovider import dbus_main |
10 | 22 | 23 | ||
11 | 23 | 24 | ||
12 | 24 | if __name__ == "__main__": | 25 | if __name__ == "__main__": |
14 | 25 | logging.basicConfig(level=logging.DEBUG) | 26 | if "--debug" in sys.argv: |
15 | 27 | logging.basicConfig(level=logging.DEBUG) | ||
16 | 26 | dbus_main() | 28 | dbus_main() |
17 | 27 | 29 | ||
18 | === modified file 'softwarecenter/db/dataprovider.py' | |||
19 | --- softwarecenter/db/dataprovider.py 2012-09-18 11:36:15 +0000 | |||
20 | +++ softwarecenter/db/dataprovider.py 2012-10-02 07:44:21 +0000 | |||
21 | @@ -19,6 +19,7 @@ | |||
22 | 19 | import dbus | 19 | import dbus |
23 | 20 | import dbus.service | 20 | import dbus.service |
24 | 21 | import logging | 21 | import logging |
25 | 22 | import time | ||
26 | 22 | 23 | ||
27 | 23 | from dbus.mainloop.glib import DBusGMainLoop | 24 | from dbus.mainloop.glib import DBusGMainLoop |
28 | 24 | DBusGMainLoop(set_as_default=True) | 25 | DBusGMainLoop(set_as_default=True) |
29 | @@ -50,11 +51,27 @@ | |||
30 | 50 | DBUS_DATA_PROVIDER_PATH = '/com/ubuntu/SoftwareCenterDataProvider' | 51 | DBUS_DATA_PROVIDER_PATH = '/com/ubuntu/SoftwareCenterDataProvider' |
31 | 51 | 52 | ||
32 | 52 | 53 | ||
33 | 54 | def update_activity_timestamp(fn): | ||
34 | 55 | def wrapped(*args, **kwargs): | ||
35 | 56 | self = args[0] | ||
36 | 57 | self._update_activity_timestamp() | ||
37 | 58 | return fn(*args, **kwargs) | ||
38 | 59 | return wrapped | ||
39 | 60 | |||
40 | 61 | |||
41 | 53 | class SoftwareCenterDataProvider(dbus.service.Object): | 62 | class SoftwareCenterDataProvider(dbus.service.Object): |
42 | 54 | 63 | ||
44 | 55 | def __init__(self, bus_name, object_path=DBUS_DATA_PROVIDER_PATH): | 64 | # 5 min by default |
45 | 65 | IDLE_TIMEOUT = 60 * 5 | ||
46 | 66 | IDLE_CHECK_INTERVAL = 60 | ||
47 | 67 | |||
48 | 68 | def __init__(self, bus_name, object_path=DBUS_DATA_PROVIDER_PATH, | ||
49 | 69 | main_loop=None): | ||
50 | 56 | dbus.service.Object.__init__(self, bus_name, object_path) | 70 | dbus.service.Object.__init__(self, bus_name, object_path) |
51 | 57 | self.bus_name = bus_name | 71 | self.bus_name = bus_name |
52 | 72 | if main_loop is None: | ||
53 | 73 | main_loop = GObject.MainLoop(GObject.main_context_default()) | ||
54 | 74 | self.main_loop = main_loop | ||
55 | 58 | # the database | 75 | # the database |
56 | 59 | self.db = StoreDatabase() | 76 | self.db = StoreDatabase() |
57 | 60 | self.db.open() | 77 | self.db.open() |
58 | @@ -66,16 +83,41 @@ | |||
59 | 66 | self.review_loader.refresh_review_stats() | 83 | self.review_loader.refresh_review_stats() |
60 | 67 | # ensure we query new applications | 84 | # ensure we query new applications |
61 | 68 | run_software_center_agent(self.db) | 85 | run_software_center_agent(self.db) |
62 | 86 | # setup inactivity timer | ||
63 | 87 | self._update_activity_timestamp() | ||
64 | 88 | self._idle_timeout = GObject.timeout_add_seconds( | ||
65 | 89 | self.IDLE_CHECK_INTERVAL, self._check_inactivity) | ||
66 | 69 | 90 | ||
67 | 70 | def stop(self): | 91 | def stop(self): |
68 | 71 | """ stop the dbus controller and remove from the bus """ | 92 | """ stop the dbus controller and remove from the bus """ |
71 | 72 | self.remove_from_connection() | 93 | LOG.debug("stop() called") |
72 | 73 | 94 | self.main_loop.quit() | |
73 | 95 | LOG.debug("exited") | ||
74 | 96 | |||
75 | 97 | # internal helper | ||
76 | 98 | def _check_inactivity(self): | ||
77 | 99 | """ Check for activity """ | ||
78 | 100 | now = time.time() | ||
79 | 101 | if (self._activity_timestamp + self.IDLE_TIMEOUT) < now: | ||
80 | 102 | LOG.info("stopping after %s inactivity" % self.IDLE_TIMEOUT) | ||
81 | 103 | self.stop() | ||
82 | 104 | return True | ||
83 | 105 | |||
84 | 106 | def _update_activity_timestamp(self): | ||
85 | 107 | self._activity_timestamp = time.time() | ||
86 | 108 | |||
87 | 109 | # public dbus methods with their implementations, the dbus decorator | ||
88 | 110 | # does not like additional decorators so we use a seperate function | ||
89 | 111 | # for the actual implementation | ||
90 | 74 | @dbus.service.method(DBUS_DATA_PROVIDER_IFACE, | 112 | @dbus.service.method(DBUS_DATA_PROVIDER_IFACE, |
91 | 75 | in_signature='ss', out_signature='a{sv}') | 113 | in_signature='ss', out_signature='a{sv}') |
92 | 76 | def GetAppDetails(self, appname, pkgname): | 114 | def GetAppDetails(self, appname, pkgname): |
93 | 77 | LOG.debug("GetAppDetails() called with ('%s', '%s')" % ( | 115 | LOG.debug("GetAppDetails() called with ('%s', '%s')" % ( |
94 | 78 | appname, pkgname)) | 116 | appname, pkgname)) |
95 | 117 | return self._get_app_details(appname, pkgname) | ||
96 | 118 | |||
97 | 119 | @update_activity_timestamp | ||
98 | 120 | def _get_app_details(self, appname, pkgname): | ||
99 | 79 | app = Application(appname, pkgname) | 121 | app = Application(appname, pkgname) |
100 | 80 | appdetails = app.get_details(self.db) | 122 | appdetails = app.get_details(self.db) |
101 | 81 | return appdetails.as_dbus_property_dict() | 123 | return appdetails.as_dbus_property_dict() |
102 | @@ -84,12 +126,20 @@ | |||
103 | 84 | in_signature='', out_signature='as') | 126 | in_signature='', out_signature='as') |
104 | 85 | def GetAvailableCategories(self): | 127 | def GetAvailableCategories(self): |
105 | 86 | LOG.debug("GetAvailableCategories() called") | 128 | LOG.debug("GetAvailableCategories() called") |
106 | 129 | return self._get_available_categories() | ||
107 | 130 | |||
108 | 131 | @update_activity_timestamp | ||
109 | 132 | def _get_available_categories(self): | ||
110 | 87 | return [cat.name for cat in self.categories] | 133 | return [cat.name for cat in self.categories] |
111 | 88 | 134 | ||
112 | 89 | @dbus.service.method(DBUS_DATA_PROVIDER_IFACE, | 135 | @dbus.service.method(DBUS_DATA_PROVIDER_IFACE, |
113 | 90 | in_signature='s', out_signature='as') | 136 | in_signature='s', out_signature='as') |
114 | 91 | def GetAvailableSubcategories(self, category_name): | 137 | def GetAvailableSubcategories(self, category_name): |
115 | 92 | LOG.debug("GetAvailableSubcategories() called") | 138 | LOG.debug("GetAvailableSubcategories() called") |
116 | 139 | return self._get_available_subcategories(category_name) | ||
117 | 140 | |||
118 | 141 | @update_activity_timestamp | ||
119 | 142 | def _get_available_subcategories(self, category_name): | ||
120 | 93 | cat = get_category_by_name(self.categories, category_name) | 143 | cat = get_category_by_name(self.categories, category_name) |
121 | 94 | return [subcat.name for subcat in cat.subcategories] | 144 | return [subcat.name for subcat in cat.subcategories] |
122 | 95 | 145 | ||
123 | @@ -97,6 +147,10 @@ | |||
124 | 97 | in_signature='s', out_signature='a(ssss)') | 147 | in_signature='s', out_signature='a(ssss)') |
125 | 98 | def GetItemsForCategory(self, category_name): | 148 | def GetItemsForCategory(self, category_name): |
126 | 99 | LOG.debug("GetItemsForCategory() called with ('%s')" % category_name) | 149 | LOG.debug("GetItemsForCategory() called with ('%s')" % category_name) |
127 | 150 | return self._get_items_for_category(category_name) | ||
128 | 151 | |||
129 | 152 | @update_activity_timestamp | ||
130 | 153 | def _get_items_for_category(self, category_name): | ||
131 | 100 | result = [] | 154 | result = [] |
132 | 101 | cat = get_category_by_name(self.categories, category_name) | 155 | cat = get_category_by_name(self.categories, category_name) |
133 | 102 | for doc in cat.get_documents(self.db): | 156 | for doc in cat.get_documents(self.db): |
134 | @@ -112,10 +166,13 @@ | |||
135 | 112 | def dbus_main(bus=None): | 166 | def dbus_main(bus=None): |
136 | 113 | if bus is None: | 167 | if bus is None: |
137 | 114 | bus = dbus.SessionBus() | 168 | bus = dbus.SessionBus() |
138 | 169 | |||
139 | 170 | main_context = GObject.main_context_default() | ||
140 | 171 | main_loop = GObject.MainLoop(main_context) | ||
141 | 172 | |||
142 | 115 | bus_name = dbus.service.BusName(DBUS_BUS_NAME, bus) | 173 | bus_name = dbus.service.BusName(DBUS_BUS_NAME, bus) |
144 | 116 | data_provider = SoftwareCenterDataProvider(bus_name) | 174 | data_provider = SoftwareCenterDataProvider(bus_name, main_loop=main_loop) |
145 | 117 | data_provider # pyflakes | 175 | data_provider # pyflakes |
146 | 118 | 176 | ||
149 | 119 | main_context = GObject.main_context_default() | 177 | # run it |
148 | 120 | main_loop = GObject.MainLoop(main_context) | ||
150 | 121 | main_loop.run() | 178 | main_loop.run() |
151 | 122 | 179 | ||
152 | === modified file 'tests/test_dataprovider.py' | |||
153 | --- tests/test_dataprovider.py 2012-09-20 01:05:10 +0000 | |||
154 | +++ tests/test_dataprovider.py 2012-10-02 07:44:21 +0000 | |||
155 | @@ -4,10 +4,15 @@ | |||
156 | 4 | import time | 4 | import time |
157 | 5 | import unittest | 5 | import unittest |
158 | 6 | 6 | ||
159 | 7 | from gi.repository import GObject | ||
160 | 8 | |||
161 | 7 | from dbus.mainloop.glib import DBusGMainLoop | 9 | from dbus.mainloop.glib import DBusGMainLoop |
162 | 8 | DBusGMainLoop(set_as_default=True) | 10 | DBusGMainLoop(set_as_default=True) |
163 | 9 | 11 | ||
165 | 10 | from mock import Mock | 12 | from mock import ( |
166 | 13 | Mock, | ||
167 | 14 | patch, | ||
168 | 15 | ) | ||
169 | 11 | 16 | ||
170 | 12 | from tests.utils import ( | 17 | from tests.utils import ( |
171 | 13 | kill_process, | 18 | kill_process, |
172 | @@ -162,6 +167,47 @@ | |||
173 | 162 | self.assertEqual(len(result), 20) | 167 | self.assertEqual(len(result), 20) |
174 | 163 | 168 | ||
175 | 164 | 169 | ||
176 | 170 | class IdleTimeoutTestCase(unittest.TestCase): | ||
177 | 171 | |||
178 | 172 | def setUp(self): | ||
179 | 173 | self.loop = GObject.MainLoop(GObject.main_context_default()) | ||
180 | 174 | |||
181 | 175 | # setup bus | ||
182 | 176 | dbus_service_name = DBUS_BUS_NAME | ||
183 | 177 | proc, dbus_address = start_dbus_daemon() | ||
184 | 178 | bus = dbus.bus.BusConnection(dbus_address) | ||
185 | 179 | # run the checks | ||
186 | 180 | self.bus_name = dbus.service.BusName(dbus_service_name, bus) | ||
187 | 181 | |||
188 | 182 | def tearDown(self): | ||
189 | 183 | self.loop.quit() | ||
190 | 184 | |||
191 | 185 | @patch.object(SoftwareCenterDataProvider, "IDLE_TIMEOUT") | ||
192 | 186 | @patch.object(SoftwareCenterDataProvider, "IDLE_CHECK_INTERVAL") | ||
193 | 187 | def test_idle_timeout(self, mock_timeout, mock_interval): | ||
194 | 188 | mock_timeout = 1 | ||
195 | 189 | mock_timeout # pyflakes | ||
196 | 190 | mock_interval = 1 | ||
197 | 191 | mock_interval # pyflakes | ||
198 | 192 | |||
199 | 193 | now = time.time() | ||
200 | 194 | provider = SoftwareCenterDataProvider( | ||
201 | 195 | self.bus_name, main_loop=self.loop) | ||
202 | 196 | provider # pyflakes | ||
203 | 197 | self.loop.run() | ||
204 | 198 | # ensure this exited within a reasonable timeout | ||
205 | 199 | self.assertTrue((time.time() - now) < 5) | ||
206 | 200 | |||
207 | 201 | def test_idle_timeout_updates(self): | ||
208 | 202 | provider = SoftwareCenterDataProvider( | ||
209 | 203 | self.bus_name, main_loop=self.loop) | ||
210 | 204 | t1 = provider._activity_timestamp | ||
211 | 205 | time.sleep(0.1) | ||
212 | 206 | provider.GetAvailableCategories() | ||
213 | 207 | t2 = provider._activity_timestamp | ||
214 | 208 | self.assertTrue(t1 < t2) | ||
215 | 209 | |||
216 | 210 | |||
217 | 165 | if __name__ == "__main__": | 211 | if __name__ == "__main__": |
218 | 166 | #import logging | 212 | #import logging |
219 | 167 | #logging.basicConfig(level=logging.DEBUG) | 213 | #logging.basicConfig(level=logging.DEBUG) |
Hi Michael, thanks for your branch! Strangely, I'm getting a bunch of errors when I run the associated unit test, test_dataprovid er.py, as shown at the following pastebin:
http:// paste.ubuntu. com/1255206/
I'm not seeing these errors in current trunk. Do you have an idea about why I might be seeing these?
Many thanks!