[couchdb-python commit] r177 - Some cleanup for the docstrings and use of decorators.

2 views
Skip to first unread message

codesite...@google.com

unread,
Jul 2, 2009, 8:00:51 AM7/2/09
to couchdb...@googlegroups.com
Author: cmlenz
Date: Thu Jul 2 04:38:47 2009
New Revision: 177

Modified:
trunk/couchdb/client.py
trunk/couchdb/schema.py
trunk/couchdb/tools/dump.py
trunk/couchdb/tools/load.py
trunk/couchdb/tools/replication_helper.py

Log:
Some cleanup for the docstrings and use of decorators.

Modified: trunk/couchdb/client.py
==============================================================================
--- trunk/couchdb/client.py (original)
+++ trunk/couchdb/client.py Thu Jul 2 04:38:47 2009
@@ -96,7 +96,7 @@

def __init__(self, uri=DEFAULT_BASE_URI, cache=None, timeout=None):
"""Initialize the server object.
-
+
:param uri: the URI of the server (for example
``http://localhost:5984/``)
:param cache: either a cache directory path (as a string) or an
object
@@ -165,30 +165,29 @@
db.resource.head() # actually make a request to the database
return db

- def _get_config(self):
- resp, data = self.resource.get('_config')
- return data
- config = property(_get_config, doc="""\
- The configuration of the CouchDB server.
+ @property
+ def config(self):
+ """The configuration of the CouchDB server.

The configuration is represented as a nested dictionary of
sections and
options from the configuration files of the server, or the default
values for options that are not explicitly configured.

:type: `dict`
- """)
+ """
+ resp, data = self.resource.get('_config')
+ return data

- def _get_version(self):
- resp, data = self.resource.get()
- return data['version']
- version = property(_get_version, doc="""\
- The version number tuple for the CouchDB server.
+ @property
+ def version(self):
+ """The version string of the CouchDB server.

Note that this results in a request being made, and can also be
used
to check for the availability of the server.
-
- :type: `unicode`
- """)
+
+ :type: `unicode`"""
+ resp, data = self.resource.get()
+ return data['version']

def create(self, name):
"""Create a new database with the given name.
@@ -203,7 +202,7 @@

def delete(self, name):
"""Delete the database with the specified name.
-
+
:param name: the name of the database
:raise ResourceNotFound: if a database with that name does not
exist
:since: 0.6
@@ -244,9 +243,9 @@
>>> doc['name'] = 'Mary Jane'
>>> db[doc.id] = doc

- The `create()` method creates a document with an auto-generated ID. If
you
- want to explicitly specify the ID, you'd use item access just as with
- updating:
+ The `create()` method creates a document with a random ID generated by
+ CouchDB (which is not recommended). If you want to explicitly specify
the
+ ID, you'd use item access just as with updating:

>>> db['JohnDoe'] = {'type': 'person', 'name': 'John Doe'}

@@ -324,17 +323,36 @@
resp, data = self.resource.put(id, content=content)
content.update({'_id': data['id'], '_rev': data['rev']})

- def _get_name(self):
+ @property
+ def name(self):
+ """The name of the database.
+
+ Note that this may require a request to the server unless the name
has
+ already been cached by the `info()` method.
+
+ :type: basestring
+ """
if self._name is None:
- self._name = self.info()['db_name']
+ self.info()
return self._name
- name = property(_get_name)

def create(self, data):
- """Create a new document in the database with a generated ID.
+ """Create a new document in the database with a random ID that is
+ generated by the server.

- Any keyword arguments are used to populate the fields of the new
- document.
+ Note that it is generally better to avoid the `create()` method and
+ instead generate document IDs on the client side. This is due to
the
+ fact that the underlying HTTP ``POST`` method is not idempotent,
and
+ an automatic retry due to a problem somewhere on the networking
stack
+ may cause multiple documents being created in the database.
+
+ To avoid such problems you can generate a UUID on the client side.
+ Python (since version 2.5) comes with a ``uuid`` module that can be
+ used for this::
+
+ from uuid import uuid4
+ doc_id = uuid4().hex
+ db[doc_id] = {'type': 'person', 'name': 'John Doe'}

:param data: the data to store in the document
:return: the ID of the created document
@@ -415,7 +433,7 @@
ResourceConflict: ('conflict', 'Document update conflict.')

>>> del server['python-tests']
-
+
:param doc: a dictionary or `Document` object holding the document
data
:raise ResourceConflict: if the document was updated in the
database
:since: 0.4.1
@@ -441,20 +459,21 @@

def info(self):
"""Return information about the database as a dictionary.
-
+
The returned dictionary exactly corresponds to the JSON response to
a ``GET`` request on the database URI.
-
+
:return: a dictionary of database properties
:rtype: ``dict``
:since: 0.4
"""
resp, data = self.resource.get()
+ self._name = data['db_name']
return data

def delete_attachment(self, doc, filename):
"""Delete the specified attachment.
-
+
Note that the provided `doc` is required to have a ``_rev`` field.
Thus, if the `doc` is based on a view row, the view row would need
to
include the ``_rev`` field.
@@ -469,7 +488,7 @@

def get_attachment(self, id_or_doc, filename, default=None):
"""Return an attachment from the specified doc id and filename.
-
+
:param id_or_doc: either a document ID or a dictionary or
`Document`
object representing the document that the
attachment
belongs to
@@ -528,7 +547,7 @@
def query(self, map_fun, reduce_fun=None, language='javascript',
wrapper=None, **options):
"""Execute an ad-hoc query (a "temp view") against the database.
-
+
>>> server = Server('http://localhost:5984/')
>>> db = server.create('python-tests')
>>> db['johndoe'] = dict(type='Person', name='John Doe')
@@ -542,18 +561,18 @@
... print row.key
John Doe
Mary Jane
-
+
>>> for row in db.query(map_fun, descending=True):
... print row.key
Mary Jane
John Doe
-
+
>>> for row in db.query(map_fun, key='John Doe'):
... print row.key
John Doe
-
+
>>> del server['python-tests']
-
+
:param map_fun: the code of the map function
:param reduce_fun: the code of the reduce function (optional)
:param language: the language of the functions, to determine which
view
@@ -571,7 +590,7 @@
def update(self, documents, **options):
"""Perform a bulk update or insertion of the given documents using
a
single HTTP request.
-
+
>>> server = Server('http://localhost:5984/')
>>> db = server.create('python-tests')
>>> for doc in db.update([
@@ -583,27 +602,27 @@
(True, '...', '...')
(True, '...', '...')
(True, '...', '...')
-
+
>>> del server['python-tests']
-
+
The return value of this method is a list containing a tuple for
every
element in the `documents` sequence. Each tuple is of the form
``(success, docid, rev_or_exc)``, where ``success`` is a boolean
indicating whether the update succeeded, ``docid`` is the ID of the
document, and ``rev_or_exc`` is either the new document revision,
or
an exception instance (e.g. `ResourceConflict`) if the update
failed.
-
+
If an object in the documents list is not a dictionary, this method
looks for an ``items()`` method that can be used to convert the
object
to a dictionary. Effectively this means you can also use this
method
with `schema.Document` objects.
-
+
:param documents: a sequence of dictionaries or `Document`
objects, or
objects providing a ``items()`` method that can
be
used to convert them to a dictionary
:return: an iterable over the resulting documents
:rtype: ``list``
-
+
:since: version 0.2
"""
docs = []
@@ -614,9 +633,11 @@
docs.append(dict(doc.items()))
else:
raise TypeError('expected dict, got %s' % type(doc))
+
content = options
content.update(docs=docs)
resp, data = self.resource.post('_bulk_docs', content=content)
+
results = []
for idx, result in enumerate(data):
if 'error' in result:
@@ -632,21 +653,22 @@
if isinstance(doc, dict): # XXX: Is this a good idea??
doc.update({'_id': result['id'], '_rev':
result['rev']})
results.append((True, result['id'], result['rev']))
+
return results

def view(self, name, wrapper=None, **options):
"""Execute a predefined view.
-
+
>>> server = Server('http://localhost:5984/')
>>> db = server.create('python-tests')
>>> db['gotham'] = dict(type='City', name='Gotham City')
-
+
>>> for row in db.view('_all_docs'):
... print row.id
gotham
-
+
>>> del server['python-tests']
-
+
:param name: the name of the view; for custom views, use the format
``design_docid/viewname``, that is, the document ID
of the
design document and the name of the view, separated
by a
@@ -677,8 +699,21 @@
dict([(k,v) for k,v in self.items()
if k not in ('_id', '_rev')]))

- id = property(lambda self: self['_id'])
- rev = property(lambda self: self['_rev'])
+ @property
+ def id(self):
+ """The document ID.
+
+ :type: basestring
+ """
+ return self['_id']
+
+ @property
+ def rev(self):
+ """The document revision.
+
+ :type: basestring
+ """
+ return self['_rev']


class View(object):
@@ -765,10 +800,10 @@
class ViewResults(object):
"""Representation of a parameterized view (either permanent or
temporary)
and the results it produces.
-
+
This class allows the specification of ``key``, ``startkey``, and
``endkey`` options using Python slice notation.
-
+
>>> server = Server('http://localhost:5984/')
>>> db = server.create('python-tests')
>>> db['johndoe'] = dict(type='Person', name='John Doe')
@@ -782,7 +817,7 @@
At this point, the view has not actually been accessed yet. It is
accessed
as soon as it is iterated over, its length is requested, or one of its
`rows`, `total_rows`, or `offset` properties are accessed:
-
+
>>> len(results)
3

@@ -796,11 +831,11 @@
Mary Jane
>>> people.total_rows, people.offset
(3, 1)
-
+
Use plain indexed notation (without a slice) to apply the ``key``
option.
Note that as CouchDB makes no claim that keys are unique in a view,
this
can still return multiple rows:
-
+
>>> list(results[['City', 'Gotham City']])
[<Row id='gotham', key=['City', 'Gotham City'], value='Gotham City'>]

@@ -844,39 +879,39 @@
self._total_rows = data.get('total_rows')
self._offset = data.get('offset', 0)

- def _get_rows(self):
+ @property
+ def rows(self):
+ """The list of rows returned by the view.
+
+ :type: `list`
+ """
if self._rows is None:
self._fetch()
return self._rows
- rows = property(_get_rows, doc="""\
- The list of rows returned by the view.
-
- :type: `list`
- """)

- def _get_total_rows(self):
+ @property
+ def total_rows(self):
+ """The total number of rows in this view.
+
+ This value is `None` for reduce views.
+
+ :type: `int` or ``NoneType`` for reduce views
+ """
if self._rows is None:
self._fetch()
return self._total_rows
- total_rows = property(_get_total_rows, doc="""\
- The total number of rows in this view.
-
- This value is `None` for reduce views.
-
- :type: `int` or ``NoneType`` for reduce views
- """)

- def _get_offset(self):
+ @property
+ def offset(self):
+ """The offset of the results from the first row in the view.
+
+ This value is 0 for reduce views.
+
+ :type: `int`
+ """
if self._rows is None:
self._fetch()
return self._offset
- offset = property(_get_offset, doc="""\
- The offset of the results from the first row in the view.
-
- This value is 0 for reduce views.
-
- :type: `int`
- """)


class Row(dict):
@@ -889,30 +924,32 @@
return '<%s id=%r, key=%r, value=%r>' % (type(self).__name__,
self.id,
self.key, self.value)

- def _get_id(self):
- return self.get('id')
- id = property(_get_id, doc="""\
- The associated Document ID if it exists. Returns `None` when it
+ @property
+ def id(self):
+ """The associated Document ID if it exists. Returns `None` when it
doesn't (reduce results).
- """)
+ """
+ return self.get('id')

- def _get_key(self):
+ @property
+ def key(self):
+ """The associated key."""
return self['key']
- key = property(_get_key, doc='The associated key.')

- def _get_value(self):
+ @property
+ def value(self):
+ """The associated value."""
return self['value']
- value = property(_get_value, doc='The associated value.')

- def _get_doc(self):
+ @property
+ def doc(self):
+ """The associated document for the row. This is only present when
the
+ view was accessed with ``include_docs=True`` as a query parameter,
+ otherwise this property will be `None`.
+ """
doc = self.get('doc')
if doc:
return Document(doc)
- doc = property(_get_doc, doc="""\
- The associated document for the row. This is only present when the
- view was accessed with ``include_docs=True`` as a query parameter,
- otherwise this property will be `None`.
- """)


# Internals

Modified: trunk/couchdb/schema.py
==============================================================================
--- trunk/couchdb/schema.py (original)
+++ trunk/couchdb/schema.py Thu Jul 2 04:38:47 2009
@@ -158,6 +158,7 @@
def unwrap(self):
return self._data

+ @classmethod
def build(cls, **d):
fields = {}
for attrname, attrval in d.items():
@@ -166,13 +167,12 @@
fields[attrname] = attrval
d['_fields'] = fields
return type('AnonymousStruct', (cls,), d)
- build = classmethod(build)

+ @classmethod
def wrap(cls, data):
instance = cls()
instance._data = data
return instance
- wrap = classmethod(wrap)

def _to_python(self, value):
return self.wrap(value)
@@ -314,13 +314,17 @@
if self.id is not None:
raise AttributeError('id can only be set on new documents')
self._data['_id'] = value
- id = property(_get_id, _set_id)
+ id = property(_get_id, _set_id, doc='The document ID')

+ @property
def rev(self):
+ """The document revision.
+
+ :type: basestring
+ """
if hasattr(self._data, 'rev'): # When data is client.Document
return self._data.rev
return self._data.get('_rev')
- rev = property(rev)

def items(self):
"""Return the fields as a list of ``(name, value)`` tuples.
@@ -348,6 +352,7 @@
retval.append((name, value))
return retval

+ @classmethod
def load(cls, db, id):
"""Load a specific document from the given database.

@@ -360,7 +365,6 @@
if doc is None:
return None
return cls.wrap(doc)
- load = classmethod(load)

def store(self, db):
"""Store the document in the given database."""
@@ -371,6 +375,7 @@
db[self.id] = self._data
return self

+ @classmethod
def query(cls, db, map_fun, reduce_fun, language='javascript',
**options):
"""Execute a CouchDB temporary view and map the result values back
to
objects of this schema.
@@ -388,8 +393,8 @@
return cls.wrap(data)
return db.query(map_fun, reduce_fun=reduce_fun, language=language,
wrapper=_wrapper, **options)
- query = classmethod(query)

+ @classmethod
def view(cls, db, viewname, **options):
"""Execute a CouchDB named view and map the result values back to
objects of this schema.
@@ -406,7 +411,6 @@
data['_id'] = row.id
return cls.wrap(data)
return db.view(viewname, wrapper=_wrapper, **options)
- view = classmethod(view)


class TextField(Field):

Modified: trunk/couchdb/tools/dump.py
==============================================================================
--- trunk/couchdb/tools/dump.py (original)
+++ trunk/couchdb/tools/dump.py Thu Jul 2 04:38:47 2009
@@ -7,6 +7,10 @@
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution.

+"""Utility for dumping a snapshot of a CouchDB database to a multipart MIME
+file.
+"""
+
from base64 import b64decode
from email.MIMEBase import MIMEBase
from email.MIMEMultipart import MIMEMultipart

Modified: trunk/couchdb/tools/load.py
==============================================================================
--- trunk/couchdb/tools/load.py (original)
+++ trunk/couchdb/tools/load.py Thu Jul 2 04:38:47 2009
@@ -7,6 +7,10 @@
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution.

+"""Utility for loading a snapshot of a CouchDB database from a multipart
MIME
+file.
+"""
+
from base64 import b64encode
from email import message_from_file
from optparse import OptionParser

Modified: trunk/couchdb/tools/replication_helper.py
==============================================================================
--- trunk/couchdb/tools/replication_helper.py (original)
+++ trunk/couchdb/tools/replication_helper.py Thu Jul 2 04:38:47 2009
@@ -7,22 +7,26 @@
# you should have received as part of this distribution.

"""
-CouchDB DbUpdateNotification Script that triggers replication
+CouchDB ``update_notification`` script that triggers replication.

-Daemon script that acts as CouchDB DbUpdateNotificationProcess and triggers
-replication on each incoming database update between the specified servers
+Daemon script that can be used as a CouchDB ``update_notification`` process
+and triggers replication on each incoming database update between the
+specified servers.

-Setup
-Add this to your local.ini, in the section [update_notification]:
-DbUpdateNotificationProcess=/path/to/this/script/replication-helper.py \
---source-server=http://127.0.0.1 --target-server=http://127.0.0.1:5985
+Setup:
+Add this to your local.ini, in the section ``[update_notification]``::

-Format of messages it reads
-{"db":"replication_notification_test","type":"updated"}
+ replication = /path/to/couchdb-replicate \\
+ --source-server=http://127.0.0.1/ \\
+ --target-server=http://127.0.0.1:5985/

-Todo:
+Format of the messages it reads::
+
+ {"db":"replication_notification_test","type":"updated"}
+
+TODO:
- Generic'-out the listener part and implement the resplication trigger as
-a delegate or subclass.
+ a delegate or subclass.
- Check if sub-second sleep delays are possible
"""

Reply all
Reply to author
Forward
0 new messages