Skip to content

Commit 907f7a8

Browse files
committed
Merge changes from default to the 0.6.x branch.
--HG-- branch : 0.6.x
2 parents e57a145 + 7f44cf8 commit 907f7a8

File tree

13 files changed

+250
-48
lines changed

13 files changed

+250
-48
lines changed

.hgignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
syntax: glob
2+
**.pyc
3+
build/*
4+
dist/*
5+
*.egg*

ChangeLog.txt

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,28 @@
1+
Version 0.6.1
2+
http://couchdb-python.googlecode.com/hg/?r=0.6.x
3+
(???, from branches/0.6.x)
4+
5+
* Compatible with CouchDB 0.9.x and 0.10.x.
6+
* Removed debugging statement from `json` module (issue 82).
7+
* Fixed a few bugs resulting from typos.
8+
* Added a `replicate()` method to the `client.Server` class (issue 61).
9+
* Honor the boundary argument in the dump script code (issue 100).
10+
* Added a `stats()` method to the `client.Server` class.
11+
* Added a `tasks()` method to the `client.Server` class.
12+
* Allow slashes in path components passed to the uri function (issue 96).
13+
* `schema.DictField` objects now have a separate backing dictionary for each
14+
instance of their `schema.Document` (issue 101).
15+
* `schema.ListField` proxy objects now have a more consistent (though somewhat
16+
slower) `count()` method (issue 91).
17+
* `schema.ListField` objects now have correct behavior for slicing operations
18+
and the `pop()` method (issue 92).
19+
* Added a `revisions()` method to the Database class (issue 99).
20+
* Make sure we always return UTF-8 from the view server (issue 81).
21+
22+
123
Version 0.6
2-
http://couchdb-python.googlecode.com/svn/tags/0.6.0
3-
(Jul 2, 2009, from branches/0.6.x)
24+
http://couchdb-python.googlecode.com/hg/?r=0.6.0
25+
(Jul 2, 2009, from 0.6.x)
426

527
* Compatible with CouchDB 0.9.x.
628
* `schema.DictField` instances no longer need to be bound to a `Schema`
@@ -36,8 +58,8 @@ http://couchdb-python.googlecode.com/svn/tags/0.6.0
3658

3759

3860
Version 0.5
39-
http://couchdb-python.googlecode.com/svn/tags/0.5.0
40-
(Nov 28, 2008, from branches/0.5.x)
61+
http://couchdb-python.googlecode.com/hg/?r=0.5.0
62+
(Nov 28, 2008, from 0.5.x)
4163

4264
* `schema.Document` objects can now be used in the documents list passed to
4365
`client.Database.update()`.
@@ -70,8 +92,8 @@ http://couchdb-python.googlecode.com/svn/tags/0.5.0
7092

7193

7294
Version 0.4
73-
http://couchdb-python.googlecode.com/svn/tags/0.4.0
74-
(Jun 28, 2008, from branches/0.4.x)
95+
http://couchdb-python.googlecode.com/hg/?r=0.4.0
96+
(Jun 28, 2008, from 0.4.x)
7597

7698
* Updated for compatibility with CouchDB 0.8.0
7799
* Added command-line scripts for importing/exporting databases.
@@ -81,8 +103,8 @@ http://couchdb-python.googlecode.com/svn/tags/0.4.0
81103

82104

83105
Version 0.3
84-
http://couchdb-python.googlecode.com/svn/tags/0.3.0
85-
(Feb 6, 2008, from branches/0.3.x)
106+
http://couchdb-python.googlecode.com/hg/?r=0.3.0
107+
(Feb 6, 2008, from 0.3.x)
86108

87109
* The `schema.Document` class now has a `view()` method that can be used to
88110
execute a CouchDB view and map the result rows back to objects of that
@@ -94,8 +116,8 @@ http://couchdb-python.googlecode.com/svn/tags/0.3.0
94116

95117

96118
Version 0.2
97-
http://couchdb-python.googlecode.com/svn/tags/0.2.0
98-
(Nov 21, 2007, from branches/0.2.x)
119+
http://couchdb-python.googlecode.com/hg/?r=0.2.0
120+
(Nov 21, 2007, from 0.2.x)
99121

100122
* Added __len__ and __iter__ to the `schema.Schema` class to iterate
101123
over and get the number of items in a document or compound field.
@@ -122,7 +144,7 @@ http://couchdb-python.googlecode.com/svn/tags/0.2.0
122144

123145

124146
Version 0.1
125-
http://couchdb-python.googlecode.com/svn/tags/0.1.0
126-
(Sep 23, 2007, from branches/0.1.x)
147+
http://couchdb-python.googlecode.com/hg/?r=0.1.0
148+
(Sep 23, 2007, from 0.1.x)
127149

128150
* First public release.

couchdb/client.py

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,16 @@ def version(self):
189189
resp, data = self.resource.get()
190190
return data['version']
191191

192+
def stats(self):
193+
"""Database statistics."""
194+
resp, data = self.resource.get('_stats')
195+
return data
196+
197+
def tasks(self):
198+
"""A list of tasks currently active on the server."""
199+
resp, data = self.resource.get('_active_tasks')
200+
return data
201+
192202
def create(self, name):
193203
"""Create a new database with the given name.
194204
@@ -209,6 +219,18 @@ def delete(self, name):
209219
"""
210220
del self[name]
211221

222+
def replicate(self, source, target, **options):
223+
"""Replicate changes from the source database to the target database.
224+
225+
:param source: URL of the source database
226+
:param target: URL of the target database
227+
:param options: optional replication args, e.g. continuous=True
228+
"""
229+
data = {'source': source, 'target': target}
230+
data.update(options)
231+
resp, data = self.resource.post('_replicate', data)
232+
return data
233+
212234

213235
class Database(object):
214236
"""Representation of a database on a CouchDB server.
@@ -457,6 +479,26 @@ def get(self, id, default=None, **options):
457479
else:
458480
return Document(data)
459481

482+
def revisions(self, id, **options):
483+
"""Return all available revisions of the given document.
484+
485+
:param id: the document ID
486+
:return: an iterator over Document objects, each a different revision,
487+
in reverse chronological order, if any were found
488+
"""
489+
try:
490+
resp, data = self.resource.get(id, revs=True)
491+
except ResourceNotFound:
492+
return
493+
494+
startrev = data['_revisions']['start']
495+
for index, rev in enumerate(data['_revisions']['ids']):
496+
options['rev'] = '%d-%s' % (startrev - index, rev)
497+
revision = self.get(id, **options)
498+
if revision is None:
499+
return
500+
yield revision
501+
460502
def info(self):
461503
"""Return information about the database as a dictionary.
462504
@@ -1037,17 +1079,32 @@ def uri(base, *path, **query):
10371079
"""Assemble a uri based on a base, any number of path segments, and query
10381080
string parameters.
10391081
1040-
>>> uri('http://example.org/', '/_all_dbs')
1082+
>>> uri('http://example.org', '_all_dbs')
10411083
'http://example.org/_all_dbs'
1084+
1085+
A trailing slash on the uri base is handled gracefully:
1086+
1087+
>>> uri('http://example.org/', '_all_dbs')
1088+
'http://example.org/_all_dbs'
1089+
1090+
And multiple positional arguments become path parts:
1091+
1092+
>>> uri('http://example.org/', 'foo', 'bar')
1093+
'http://example.org/foo/bar'
1094+
1095+
All slashes within a path part are escaped:
1096+
1097+
>>> uri('http://example.org/', 'foo/bar')
1098+
'http://example.org/foo%2Fbar'
1099+
>>> uri('http://example.org/', 'foo', '/bar/')
1100+
'http://example.org/foo/%2Fbar%2F'
10421101
"""
10431102
if base and base.endswith('/'):
10441103
base = base[:-1]
10451104
retval = [base]
10461105

10471106
# build the path
1048-
path = '/'.join([''] +
1049-
[unicode_quote(s.strip('/')) for s in path
1050-
if s is not None])
1107+
path = '/'.join([''] + [unicode_quote(s) for s in path if s is not None])
10511108
if path:
10521109
retval.append(path)
10531110

couchdb/json.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
#!/usr/bin/env python
21
# -*- coding: utf-8 -*-
32
#
43
# Copyright (C) 2009 Christopher Lenz

couchdb/multipart.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ def _make_boundary(self):
165165
from random import randrange
166166
token = randrange(sys.maxint)
167167
format = '%%0%dd' % len(repr(sys.maxint - 1))
168-
return '===============' + (fmt % token) + '=='
168+
return '===============' + (format % token) + '=='
169169

170170
def _write_headers(self, headers):
171171
if headers:

couchdb/schema.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ def get(self, name, default):
153153
return self._data.get(name, default)
154154

155155
def setdefault(self, name, default):
156-
return sef._data.setdefault(name, default)
156+
return self._data.setdefault(name, default)
157157

158158
def unwrap(self):
159159
return self._data
@@ -567,7 +567,8 @@ class DictField(Field):
567567
>>> del server['python-tests']
568568
"""
569569
def __init__(self, schema=None, name=None, default=None):
570-
Field.__init__(self, name=name, default=default or {})
570+
default = default or {}
571+
Field.__init__(self, name=name, default=lambda: default.copy())
571572
self.schema = schema
572573

573574
def _to_python(self, value):
@@ -676,7 +677,16 @@ def __getitem__(self, index):
676677
return self.field._to_python(self.list[index])
677678

678679
def __setitem__(self, index, value):
679-
self.list[index] = self.field._to_json(item)
680+
self.list[index] = self.field._to_json(value)
681+
682+
def __delslice__(self, i, j):
683+
del self.list[i:j]
684+
685+
def __getslice__(self, i, j):
686+
return ListField.Proxy(self.list[i:j], self.field)
687+
688+
def __setslice__(self, i, j, seq):
689+
self.list[i:j] = (self.field._to_json(v) for v in seq)
680690

681691
def __contains__(self, value):
682692
for item in self.list:
@@ -705,7 +715,7 @@ def append(self, *args, **kwargs):
705715
self.list.append(self.field._to_json(value))
706716

707717
def count(self, value):
708-
return self.list.count(self.field._to_json(value))
718+
return [i for i in self].count(value)
709719

710720
def extend(self, list):
711721
for item in list:
@@ -726,3 +736,6 @@ def insert(self, idx, *args, **kwargs):
726736

727737
def remove(self, value):
728738
return self.list.remove(self.field._to_json(value))
739+
740+
def pop(self, *args):
741+
return self.field._to_python(self.list.pop(*args))

couchdb/tests/client.py

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,20 @@ def setUp(self):
2121
self.server = client.Server(uri)
2222

2323
def tearDown(self):
24-
if 'python-tests' in self.server:
25-
del self.server['python-tests']
24+
try:
25+
self.server.delete('python-tests')
26+
except client.ResourceNotFound:
27+
pass
28+
try:
29+
self.server.delete('python-tests-a')
30+
except client.ResourceNotFound:
31+
pass
2632

2733
def test_server_vars(self):
2834
version = self.server.version
2935
config = self.server.config
36+
stats = self.server.stats()
37+
tasks = self.server.tasks()
3038

3139
def test_get_db_missing(self):
3240
self.assertRaises(client.ResourceNotFound,
@@ -47,19 +55,46 @@ def test_delete_db_missing(self):
4755
self.assertRaises(client.ResourceNotFound, self.server.delete,
4856
'python-tests')
4957

58+
def test_replicate(self):
59+
a = self.server.create('python-tests')
60+
id = a.create({'test': 'a'})
61+
b = self.server.create('python-tests-a')
62+
result = self.server.replicate('python-tests', 'python-tests-a')
63+
self.assertEquals(result['ok'], True)
64+
self.assertEquals(b[id]['test'], 'a')
65+
66+
doc = b[id]
67+
doc['test'] = 'b'
68+
b.update([doc])
69+
self.server.replicate(client.DEFAULT_BASE_URI + 'python-tests-a',
70+
'python-tests')
71+
self.assertEquals(b[id]['test'], 'b')
72+
73+
def test_replicate_continuous(self):
74+
a = self.server.create('python-tests')
75+
b = self.server.create('python-tests-a')
76+
result = self.server.replicate('python-tests', 'python-tests-a', continuous=True)
77+
self.assertEquals(result['ok'], True)
78+
version = tuple(int(i) for i in self.server.version.split('.'))
79+
if version >= (0, 10):
80+
self.assertTrue('_local_id' in result)
5081

5182
class DatabaseTestCase(unittest.TestCase):
5283

5384
def setUp(self):
5485
uri = os.environ.get('COUCHDB_URI', client.DEFAULT_BASE_URI)
5586
self.server = client.Server(uri)
56-
if 'python-tests' in self.server:
57-
del self.server['python-tests']
87+
try:
88+
self.server.delete('python-tests')
89+
except client.ResourceNotFound:
90+
pass
5891
self.db = self.server.create('python-tests')
5992

6093
def tearDown(self):
61-
if 'python-tests' in self.server:
62-
del self.server['python-tests']
94+
try:
95+
self.server.delete('python-tests')
96+
except client.ResourceNotFound:
97+
pass
6398

6499
def test_create_large_doc(self):
65100
self.db['foo'] = {'data': '0123456789' * 110 * 1024} # 10 MB
@@ -98,10 +133,21 @@ def test_doc_revs(self):
98133
old_doc = self.db.get('foo', rev=old_rev)
99134
self.assertEqual(old_rev, old_doc['_rev'])
100135

136+
revs = [i for i in self.db.revisions('foo')]
137+
self.assertEqual(revs[0]['_rev'], new_rev)
138+
self.assertEqual(revs[1]['_rev'], old_rev)
139+
101140
self.assertTrue(self.db.compact())
102141
while self.db.info()['compact_running']:
103142
pass
104-
self.assertRaises(client.ServerError, self.db.get, 'foo', rev=old_rev)
143+
144+
# 0.10 responds with 404, 0.9 responds with 500, same content
145+
doc = 'fail'
146+
try:
147+
doc = self.db.get('foo', rev=old_rev)
148+
except client.ServerError:
149+
doc = None
150+
assert doc is None
105151

106152
def test_attachment_crud(self):
107153
doc = {'bar': 42}
@@ -240,6 +286,7 @@ def test_bulk_update_all_or_nothing(self):
240286

241287
# update the first doc to provoke a conflict in the next bulk update
242288
doc = docs[0].copy()
289+
doc['name'] = 'Jane Doe'
243290
self.db[doc['_id']] = doc
244291

245292
results = self.db.update(docs, all_or_nothing=True)

couchdb/tests/couch_tests.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,17 @@ def setUp(self):
2020
uri = os.environ.get('COUCHDB_URI', 'http://localhost:5984/')
2121
self.cache_dir = tempfile.mkdtemp(prefix='couchdb')
2222
self.server = Server(uri, cache=self.cache_dir)
23-
if 'python-tests' in self.server:
24-
del self.server['python-tests']
23+
try:
24+
self.server.delete('python-tests')
25+
except ResourceNotFound:
26+
pass
2527
self.db = self.server.create('python-tests')
2628

2729
def tearDown(self):
28-
if 'python-tests' in self.server:
29-
del self.server['python-tests']
30+
try:
31+
self.server.delete('python-tests')
32+
except ResourceNotFound:
33+
pass
3034
shutil.rmtree(self.cache_dir)
3135

3236
def _create_test_docs(self, num):

0 commit comments

Comments
 (0)