Merge lp:~cmiller/ubuntu/oneiric/desktopcouch/1.0.8-0u1 into lp:ubuntu/oneiric/desktopcouch

Proposed by Chad Miller
Status: Merged
Merged at revision: 47
Proposed branch: lp:~cmiller/ubuntu/oneiric/desktopcouch/1.0.8-0u1
Merge into: lp:ubuntu/oneiric/desktopcouch
Diff against target: 1660 lines (+521/-554)
29 files modified
MANIFEST (+126/-0)
PKG-INFO (+21/-2)
config/desktop-couch/default.ini (+3/-0)
data/epydoc.conf (+25/-0)
debian/changelog (+21/-0)
debian/control (+2/-2)
debian/patches/5-defer-dbus-service-for-plugins.patch (+0/-249)
debian/python-desktopcouch-application.install (+6/-2)
debian/python-desktopcouch-recordtypes.install (+4/-1)
debian/rules (+0/-1)
desktopcouch.egg-info/PKG-INFO (+0/-10)
desktopcouch.egg-info/SOURCES.txt (+0/-126)
desktopcouch.egg-info/dependency_links.txt (+0/-1)
desktopcouch.egg-info/top_level.txt (+0/-1)
desktopcouch/application/local_files.py (+5/-2)
desktopcouch/application/platform/linux/tests/test_keyring.py (+1/-1)
desktopcouch/application/platform/windows/tests/test_base_dirs.py (+2/-2)
desktopcouch/application/plugins/__init__.py (+13/-3)
desktopcouch/application/plugins/tests/test_plugins.py (+3/-1)
desktopcouch/application/plugins/tests/test_ubuntuone_pairing.py (+36/-8)
desktopcouch/application/plugins/ubuntuone_pairing.py (+75/-44)
desktopcouch/application/replication.py (+1/-1)
desktopcouch/application/service.py (+51/-11)
desktopcouch/application/start_local_couchdb.py (+1/-1)
desktopcouch/application/tests/test_service.py (+96/-62)
desktopcouch/records/tests/test_mocked_server.py (+4/-3)
desktopcouch/recordtypes/contacts/tests/test_view.py (+8/-8)
setup.cfg (+4/-4)
setup.py (+13/-8)
To merge this branch: bzr merge lp:~cmiller/ubuntu/oneiric/desktopcouch/1.0.8-0u1
Reviewer Review Type Date Requested Status
Ubuntu branches Pending
Review via email: mp+75028@code.launchpad.net

Description of the change

New version:

Behavior change: Preserves Ubuntu One service through longer replication period, 10 minutes changed to 60 minutes.

Feature: Allow COUCH_INI environment variable to override /etc/ default config.

Feature: Install apport hook.

Bug fix: When ubuntuone credentials don't exist, don't wait forever and consume CPU.

Bug fix: Accept "linux3" kernel also.

Bug fix: Clean up all children when service exits.

To post a comment you must log in.
48. By Chad Miller

  - Behavior change: Preserves Ubuntu One service through longer replication
    period, 10 minutes changed to 60 minutes.
  - Feature: Allow COUCH_INI environment variable to override /etc/ default
    config.
  - Feature: Install apport hook.
  - Bug fix: When ubuntuone credentials don't exist, don't wait forever and
    consume CPU.
  - Bug fix: Accept "linux3" kernel also.
  - Bug fix: Clean up all children when service exits.

49. By Chad Miller

    consume CPU. (LP: #760236, #787583)
  - Bug fix: Accept "linux3" kernel also. (LP: #803062)
  - Bug fix: Clean up all children when service exits. (LP: #597197)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'MANIFEST'
2--- MANIFEST 1970-01-01 00:00:00 +0000
3+++ MANIFEST 2011-09-13 15:01:24 +0000
4@@ -0,0 +1,126 @@
5+# file GENERATED by distutils, do NOT edit
6+COPYING
7+COPYING.LESSER
8+MANIFEST
9+MANIFEST.in
10+README
11+desktopcouch-pair.desktop.in
12+org.desktopcouch.CouchDB.service
13+setup.cfg
14+setup.py
15+start-desktop-couchdb.sh
16+stop-desktop-couchdb.sh
17+bin/desktopcouch-get-port
18+bin/desktopcouch-pair
19+bin/desktopcouch-service
20+bin/desktopcouch-stop
21+config/desktop-couch/compulsory-auth.ini
22+config/desktop-couch/default.ini
23+data/couchdb.tmpl
24+data/epydoc.conf
25+data/source_desktopcouch.py
26+desktopcouch/__init__.py
27+desktopcouch/local_files.py
28+desktopcouch/replication.py
29+desktopcouch/service.py
30+desktopcouch/start_local_couchdb.py
31+desktopcouch/stop_local_couchdb.py
32+desktopcouch/util.py
33+desktopcouch/application/__init__.py
34+desktopcouch/application/local_files.py
35+desktopcouch/application/replication.py
36+desktopcouch/application/server.py
37+desktopcouch/application/service.py
38+desktopcouch/application/start_local_couchdb.py
39+desktopcouch/application/stop_local_couchdb.py
40+desktopcouch/application/util.py
41+desktopcouch/application/migration/__init__.py
42+desktopcouch/application/migration/tests/__init__.py
43+desktopcouch/application/migration/tests/test_migration.py
44+desktopcouch/application/pair/__init__.py
45+desktopcouch/application/pair/couchdb_pairing/__init__.py
46+desktopcouch/application/pair/couchdb_pairing/couchdb_io.py
47+desktopcouch/application/pair/couchdb_pairing/dbus_io.py
48+desktopcouch/application/pair/couchdb_pairing/network_io.py
49+desktopcouch/application/pair/tests/__init__.py
50+desktopcouch/application/pair/tests/test_couchdb_io.py
51+desktopcouch/application/pair/tests/test_network_io.py
52+desktopcouch/application/platform/__init__.py
53+desktopcouch/application/platform/linux/__init__.py
54+desktopcouch/application/platform/linux/base_dirs.py
55+desktopcouch/application/platform/linux/ipc.py
56+desktopcouch/application/platform/linux/keyring.py
57+desktopcouch/application/platform/linux/tests/__init__.py
58+desktopcouch/application/platform/linux/tests/test_keyring.py
59+desktopcouch/application/platform/windows/__init__.py
60+desktopcouch/application/platform/windows/base_dirs.py
61+desktopcouch/application/platform/windows/keyring.py
62+desktopcouch/application/platform/windows/tests/__init__.py
63+desktopcouch/application/platform/windows/tests/test_base_dirs.py
64+desktopcouch/application/platform/windows/tests/test_keyring.py
65+desktopcouch/application/plugins/__init__.py
66+desktopcouch/application/plugins/ubuntuone_pairing.py
67+desktopcouch/application/plugins/tests/__init__.py
68+desktopcouch/application/plugins/tests/test_plugins.py
69+desktopcouch/application/plugins/tests/test_ubuntuone_pairing.py
70+desktopcouch/application/replication_services/__init__.py
71+desktopcouch/application/replication_services/example.py
72+desktopcouch/application/replication_services/ubuntuone.py
73+desktopcouch/application/tests/__init__.py
74+desktopcouch/application/tests/test_local_files.py
75+desktopcouch/application/tests/test_replication.py
76+desktopcouch/application/tests/test_service.py
77+desktopcouch/application/tests/test_start_local_couchdb.py
78+desktopcouch/bookmarks/__init__.py
79+desktopcouch/bookmarks/record.py
80+desktopcouch/contacts/__init__.py
81+desktopcouch/contacts/record.py
82+desktopcouch/contacts/view.py
83+desktopcouch/notes/__init__.py
84+desktopcouch/notes/record.py
85+desktopcouch/pair/__init__.py
86+desktopcouch/pair/couchdb_pairing/__init__.py
87+desktopcouch/pair/couchdb_pairing/couchdb_io.py
88+desktopcouch/pair/couchdb_pairing/dbus_io.py
89+desktopcouch/pair/couchdb_pairing/network_io.py
90+desktopcouch/pair/couchdb_pairing/ubuntuone_pairing.py
91+desktopcouch/records/__init__.py
92+desktopcouch/records/database.py
93+desktopcouch/records/field_registry.py
94+desktopcouch/records/http.py
95+desktopcouch/records/record.py
96+desktopcouch/records/server.py
97+desktopcouch/records/server_base.py
98+desktopcouch/records/doc/an_example_application.txt
99+desktopcouch/records/doc/field_registry.txt
100+desktopcouch/records/doc/records.txt
101+desktopcouch/records/tests/__init__.py
102+desktopcouch/records/tests/test_field_registry.py
103+desktopcouch/records/tests/test_mocked_server.py
104+desktopcouch/records/tests/test_record.py
105+desktopcouch/records/tests/test_server.py
106+desktopcouch/recordtypes/__init__.py
107+desktopcouch/recordtypes/bookmarks.py
108+desktopcouch/recordtypes/notes.py
109+desktopcouch/recordtypes/tasks.py
110+desktopcouch/recordtypes/contacts/__init__.py
111+desktopcouch/recordtypes/contacts/schema.txt
112+desktopcouch/recordtypes/contacts/view.py
113+desktopcouch/recordtypes/contacts/testing/__init__.py
114+desktopcouch/recordtypes/contacts/testing/create.py
115+desktopcouch/recordtypes/contacts/tests/__init__.py
116+desktopcouch/recordtypes/contacts/tests/test_create.py
117+desktopcouch/recordtypes/contacts/tests/test_record.py
118+desktopcouch/recordtypes/contacts/tests/test_view.py
119+desktopcouch/recordtypes/tests/__init__.py
120+desktopcouch/recordtypes/tests/test_bookmarks.py
121+desktopcouch/recordtypes/tests/test_notes.py
122+desktopcouch/recordtypes/tests/test_tasks.py
123+desktopcouch/replication_services/__init__.py
124+desktopcouch/replication_services/example.py
125+desktopcouch/replication_services/ubuntuone.py
126+desktopcouch/tasks/__init__.py
127+desktopcouch/tasks/record.py
128+desktopcouch/tests/__init__.py
129+docs/man/desktopcouch-pair.1
130+po/POTFILES.in
131
132=== modified file 'PKG-INFO'
133--- PKG-INFO 2011-04-08 20:42:51 +0000
134+++ PKG-INFO 2011-09-13 15:01:24 +0000
135@@ -1,6 +1,6 @@
136-Metadata-Version: 1.0
137+Metadata-Version: 1.1
138 Name: desktopcouch
139-Version: 1.0.7
140+Version: 1.0.8
141 Summary: A Desktop CouchDB instance.
142 Home-page: https://launchpad.net/desktopcouch
143 Author: Stuart Langridge
144@@ -8,3 +8,22 @@
145 License: LGPL-3
146 Description: UNKNOWN
147 Platform: UNKNOWN
148+Requires: avahi
149+Requires: couchdb
150+Requires: dbus
151+Requires: gnomekeyring
152+Requires: gobject
153+Requires: gtk
154+Requires: mocker
155+Requires: oauth
156+Requires: pango
157+Requires: pygtk
158+Requires: simplejson
159+Requires: twisted.internet
160+Requires: twisted.protocols
161+Requires: twisted.python.threadable
162+Requires: ubuntu_sso
163+Requires: ubuntuone.clientdefs
164+Requires: ubuntuone.devtools.testcase
165+Requires: xdg.BaseDirectory
166+Provides: desktopcouch
167
168=== added file 'config/desktop-couch/default.ini'
169--- config/desktop-couch/default.ini 1970-01-01 00:00:00 +0000
170+++ config/desktop-couch/default.ini 2011-09-13 15:01:24 +0000
171@@ -0,0 +1,3 @@
172+[replicator]
173+max_http_sessions = 1
174+
175
176=== added file 'data/epydoc.conf'
177--- data/epydoc.conf 1970-01-01 00:00:00 +0000
178+++ data/epydoc.conf 2011-09-13 15:01:24 +0000
179@@ -0,0 +1,25 @@
180+[epydoc] # Epydoc section marker (required by ConfigParser)
181+
182+# Information about the project.
183+name: desktopcouch
184+url: http://www.freedesktop.org/wiki/Specifications/desktopcouch
185+
186+# The list of modules to document. Modules can be named using
187+# dotted names, module filenames, or package directory names.
188+# This option may be repeated.
189+modules: desktopcouch, desktopcouch.records
190+exclude: test
191+
192+# Write html output to the directory "apidocs"
193+output: html
194+target: docs/html/api/
195+
196+
197+parse: yes
198+introspect: no
199+
200+# Include all automatically generated graphs. These graphs are
201+# generated using Graphviz dot.
202+graph: all
203+dotpath: /usr/bin/dot
204+
205
206=== modified file 'debian/changelog'
207--- debian/changelog 2011-08-25 09:29:15 +0000
208+++ debian/changelog 2011-09-13 15:01:24 +0000
209@@ -1,3 +1,24 @@
210+desktopcouch (1.0.8-0ubuntu1) UNRELEASED; urgency=low
211+
212+ * New upstream release.
213+ - Behavior change: Preserves Ubuntu One service through longer replication
214+ period, 10 minutes changed to 60 minutes.
215+ - Feature: Allow COUCH_INI environment variable to override /etc/ default
216+ config.
217+ - Feature: Install apport hook.
218+ - Bug fix: When ubuntuone credentials don't exist, don't wait forever and
219+ consume CPU. (LP: #760236, #787583)
220+ - Bug fix: Accept "linux3" kernel also. (LP: #803062)
221+ - Bug fix: Clean up all children when service exits. (LP: #597197)
222+ * Update standards-version 3.9.1 to 3.9.2.
223+ * Remove brace-expansion from python-desktopcouch-application.install .
224+ * Remove brace-expansion from python-desktopcouch-recordtypes.install .
225+ * Remove patch
226+ - patches/5-defer-dbus-service-for-plugins.patch
227+ * Remove deprecated CDBS simple-patch system from rules.
228+
229+ -- Chad MILLER <chad.miller@canonical.com> Mon, 12 Sep 2011 13:08:25 -0400
230+
231 desktopcouch (1.0.7-0ubuntu3) oneiric; urgency=low
232
233 * debian/rules: langpack.mk does not exist any more, call dh_translations
234
235=== modified file 'debian/control'
236--- debian/control 2011-01-12 15:08:25 +0000
237+++ debian/control 2011-09-13 15:01:24 +0000
238@@ -8,7 +8,7 @@
239 python-distutils-extra (>= 2.8),
240 python-setuptools
241 Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
242-Standards-Version: 3.9.1
243+Standards-Version: 3.9.2
244 XS-Python-Version: current
245 Homepage: http://launchpad.net/desktopcouch
246
247@@ -20,7 +20,7 @@
248 couchdb-bin (>= 0.10.0-0ubuntu3),
249 python-desktopcouch-application (= ${source:Version})
250 Breaks: gwibber-service (<< 2.31.0), gwibber (<< 2.31.0)
251-Description: A Desktop CouchDB instance
252+Description: Desktop CouchDB instance
253 Runs an instance of CouchDB with the user's session. Includes python library
254 for interacting with database.
255
256
257=== removed file 'debian/patches/5-defer-dbus-service-for-plugins.patch'
258--- debian/patches/5-defer-dbus-service-for-plugins.patch 2011-04-15 23:48:39 +0000
259+++ debian/patches/5-defer-dbus-service-for-plugins.patch 1970-01-01 00:00:00 +0000
260@@ -1,249 +0,0 @@
261-Description: Make plugins be able to stall dbus service from signaling that
262- couchdb is ready to use.
263-Bug-Ubuntu: https://bugs.edge.launchpad.net/ubuntu/+source/desktopcouch/+bug/760236
264-Applied-upstream: bzr id chad.miller@canonical.com-20110412173802-flmya9dma0et83qr
265-Author: Chad Miller <chad.miller@canonical.com>
266-
267-
268-=== modified file 'desktopcouch/application/plugins/__init__.py'
269---- desktopcouch/application/plugins/__init__.py 2011-02-02 18:15:53 +0000
270-+++ desktopcouch/application/plugins/__init__.py 2011-04-15 23:40:28 +0000
271-@@ -19,8 +19,18 @@
272- DESKTOPCOUCH_PLUGIN_PATHS = [os.path.join(os.path.dirname(__file__))]
273-
274-
275--def load_plugins(couchdb_port):
276-- """Load the desktopcouch application plug-ins."""
277-+def load_plugins(couchdb_port, blocking_semaphores, gobject):
278-+ """Load the desktopcouch application plug-ins.
279-+
280-+ The blocking_semaphores set is OPTIONALLY mutated by any plugin to signal
281-+ that the service is not ready until a plugin has finished its asynchronous
282-+ operations. Plugins may add a distinguishing object to the set, and it
283-+ must remove what it adds when it is finished.
284-+
285-+ couchdb -- the integer of the port number of the running couchdb
286-+ blocking_semaphores -- the set() of semaphores, which we will mutate
287-+ gobject -- the mainloop module. always 'gobject' except when testing.
288-+ """
289- plugin_names = set()
290- for path in DESKTOPCOUCH_PLUGIN_PATHS:
291- try:
292-@@ -37,6 +47,6 @@
293- modpath = name.replace(os.path.sep, '.')[:-3]
294- try:
295- plugin = __import__(modpath, None, None, [''])
296-- plugin.plugin_init(couchdb_port)
297-+ plugin.plugin_init(couchdb_port, blocking_semaphores, gobject)
298- except (ImportError, AttributeError):
299- logging.warning('Failed to load plug-in: %s', modpath)
300-
301-=== modified file 'desktopcouch/application/plugins/ubuntuone_pairing.py'
302---- desktopcouch/application/plugins/ubuntuone_pairing.py 2011-02-02 18:15:53 +0000
303-+++ desktopcouch/application/plugins/ubuntuone_pairing.py 2011-04-15 23:41:29 +0000
304-@@ -24,6 +24,7 @@
305- from desktopcouch.application.server import DesktopDatabase
306- from ubuntuone.clientdefs import APP_NAME
307-
308-+PLUGIN_NAME = __name__
309- U1_PAIR_RECORD = "ubuntu_one_pair_record"
310- MAP_JS = """function(doc) {
311- if (doc.service_name == "ubuntuone") {
312-@@ -33,48 +34,57 @@
313- """
314-
315-
316--def pair_with_ubuntuone(couchdb_port, management_db=None,
317-+def pair_with_ubuntuone(couchdb_port, blocking_semaphores,
318-+ management_db=None,
319- db_class=DesktopDatabase,
320- put_service_fn=put_static_paired_service):
321- """Adds a pairing record with ubuntu one when needed."""
322-- # Use explicit uri so that we do not access dbus service.
323-- uri = "http://localhost:%s" % (couchdb_port,)
324-- if not management_db:
325-- management_db = db_class("management", uri=uri, create=True, ctx=None)
326-- # we indeed have credentials to add to the pairing records
327-- # but first we ensure that the required view is present
328-- if not management_db.view_exists(U1_PAIR_RECORD, U1_PAIR_RECORD):
329-- management_db.add_view(
330-- U1_PAIR_RECORD, MAP_JS, design_doc=U1_PAIR_RECORD)
331-- view_results = management_db.execute_view(U1_PAIR_RECORD, U1_PAIR_RECORD)
332-- pairing_found = False
333-- # Results should contain either one row or no rows
334-- # If there is one row, its value will be 0, meaning that there is
335-- # already an Ubuntu One pairing record, or 1, meaning that there
336-- # was an Ubuntu One pairing record but it has since been unpaired
337-- # Only create a new record if there is not one already. Specifically,
338-- # do not add the record if there is a deleted one, as this means
339-- # that the user explicitly unpaired it!
340-- for row in view_results:
341-- pairing_found = True
342-- if row.value == 1:
343-- logging.debug("Not adding desktopcouch pairing since the user "
344-- "has explicitly unpaired with Ubuntu One")
345-- else:
346-- logging.debug("Not adding desktopcouch pairing since we are "
347-- "already paired")
348-- if not pairing_found:
349-- put_service_fn(None, "ubuntuone", uri=uri, ctx=None)
350-- logging.debug("Pairing desktopcouch with Ubuntu One")
351--
352--
353--def got_new_credentials(couchdb_port, app_name, credentials):
354-+ try:
355-+ # Use explicit uri so that we do not access dbus service.
356-+ uri = "http://localhost:%s" % (couchdb_port,)
357-+ if not management_db:
358-+ management_db = db_class("management", uri=uri, create=True,
359-+ ctx=None)
360-+ # we indeed have credentials to add to the pairing records
361-+ # but first we ensure that the required view is present
362-+ if not management_db.view_exists(U1_PAIR_RECORD, U1_PAIR_RECORD):
363-+ management_db.add_view(
364-+ U1_PAIR_RECORD, MAP_JS, design_doc=U1_PAIR_RECORD)
365-+ view_results = management_db.execute_view(U1_PAIR_RECORD,
366-+ U1_PAIR_RECORD)
367-+ pairing_found = False
368-+ # Results should contain either one row or no rows
369-+ # If there is one row, its value will be 0, meaning that there is
370-+ # already an Ubuntu One pairing record, or 1, meaning that there
371-+ # was an Ubuntu One pairing record but it has since been unpaired
372-+ # Only create a new record if there is not one already. Specifically,
373-+ # do not add the record if there is a deleted one, as this means
374-+ # that the user explicitly unpaired it!
375-+ for row in view_results:
376-+ pairing_found = True
377-+ if row.value == 1:
378-+ logging.debug("Not adding desktopcouch pairing since the user "
379-+ "has explicitly unpaired with Ubuntu One")
380-+ else:
381-+ logging.debug("Not adding desktopcouch pairing since we are "
382-+ "already paired")
383-+ if not pairing_found:
384-+ put_service_fn(None, "ubuntuone", uri=uri, ctx=None)
385-+ logging.debug("Pairing desktopcouch with Ubuntu One")
386-+
387-+ finally:
388-+ logging.info("removing semaphore for %s", PLUGIN_NAME)
389-+ blocking_semaphores.discard(PLUGIN_NAME)
390-+
391-+
392-+def got_new_credentials(couchdb_port, blocking_semaphores,
393-+ app_name, credentials):
394- """Pair with Ubuntu One when we get the new credentials."""
395- if app_name == APP_NAME:
396-- pair_with_ubuntuone(couchdb_port)
397--
398--
399--def listen_to_dbus(couchdb_port):
400-+ pair_with_ubuntuone(couchdb_port, blocking_semaphores)
401-+
402-+
403-+def listen_to_dbus(couchdb_port, blocking_semaphores):
404- """Set up the signal handler on D-Bus for Ubuntu One pairing."""
405- import dbus
406- bus = dbus.SessionBus()
407-@@ -82,7 +92,9 @@
408- try:
409- import ubuntu_sso
410-
411-- receiver = lambda *args: got_new_credentials(couchdb_port, *args)
412-+ receiver = lambda *args: \
413-+ got_new_credentials(couchdb_port, blocking_semaphores,
414-+ *args)
415-
416- iface = ubuntu_sso.DBUS_CREDENTIALS_IFACE
417- bus.add_signal_receiver(handler_function=receiver,
418-@@ -96,13 +108,20 @@
419- sso_backend.find_credentials(APP_NAME, {})
420- except ImportError:
421- logging.info('Ubuntu SSO is not available.')
422--
423--
424--def plugin_init(couchdb_port):
425-+ blocking_semaphores.discard(PLUGIN_NAME)
426-+
427-+
428-+def plugin_init(couchdb_port, blocking_semaphores, gobject):
429- """Set up the signal handler for pairing with Ubuntu One."""
430- logging.info('Loaded Ubuntu One extension for desktopcouch.')
431- if sys.platform == 'win32':
432- logging.warning('Windows support for Ubuntu One is not yet ready.')
433- else:
434-- import gobject
435-- gobject.idle_add(listen_to_dbus, couchdb_port)
436-+
437-+ # Signal that we are critical for desktopcouch usage, and the server
438-+ # must not begin until we are finished. We are responsible for
439-+ # removing this item from the list.
440-+ logging.info("adding %s to to blocking semaphore list", PLUGIN_NAME)
441-+ blocking_semaphores.add(PLUGIN_NAME)
442-+
443-+ gobject.idle_add(listen_to_dbus, couchdb_port, blocking_semaphores)
444-
445-=== modified file 'desktopcouch/application/service.py'
446---- desktopcouch/application/service.py 2011-02-02 18:15:53 +0000
447-+++ desktopcouch/application/service.py 2011-04-15 23:42:17 +0000
448-@@ -39,6 +39,7 @@
449- import logging
450- import logging.handlers
451- import signal
452-+import gobject
453-
454- from desktopcouch.application import local_files
455- from desktopcouch.application import replication
456-@@ -84,7 +85,8 @@
457- replication_actions=replication,
458- advertiser_factory=PortAdvertiser, set_logging=set_up_logging,
459- fork=os.fork, nice=os.nice,
460-- kill=os.kill, sleep=time.sleep):
461-+ kill=os.kill, sleep=time.sleep, set_type=set,
462-+ gobject_module=gobject):
463- self._mainloop = main_loop
464- self._pid_finder = pid_finder
465- self._port_finder = port_finder
466-@@ -97,6 +99,8 @@
467- self._nice = nice
468- self._kill = kill
469- self._sleep = sleep
470-+ self._set = set_type
471-+ self._gobject = gobject_module
472- # pylint: enable=C0301
473-
474- def _start_replicator_main(self, couchdb_port):
475-@@ -112,11 +116,30 @@
476- replication.tear_down(*replication_runtime)
477-
478- def _start_server_main(self, couchdb_port):
479-- """Start server."""
480-- self._advertiser_factory(self._mainloop.stop, self._ctx)
481-+ """Start server answering DBus calls, and run plugins first."""
482-+
483-+ def if_all_semaphores_cleared(blocking_semaphores,
484-+ func, *args, **kwargs):
485-+ """Run a function if no semaphores exist, else try later."""
486-+ if blocking_semaphores:
487-+ return True # Make idle call try us again.
488-+ else:
489-+ func(*args, **kwargs)
490-+ return False # Handled!
491-+
492-+ blocking_semaphores = self._set()
493-+ load_plugins(couchdb_port, blocking_semaphores, self._gobject)
494-+
495-+ # Answering queries on DBus signals that we are ready for users
496-+ # to connect. We mustn't begin that until every plugin has a chance
497-+ # to run to completion if it needs it.
498-+ self._gobject.idle_add(if_all_semaphores_cleared, blocking_semaphores,
499-+ self._advertiser_factory,
500-+ self._mainloop.stop,
501-+ self._ctx)
502-+
503- logging.debug("starting dbus main loop")
504- try:
505-- load_plugins(couchdb_port)
506- self._mainloop.run()
507- finally:
508- logging.debug("ending dbus main loop")
509-
510
511=== modified file 'debian/python-desktopcouch-application.install'
512--- debian/python-desktopcouch-application.install 2011-01-12 15:08:25 +0000
513+++ debian/python-desktopcouch-application.install 2011-09-13 15:01:24 +0000
514@@ -1,5 +1,8 @@
515 debian/tmp/usr/lib/*/*/desktopcouch/application/*.py
516-debian/tmp/usr/lib/*/*/desktopcouch/application/{migration,pair,platform,replication_services}
517+debian/tmp/usr/lib/*/*/desktopcouch/application/migration
518+debian/tmp/usr/lib/*/*/desktopcouch/application/pair
519+debian/tmp/usr/lib/*/*/desktopcouch/application/platform
520+debian/tmp/usr/lib/*/*/desktopcouch/application/replication_services
521 debian/tmp/usr/lib/*/*/desktopcouch/application/plugins/__init__.py
522 debian/tmp/usr/lib/*/*/desktopcouch/local_files.py
523 debian/tmp/usr/lib/*/*/desktopcouch/replication.py
524@@ -7,4 +10,5 @@
525 debian/tmp/usr/lib/*/*/desktopcouch/start_local_couchdb.py
526 debian/tmp/usr/lib/*/*/desktopcouch/stop_local_couchdb.py
527 debian/tmp/usr/lib/*/*/desktopcouch/util.py
528-debian/tmp/usr/lib/*/*/desktopcouch/{pair,replication_services}
529+debian/tmp/usr/lib/*/*/desktopcouch/pair
530+debian/tmp/usr/lib/*/*/desktopcouch/replication_services
531
532=== modified file 'debian/python-desktopcouch-recordtypes.install'
533--- debian/python-desktopcouch-recordtypes.install 2011-01-12 15:08:25 +0000
534+++ debian/python-desktopcouch-recordtypes.install 2011-09-13 15:01:24 +0000
535@@ -1,2 +1,5 @@
536 debian/tmp/usr/lib/*/*/desktopcouch/recordtypes
537-debian/tmp/usr/lib/*/*/desktopcouch/{bookmarks,contacts,notes,tasks}
538+debian/tmp/usr/lib/*/*/desktopcouch/bookmarks
539+debian/tmp/usr/lib/*/*/desktopcouch/contacts
540+debian/tmp/usr/lib/*/*/desktopcouch/notes
541+debian/tmp/usr/lib/*/*/desktopcouch/tasks
542
543=== modified file 'debian/rules'
544--- debian/rules 2011-08-25 09:28:07 +0000
545+++ debian/rules 2011-09-13 15:01:24 +0000
546@@ -5,7 +5,6 @@
547
548 include /usr/share/cdbs/1/rules/debhelper.mk
549 include /usr/share/cdbs/1/class/python-distutils.mk
550-include /usr/share/cdbs/1/rules/simple-patchsys.mk
551
552 common-binary-post-install-indep::
553 dh_translations
554
555=== removed directory 'desktopcouch.egg-info'
556=== removed file 'desktopcouch.egg-info/PKG-INFO'
557--- desktopcouch.egg-info/PKG-INFO 2011-04-08 20:42:51 +0000
558+++ desktopcouch.egg-info/PKG-INFO 1970-01-01 00:00:00 +0000
559@@ -1,10 +0,0 @@
560-Metadata-Version: 1.0
561-Name: desktopcouch
562-Version: 1.0.7
563-Summary: A Desktop CouchDB instance.
564-Home-page: https://launchpad.net/desktopcouch
565-Author: Stuart Langridge
566-Author-email: stuart.langridge@canonical.com
567-License: LGPL-3
568-Description: UNKNOWN
569-Platform: UNKNOWN
570
571=== removed file 'desktopcouch.egg-info/SOURCES.txt'
572--- desktopcouch.egg-info/SOURCES.txt 2011-01-12 15:08:25 +0000
573+++ desktopcouch.egg-info/SOURCES.txt 1970-01-01 00:00:00 +0000
574@@ -1,126 +0,0 @@
575-COPYING
576-COPYING.LESSER
577-MANIFEST.in
578-README
579-desktopcouch-pair.desktop.in
580-org.desktopcouch.CouchDB.service
581-setup.cfg
582-setup.py
583-start-desktop-couchdb.sh
584-stop-desktop-couchdb.sh
585-bin/desktopcouch-get-port
586-bin/desktopcouch-pair
587-bin/desktopcouch-service
588-bin/desktopcouch-stop
589-config/desktop-couch/compulsory-auth.ini
590-data/couchdb.tmpl
591-data/source_desktopcouch.py
592-desktopcouch/__init__.py
593-desktopcouch/local_files.py
594-desktopcouch/replication.py
595-desktopcouch/service.py
596-desktopcouch/start_local_couchdb.py
597-desktopcouch/stop_local_couchdb.py
598-desktopcouch/util.py
599-desktopcouch.egg-info/PKG-INFO
600-desktopcouch.egg-info/SOURCES.txt
601-desktopcouch.egg-info/dependency_links.txt
602-desktopcouch.egg-info/top_level.txt
603-desktopcouch/application/__init__.py
604-desktopcouch/application/local_files.py
605-desktopcouch/application/replication.py
606-desktopcouch/application/server.py
607-desktopcouch/application/service.py
608-desktopcouch/application/start_local_couchdb.py
609-desktopcouch/application/stop_local_couchdb.py
610-desktopcouch/application/util.py
611-desktopcouch/application/migration/__init__.py
612-desktopcouch/application/migration/tests/__init__.py
613-desktopcouch/application/migration/tests/test_migration.py
614-desktopcouch/application/pair/__init__.py
615-desktopcouch/application/pair/couchdb_pairing/__init__.py
616-desktopcouch/application/pair/couchdb_pairing/couchdb_io.py
617-desktopcouch/application/pair/couchdb_pairing/dbus_io.py
618-desktopcouch/application/pair/couchdb_pairing/network_io.py
619-desktopcouch/application/pair/tests/__init__.py
620-desktopcouch/application/pair/tests/test_couchdb_io.py
621-desktopcouch/application/pair/tests/test_network_io.py
622-desktopcouch/application/platform/__init__.py
623-desktopcouch/application/platform/linux/__init__.py
624-desktopcouch/application/platform/linux/base_dirs.py
625-desktopcouch/application/platform/linux/ipc.py
626-desktopcouch/application/platform/linux/keyring.py
627-desktopcouch/application/platform/linux/tests/__init__.py
628-desktopcouch/application/platform/linux/tests/test_keyring.py
629-desktopcouch/application/platform/windows/__init__.py
630-desktopcouch/application/platform/windows/base_dirs.py
631-desktopcouch/application/platform/windows/keyring.py
632-desktopcouch/application/platform/windows/tests/__init__.py
633-desktopcouch/application/platform/windows/tests/test_base_dirs.py
634-desktopcouch/application/platform/windows/tests/test_keyring.py
635-desktopcouch/application/plugins/__init__.py
636-desktopcouch/application/plugins/ubuntuone_pairing.py
637-desktopcouch/application/plugins/tests/__init__.py
638-desktopcouch/application/plugins/tests/test_plugins.py
639-desktopcouch/application/plugins/tests/test_ubuntuone_pairing.py
640-desktopcouch/application/replication_services/__init__.py
641-desktopcouch/application/replication_services/example.py
642-desktopcouch/application/replication_services/ubuntuone.py
643-desktopcouch/application/tests/__init__.py
644-desktopcouch/application/tests/test_local_files.py
645-desktopcouch/application/tests/test_replication.py
646-desktopcouch/application/tests/test_service.py
647-desktopcouch/application/tests/test_start_local_couchdb.py
648-desktopcouch/bookmarks/__init__.py
649-desktopcouch/bookmarks/record.py
650-desktopcouch/contacts/__init__.py
651-desktopcouch/contacts/record.py
652-desktopcouch/contacts/view.py
653-desktopcouch/notes/__init__.py
654-desktopcouch/notes/record.py
655-desktopcouch/pair/__init__.py
656-desktopcouch/pair/couchdb_pairing/__init__.py
657-desktopcouch/pair/couchdb_pairing/couchdb_io.py
658-desktopcouch/pair/couchdb_pairing/dbus_io.py
659-desktopcouch/pair/couchdb_pairing/network_io.py
660-desktopcouch/pair/couchdb_pairing/ubuntuone_pairing.py
661-desktopcouch/records/__init__.py
662-desktopcouch/records/database.py
663-desktopcouch/records/field_registry.py
664-desktopcouch/records/http.py
665-desktopcouch/records/record.py
666-desktopcouch/records/server.py
667-desktopcouch/records/server_base.py
668-desktopcouch/records/doc/an_example_application.txt
669-desktopcouch/records/doc/field_registry.txt
670-desktopcouch/records/doc/records.txt
671-desktopcouch/records/tests/__init__.py
672-desktopcouch/records/tests/test_field_registry.py
673-desktopcouch/records/tests/test_mocked_server.py
674-desktopcouch/records/tests/test_record.py
675-desktopcouch/records/tests/test_server.py
676-desktopcouch/recordtypes/__init__.py
677-desktopcouch/recordtypes/bookmarks.py
678-desktopcouch/recordtypes/notes.py
679-desktopcouch/recordtypes/tasks.py
680-desktopcouch/recordtypes/contacts/__init__.py
681-desktopcouch/recordtypes/contacts/schema.txt
682-desktopcouch/recordtypes/contacts/view.py
683-desktopcouch/recordtypes/contacts/testing/__init__.py
684-desktopcouch/recordtypes/contacts/testing/create.py
685-desktopcouch/recordtypes/contacts/tests/__init__.py
686-desktopcouch/recordtypes/contacts/tests/test_create.py
687-desktopcouch/recordtypes/contacts/tests/test_record.py
688-desktopcouch/recordtypes/contacts/tests/test_view.py
689-desktopcouch/recordtypes/tests/__init__.py
690-desktopcouch/recordtypes/tests/test_bookmarks.py
691-desktopcouch/recordtypes/tests/test_notes.py
692-desktopcouch/recordtypes/tests/test_tasks.py
693-desktopcouch/replication_services/__init__.py
694-desktopcouch/replication_services/example.py
695-desktopcouch/replication_services/ubuntuone.py
696-desktopcouch/tasks/__init__.py
697-desktopcouch/tasks/record.py
698-desktopcouch/tests/__init__.py
699-docs/man/desktopcouch-pair.1
700-po/POTFILES.in
701\ No newline at end of file
702
703=== removed file 'desktopcouch.egg-info/dependency_links.txt'
704--- desktopcouch.egg-info/dependency_links.txt 2009-10-22 17:15:57 +0000
705+++ desktopcouch.egg-info/dependency_links.txt 1970-01-01 00:00:00 +0000
706@@ -1,1 +0,0 @@
707-
708
709=== removed file 'desktopcouch.egg-info/top_level.txt'
710--- desktopcouch.egg-info/top_level.txt 2009-10-22 17:15:57 +0000
711+++ desktopcouch.egg-info/top_level.txt 1970-01-01 00:00:00 +0000
712@@ -1,1 +0,0 @@
713-desktopcouch
714
715=== modified file 'desktopcouch/application/local_files.py'
716--- desktopcouch/application/local_files.py 2011-04-08 20:40:53 +0000
717+++ desktopcouch/application/local_files.py 2011-09-13 15:01:24 +0000
718@@ -83,9 +83,11 @@
719 if "-hashed-" in bookmark_file_contents:
720 raise ValueError("Basic-auth cred lost.")
721 # trial run, check sanity.
722+ # pylint: disable=W0106
723 re.findall(
724 "<!-- !!([^!]+)!!([^!]+)!! -->",
725 bookmark_file_contents)[-1]
726+ # pylint: enable=W0106
727 self._fill_from_file(self.file_name_used)
728 return
729 except (IOError, ValueError, IndexError):
730@@ -196,7 +198,7 @@
731 for d in (run_dir, db_dir, config_dir):
732 try:
733 os.makedirs(d, 0700)
734- except OSError, ex:
735+ except OSError:
736 pass # Probably that it already exists.
737 try:
738 os.chmod(d, 0700)
739@@ -257,7 +259,8 @@
740 def couch_chain_ini_files(self):
741 """Chain couchdb ini files."""
742 # Explicitly add default ini file
743- ini_files = ["/etc/couchdb/default.ini"]
744+ ini_files = [os.environ.get("COUCHDB_INI") or
745+ "/etc/couchdb/default.ini"]
746
747 # find all ini files in the desktopcouch XDG_CONFIG_DIRS and
748 # add them to the chain
749
750=== modified file 'desktopcouch/application/platform/linux/tests/test_keyring.py'
751--- desktopcouch/application/platform/linux/tests/test_keyring.py 2011-01-12 15:08:25 +0000
752+++ desktopcouch/application/platform/linux/tests/test_keyring.py 2011-09-13 15:01:24 +0000
753@@ -30,7 +30,7 @@
754 try:
755 import gnomekeyring
756 except ImportError, e:
757- if sys.platform != 'linux2':
758+ if not sys.platform.startswith('linux'):
759 gnomekeyring = None
760 else:
761 raise e
762
763=== modified file 'desktopcouch/application/platform/windows/tests/test_base_dirs.py'
764--- desktopcouch/application/platform/windows/tests/test_base_dirs.py 2011-02-02 18:15:53 +0000
765+++ desktopcouch/application/platform/windows/tests/test_base_dirs.py 2011-09-13 15:01:24 +0000
766@@ -63,7 +63,7 @@
767 self.mocker.result('hive')
768 self._winreg.OpenKey('hive', SHELL_FOLDERS_KEY)
769 self.mocker.result('key')
770- self._winreg.QueryInfoKey('key')[1]
771+ self._winreg.QueryInfoKey('key')[1] # pylint: disable=W0106
772 self.mocker.throw(Exception('Cannot get info.'))
773 self._winreg.CloseKey('hive')
774 self._winreg.CloseKey('key')
775@@ -78,7 +78,7 @@
776 self.mocker.result('hive')
777 self._winreg.OpenKey('hive', SHELL_FOLDERS_KEY)
778 self.mocker.result('key')
779- self._winreg.QueryInfoKey('key')[1]
780+ self._winreg.QueryInfoKey('key')[1] # pylint: disable=W0106
781 self.mocker.result(1)
782 self._winreg.EnumValue('key', 0)
783 self.mocker.result(('AppData', 'path', 1))
784
785=== modified file 'desktopcouch/application/plugins/__init__.py'
786--- desktopcouch/application/plugins/__init__.py 2011-02-02 18:15:53 +0000
787+++ desktopcouch/application/plugins/__init__.py 2011-09-13 15:01:24 +0000
788@@ -19,8 +19,18 @@
789 DESKTOPCOUCH_PLUGIN_PATHS = [os.path.join(os.path.dirname(__file__))]
790
791
792-def load_plugins(couchdb_port):
793- """Load the desktopcouch application plug-ins."""
794+def load_plugins(couchdb_port, blocking_semaphores, gobject):
795+ """Load the desktopcouch application plug-ins.
796+
797+ The blocking_semaphores set is OPTIONALLY mutated by any plugin to signal
798+ that the service is not ready until a plugin has finished its asynchronous
799+ operations. Plugins may add a distinguishing object to the set, and it
800+ must remove what it adds when it is finished.
801+
802+ couchdb -- the integer of the port number of the running couchdb
803+ blocking_semaphores -- the set() of semaphores, which we will mutate
804+ gobject -- the mainloop module. always 'gobject' except when testing.
805+ """
806 plugin_names = set()
807 for path in DESKTOPCOUCH_PLUGIN_PATHS:
808 try:
809@@ -37,6 +47,6 @@
810 modpath = name.replace(os.path.sep, '.')[:-3]
811 try:
812 plugin = __import__(modpath, None, None, [''])
813- plugin.plugin_init(couchdb_port)
814+ plugin.plugin_init(couchdb_port, blocking_semaphores, gobject)
815 except (ImportError, AttributeError):
816 logging.warning('Failed to load plug-in: %s', modpath)
817
818=== modified file 'desktopcouch/application/plugins/tests/test_plugins.py'
819--- desktopcouch/application/plugins/tests/test_plugins.py 2011-02-02 18:15:53 +0000
820+++ desktopcouch/application/plugins/tests/test_plugins.py 2011-09-13 15:01:24 +0000
821@@ -28,6 +28,8 @@
822
823 def setUp(self):
824 self.couchdb_port = platform.find_port(ctx=test_context)
825+ self.blockers = set()
826+ self.gobject = None
827
828 def test_load_plugins(self):
829 """Test that plug-ins are loaded correctly."""
830@@ -44,7 +46,7 @@
831 self._imported = modname
832 old_import = __import__
833 __builtins__['__import__'] = _fake_import
834- plugins.load_plugins(self.couchdb_port)
835+ plugins.load_plugins(self.couchdb_port, self.blockers, self.gobject)
836 __builtins__['__import__'] = old_import
837 plugins.DESKTOPCOUCH_PLUGIN_PATHS = old_paths
838 self.assertEqual(self._imported, __name__)
839
840=== modified file 'desktopcouch/application/plugins/tests/test_ubuntuone_pairing.py'
841--- desktopcouch/application/plugins/tests/test_ubuntuone_pairing.py 2011-02-02 18:15:53 +0000
842+++ desktopcouch/application/plugins/tests/test_ubuntuone_pairing.py 2011-09-13 15:01:24 +0000
843@@ -38,9 +38,11 @@
844 self.couchdb_port = self.mocker.mock()
845 self.put_static_paired_service = self.mocker.mock()
846 self.database_class = self.mocker.mock()
847+ self.blocking_semaphores = self.mocker.mock()
848
849 def test_pair_with_ubuntuone_no_view(self):
850 """Test that when the view is not present it is indeed created."""
851+ # plugin_init adds name to blocking semaphores, but we remove it.
852 self.couchdb.view_exists(U1_PAIR_RECORD, U1_PAIR_RECORD)
853 self.mocker.result(False)
854 # we are interested in the fact that the view is created
855@@ -50,8 +52,11 @@
856 self.couchdb.execute_view(U1_PAIR_RECORD, U1_PAIR_RECORD)
857 self.mocker.result([])
858 self.put_static_paired_service(None, 'ubuntuone', ctx=None, uri=ANY)
859+ self.blocking_semaphores.discard(ANY)
860+
861 self.mocker.replay()
862- pair_with_ubuntuone(self.couchdb_port, self.couchdb,
863+ pair_with_ubuntuone(self.couchdb_port, self.blocking_semaphores,
864+ management_db=self.couchdb,
865 db_class=self.database_class,
866 put_service_fn=self.put_static_paired_service)
867 self.mocker.verify()
868@@ -59,21 +64,27 @@
869 def test_pair_with_ubuntuone_no_record(self):
870 """Ensure pairing is not done when there are no records ."""
871 # execute the steps when no records are returned by the view
872+ # plugin_init adds name to blocking semaphores, but we remove it.
873 self.couchdb.view_exists(U1_PAIR_RECORD, U1_PAIR_RECORD)
874 self.mocker.result(True)
875 self.couchdb.execute_view(U1_PAIR_RECORD, U1_PAIR_RECORD)
876 self.mocker.result([])
877 self.put_static_paired_service(None, 'ubuntuone', ctx=None, uri=ANY)
878+ self.blocking_semaphores.discard(ANY)
879+
880 self.mocker.replay()
881- pair_with_ubuntuone(self.couchdb_port, self.couchdb,
882+ pair_with_ubuntuone(self.couchdb_port, self.blocking_semaphores,
883+ management_db=self.couchdb,
884 db_class=self.database_class,
885 put_service_fn=self.put_static_paired_service)
886+
887 self.mocker.verify()
888
889 def test_pair_with_ubuntuone_user_deleted_record(self):
890 """Ensure pairing is not done when the user explicitly removed it."""
891 # create a mock object that will be the result from the view
892 row = self.mocker.mock()
893+ # plugin_init adds name to blocking semaphores, but we remove it.
894 # execute the steps to show that the user deleted the record
895 self.couchdb.view_exists(U1_PAIR_RECORD, U1_PAIR_RECORD)
896 self.mocker.result(True)
897@@ -82,14 +93,18 @@
898 # FIXME does this do anything?
899 _ = row.value
900 self.mocker.result(1)
901+ self.blocking_semaphores.discard(ANY)
902+
903 self.mocker.replay()
904- pair_with_ubuntuone(self.couchdb_port, self.couchdb)
905+ pair_with_ubuntuone(self.couchdb_port, self.blocking_semaphores,
906+ management_db=self.couchdb)
907 self.mocker.verify()
908
909 def test_pair_with_ubuntuone_record_present(self):
910 """Ensure pairing is not done when the record is already present."""
911 # create a mock object that will be the result from the view
912 row = self.mocker.mock()
913+ # plugin_init adds name to blocking semaphores, but we remove it.
914 # execute the steps to show that the user deleted the record
915 self.couchdb.view_exists(U1_PAIR_RECORD, U1_PAIR_RECORD)
916 self.mocker.result(True)
917@@ -98,8 +113,11 @@
918 # FIXME does this do anything?
919 _ = row.value
920 self.mocker.result(0)
921+ self.blocking_semaphores.discard(ANY)
922+
923 self.mocker.replay()
924- pair_with_ubuntuone(self.couchdb_port, self.couchdb)
925+ pair_with_ubuntuone(self.couchdb_port, self.blocking_semaphores,
926+ management_db=self.couchdb)
927 self.mocker.verify()
928
929
930@@ -109,6 +127,7 @@
931 def setUp(self):
932 super(TestUbuntuOnePlugin, self).setUp()
933 self.couchdb_port = self.mocker.mock()
934+ self.blocking_semaphores = self.mocker.mock()
935 self.called = False
936
937 def test_got_new_credentials_other(self):
938@@ -117,21 +136,27 @@
939 """Fail if we get called."""
940 self.called = True
941
942+ saved_pair = uone.pair_with_ubuntuone
943 uone.pair_with_ubuntuone = fail_if_called
944- uone.got_new_credentials(self.couchdb_port, 'Unknown App', {})
945+ uone.got_new_credentials(self.couchdb_port, self.blocking_semaphores,
946+ 'Unknown App', {})
947 self.assertFalse(self.called, 'pair_with_ubuntuone was not expected.')
948 self.mocker.replay()
949+ uone.pair_with_ubuntuone = saved_pair
950
951 def test_got_new_credentials(self):
952 """Check that pairing is called for Ubuntu One."""
953- def pass_if_called(db=None):
954+ def pass_if_called(db=None, semaphores=None):
955 """Check that pair_with_ubuntuone was called."""
956 self.called = True
957
958+ saved_pair = uone.pair_with_ubuntuone
959 uone.pair_with_ubuntuone = pass_if_called
960- uone.got_new_credentials(self.couchdb_port, uone.APP_NAME, {})
961+ uone.got_new_credentials(self.couchdb_port, self.blocking_semaphores,
962+ uone.APP_NAME, {})
963 self.assertTrue(self.called, 'pair_with_ubuntuone was not called.')
964 self.mocker.replay()
965+ uone.pair_with_ubuntuone = saved_pair
966
967 def test_listen_to_dbus(self):
968 """Test that listening to credentails works."""
969@@ -144,6 +169,9 @@
970
971 iface = ubuntu_sso.DBUS_CREDENTIALS_IFACE
972 bus.add_signal_receiver(handler_function=ANY,
973+ signal_name='CredentialsNotFound',
974+ dbus_interface=iface)
975+ bus.add_signal_receiver(handler_function=ANY,
976 signal_name='CredentialsFound',
977 dbus_interface=iface)
978
979@@ -161,4 +189,4 @@
980
981 self.mocker.replay()
982
983- uone.listen_to_dbus(self.couchdb_port)
984+ uone.listen_to_dbus(self.couchdb_port, self.blocking_semaphores)
985
986=== modified file 'desktopcouch/application/plugins/ubuntuone_pairing.py'
987--- desktopcouch/application/plugins/ubuntuone_pairing.py 2011-02-02 18:15:53 +0000
988+++ desktopcouch/application/plugins/ubuntuone_pairing.py 2011-09-13 15:01:24 +0000
989@@ -24,6 +24,8 @@
990 from desktopcouch.application.server import DesktopDatabase
991 from ubuntuone.clientdefs import APP_NAME
992
993+TIMEOUT_SEC = 20
994+PLUGIN_NAME = __name__
995 U1_PAIR_RECORD = "ubuntu_one_pair_record"
996 MAP_JS = """function(doc) {
997 if (doc.service_name == "ubuntuone") {
998@@ -32,49 +34,57 @@
999 }
1000 """
1001
1002-
1003-def pair_with_ubuntuone(couchdb_port, management_db=None,
1004+def pair_with_ubuntuone(couchdb_port, blocking_semaphores,
1005+ management_db=None,
1006 db_class=DesktopDatabase,
1007 put_service_fn=put_static_paired_service):
1008 """Adds a pairing record with ubuntu one when needed."""
1009- # Use explicit uri so that we do not access dbus service.
1010- uri = "http://localhost:%s" % (couchdb_port,)
1011- if not management_db:
1012- management_db = db_class("management", uri=uri, create=True, ctx=None)
1013- # we indeed have credentials to add to the pairing records
1014- # but first we ensure that the required view is present
1015- if not management_db.view_exists(U1_PAIR_RECORD, U1_PAIR_RECORD):
1016- management_db.add_view(
1017- U1_PAIR_RECORD, MAP_JS, design_doc=U1_PAIR_RECORD)
1018- view_results = management_db.execute_view(U1_PAIR_RECORD, U1_PAIR_RECORD)
1019- pairing_found = False
1020- # Results should contain either one row or no rows
1021- # If there is one row, its value will be 0, meaning that there is
1022- # already an Ubuntu One pairing record, or 1, meaning that there
1023- # was an Ubuntu One pairing record but it has since been unpaired
1024- # Only create a new record if there is not one already. Specifically,
1025- # do not add the record if there is a deleted one, as this means
1026- # that the user explicitly unpaired it!
1027- for row in view_results:
1028- pairing_found = True
1029- if row.value == 1:
1030- logging.debug("Not adding desktopcouch pairing since the user "
1031- "has explicitly unpaired with Ubuntu One")
1032- else:
1033- logging.debug("Not adding desktopcouch pairing since we are "
1034- "already paired")
1035- if not pairing_found:
1036- put_service_fn(None, "ubuntuone", uri=uri, ctx=None)
1037- logging.debug("Pairing desktopcouch with Ubuntu One")
1038-
1039-
1040-def got_new_credentials(couchdb_port, app_name, credentials):
1041+ try:
1042+ # Use explicit uri so that we do not access dbus service.
1043+ uri = "http://localhost:%s" % (couchdb_port,)
1044+ if not management_db:
1045+ management_db = db_class("management", uri=uri, create=True,
1046+ ctx=None)
1047+ # we indeed have credentials to add to the pairing records
1048+ # but first we ensure that the required view is present
1049+ if not management_db.view_exists(U1_PAIR_RECORD, U1_PAIR_RECORD):
1050+ management_db.add_view(
1051+ U1_PAIR_RECORD, MAP_JS, design_doc=U1_PAIR_RECORD)
1052+ view_results = management_db.execute_view(U1_PAIR_RECORD,
1053+ U1_PAIR_RECORD)
1054+ pairing_found = False
1055+ # Results should contain either one row or no rows
1056+ # If there is one row, its value will be 0, meaning that there is
1057+ # already an Ubuntu One pairing record, or 1, meaning that there
1058+ # was an Ubuntu One pairing record but it has since been unpaired
1059+ # Only create a new record if there is not one already. Specifically,
1060+ # do not add the record if there is a deleted one, as this means
1061+ # that the user explicitly unpaired it!
1062+ for row in view_results:
1063+ pairing_found = True
1064+ if row.value == 1:
1065+ logging.debug("Not adding desktopcouch pairing since the user "
1066+ "has explicitly unpaired with Ubuntu One")
1067+ else:
1068+ logging.debug("Not adding desktopcouch pairing since we are "
1069+ "already paired")
1070+ if not pairing_found:
1071+ put_service_fn(None, "ubuntuone", uri=uri, ctx=None)
1072+ logging.debug("Pairing desktopcouch with Ubuntu One")
1073+
1074+ finally:
1075+ logging.info("removing semaphore for %s", PLUGIN_NAME)
1076+ blocking_semaphores.discard(PLUGIN_NAME)
1077+
1078+
1079+def got_new_credentials(couchdb_port, blocking_semaphores,
1080+ app_name, credentials):
1081 """Pair with Ubuntu One when we get the new credentials."""
1082 if app_name == APP_NAME:
1083- pair_with_ubuntuone(couchdb_port)
1084-
1085-
1086-def listen_to_dbus(couchdb_port):
1087+ pair_with_ubuntuone(couchdb_port, blocking_semaphores)
1088+
1089+
1090+def listen_to_dbus(couchdb_port, blocking_semaphores):
1091 """Set up the signal handler on D-Bus for Ubuntu One pairing."""
1092 import dbus
1093 bus = dbus.SessionBus()
1094@@ -82,9 +92,20 @@
1095 try:
1096 import ubuntu_sso
1097
1098- receiver = lambda *args: got_new_credentials(couchdb_port, *args)
1099+ receiver = lambda *args: \
1100+ got_new_credentials(couchdb_port, blocking_semaphores,
1101+ *args)
1102+ def abort_getting_credentials(*args):
1103+ """Log and remove our semaphore."""
1104+ logging.warn("Credentials not found for ubuntuone.")
1105+ blocking_semaphores.discard(PLUGIN_NAME)
1106
1107 iface = ubuntu_sso.DBUS_CREDENTIALS_IFACE
1108+
1109+ bus.add_signal_receiver(handler_function=abort_getting_credentials,
1110+ signal_name='CredentialsNotFound',
1111+ dbus_interface=iface)
1112+
1113 bus.add_signal_receiver(handler_function=receiver,
1114 signal_name='CredentialsFound',
1115 dbus_interface=iface)
1116@@ -96,13 +117,23 @@
1117 sso_backend.find_credentials(APP_NAME, {})
1118 except ImportError:
1119 logging.info('Ubuntu SSO is not available.')
1120-
1121-
1122-def plugin_init(couchdb_port):
1123+ blocking_semaphores.discard(PLUGIN_NAME)
1124+
1125+
1126+def plugin_init(couchdb_port, blocking_semaphores, gobject):
1127 """Set up the signal handler for pairing with Ubuntu One."""
1128 logging.info('Loaded Ubuntu One extension for desktopcouch.')
1129 if sys.platform == 'win32':
1130 logging.warning('Windows support for Ubuntu One is not yet ready.')
1131 else:
1132- import gobject
1133- gobject.idle_add(listen_to_dbus, couchdb_port)
1134+
1135+ # Signal that we are critical for desktopcouch usage, and the server
1136+ # must not begin until we are finished. We are responsible for
1137+ # removing this item from the list.
1138+ logging.info("adding %s to to blocking semaphore list", PLUGIN_NAME)
1139+ blocking_semaphores.add(PLUGIN_NAME)
1140+
1141+ gobject.idle_add(listen_to_dbus, couchdb_port, blocking_semaphores)
1142+
1143+ gobject.timeout_add_seconds(TIMEOUT_SEC, blocking_semaphores.discard,
1144+ PLUGIN_NAME)
1145
1146=== modified file 'desktopcouch/application/replication.py'
1147--- desktopcouch/application/replication.py 2011-01-12 15:08:25 +0000
1148+++ desktopcouch/application/replication.py 2011-09-13 15:01:24 +0000
1149@@ -246,7 +246,7 @@
1150 dbus_io.maintain_discovered_servers()
1151
1152 task_running = task.LoopingCall(do_all_replication, int(port))
1153- task_running.start(600)
1154+ task_running.start(3600)
1155
1156 # TODO: port may change, so every so often, check it and
1157 # perhaps refresh the beacons. We return an array of beacons, so we could
1158
1159=== modified file 'desktopcouch/application/service.py'
1160--- desktopcouch/application/service.py 2011-02-02 18:15:53 +0000
1161+++ desktopcouch/application/service.py 2011-09-13 15:01:24 +0000
1162@@ -39,6 +39,7 @@
1163 import logging
1164 import logging.handlers
1165 import signal
1166+import gobject
1167
1168 from desktopcouch.application import local_files
1169 from desktopcouch.application import replication
1170@@ -84,7 +85,8 @@
1171 replication_actions=replication,
1172 advertiser_factory=PortAdvertiser, set_logging=set_up_logging,
1173 fork=os.fork, nice=os.nice,
1174- kill=os.kill, sleep=time.sleep):
1175+ kill=os.kill, sleep=time.sleep, set_type=set,
1176+ gobject_module=gobject):
1177 self._mainloop = main_loop
1178 self._pid_finder = pid_finder
1179 self._port_finder = port_finder
1180@@ -97,6 +99,8 @@
1181 self._nice = nice
1182 self._kill = kill
1183 self._sleep = sleep
1184+ self._set = set_type
1185+ self._gobject = gobject_module
1186 # pylint: enable=C0301
1187
1188 def _start_replicator_main(self, couchdb_port):
1189@@ -112,25 +116,46 @@
1190 replication.tear_down(*replication_runtime)
1191
1192 def _start_server_main(self, couchdb_port):
1193- """Start server."""
1194- self._advertiser_factory(self._mainloop.stop, self._ctx)
1195+ """Start server answering DBus calls, and run plugins first."""
1196+
1197+ def if_all_semaphores_cleared(blocking_semaphores,
1198+ func, *args, **kwargs):
1199+ """Run a function if no semaphores exist, else try later."""
1200+ if blocking_semaphores:
1201+ self._sleep(0.2) # Never peg the CPU
1202+ return True # Make idle call try us again.
1203+ else:
1204+ func(*args, **kwargs)
1205+ return False # Handled!
1206+
1207+ blocking_semaphores = self._set()
1208+ load_plugins(couchdb_port, blocking_semaphores, self._gobject)
1209+
1210+ # Answering queries on DBus signals that we are ready for users
1211+ # to connect. We mustn't begin that until every plugin has a chance
1212+ # to run to completion if it needs it.
1213+ self._gobject.idle_add(if_all_semaphores_cleared, blocking_semaphores,
1214+ self._advertiser_factory,
1215+ self._mainloop.stop,
1216+ self._ctx)
1217+
1218 logging.debug("starting dbus main loop")
1219 try:
1220- load_plugins(couchdb_port)
1221 self._mainloop.run()
1222 finally:
1223 logging.debug("ending dbus main loop")
1224
1225 def start(self):
1226 """Start the services used by desktopcouch."""
1227- should_shut_down_couchdb = False
1228+ maintained_child_pids = list()
1229 couchdb_pid = self._pid_finder(start_if_not_running=False,
1230 ctx=self._ctx)
1231 if couchdb_pid is None:
1232 logging.warn("Starting up personal couchdb.")
1233 couchdb_pid = self._pid_finder(start_if_not_running=True,
1234 ctx=self._ctx)
1235- should_shut_down_couchdb = True
1236+ if couchdb_pid:
1237+ maintained_child_pids.append(couchdb_pid)
1238 else:
1239 logging.warn("Personal couchdb is already running at PID#%d.",
1240 couchdb_pid)
1241@@ -145,6 +170,7 @@
1242 return
1243 else:
1244 assert child_pid > 0
1245+ maintained_child_pids.append(child_pid)
1246 child_pid = self._fork() # Split!
1247 if child_pid == 0:
1248 # Let's be the migration tool!
1249@@ -161,8 +187,18 @@
1250 return
1251 else:
1252 assert child_pid > 0
1253+ maintained_child_pids.append(child_pid)
1254 # Let's be the dbus server!
1255 # This is the parent process. When we exit, we kill children.
1256+
1257+ def receive_signal(signum, stackframe):
1258+ """On signal, quit main loop gracefully."""
1259+ logging.warn("Received signal %d. Quitting.", signum)
1260+ self._mainloop.stop()
1261+
1262+ signal.signal(signal.SIGHUP, receive_signal)
1263+ signal.signal(signal.SIGTERM, receive_signal)
1264+
1265 try:
1266 set_up_logging("dbus")
1267 # offer the dbus service
1268@@ -172,14 +208,18 @@
1269 "uncaught exception makes us shut down.")
1270 finally:
1271 logging.info("exiting.")
1272- if should_shut_down_couchdb:
1273- logging.warn("shutting down personal couchdb.")
1274- self._stop_couchdb(ctx=self._ctx)
1275+ self._stop_couchdb(ctx=self._ctx)
1276
1277+ for child_pid in maintained_child_pids:
1278 try:
1279 self._kill(child_pid, signal.SIGTERM)
1280- self._sleep(1)
1281+ logging.warn("Sent SIGTERM to %d", child_pid)
1282+ except OSError:
1283+ pass
1284+ self._sleep(1)
1285+ for child_pid in maintained_child_pids:
1286+ try:
1287 self._kill(child_pid, signal.SIGKILL)
1288+ logging.warn("Sent SIGKILL to %d", child_pid)
1289 except OSError:
1290 pass
1291- return # pylint: disable=W0150
1292
1293=== modified file 'desktopcouch/application/start_local_couchdb.py'
1294--- desktopcouch/application/start_local_couchdb.py 2011-04-08 20:40:53 +0000
1295+++ desktopcouch/application/start_local_couchdb.py 2011-09-13 15:01:24 +0000
1296@@ -166,7 +166,7 @@
1297 break
1298 except RuntimeError, ex:
1299 saved_exception = ex
1300- logging.exception("Starting couchdb failed on try %d", (retry,))
1301+ logging.exception("Starting couchdb failed on try %d", retry)
1302 time.sleep(1)
1303 continue
1304
1305
1306=== modified file 'desktopcouch/application/tests/test_service.py'
1307--- desktopcouch/application/tests/test_service.py 2011-02-02 18:15:53 +0000
1308+++ desktopcouch/application/tests/test_service.py 2011-09-13 15:01:24 +0000
1309@@ -37,6 +37,8 @@
1310 self._replication = self.mocker.mock()
1311 self._advertiser = self.mocker.mock()
1312 self._resources = self.mocker.mock()
1313+ self._set = self.mocker.mock()
1314+ self._gobject = self.mocker.mock()
1315 self._service = DesktopcouchService(self._mainloop,
1316 pid_finder=self._pid_finder,
1317 port_finder=self._port_finder,
1318@@ -48,65 +50,97 @@
1319 fork=self._fork,
1320 nice=self._nice,
1321 kill=self._kill,
1322- sleep=self._sleep)
1323-
1324- def test_start_new_desktopcouch_no_extensions(self):
1325- """Test that desktopcouch is started.
1326-
1327- We test that when the pid cannot be found we ensure
1328- that the desktopcouch instance is started and that the
1329- start as the dbus service,
1330- """
1331- self._pid_finder(start_if_not_running=False, ctx=self._ctx)
1332- self.mocker.result(None)
1333- self._pid_finder(start_if_not_running=True, ctx=self._ctx)
1334- self.mocker.result(self._pid_result)
1335- self._port_finder(pid=self._pid_result, ctx=self._ctx)
1336- self.mocker.result(self._port_result)
1337- self._fork()
1338- self.mocker.result(234)
1339- self._fork()
1340- self.mocker.result(234)
1341- # XXX: call this?
1342- self._mainloop.stop # pylint: disable=W0104
1343- self.mocker.result(ANY)
1344- self._advertiser(ANY, self._ctx)
1345- self._mainloop.run()
1346- self._stop_couchdb(ctx=self._ctx)
1347- self._kill(234, signal.SIGTERM)
1348- self._sleep(1)
1349- self._kill(234, signal.SIGKILL)
1350- self.mocker.replay()
1351- self._service.start()
1352-
1353- def test_start_new_desktopcouch_extensions(self):
1354- """Test that desktopcouch is started.
1355-
1356- We test that when the pid cannot be found we ensure
1357- that the desktopcouch instance is started and that the
1358- start as the dbus service,
1359- """
1360- self._pid_finder(start_if_not_running=False, ctx=self._ctx)
1361- self.mocker.result(None)
1362- self._pid_finder(start_if_not_running=True, ctx=self._ctx)
1363- self.mocker.result(self._pid_result)
1364- self._port_finder(pid=self._pid_result, ctx=self._ctx)
1365- self.mocker.result(self._port_result)
1366- self._fork()
1367- self.mocker.result(234)
1368- self._fork()
1369- self.mocker.result(234)
1370- # XXX: call this?
1371- self._mainloop.stop # pylint: disable=W0104
1372- self.mocker.result(ANY)
1373- self._advertiser(ANY, self._ctx)
1374- self._mainloop.run()
1375- self._stop_couchdb(ctx=self._ctx)
1376- self._kill(234, signal.SIGTERM)
1377- self._sleep(1)
1378- self._kill(234, signal.SIGKILL)
1379- self.mocker.replay()
1380- self._service.start()
1381+ sleep=self._sleep,
1382+ set_type=self._set,
1383+ gobject_module=self._gobject)
1384+
1385+ self.gobject_idle_task_list = list()
1386+
1387+ def test_start_new_desktopcouch_with_plugins(self):
1388+ """Test that desktopcouch is started.
1389+
1390+ We test that when the pid cannot be found we ensure
1391+ that the desktopcouch instance is started and that the
1392+ start as the dbus service,
1393+ """
1394+ self._pid_finder(start_if_not_running=False, ctx=self._ctx)
1395+ self.mocker.result(None)
1396+ self._pid_finder(start_if_not_running=True, ctx=self._ctx)
1397+ self.mocker.result(self._pid_result)
1398+ self._port_finder(pid=self._pid_result, ctx=self._ctx)
1399+ self.mocker.result(self._port_result)
1400+ self._fork()
1401+ self.mocker.result(234) # We are parent
1402+ self._fork()
1403+ self.mocker.result(345) # We are parent
1404+
1405+ # plugins load
1406+ semaphores = self.mocker.mock()
1407+ self._set()
1408+ self.mocker.result(semaphores)
1409+
1410+ self._mainloop.stop # pylint: disable=W0104
1411+ self.mocker.result(ANY)
1412+
1413+ semaphores.discard # pylint: disable=W0104
1414+ self.mocker.result("disc")
1415+ self._gobject.timeout_add_seconds(ANY, "disc", ANY)
1416+
1417+ self._gobject.idle_add(ANY, self._port_result, semaphores)
1418+ self._gobject.idle_add(ANY, semaphores, self._advertiser, ANY,
1419+ self._ctx)
1420+
1421+ self._mainloop.run()
1422+
1423+ # Tasks are added to the mainloop's idle queue. With that^
1424+ # mainloop.run, they'd be called. Simulate their call.
1425+
1426+ # Idle loop to add plugins calls a plugin. ubuntuone_pairing
1427+ semaphores.add(ANY) # A plugin asserts it must be completed.
1428+
1429+ # Idle loop calls plugin for ubuntuone_pairing, which fires up DBus
1430+ # client to get credentials from ubuntu-login app. When that returns,
1431+ # it calls got_new_credentials, which calls pair_with_ubuntuone.
1432+
1433+ # Mock call to pair_with_ubuntuone
1434+ management_db = self.mocker.mock()
1435+ put_service_fn = self.mocker.mock()
1436+
1437+ bool(management_db)
1438+ self.mocker.result(True)
1439+
1440+ management_db.view_exists(ANY, ANY)
1441+ self.mocker.result(False)
1442+ management_db.add_view(ANY, ANY, design_doc=ANY)
1443+
1444+ row = self.mocker.mock()
1445+ management_db.execute_view(ANY, ANY)
1446+ self.mocker.result([row])
1447+
1448+ row.value # pylint: disable=W0104
1449+ self.mocker.result(1)
1450+
1451+ semaphores.discard(ANY)
1452+
1453+ # and then dbus service is ready to answer queries.
1454+
1455+ self._stop_couchdb(ctx=self._ctx)
1456+ self._kill(self._pid_result, signal.SIGTERM)
1457+ self._kill(234, signal.SIGTERM)
1458+ self._kill(345, signal.SIGTERM)
1459+ self._sleep(1)
1460+ self._kill(self._pid_result, signal.SIGKILL)
1461+ self._kill(234, signal.SIGKILL)
1462+ self._kill(345, signal.SIGKILL)
1463+
1464+ self.mocker.replay()
1465+
1466+ self._service.start()
1467+ # Manually do what dbus idle-loop would do.
1468+ import desktopcouch.application.plugins.ubuntuone_pairing as u_p
1469+ u_p.pair_with_ubuntuone(self._port_result, semaphores,
1470+ management_db=management_db,
1471+ put_service_fn=put_service_fn)
1472
1473 def test_start_desktopcouch_replication(self):
1474 """ Test that the repliciation works.
1475@@ -119,7 +153,7 @@
1476 self._port_finder(pid=self._pid_result, ctx=self._ctx)
1477 self.mocker.result(self._port_result)
1478 self._fork()
1479- self.mocker.result(0)
1480+ self.mocker.result(0) # We are child process
1481 self._nice(10)
1482 self._replication.set_up(ANY)
1483 self._mainloop.run()
1484@@ -146,9 +180,9 @@
1485 self._port_finder(pid=self._pid_result, ctx=self._ctx)
1486 self.mocker.result(self._port_result)
1487 self._fork()
1488- self.mocker.result(567)
1489+ self.mocker.result(567) # We are parent process
1490 self._fork()
1491- self.mocker.result(0)
1492+ self.mocker.result(0) # We are child process
1493 self._sleep(ANY)
1494 self._ctx.db_dir # searching for files # pylint: disable=W0104
1495 self.mocker.result("/tmp/migration-data/does/not/exist")
1496
1497=== modified file 'desktopcouch/records/tests/test_mocked_server.py'
1498--- desktopcouch/records/tests/test_mocked_server.py 2011-04-08 20:40:53 +0000
1499+++ desktopcouch/records/tests/test_mocked_server.py 2011-09-13 15:01:24 +0000
1500@@ -61,6 +61,7 @@
1501
1502
1503 class TestMockedCouchDatabaseCreateStates(MockerTestCase):
1504+ """Mocked Couch Database tests for create= flag states."""
1505 def setUp(self):
1506 """Set up tests."""
1507 super(TestMockedCouchDatabaseCreateStates, self).setUp()
1508@@ -85,7 +86,7 @@
1509 self.mocker.result({'update_seq': []})
1510
1511 self.mocker.replay()
1512- database = DesktopDatabase(self.dbname, uri=self.uri,
1513+ DesktopDatabase(self.dbname, uri=self.uri,
1514 record_factory=self.record_factory, create=True,
1515 server_class=self.server_class,
1516 oauth_tokens=self.oauth_tokens, ctx=self.ctx)
1517@@ -105,7 +106,7 @@
1518 info["update_seq"]
1519 self.mocker.result({})
1520 self.mocker.replay()
1521- database = DesktopDatabase(self.dbname, uri=self.uri,
1522+ DesktopDatabase(self.dbname, uri=self.uri,
1523 record_factory=self.record_factory, create=True,
1524 server_class=self.server_class,
1525 oauth_tokens=self.oauth_tokens, ctx=self.ctx)
1526@@ -140,7 +141,7 @@
1527 self.mocker.result({})
1528
1529 self.mocker.replay()
1530- database = DesktopDatabase(self.dbname, uri=self.uri,
1531+ DesktopDatabase(self.dbname, uri=self.uri,
1532 record_factory=self.record_factory, create=False,
1533 server_class=self.server_class,
1534 oauth_tokens=self.oauth_tokens, ctx=self.ctx)
1535
1536=== modified file 'desktopcouch/recordtypes/contacts/tests/test_view.py'
1537--- desktopcouch/recordtypes/contacts/tests/test_view.py 2011-01-12 15:08:25 +0000
1538+++ desktopcouch/recordtypes/contacts/tests/test_view.py 2011-09-13 15:01:24 +0000
1539@@ -280,29 +280,29 @@
1540
1541 contacts = list(
1542 view.find_contacts_starting(self.db, first_name="Frances"))
1543- self.assertEqual(len(contacts), 1)
1544+ self.assertEqual(len(contacts), 1, "Length of 1. %s" % (contacts,))
1545
1546 contacts = list(
1547 view.find_contacts_starting(self.db, birth_date="1918"))
1548- self.assertEqual(len(contacts), 1)
1549+ self.assertEqual(len(contacts), 1, "Length of 1. %s" % (contacts,))
1550
1551 contacts = list(
1552 view.find_contacts_starting(self.db, birth_date="1918-08"))
1553- self.assertEqual(len(contacts), 1)
1554+ self.assertEqual(len(contacts), 1, "Length of 1. %s" % (contacts,))
1555
1556 contacts = list(
1557 view.find_contacts_starting(self.db, wedding_date="1970"))
1558- self.assertEqual(len(contacts), 1)
1559+ self.assertEqual(len(contacts), 1, "Length of 1. %s" % (contacts,))
1560
1561 contacts = list(
1562 view.find_contacts_starting(
1563 self.db, email_addressesaddress="blah.example.com"))
1564- self.assertEqual(len(contacts), 1)
1565+ self.assertEqual(len(contacts), 1, "Length of 1. %s" % (contacts,))
1566
1567 contacts = list(
1568 view.find_contacts_starting(
1569 self.db, email_addressesaddress="berkeley"))
1570- self.assertEqual(len(contacts), 1)
1571+ self.assertEqual(len(contacts), 1, "Length of 1. %s" % (contacts,))
1572
1573 contacts = list(
1574 view.find_contacts_starting(self.db, first_name="random"))
1575@@ -317,7 +317,7 @@
1576
1577 contacts = list(
1578 view.find_contacts_exact(self.db, first_name="Frances"))
1579- self.assertEqual(len(contacts), 1)
1580+ self.assertEqual(len(contacts), 1, "Length of 1. %s" % (contacts,))
1581
1582 contacts = list(view.find_contacts_exact(self.db, birth_date="-08-23"))
1583- self.assertEqual(len(contacts), 1)
1584+ self.assertEqual(len(contacts), 1, "Length of 1. %s" % (contacts,))
1585
1586=== modified file 'setup.cfg'
1587--- setup.cfg 2011-01-12 15:08:25 +0000
1588+++ setup.cfg 2011-09-13 15:01:24 +0000
1589@@ -4,10 +4,10 @@
1590 tag_svn_revision = 0
1591
1592 [build]
1593-i18n = True
1594-icons = True
1595+i18n=True
1596+icons=True
1597
1598 [build_i18n]
1599-domain = desktopcouch
1600-desktop_files = [("share/applications", ("desktopcouch-pair.desktop.in",))]
1601+domain=desktopcouch
1602+desktop_files=[("share/applications", ("desktopcouch-pair.desktop.in",))]
1603
1604
1605=== modified file 'setup.py'
1606--- setup.py 2011-04-08 20:42:51 +0000
1607+++ setup.py 2011-09-13 15:01:24 +0000
1608@@ -1,6 +1,6 @@
1609 #!/usr/bin/env python
1610 #
1611-# Copyright 2009-2010 Canonical Ltd.
1612+# Copyright 2009-2011 Canonical Ltd.
1613 #
1614 # This file is part of desktopcouch.
1615 #
1616@@ -17,12 +17,13 @@
1617 # along with desktopcouch. If not, see <http://www.gnu.org/licenses/>.
1618 """setup.py"""
1619
1620-from setuptools import setup, find_packages
1621+from setuptools import find_packages
1622 from DistUtilsExtra.command import build_extra, build_i18n
1623+from DistUtilsExtra.auto import setup
1624
1625 setup(
1626 name='desktopcouch',
1627- version='1.0.7',
1628+ version='1.0.8',
1629 description='A Desktop CouchDB instance.',
1630 url='https://launchpad.net/desktopcouch',
1631 license='LGPL-3',
1632@@ -30,19 +31,23 @@
1633 author_email='stuart.langridge@canonical.com',
1634 packages=find_packages(),
1635 scripts=['bin/desktopcouch-pair'],
1636- data_files=[('/usr/lib/desktopcouch/', ['bin/desktopcouch-service',
1637+ data_files=[('lib/desktopcouch/', ['bin/desktopcouch-service',
1638 'bin/desktopcouch-pair',
1639 'bin/desktopcouch-get-port',
1640 'bin/desktopcouch-stop']),
1641 # Be sure all additions are reflected in MANIFEST.in !
1642- ('/usr/share/doc/python-desktopcouch-records/api/',
1643+ ('share/doc/python-desktopcouch-records/api/',
1644 ['desktopcouch/records/doc/records.txt',
1645+ 'desktopcouch/records/doc/an_example_application.txt',
1646 'desktopcouch/records/doc/field_registry.txt',
1647 'desktopcouch/recordtypes/contacts/schema.txt']),
1648 ('/etc/xdg/desktop-couch/',
1649- ['config/desktop-couch/compulsory-auth.ini']),
1650- ('/usr/share/desktopcouch/', ['data/couchdb.tmpl']),
1651- ('/usr/share/dbus-1/services/', [
1652+ ['config/desktop-couch/compulsory-auth.ini',
1653+ 'config/desktop-couch/default.ini']),
1654+ ('share/desktopcouch/', ['data/couchdb.tmpl']),
1655+ ('share/apport/package-hooks/', [
1656+ 'data/source_desktopcouch.py']),
1657+ ('share/dbus-1/services/', [
1658 'org.desktopcouch.CouchDB.service']),
1659 ('share/man/man1/', ['docs/man/desktopcouch-pair.1'])],
1660 cmdclass={"build": build_extra.build_extra,

Subscribers

People subscribed via source and target branches

to all changes: