aiocouchdb 0.8.0 documentation

Welcome to aiocouchdb’s documentation!

Contents   ::   CouchDB 1.x API  »

Welcome to aiocouchdb’s documentation!

source:https://github.com/kxepal/aiocouchdb
documentation:http://aiocouchdb.readthedocs.org/en/latest/
license:BSD

Getting started

If you’d some background experience with couchdb-python client, you’ll find aiocouchdb API a bit familiar. That project is my lovely too, but suddenly it’s completely synchronous.

At first, you need to create instance of Server object which interacts with CouchDB Server API:

>>> import aiocouchdb
>>> server = aiocouchdb.Server()
>>> server
<aiocouchdb.v1.server.Server(http://localhost:5984) object at 0x7f8199a80350>

As like as couchdb-python Server instance has resource attribute that acts very familiar:

>>> server.resource
<aiocouchdb.client.Resource(http://localhost:5984) object at 0x7f9cba5e2490>
>>> server.resource('db', 'doc1')
<aiocouchdb.client.Resource(http://localhost:5984/db/doc1) object at 0x7f9cb9fc2e10>

With the only exception that it’s a coroutine:

Note

Python doesn’t supports yield from in shell, so examples below are a bit out of a real, but be sure - that’s how they works in real.

>>> resp = yield from server.resource.get()
<ClientResponse(http://localhost:5984) [200 OK]>
<CIMultiDictProxy {'SERVER': 'CouchDB/1.6.1 (Erlang OTP/17)', 'DATE': 'Sun, 08 Mar 2015 15:13:12 GMT', 'CONTENT-TYPE': 'application/json', 'CONTENT-LENGTH': '139', 'CACHE-CONTROL': 'must-revalidate'}>
>>> yield from resp.json()
{'couchdb': 'Welcome!',
 'vendor': {'version': '1.6.1', 'name': 'The Apache Software Foundation'},
 'uuid': '0510c29b75ae33fd3975eb505db2dd12',
 'version': '1.6.1'}

The Resource object provides a tiny wrapper over aiohttp.client.request() function so you can use it in case of raw API access.

But, libraries are made to hide all the implementation details and make work with API nice and easy one and aiocouchdb isn’t an exception. The example above is actually what the Server.info() method does:

>>> yield from server.info()
{'couchdb': 'Welcome!',
 'vendor': {'version': '1.6.1', 'name': 'The Apache Software Foundation'},
 'uuid': '0510c29b75ae33fd3975eb505db2dd12',
 'version': '1.6.1'}

Most of Server and not only methods are named similar to the real HTTP API endpoints:

>>> yield from server.all_dbs()
['_replicator', '_users', 'db']
>>> yield from server.active_tasks()
[{'database': 'db',
  'pid': '<0.10209.20>',
  'changes_done': 0,
  'progress': 0,
  'started_on': 1425805499,
  'total_changes': 1430818,
  'type': 'database_compaction',
  'updated_on': 1425805499}]

With a few exceptions like Server.session or Server.config which has complex use-case behind and are operated by other objects.

Speaking about aiocouchdb.v1.server.Server.session, aiocouchdb supports multiuser workflow where you pass session object as an argument on resource request.

>>> admin = yield from server.session.open('admin', 's3cr1t')
>>> user = yield from server.session.open('user', 'pass')

Here we just opened two session for different users. Their usage is pretty trivial - just pass them as auth keyword parameter to every API function call:

>>> yield from server.active_tasks(auth=admin)
[{'database': 'db',
  'pid': '<0.10209.20>',
  'changes_done': 50413,
  'progress': 3,
  'started_on': 1425805499,
  'total_changes': 1430818,
  'type': 'database_compaction',
  'updated_on': 1425806018}]
>>> yield from server.active_tasks(auth=user)
Traceback:
...
Unauthorized: [forbidden] You are not a server admin.

Another important moment that aiocouchdb raises exception on HTTP errors. By using Resource object you’ll receive raw response and may build custom logic on processing such errors: to raise an exception or to not.

With using Server.session.open() you implicitly creates CookieAuthProvider which hold received from CouchDB cookie with authentication token. aiocouchdb also provides the way to authorize via Basic Auth, OAuth (oauthlib required) and others. Their usage is also pretty trivial:

>>> admin = aiocouchdb.BasicAuthProvider('admin', 's3cr1t')
>>> yield from server.active_tasks(auth=admin)
[{'database': 'db',
  'pid': '<0.10209.20>',
  'changes_done': 50413,
  'progress': 3,
  'started_on': 1425805499,
  'total_changes': 1430818,
  'type': 'database_compaction',
  'updated_on': 1425806018}]

Working with databases

To create a database object which will interact with CouchDB Database API you have three ways to go:

  1. Using direct object instance creation:
>>> aiocouchdb.Database('http://localhost:5984/db')
<aiocouchdb.v1.database.Database(http://localhost:5984/db) object at 0x7ffd44d58f90>
  1. Using __getitem__ protocol similar to couchdb-python:
>>> server['db']
<aiocouchdb.v1.database.Database(http://localhost:5984/db) object at 0x7ffd44cf30d0>
  1. Using Server.db() method:
>>> yield from server.db('db')
<aiocouchdb.v1.database.Database(http://localhost:5984/db) object at 0x7ffd44cf3390>

What’s their difference? First method is useful when you don’t have access to a Server instance, but knows database URL. Second one returns instantly a Database instance for the name you specified.

But the third one is smarter: it verifies that database by name you’d specified is accessible for you and if it’s not - raises an exception:

>>> yield from server.db('_users')
Traceback:
...
Unauthorized: [forbidden] You are not a server admin.
>>> yield from server.db('_foo')
Traceback:
...
BadRequest: [illegal_database_name] Name: '_foo'. Only lowercase characters (a-z), digits (0-9), and any of the characters _, $, (, ), +, -, and / are allowed. Must begin with a letter.

This costs you an additional HTTP request, but gives the insurance that the following methods calls will not fail by unrelated reasons.

This method doesn’t raises an exception if database doesn’t exists to allow you create it:

>>> db = yield from server.db('newdb')
>>> yield from db.exists()
False
>>> yield from db.create()
True
>>> yield from db.exists()
True

Iterating over documents

In couchdb-python you might done it with in the following way:

>>> for docid in db:
...    do_something(db[docid])

Or:

>>> for row in db.view('_all_docs'):
...    do_something(db[row['id']])

aiocouchdb does that quite differently:

>>> res = yield from db.all_docs()
>>> while True:
...     rec = yield from res.next()
...     if rec is None:
...         break
...     do_something(rec['id'])

What’s going on here?

  1. You requesting /db/_all_docs endpoint explicitly and may pass all his query parameters as you need;
  2. On Database.all_docs() call returns not a list of view results, but a special instance of ViewFeed object which fetches results one by one in background into internal buffer without loading whole result into memory in single shot. You can control this buffer size with feed_buffer_size keyword argument;
  3. When all the records are processed it emits None which signs on empty feed and the loop breaking out;

aiocouchdb tries never load large streams, but process them in iterative way. This may looks ugly for small data sets, but when you deal with the large ones it’ll save you a lot of resources.

The same loop pattern in used to process Database.changes() as well.

Working with documents

To work with a document you need get Document instance first - Database doesn’t knows anything about CouchDB Document API. The way to do this is the same as for database:

>>> aiocouchdb.Document('http://localhost:5984/db/doc1')
<aiocouchdb.v1.document.Document(http://localhost:5984/db/doc1) object at 0x7ff3ef7af070>
>>> server['db']['doc1']
<aiocouchdb.v1.document.Document(http://localhost:5984/db/doc1) object at 0x7fda92ff4850>
>>> doc = yield from db.doc('doc1')
<aiocouchdb.v1.document.Document(http://localhost:5984/db/doc1) object at 0x7fda981380d0>

Their difference is the same as for Database mentioned above.

>>> yield from doc.exists()
False
>>> meta = yield from doc.update({'hello': 'CouchDB'})
>>> meta
{'ok': True, 'rev': '1-7c6fb984afda7e07d030cce000dc5965', 'id': 'doc1'}
>>> yield from doc.exists()
True
>>> meta = yield from doc.update({'hello': 'CouchDB'}, rev=meta['rev'])
>>> meta
{'ok': True, 'rev': '2-c5298951d02b03f3d6273ad5854ea729', 'id': 'doc1'}
>>> yield from doc.get()
{'hello': 'CouchDB',
 '_id': 'doc1',
 '_rev': '2-c5298951d02b03f3d6273ad5854ea729'}
>>> yield from doc.delete('2-c5298951d02b03f3d6273ad5854ea729')
{'ok': True, 'rev': '3-cfa05c76fb4a0557605d6a8b1a765055', 'id': 'doc1'}
>>> yield from doc.exists()
False

Pretty simple, right?

What’s next?

There are a lot of things are left untold. Checkout CouchDB 1x API for more. Happy hacking!

Changes

0.8.0 (2015-03-20)

  • Source tree was refactored in the way to support multiple major CouchDB versions as like as the other friendly forks
  • Database create and delete methods now return exact the same response as CouchDB sends back
  • Each module now contains __all__ list to normalize their exports
  • API classes and Resource now has nicer __repr__ output
  • Better error messages format
  • Fix function_clause error on attempt to update a document with attachments by using multipart request
  • Document.update doesn’t makes document’s dict invalid for further requests after multipart one
  • Fixed accidental payload sent with HEAD/GET/DELETE requests which caused connection close from CouchDB side
  • Added integration with Travis CI
  • Code cleaned by following pylint and flake8 notices
  • Added short tutorial for documentation
  • Minor fixes and Makefile improvements

0.7.0 (2015-02-18)

  • Greatly improved multipart module, added multipart writer
  • Document.update now supports multipart requests to upload multiple attachments in single request
  • Added Proxy Authentication provider
  • Minimal requirements for aiohttp raised up to 0.14.0 version

0.6.0 (2014-11-12)

  • Adopt test suite to run against real CouchDB instance
  • Database, documents and attachments now provides access to their name/id
  • Remove redundant longnamed constructors
  • Construct Database/Document/Attachment instances through __getitem__ protocol
  • Add Document.rev method to get current document`s revision
  • Add helpers to work with authentication database (_users)
  • Add optional limitation of feeds buffer
  • All remove(...) methods are renamed to delete(...) ones
  • Add support for config option existence check
  • Correctly set members for database security
  • Fix requests with Accept-Ranges header against attachments
  • Fix views requests when startkey/endkey should be null
  • Allow to pass custom query parameters and request headers onto changes feed request
  • Handle correctly HTTP 416 error response
  • Minor code fixes and cleanup

0.5.0 (2014-09-26)

  • Last checkpoint release. It’s in beta now!
  • Implements CouchDB Design Documents HTTP API
  • Views refactoring and implementation consolidation

0.4.0 (2014-09-17)

  • Another checkpoint release
  • Implements CouchDB Attachment HTTP API
  • Minimal requirements for aiohttp raised up to 0.9.1 version
  • Minor fixes for Document API

0.3.0 (2014-08-18)

  • Third checkpoint release
  • Implements CouchDB Document HTTP API
  • Support document`s multipart API (but not doc update due to COUCHDB-2295)
  • Minimal requirements for aiohttp raised up to 0.9.0 version
  • Better documentation

0.2.0 (2014-07-08)

  • Second checkpoint release
  • Implements CouchDB Database HTTP API
  • Bulk docs accepts generator as an argument and streams request doc by doc
  • Views are processed as stream
  • Unified output for various changes feed types
  • Basic Auth accepts non-ASCII credentials
  • Minimal requirements for aiohttp raised up to 0.8.4 version

0.1.0 (2014-07-01)

  • Initial checkpoint release
  • Implements CouchDB Server HTTP API
  • BasicAuth, Cookie, OAuth authentication providers
  • Multi-session workflow

License

Copyright (C) 2014-2015 Alexander Shorin
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Contents   ::   CouchDB 1.x API  »