28
28
import re
29
29
import simplejson as json
30
30
31
- __all__ = ['ResourceNotFound' , 'ResourceConflict' , 'ServerError' , 'Server' ,
32
- 'Database' , 'Document' , 'ViewResults' , 'Row' ]
31
+ __all__ = ['PreconditionFailed' , 'ResourceNotFound' , 'ResourceConflict' ,
32
+ 'ServerError' , 'Server' , 'Database' , 'Document' , 'ViewResults' ,
33
+ 'Row' ]
33
34
__docformat__ = 'restructuredtext en'
34
35
35
36
37
+ class PreconditionFailed (Exception ):
38
+ """Exception raised when a 412 HTTP error is received in response to a
39
+ request.
40
+ """
41
+
42
+
36
43
class ResourceNotFound (Exception ):
37
44
"""Exception raised when a 404 HTTP error is received in response to a
38
45
request.
@@ -196,8 +203,6 @@ class Database(object):
196
203
True
197
204
>>> len(db)
198
205
2
199
- >>> list(db) #doctest: +ELLIPSIS
200
- [u'...', u'JohnDoe']
201
206
202
207
>>> del server['python-tests']
203
208
"""
@@ -291,43 +296,47 @@ def get(self, id, default=None, **options):
291
296
except ResourceNotFound :
292
297
return default
293
298
294
- def query (self , code , content_type = 'text/javascript' , wrapper = None ,
295
- ** options ):
299
+ def query (self , map_fun , reduce_fun = None , language = 'javascript' ,
300
+ wrapper = None , ** options ):
296
301
"""Execute an ad-hoc query (a "temp view") against the database.
297
302
298
303
>>> server = Server('http://localhost:5984/')
299
304
>>> db = server.create('python-tests')
300
305
>>> db['johndoe'] = dict(type='Person', name='John Doe')
301
306
>>> db['maryjane'] = dict(type='Person', name='Mary Jane')
302
307
>>> db['gotham'] = dict(type='City', name='Gotham City')
303
- >>> code = '''function(doc) {
304
- ... if (doc.type== 'Person')
305
- ... map (doc.name, null);
308
+ >>> map_fun = '''function(doc) {
309
+ ... if (doc.type == 'Person')
310
+ ... emit (doc.name, null);
306
311
... }'''
307
- >>> for row in db.query(code ):
312
+ >>> for row in db.query(map_fun ):
308
313
... print row.key
309
314
John Doe
310
315
Mary Jane
311
316
312
- >>> for row in db.query(code , descending=True):
317
+ >>> for row in db.query(map_fun , descending=True):
313
318
... print row.key
314
319
Mary Jane
315
320
John Doe
316
321
317
- >>> for row in db.query(code , key='John Doe'):
322
+ >>> for row in db.query(map_fun , key='John Doe'):
318
323
... print row.key
319
324
John Doe
320
325
321
326
>>> del server['python-tests']
322
327
323
- :param code: the code of the view function
324
- :param content_type: the MIME type of the code, which determines the
325
- language the view function is written in
328
+ :param map_fun: the code of the map function
329
+ :param reduce_fun: the code of the reduce function (optional)
330
+ :param language: the language of the functions, to determine which view
331
+ server to use
332
+ :param wrapper: an optional callable that should be used to wrap the
333
+ result rows
334
+ :param options: optional query string parameters
326
335
:return: the view reults
327
336
:rtype: `ViewResults`
328
337
"""
329
- return TemporaryView (uri (self .resource .uri , '_temp_view' ), code ,
330
- content_type = content_type , wrapper = wrapper ,
338
+ return TemporaryView (uri (self .resource .uri , '_temp_view' ), map_fun ,
339
+ reduce_fun , language = language , wrapper = wrapper ,
331
340
http = self .resource .http )(** options )
332
341
333
342
def update (self , documents ):
@@ -355,12 +364,14 @@ def update(self, documents):
355
364
:since: version 0.2
356
365
"""
357
366
documents = list (documents )
358
- data = self .resource .post ('_bulk_docs' , content = documents )
359
- for idx , result in enumerate (data ['results' ]):
360
- assert 'ok' in result # FIXME: how should error handling work here?
361
- doc = documents [idx ]
362
- doc .update ({'_id' : result ['id' ], '_rev' : result ['rev' ]})
363
- yield doc
367
+ data = self .resource .post ('_bulk_docs' , content = {'docs' : documents })
368
+ assert data ['ok' ] # FIXME: Should probably raise a proper exception
369
+ def _update ():
370
+ for idx , result in enumerate (data ['new_revs' ]):
371
+ doc = documents [idx ]
372
+ doc .update ({'_id' : result ['id' ], '_rev' : result ['rev' ]})
373
+ yield doc
374
+ return _update ()
364
375
365
376
def view (self , name , wrapper = None , ** options ):
366
377
"""Execute a predefined view.
@@ -375,6 +386,11 @@ def view(self, name, wrapper=None, **options):
375
386
376
387
>>> del server['python-tests']
377
388
389
+ :param name: the name of the view, including the `_view/design_docid`
390
+ prefix for custom views
391
+ :param wrapper: an optional callable that should be used to wrap the
392
+ result rows
393
+ :param options: optional query string parameters
378
394
:return: the view results
379
395
:rtype: `ViewResults`
380
396
"""
@@ -443,21 +459,24 @@ def _exec(self, options):
443
459
class TemporaryView (View ):
444
460
"""Representation of a temporary view."""
445
461
446
- def __init__ (self , uri , code = None , content_type = 'text/javascript' ,
447
- wrapper = None , http = None ):
462
+ def __init__ (self , uri , map_fun = None , reduce_fun = None ,
463
+ language = 'javascript' , wrapper = None , http = None ):
448
464
View .__init__ (self , uri , wrapper = wrapper , http = http )
449
465
self .resource = Resource (http , uri )
450
- self .code = code
451
- self .content_type = content_type
466
+ self .map_fun = map_fun
467
+ self .reduce_fun = reduce_fun
468
+ self .language = language
452
469
453
470
def __repr__ (self ):
454
- return '<%s %r>' % (type (self ).__name__ , self .code )
471
+ return '<%s %r %r>' % (type (self ).__name__ , self .map_fun ,
472
+ self .reduce_fun )
455
473
456
474
def _exec (self , options ):
457
- headers = {}
458
- if self .content_type :
459
- headers ['Content-Type' ] = self .content_type
460
- return self .resource .post (content = self .code , headers = headers ,
475
+ body = {'map' : self .map_fun , 'language' : self .language }
476
+ if self .reduce_fun :
477
+ body ['reduce' ] = self .reduce_fun
478
+ return self .resource .post (content = json .dumps (body ),
479
+ headers = {'Content-Type' : 'application/json' },
461
480
** self ._encode_options (options ))
462
481
463
482
@@ -473,10 +492,10 @@ class ViewResults(object):
473
492
>>> db['johndoe'] = dict(type='Person', name='John Doe')
474
493
>>> db['maryjane'] = dict(type='Person', name='Mary Jane')
475
494
>>> db['gotham'] = dict(type='City', name='Gotham City')
476
- >>> code = '''function(doc) {
477
- ... map ([doc.type, doc.name], doc.name);
495
+ >>> map_fun = '''function(doc) {
496
+ ... emit ([doc.type, doc.name], doc.name);
478
497
... }'''
479
- >>> results = db.query(code )
498
+ >>> results = db.query(map_fun )
480
499
481
500
At this point, the view has not actually been accessed yet. It is accessed
482
501
as soon as it is iterated over, its length is requested, or one of its
@@ -646,6 +665,8 @@ def _request(self, method, path=None, content=None, headers=None,
646
665
raise ResourceNotFound (error )
647
666
elif status_code == 409 :
648
667
raise ResourceConflict (error )
668
+ elif status_code == 412 :
669
+ raise PreconditionFailed (error )
649
670
else :
650
671
raise ServerError ((status_code , error ))
651
672
0 commit comments