Skip to content

Commit

Permalink
Changed the couchable constructor to possibly construct new couchdb s…
Browse files Browse the repository at this point in the history
…erver/db instances. Also updated tests.
  • Loading branch information
elistevens committed Aug 11, 2010
1 parent 5ae9164 commit 4764b3d
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 53 deletions.
20 changes: 20 additions & 0 deletions README.txt
Expand Up @@ -2,3 +2,23 @@ Currently requires:
- Python 2.7
- CouchDB 0.11+ (untested on lower)
- http://pypi.python.org/pypi/CouchDB 0.7+ (untested on lower)

>>> import couchable
>>> cdb=couchable.CouchableDb('testing')
>>> class SimpleDoc(couchable.CouchableDoc):
... def __init__(self, **kwargs):
... for name, value in kwargs.items():
... setattr(self, name, value)
...
>>> a = SimpleDoc(name='AAA')
>>> b = SimpleDoc(name='BBB', a=a)
>>> c = SimpleDoc(name='CCC', a=a)
>>> id_list = cdb.store([b, c])
>>> id_list
['main__.SimpleDoc:...', 'main__.SimpleDoc:...']
>>> b, c = cdb.load(id_list)
>>> assert b.a is c.a
>>> cdb.db[b._id]
<Document 'main__.SimpleDoc:...'@'...' {'a': 'couchable:id:main__.SimpleDoc:...', 'couchable:': {'class': 'SimpleDoc', 'module': '__main__'}, 'name': 'BBB'}>


125 changes: 78 additions & 47 deletions couchable/core.py
Expand Up @@ -99,7 +99,7 @@ def __init__(self, msg, cls, obj):
self.obj = obj

# type packing / unpacking
_pack_visitors = collections.OrderedDict()
_pack_handlers = collections.OrderedDict()
def _packer(*args):
def func(func_):
for type_ in args:
Expand All @@ -108,10 +108,10 @@ def func(func_):
return func

def packer(type_, func_):
_pack_visitors[type_] = func_
_pack_visitors[typestr(type_)] = func_
_pack_handlers[type_] = func_
_pack_handlers[typestr(type_)] = func_

_unpack_visitors = collections.OrderedDict()
_unpack_handlers = collections.OrderedDict()
def _unpacker(*args):
def func(func_):
for type_ in args:
Expand All @@ -120,46 +120,77 @@ def func(func_):
return func

def unpacker(type_, func_):
_unpack_visitors[type_] = func_
_unpack_visitors[typestr(type_)] = func_
_unpack_handlers[type_] = func_
_unpack_handlers[typestr(type_)] = func_



# function for navigating the above dics of visitors, etc.
def findVisitor(cls_or_name, visitor_dict):
# function for navigating the above dics of handlers, etc.
def findHandler(cls_or_name, handler_dict):
"""
>>> class A(object): pass
...
>>> class B(A): pass
...
>>> class C(object): pass
...
>>> visitors={A:'AAA'}
>>> findVisitor(A, visitors)
>>> handlers={A:'AAA'}
>>> findHandler(A, handlers)
(<class 'couchable.core.A'>, 'AAA')
>>> findVisitor(B, visitors)
>>> findHandler(B, handlers)
(<class 'couchable.core.A'>, 'AAA')
>>> findVisitor(C, visitors)
>>> findHandler(C, handlers)
(None, None)
"""
#if isinstance(cls_or_name, basestring):
# for type_, visitor in reversed(visitor_dict.items()):
# for type_, handler in reversed(handler_dict.items()):
# if cls_or_name == str(type_):
# return type_, visitor
# return type_, handler
#el
if cls_or_name in visitor_dict:
return cls_or_name, visitor_dict[cls_or_name]
if cls_or_name in handler_dict:
return cls_or_name, handler_dict[cls_or_name]
else:
for type_, visitor in reversed(visitor_dict.items()):
for type_, handler in reversed(handler_dict.items()):
if isinstance(type_, type) and issubclass(cls_or_name, type_):
return type_, visitor
return type_, handler

return None, None

class CouchableDb(object):
"""
>>> import couchable
>>> cdb=couchable.CouchableDb('testing')
>>> class SimpleDoc(couchable.CouchableDoc):
... def __init__(self, **kwargs):
... for name, value in kwargs.items():
... setattr(self, name, value)
...
>>> a = SimpleDoc(name='AAA')
>>> b = SimpleDoc(name='BBB', a=a)
>>> c = SimpleDoc(name='CCC', a=a)
>>> id_list = cdb.store([b, c])
>>> id_list
['...SimpleDoc:...', '...SimpleDoc:...']
>>> b, c = cdb.load(id_list)
>>> assert b.a is c.a
>>> cdb.db[b._id]
<Document '...SimpleDoc:...'@'...' {'a': 'couchable:id:...SimpleDoc:...', 'couchable:': {'class': 'SimpleDoc', 'module': '...'}, 'name': 'BBB'}>
"""

_wrapper_cache = weakref.WeakValueDictionary()

def __init__(self, db):
def __init__(self, name=None, url=None, db=None):
if db is None:
if url is None:
server = couchdb.Server()
else:
server = couchdb.Server(url)

try:
db = server['pykour']
except:
db = server.create('pykour')

assert db not in self._wrapper_cache

self._wrapper_cache[db] = self
Expand Down Expand Up @@ -214,7 +245,7 @@ def _store(self, obj):
if isinstance(obj, (CouchableDb, couchdb.client.Server, couchdb.client.Database)):
raise UncouchableException("Illegal to attempt to store objects of type", type(obj), obj)

base_cls, func_tuple = findVisitor(type(obj), _couchable_types)
base_cls, func_tuple = findHandler(type(obj), _couchable_types)
if func_tuple:
func_tuple[0](obj, self)

Expand All @@ -237,17 +268,17 @@ def _store(self, obj):
def _pack(self, parent_doc, data, attachment_list, name, isKey=False):
cls = type(data)

base_cls, visitor = findVisitor(cls, _pack_visitors)
base_cls, handler = findHandler(cls, _pack_handlers)

if visitor:
return visitor(self, parent_doc, data, attachment_list, name, isKey)
if handler:
return handler(self, parent_doc, data, attachment_list, name, isKey)
else:
raise UncouchableException("No _packer for type", cls, data)

#if cls in _pack_visitors:
# return _pack_visitors[cls](self, parent_doc, data, attachment_list, name, isKey)
#if cls in _pack_handlers:
# return _pack_handlers[cls](self, parent_doc, data, attachment_list, name, isKey)
#else:
# for types, func in reversed(_pack_visitors.items()):
# for types, func in reversed(_pack_handlers.items()):
# if isinstance(data, types):
# return func(self, parent_doc, data, attachment_list, name, isKey)
# break
Expand All @@ -256,7 +287,7 @@ def _pack(self, parent_doc, data, attachment_list, name, isKey=False):

def _obj2doc_empty(self, data):
"""
>>> cdb=CouchableDb(couchdb.Server()['testing'])
>>> cdb=CouchableDb('testing')
>>> obj = object()
>>> pprint.pprint(cdb._obj2doc_empty(obj))
{'couchable:': {'class': 'object', 'module': '__builtin__'}}
Expand All @@ -270,7 +301,7 @@ def _obj2doc_empty(self, data):

def _obj2doc_consargs(self, data, args=None, kwargs=None):
"""
>>> cdb=CouchableDb(couchdb.Server()['testing'])
>>> cdb=CouchableDb('testing')
>>> obj = tuple([1, 2, 3])
>>> pprint.pprint(cdb._obj2doc_consargs(obj, list(obj), {}))
{'couchable:': {'args': [1, 2, 3],
Expand All @@ -294,7 +325,7 @@ def _obj2doc_consargs(self, data, args=None, kwargs=None):
@_packer(object)
def _pack_object(self, parent_doc, data, attachment_list, name, isKey):
"""
>>> cdb=CouchableDb(couchdb.Server()['testing'])
>>> cdb=CouchableDb('testing')
>>> parent_doc = {}
>>> attachment_list = []
>>> class Foo(object):
Expand Down Expand Up @@ -323,7 +354,7 @@ def _pack_object(self, parent_doc, data, attachment_list, name, isKey):
'module': '__builtin__'}}}}}
"""
cls = type(data)
base_cls, callback_tuple = findVisitor(cls, _couchable_types)
base_cls, callback_tuple = findHandler(cls, _couchable_types)

if base_cls:
self._store(data)
Expand All @@ -348,7 +379,7 @@ def _pack_object(self, parent_doc, data, attachment_list, name, isKey):
@_packer(str, unicode)
def _pack_native(self, parent_doc, data, attachment_list, name, isKey):
"""
>>> cdb=CouchableDb(couchdb.Server()['testing'])
>>> cdb=CouchableDb('testing')
>>> parent_doc = {}
>>> attachment_list = []
Expand Down Expand Up @@ -377,7 +408,7 @@ def _pack_native(self, parent_doc, data, attachment_list, name, isKey):
@_packer(int, long, float)
def _pack_native_keyAsRepr(self, parent_doc, data, attachment_list, name, isKey):
"""
>>> cdb=CouchableDb(couchdb.Server()['testing'])
>>> cdb=CouchableDb('testing')
>>> parent_doc = {}
>>> attachment_list = []
>>> data = 1234
Expand All @@ -399,7 +430,7 @@ def _pack_native_keyAsRepr(self, parent_doc, data, attachment_list, name, isKey)
@_packer(tuple, frozenset)
def _pack_consargs_keyAsKey(self, parent_doc, data, attachment_list, name, isKey):
"""
>>> cdb=CouchableDb(couchdb.Server()['testing'])
>>> cdb=CouchableDb('testing')
>>> parent_doc = {}
>>> attachment_list = []
Expand Down Expand Up @@ -454,7 +485,7 @@ def _pack_consargs_keyAsKey(self, parent_doc, data, attachment_list, name, isKey
@_packer(list)
def _pack_list_noKey(self, parent_doc, data, attachment_list, name, isKey):
"""
>>> cdb=CouchableDb(couchdb.Server()['testing'])
>>> cdb=CouchableDb('testing')
>>> parent_doc = {}
>>> attachment_list = []
Expand All @@ -479,7 +510,7 @@ def _pack_list_noKey(self, parent_doc, data, attachment_list, name, isKey):
@_packer(dict)
def _pack_dict_keyMeansObject(self, parent_doc, data, attachment_list, name, isObjDict):
"""
>>> cdb=CouchableDb(couchdb.Server()['testing'])
>>> cdb=CouchableDb('testing')
>>> parent_doc = {}
>>> attachment_list = []
Expand Down Expand Up @@ -525,11 +556,11 @@ def _pack_dict_keyMeansObject(self, parent_doc, data, attachment_list, name, isO
def _pack_attachment(self, parent_doc, data, attachment_list, name, isKey):
cls = type(data)

base_cls, visitor_tuple = findVisitor(cls, _attachment_visitors)
base_cls, handler_tuple = findHandler(cls, _attachment_handlers)

content = visitor_tuple[0](data)
content = handler_tuple[0](data)

attachment_list.append((content, name, visitor_tuple[2]))
attachment_list.append((content, name, handler_tuple[2]))
return '{}{}:{}:{}'.format(FIELD_NAME, 'attachment', typestr(base_cls), name)

def _unpack(self, parent_doc, doc, loaded_dict, inst=None):
Expand Down Expand Up @@ -558,14 +589,14 @@ def _unpack(self, parent_doc, doc, loaded_dict, inst=None):
return self._unpack(parent_doc, parent_doc[FIELD_NAME]['keys'][doc], loaded_dict)

elif method_str == 'attachment':
base_cls, visitor_tuple = findVisitor(type_str, _attachment_visitors)
base_cls, handler_tuple = findHandler(type_str, _attachment_handlers)

if base_cls is None:
# FIXME: error?
print type_str, data, _attachment_visitors
print type_str, data, _attachment_handlers

attachment_response = self.db.get_attachment(parent_doc, data)
return visitor_tuple[1](attachment_response.read())
return handler_tuple[1](attachment_response.read())
else:
# FIXME: error?
pass
Expand Down Expand Up @@ -669,7 +700,7 @@ def _load(self, _id, loaded_dict):
#print self._obj_by_id.items()
obj = self._unpack(doc, doc, loaded_dict, obj)

base_cls, func_tuple = findVisitor(type(obj), _couchable_types)
base_cls, func_tuple = findHandler(type(obj), _couchable_types)
if func_tuple:
func_tuple[1](obj, self)

Expand Down Expand Up @@ -726,7 +757,7 @@ def doGunzip(data):
gz_file = gzip.GzipFile(mode='rb', fileobj=str_io)
return gz_file.read()

_attachment_visitors = collections.OrderedDict()
_attachment_handlers = collections.OrderedDict()
def registerAttachmentType(type_, pack_func, unpack_func, content_type, gzip=False):
"""
Example: registerAttachmentType(CouchableAttachment, \
Expand All @@ -735,13 +766,13 @@ def registerAttachmentType(type_, pack_func, unpack_func, content_type, gzip=Fal
'application/octet-stream')
"""
if gzip:
visitor_tuple = (lambda data: doGzip(pack_func(data)), lambda data: unpack_func(doGunzip(data)), content_type)
handler_tuple = (lambda data: doGzip(pack_func(data)), lambda data: unpack_func(doGunzip(data)), content_type)
else:
visitor_tuple = (pack_func, unpack_func, content_type)
handler_tuple = (pack_func, unpack_func, content_type)

_packer(type_)(CouchableDb._pack_attachment)
_attachment_visitors[type_] = visitor_tuple
_attachment_visitors[typestr(type_)] = visitor_tuple
_attachment_handlers[type_] = handler_tuple
_attachment_handlers[typestr(type_)] = handler_tuple

class CouchableAttachment(object):
"""
Expand Down
11 changes: 6 additions & 5 deletions couchable/test_couchable.py
Expand Up @@ -35,7 +35,7 @@
import couchable.core

def dumpcdb(func):
def _func(self):
def test_dumpcdb_(self):
try:
func(self)
except:
Expand All @@ -47,7 +47,7 @@ def _func(self):

raise

return _func
return test_dumpcdb_

class Simple(object):
def __init__(self, **kwargs):
Expand Down Expand Up @@ -87,7 +87,7 @@ def setUp(self):
except:
pass

self.cdb = couchable.CouchableDb(self.server.create('testing'))
self.cdb = couchable.CouchableDb('testing')

self.simple_dict = {
'int': 1,
Expand All @@ -105,7 +105,8 @@ def tearDown(self):
@dumpcdb
def test_docs(self):
# doctest returns a tuple of (failed, attempted)
self.assertEqual(doctest.testmod(couchable.core, optionflags=(doctest.REPORT_CDIFF | doctest.NORMALIZE_WHITESPACE))[0], 0)
self.assertEqual(doctest.testmod(couchable.core,
optionflags=(doctest.REPORT_CDIFF | doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS))[0], 0)


@dumpcdb
Expand Down Expand Up @@ -195,7 +196,7 @@ def test_docCycles(self):
limit = sys.getrecursionlimit()
try:
# This number might need to get tweaked if this test is failing; that's fine
sys.setrecursionlimit(70)
sys.setrecursionlimit(100)

a = SimpleDoc(name='AAA')
b = SimpleDoc(name='BBB', a=a)
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -23,7 +23,7 @@

setup(
name='couchable',
version='0.0.1a3',
version='0.0.1a4',
author='Eli Stevens',
author_email='wickedgrey@gmail.com',
url='http://github.com/wickedgrey/couchable',
Expand Down

0 comments on commit 4764b3d

Please sign in to comment.