Merge lp:~cmiller/desktopcouch/preconditionfailed-lp707321 into lp:desktopcouch

Proposed by Chad Miller
Status: Merged
Approved by: Chad Miller
Approved revision: 269
Merged at revision: 268
Proposed branch: lp:~cmiller/desktopcouch/preconditionfailed-lp707321
Merge into: lp:desktopcouch
Diff against target: 170 lines (+104/-15)
2 files modified
desktopcouch/records/database.py (+14/-10)
desktopcouch/records/tests/test_mocked_server.py (+90/-5)
To merge this branch: bzr merge lp:~cmiller/desktopcouch/preconditionfailed-lp707321
Reviewer Review Type Date Requested Status
Roberto Alsina (community) Approve
Eric Casteleijn (community) Approve
Review via email: mp+56246@code.launchpad.net

Commit message

Change creation of the Database object to EAFP. LBYL is impossible with HTTP and no locking. If create-flag is set, then go do it and swallow an exception about it already existing. (LP: #707321)

In addition, simplify that code so it makes fewer round-trips to the server.

To post a comment you must log in.
Revision history for this message
Eric Casteleijn (thisfred) wrote :

Looks good, let's land it and run it past one of the bug reporters to see if it fixes it for them.

review: Approve
269. By Chad Miller

Attach bug.

Revision history for this message
Roberto Alsina (ralsina) wrote :

Looks good to me :-)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'desktopcouch/records/database.py'
2--- desktopcouch/records/database.py 2011-01-27 20:00:45 +0000
3+++ desktopcouch/records/database.py 2011-04-04 19:13:27 +0000
4@@ -43,7 +43,7 @@
5 # pylint: enable=F0401
6
7 from couchdb import Server
8-from couchdb.http import ResourceNotFound, ResourceConflict
9+from couchdb.http import ResourceNotFound, ResourceConflict, PreconditionFailed
10 from couchdb.design import ViewDefinition
11
12 from desktopcouch.records import Record
13@@ -134,17 +134,21 @@
14 """Reconnect after losing connection."""
15 self._server = self._server_class(uri or self.server_uri,
16 **self._server_class_extras)
17- if self._database_name not in self._server:
18- if self._create:
19+ if self._create:
20+ try:
21 self._server.create(self._database_name)
22+ except PreconditionFailed:
23+ pass # Not an error to want it created and DB exists.
24+
25+ try:
26+ if self.db is None:
27+ self.db = self._server[self._database_name]
28 else:
29- raise NoSuchDatabase(self._database_name)
30- if self.db is None:
31- self.db = self._server[self._database_name]
32- else:
33- # Monkey-patch the object the user already uses. Oook!
34- new_db = self._server[self._database_name]
35- self.db.resource = new_db.resource
36+ # Monkey-patch the object the user already uses. Oook!
37+ new_db = self._server[self._database_name]
38+ self.db.resource = new_db.resource
39+ except ResourceNotFound:
40+ raise NoSuchDatabase(self._database_name)
41
42 def _temporary_query(self, map_fun, reduce_fun=None, language='javascript',
43 wrapper=None, **options):
44
45=== modified file 'desktopcouch/records/tests/test_mocked_server.py'
46--- desktopcouch/records/tests/test_mocked_server.py 2011-01-25 17:58:49 +0000
47+++ desktopcouch/records/tests/test_mocked_server.py 2011-04-04 19:13:27 +0000
48@@ -17,7 +17,7 @@
49 # Authors: Manuel de la Pena<manuel@canonical.com>
50 """Test the database code mocking the db access."""
51
52-from couchdb.http import ResourceNotFound
53+from couchdb.http import ResourceNotFound, PreconditionFailed
54 from mocker import MockerTestCase, ANY, KWARGS
55
56 from desktopcouch.application.server import DesktopDatabase
57@@ -31,8 +31,7 @@
58 testcase.server_class(testcase.uri, ctx=testcase.ctx,
59 oauth_tokens=testcase.oauth_tokens)
60 testcase.mocker.result(testcase.server_class)
61- testcase.dbname in testcase.server_class
62- testcase.mocker.result(True)
63+ testcase.server_class.create(testcase.dbname)
64 testcase.server_class[testcase.dbname]
65 testcase.mocker.result(testcase.server_class)
66 testcase.server_class.info()
67@@ -61,6 +60,92 @@
68 views_factory=testcase.views_factory)
69
70
71+class TestMockedCouchDatabaseCreateStates(MockerTestCase):
72+ def setUp(self):
73+ """Set up tests."""
74+ super(TestMockedCouchDatabaseCreateStates, self).setUp()
75+ self.record_factory = self.mocker.mock()
76+ self.server_class = self.mocker.mock()
77+ self.oauth_tokens = self.mocker.mock()
78+ self.ctx = self.mocker.mock()
79+ self.uri = 'uri'
80+ self.dbname = self._testMethodName
81+
82+ def test_create_already_exists(self):
83+ """Access DB with create=True, when it already exists."""
84+ self.server_class(self.uri, ctx=self.ctx,
85+ oauth_tokens=self.oauth_tokens)
86+ self.mocker.result(self.server_class)
87+ self.server_class.create(self.dbname)
88+ self.mocker.throw(PreconditionFailed)
89+
90+ self.server_class[self.dbname]
91+ self.mocker.result(self.server_class)
92+ self.server_class.info()
93+ self.mocker.result({'update_seq': []})
94+
95+ self.mocker.replay()
96+ database = DesktopDatabase(self.dbname, uri=self.uri,
97+ record_factory=self.record_factory, create=True,
98+ server_class=self.server_class,
99+ oauth_tokens=self.oauth_tokens, ctx=self.ctx)
100+
101+ def test_create_new(self):
102+ """Access DB with create=True, when it doesn't exist."""
103+ self.server_class(self.uri, ctx=self.ctx,
104+ oauth_tokens=self.oauth_tokens)
105+ self.mocker.result(self.server_class)
106+ self.server_class.create(self.dbname)
107+ self.mocker.result(self.server_class)
108+ self.server_class[self.dbname]
109+ self.mocker.result(self.server_class)
110+ info = self.mocker.mock()
111+ self.server_class.info()
112+ self.mocker.result(info)
113+ info["update_seq"]
114+ self.mocker.result({})
115+ self.mocker.replay()
116+ database = DesktopDatabase(self.dbname, uri=self.uri,
117+ record_factory=self.record_factory, create=True,
118+ server_class=self.server_class,
119+ oauth_tokens=self.oauth_tokens, ctx=self.ctx)
120+
121+ def test_no_create_not_exists(self):
122+ """Access DB with create=False, when it already exists."""
123+ self.server_class(self.uri, ctx=self.ctx,
124+ oauth_tokens=self.oauth_tokens)
125+ self.mocker.result(self.server_class)
126+
127+ self.server_class[self.dbname]
128+ self.mocker.throw(ResourceNotFound)
129+
130+ self.mocker.replay()
131+ self.assertRaises(NoSuchDatabase,
132+ DesktopDatabase, self.dbname, uri=self.uri,
133+ record_factory=self.record_factory, create=False,
134+ server_class=self.server_class,
135+ oauth_tokens=self.oauth_tokens, ctx=self.ctx)
136+
137+ def test_no_create_already_exists(self):
138+ """Access DB with create=False, when it doesn't exist."""
139+ self.server_class(self.uri, ctx=self.ctx,
140+ oauth_tokens=self.oauth_tokens)
141+ self.mocker.result(self.server_class)
142+ self.server_class[self.dbname]
143+ self.mocker.result(self.server_class)
144+ info = self.mocker.mock()
145+ self.server_class.info()
146+ self.mocker.result(info)
147+ info["update_seq"]
148+ self.mocker.result({})
149+
150+ self.mocker.replay()
151+ database = DesktopDatabase(self.dbname, uri=self.uri,
152+ record_factory=self.record_factory, create=False,
153+ server_class=self.server_class,
154+ oauth_tokens=self.oauth_tokens, ctx=self.ctx)
155+
156+
157 class TestMockedCouchDatabaseDeprecated(MockerTestCase):
158 """Test the deprecated API."""
159
160@@ -199,8 +284,8 @@
161 self.server_class(self.uri, ctx=self.ctx,
162 oauth_tokens=self.oauth_tokens)
163 self.mocker.result(self.server_class)
164- self.dbname in self.server_class
165- self.mocker.result(False)
166+ self.server_class[self.dbname]
167+ self.mocker.throw(ResourceNotFound)
168 self.mocker.replay()
169 self.assertRaises(NoSuchDatabase, DesktopDatabase, self.dbname,
170 uri=self.uri, record_factory=self.record_factory, create=False,

Subscribers

People subscribed via source and target branches