Commits

codernity committed 8152dc6

First public release

Comments (0)

Files changed (57)

+.+\.pyc
+.*~
+\.ropeproject
+.*eproject.cfg
+\#.*\#
+.tox
+.*test_db.*
+build/
+htmlcov/
+.*egg-info.*
+dist/
+.coverage/
+.project
+.coverage
+.settings/
+htmlcov_.*
+coverage.*\.xml
+junit-.*\.xml
+.*\.orig

CodernityDBPyClient/__init__.py

+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright 2011-2012 Codernity (http://codernity.com)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+__version__ = '0.3.1'

CodernityDBPyClient/all_exceptions.py

+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright 2011-2012 Codernity (http://codernity.com)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#CodernityDB.index:
+
+
+class IndexException(Exception):
+    pass
+
+
+class IndexNotFoundException(IndexException):
+    pass
+
+
+class ReindexException(IndexException):
+    pass
+
+
+class TryReindexException(ReindexException):
+    pass
+
+
+class ElemNotFound(IndexException):
+    pass
+
+
+class DocIdNotFound(ElemNotFound):
+    pass
+
+
+class IndexConflict(IndexException):
+    pass
+
+
+class IndexPreconditionsException(IndexException):
+    pass
+
+
+#CodernityDB.storage:
+class StorageException(Exception):
+    pass
+
+
+#CodernityDB.tree_index:
+class NodeCapacityException(IndexException):
+    pass
+
+
+#CodernityDB.database:
+class DatabaseException(Exception):
+    pass
+
+
+class RecordNotFound(DatabaseException):
+    pass
+
+
+class RevConflict(DatabaseException):
+    pass
+
+
+class DatabaseClientException(Exception):
+    pass
+
+
+class PreconditionsException(DatabaseException):
+    pass
+
+
+class RecordDeleted(DatabaseException):
+    pass
+
+
+class DatabaseConflict(DatabaseException):
+    pass
+
+
+class DatabasePathException(DatabaseException):
+    pass
+
+
+class DatabaseIsNotOpened(PreconditionsException):
+    pass

CodernityDBPyClient/client.py

+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright 2011-2012 Codernity (http://codernity.com)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import sys
+import imp
+
+
+class CodernityDBImportFinder(object):
+    """
+    An import hook that performs lookups for CodernityDB modules. It makes a embeded imports to make with remote database.
+    """
+    replace_classes = ['Database', ]
+    rename_classes = {'CodernityDB.database_thread_safe': {'Database': 'ThreadSafeDatabase'},
+                      'CodernityDB.database_super_thread_safe': {'Database': 'SuperThreadSafeDatabase'},
+                      'CodernityDB.database_gevent': {'Database': 'GeventDatabase'}}
+
+    replace_modules = {'CodernityDB.database': ['CodernityDBPyClient.all_exceptions', 'CodernityDBPyClient.database'],
+                       'CodernityDB.index': ['CodernityDBPyClient.all_exceptions'],
+                       'CodernityDB.hash_index': ['CodernityDBPyClient.all_exceptions'],
+                       'CodernityDB.tree_index': ['CodernityDBPyClient.all_exceptions'],
+                       'CodernityDB.storage': ['CodernityDBPyClient.all_exceptions'],
+                       'CodernityDB.database_gevent': ['CodernityDBPyClient.database'],
+                       'CodernityDB.database_super_thread_safe': ['CodernityDBPyClient.database'],
+                       'CodernityDB.database_thread_safe': ['CodernityDBPyClient.database'],
+                       'CodernityDB.debug_stuff': ['CodernityDBPyClient.all_exceptions'],
+                       'CodernityDB.env': ['CodernityDBPyClient.all_exceptions'],
+                       'CodernityDB.lfu_cache_with_lock': ['CodernityDBPyClient.all_exceptions'],
+                       'CodernityDB.lfu_cache': ['CodernityDBPyClient.all_exceptions'],
+                       'CodernityDB.misc': ['CodernityDBPyClient.all_exceptions'],
+                       'CodernityDB.patch': ['CodernityDBPyClient.all_exceptions'],
+                       'CodernityDB.rr_cache_with_lock': ['CodernityDBPyClient.all_exceptions'],
+                       'CodernityDB.rr_cache': ['CodernityDBPyClient.all_exceptions'],
+                       }
+
+    recur_set = set()
+
+    def find_module(self, fullname, path=None):
+        self.path = path
+        self.main_replace_modules = [name.split(
+            '.')[0] for name in self.replace_modules.keys()]
+        if fullname == 'CodernityDB' and not fullname in self.recur_set:
+            return self
+        elif fullname in self.recur_set or not fullname.startswith(tuple(self.replace_modules)) or fullname.startswith('CodernityDBPyClient'):
+            try:
+                self.recur_set.remove(fullname)
+            except:
+                pass
+            return None
+        else:
+            return self
+
+    def load_module(self, fullname):
+        self.recur_set.add(fullname)
+        name_tab = fullname.split('.')
+        if len(name_tab) > 2:
+            name = name_tab[-1]
+            if not name in sys.modules['.'.join(name_tab[:-1])].__dict__:
+                raise ImportError('cannot import name %s' % name)
+            return sys.modules['.'.join(name_tab[:-1])].__dict__[name]
+        else:
+            try:
+                if len(name_tab) > 1:
+                    imported_module = __import__(fullname, globals(), locals(
+                    ), [], 0).__dict__[name_tab[-1]]
+                else:
+                    imported_module = __import__(fullname,
+                                                 globals(), locals(), [], 0)
+            except ImportError:
+                is_imported = False
+                if len(name_tab) == 1:
+                    imported_module = sys.modules.setdefault(fullname,
+                                                             imp.new_module(fullname))
+                    imported_module.__file__ = fullname
+                    imported_module.__name__ = fullname
+                    imported_module.__path__ = []
+                    imported_module.__loader__ = self
+                    imported_module.__package__ = fullname
+                elif fullname in self.replace_modules:
+                    imported_module = __import__('.'.join(name_tab[:-1]))
+                    new_module = sys.modules.setdefault(fullname,
+                                                        imp.new_module(fullname))
+                    new_module.__file__ = fullname
+                    new_module.__name__ = fullname
+                    new_module.__path__ = []
+                    new_module.__loader__ = self
+                    new_module.__package__ = fullname
+                    imported_module.__dict__[name_tab[-1]] = new_module
+                else:
+                    raise
+            else:
+                is_imported = True
+
+            if fullname in self.replace_modules:
+                for replace_name in self.replace_modules[fullname]:
+                    replaced_module = __import__(replace_name)
+                    for key in sys.modules[replace_name].__dict__:
+                        if (not key.startswith('__') or not key.endswith('__')) and (key in self.replace_classes or not is_imported):
+                            if fullname in self.rename_classes and key in self.rename_classes[fullname]:
+                                new_key = self.rename_classes[fullname][key]
+                            else:
+                                new_key = key
+                            sys.modules[fullname].__dict__[new_key] = sys.modules[replace_name].__dict__[key]
+        return imported_module
+
+
+def setup():
+    """
+    Call this function **before** any of your CodernityDB imports to make use of import_hook and remote database.
+    """
+    sys.meta_path.append(CodernityDBImportFinder())

CodernityDBPyClient/database.py

+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright 2011-2012 Codernity (http://codernity.com)
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# needed because of globals()[...]
+from CodernityDB.database import DatabaseException, RecordNotFound, RevConflict, PreconditionsException, \
+    RecordDeleted, DatabaseConflict, DatabasePathException, DatabaseIsNotOpened
+from CodernityDB.index import IndexException, IndexNotFoundException, ReindexException, TryReindexException, ElemNotFound, DocIdNotFound, IndexConflict, IndexPreconditionsException
+from CodernityDB.storage import StorageException
+# ^^
+import requests
+import json
+import sys
+from inspect import getsource
+
+try:
+    import msgpack
+except ImportError:
+    has_msgpack = False
+else:
+    has_msgpack = True
+
+
+try:
+    from CodernityDB.index import Index
+except ImportError:
+    has_index = False
+else:
+    has_index = True
+
+
+class Unauthorized(DatabaseException):
+    pass
+
+
+class DatabaseClientException(DatabaseException):
+    pass
+
+content_types = {
+    'application/json': (lambda x, enc: json.loads(x, encoding=enc),
+                         lambda x, enc: json.dumps(x, encoding=enc))
+}
+content_types['json'] = content_types['application/json']
+
+if has_msgpack:
+    content_types['application/msgpack'] = (lambda x, enc: msgpack.unpackb(x, encoding=enc),
+                                            lambda x, enc: msgpack.packb(x, encoding=enc))
+    content_types['msgpack'] = content_types['application/msgpack']
+
+
+def remote_excepthook(type, value, traceback):
+    _traceback = getattr(value, 'remote_traceback', traceback)
+    if _traceback is None:
+        _traceback = traceback
+    # for some reason when _traceback it doesn't work at all (replaced by traceback)
+    sys.__excepthook__(type, value, traceback)
+
+sys.excepthook = remote_excepthook
+
+
+def raise_proper_exception(response, load_fun):
+    """
+    Raises correct exception it will try to raise the same exception as on remote side
+    """
+    def get_exception_from_response(data):
+        if not 'exception' in data or not 'reason' in data:
+            raise DatabaseException('Invalid response')
+
+        if data['exception'] in globals():
+            exc = globals()[data['exception']]
+        else:
+            try:
+                import exceptions
+                exc = getattr(exceptions, data['exception'])
+            except AttributeError:
+                exc = Exception
+        if 'traceback' in data:
+            traceback = data['traceback']
+        else:
+            traceback = None
+        return traceback, exc(data['reason'])
+
+    if response.status_code == 401:
+        response.raw.read()
+        raise Unauthorized("Authorization failed")
+    try:
+        data = load_fun(response.raw.read())
+    except:
+        raise DatabaseException(response.raw.reason)
+    else:
+        traceback, exc = get_exception_from_response(data)
+        if traceback is not None:
+            exc.remote_traceback = traceback
+        raise exc
+
+
+class NONE:
+    """
+    For internal use only, different "none" than builtin None.
+    """
+    pass
+
+
+class Database(object):
+    """
+    All methods are exactly like methods in CodernityDB embeded. So refer to that documentation for methods description
+
+    :param string path: path to database (will work only when remote database is not opened yet)
+    :param string url: a url to connect to remote database
+    :param boolean keep_alive: keep alive for http or not
+    :param tuple auth: an authorization data for remote database. an iterable of (login, password)
+    :param string content_type: a default content type to use with database. by default ``application/json`` but change it to ``application/msgpack`` if you want use msgpack instead of json (which is recommended)
+    :param string encoding: a data encoding
+    :param boolean connect: connect to remote database automaticaly or not.
+
+    """
+
+    custom_header = ""
+
+    def __init__(self, path=None, url='http://localhost:9876', keep_alive=True, auth=('admin', 'password'), content_type='application/json', encoding='utf-8', connect=True):
+        self._path = path
+        self.set_content_type(content_type)
+        self.url = url
+        self.path = path
+        self.keep_alive = keep_alive
+        self.auth = auth
+        self.set_encoding(encoding)
+        self.session = None
+        if connect:
+            self.connect()
+            #self.initialize(path, makedir=False)
+
+    @property
+    def path(self):
+        return self._path
+
+    @path.setter
+    def path(self, data):
+        if self._path is None:
+            self._path = data
+        elif self._path == data:
+            pass
+        else:
+            raise DatabaseClientException(
+                "You can't change path when remote database is connected")
+
+    def __dumps(self, data):
+        fun = content_types[self.content_type][1]
+        return fun(data, self.encoding)
+
+    def __loads(self, data):
+        fun = content_types[self.content_type][0]
+        return fun(data, self.encoding)
+
+    def remote_init(self):
+        if self.opened():
+            details = self.get_db_details()
+            self.path = details['path']
+        else:
+            if self.path:
+                self.initialize(self.path, makedir=False)
+
+    def check_connection(self):
+        try:
+            self.get_version()
+        except:
+            self.disconnect()
+            raise DatabaseClientException("Database not found")
+        else:
+            return True
+
+    def _request(self, function, data={}):
+        if not self.session:
+            raise DatabaseClientException("Not connected")
+        headers = {
+            'Content-Type': self.content_type,
+            'Content-Encoding': self.encoding
+        }
+        url = "%s/%s" % (self.url, function)
+        data = self.__dumps(data)
+        result = self.session.post(url, headers=headers,
+                                   auth=self.auth, data=data, prefetch=False)
+        if not result.status_code == requests.codes.ok:
+            raise_proper_exception(result, self.__loads)
+        return result.raw.read()
+
+    def set_auth(self, login, password):
+        self.auth = (login, password)
+
+    def set_content_type(self, content_type):
+        if content_type in content_types.keys():
+            self.content_type = content_type
+        else:
+            raise DatabaseClientException(
+                'Use one of these: %s' % (content_types.keys()))
+
+    def set_encoding(self, encoding):
+        self.encoding = encoding
+
+    def connect(self):
+        self.session = requests.session()
+        self.session.config['keep_alive'] = self.keep_alive
+        res = self.check_connection()
+        if res:
+            self.remote_init()
+
+    def disconnect(self):
+        self.session = None
+        self._path = None
+
+    def header_for_indexes(self, index_name, index_class, db_custom="", ind_custom="", classes_code=""):
+        function = 'get_index_header'
+        result = self._request(function)
+        header = self.__loads(result)
+        header = header.replace("__REPLACE_NAME__", index_name)
+        header = header.replace("__REPLACE_CLASS__", index_class)
+        header = header.replace("# db_custom\n", "# db_custom\n%s" % db_custom)
+        header = header.replace(
+            "# ind_custom\n", "# ind_custom\n%s" % ind_custom)
+        header = header.replace(
+            "# classes_code\n", "# classes_code\n%s" % classes_code)
+        return header
+
+    def opened(self):
+        function = 'opened'
+        result = self._request(function)
+        return self.__loads(result)
+
+    def open(self, path=None):
+        if self.opened():
+            return self.path
+        function = 'open'
+        req_data = {}
+        if path:
+            self.path = path
+            req_data = {'path': self.path}
+        result = self._request(function, req_data)
+        return self.__loads(result)
+
+    def close(self):
+        function = 'close'
+        result = self._request(function)
+        return self.__loads(result)
+
+    def exists(self):
+        function = 'exists'
+        result = self._request(function)
+        return self.__loads(result)
+
+    def destroy(self):
+        function = 'destroy'
+        result = self._request(function)
+        return self.__loads(result)
+
+    def initialize(self, path=None, makedir=True):
+        function = 'initialize'
+        if path:
+            self.path = path
+        req_data = {'path': self.path, 'makedir': makedir}
+        result = self._request(function, req_data)
+        return self.__loads(result)
+
+    def __get_new_index_code(self, new_index, ind_kwargs):
+        if isinstance(new_index, basestring) and not new_index.startswith("path:"):
+            new_index_code = new_index
+            name = new_index.splitlines()[0][2:]
+            name = name.strip()
+        elif isinstance(new_index, basestring) and new_index.startswith("path:"):
+            raise DatabaseClientException("Not supported on client side")
+        elif has_index and isinstance(new_index, Index):
+            name = new_index.name
+            code = getsource(new_index.__class__)
+            cls_code = getattr(new_index, 'classes_code', [])
+            classes_code = ""
+            for curr in cls_code:
+                classes_code += getsource(curr) + '\n\n'
+            header = self.header_for_indexes(name,
+                                             new_index.__class__.__name__,
+                                             getattr(
+                                                 self, 'custom_header', ''),
+                                             getattr(new_index,
+                                                     'custom_header', ''),
+                                             classes_code)
+            new_index_code = header + code
+            init_arguments = new_index.__class__.__init__.im_func.func_code.co_varnames[3:]  # ignore self, path and name
+            for curr in init_arguments:
+                if curr not in ('args', 'kwargs'):
+                    v = getattr(new_index, curr, NONE())
+                    if not isinstance(v, NONE):
+                        ind_kwargs[curr] = v
+        else:
+            raise PreconditionsException("Argument must be Index instance or valid string index format")
+        return new_index_code
+
+    def add_index(self, new_index, create=True, ind_kwargs=None):
+        if ind_kwargs is None:
+            ind_kwargs = {}
+        function = 'add_index'
+        new_index_code = self.__get_new_index_code(new_index, ind_kwargs)
+        data = {"new_index": new_index_code,
+                "create": create,
+                "ind_kwargs": ind_kwargs}
+        result = self._request(function, data)
+        return self.__loads(result)
+
+    def edit_index(self, index, reindex=False, ind_kwargs=None):
+        if ind_kwargs is None:
+            ind_kwargs = {}
+        function = 'edit_index'
+        new_index_code = self.__get_new_index_code(index, ind_kwargs)
+        data = {"index": new_index_code,
+                "reindex": reindex,
+                "ind_kwargs": ind_kwargs}
+        result = self._request(function, data)
+        return self.__loads(result)
+
+    def get_version(self):
+        function = 'get_version'
+        data = {}
+        result = self._request(function, data)
+        return self.__loads(result)
+
+    def get_index_code(self, index_name):
+        function = 'get_index_code'
+        data = {'index_name': index_name}
+        result = self._request(function, data)
+        return self.__loads(result)
+
+    def get_indexes_names(self):
+        function = 'get_indexes_names'
+        result = self._request(function)
+        return self.__loads(result)
+
+    def destroy_index(self, index):
+        function = 'destroy_index'
+        if has_index and isinstance(index, Index):
+            raise DatabaseClientException("Not supported on client side")
+        if index == 'id':
+            raise PreconditionsException("Id index cannot be destroyed")
+        data = {'index': index}
+        self._request(function, data)
+
+    def compact_index(self, index):
+        function = 'compact_index'
+        if has_index and isinstance(index, Index):
+            index = index.name
+        data = {'index': index}
+        self._request(function, data)
+        return None
+
+    def compact_indexes(self):
+        indexes = self.get_indexes_names()
+        for index in indexes:
+            self.compact_index(index)
+
+    def reindex_index(self, index):
+        function = 'reindex_index'
+        if has_index and isinstance(index, Index):
+            raise DatabaseClientException("Not supported on client side")
+#            index = index.name
+        if index == 'id':
+            raise PreconditionsException("Id index cannot be reindexed")
+        data = {'index': index}
+        self._request(function, data)
+
+    def reindex_indexes(self):
+        function = 'reindex'
+        self._request(function)
+
+    def reindex(self):
+        self.reindex_indexes()
+
+    def insert(self, data):
+        function = 'insert'
+        if '_rev' in data:
+            raise PreconditionsException(
+                "Can't add record with forbidden fields")
+        req_data = {'data': data}
+        result = self._request(function, req_data)
+        data_info = self.__loads(result)
+        data.update(data_info)
+        return data_info
+
+    def update(self, data):
+        function = 'update'
+        if not '_rev' in data or not '_id' in data:
+            raise PreconditionsException("Can't update without _rev or _id")
+        req_data = {'data': data}
+        result = self._request(function, req_data)
+        data_info = self.__loads(result)
+        data.update(data_info)
+        return data_info
+
+    def get(self, index_name, key, with_doc=False, with_storage=True):
+        function = 'get'
+        data = {'index_name': index_name,
+                'key': key,
+                'with_doc': with_doc,
+                'with_storage': with_storage
+                }
+        result = self._request(function, data)
+        return self.__loads(result)
+
+    def get_many(self, index_name, key=None, limit=1, offset=0, with_doc=False, with_storage=True, **kwargs):
+        function = 'get_many'
+        if index_name == 'id':
+            raise PreconditionsException("Can't get many from `id`")
+        data = {'index_name': index_name,
+                'key': key,
+                'limit': limit,
+                'offset': offset,
+                'with_doc': with_doc,
+                'with_storage': with_storage
+                }
+        data.update(kwargs)
+        result = self._request(function, data)
+        return self.__loads(result)
+
+    def all(self, index_name, limit=-1, offset=0, with_doc=False, with_storage=True):
+        function = 'all'
+        data = {'index_name': index_name,
+                'limit': limit,
+                'offset': offset,
+                'with_doc': with_doc,
+                'with_storage': with_storage
+                }
+        result = self._request(function, data)
+        return self.__loads(result)
+
+    def delete(self, data):
+        function = 'delete'
+        req_data = {'data': data}
+        self._request(function, req_data)
+        return True
+
+    def compact(self):
+        function = 'compact'
+        self._request(function)
+
+    def flush(self):
+        function = 'flush'
+        self._request(function)
+
+    def fsync(self):
+        function = 'fsync'
+        self._request(function)
+
+    def get_index_details(self, name):
+        function = 'get_index_details'
+        req_data = {'name': name}
+        result = self._request(function, req_data)
+        return self.__loads(result)
+
+    def get_db_details(self):
+        function = 'get_db_details'
+        result = self._request(function)
+        return self.__loads(result)
+
+    def count(self, target_funct, *args, **kwargs):
+        if callable(target_funct):
+            target_funct = target_funct.__name__
+        function = 'count/%s' % target_funct
+        if args:
+            function += '/' + '/'.join(map(self.__dumps, args))
+        req_data = kwargs
+        result = self._request(function, req_data)
+        return self.__loads(result)
+
+    def run(self, target_index, target_funct, *args, **kwargs):
+        if callable(target_funct):
+            target_funct = target_funct.__name__
+        function = 'run/%s' % target_index
+        args = (target_funct, ) + args
+        function += '/' + '/'.join(map(self.__dumps, args))
+        req_data = kwargs
+        result = self._request(function, req_data)
+        return self.__loads(result)
+
+    def create(self, path=None, **kwargs):
+        function = 'create'
+        req_data = kwargs
+        if path:
+            self.path = path
+            req_data['path'] = path
+        result = self._request(function, req_data)
+        return self.__loads(result)
+
+    def set_indexes(self, indexes=[]):
+        for index in indexes:
+            self.add_index(index, create=False)
+
+####################################################################
+#
+#                        Not supported
+#
+####################################################################
+
+    def single_update_index(self):
+        raise DatabaseClientException("Not supported on client side")
+
+    def update_id_index(self, data):
+        raise DatabaseClientException("Not supported on client side")
+
+    def update_indexes(self, data):
+        raise DatabaseClientException("Not supported on client side")
+
+    def single_insert_index(self, index, data, doc_id):
+        raise DatabaseClientException("Not supported on client side")
+
+    def insert_id_index(self, data):
+        raise DatabaseClientException("Not supported on client side")
+
+    def insert_indexes(self, data):
+        raise DatabaseClientException("Not supported on client side")
+
+    def single_delete_index(self, index, data, doc_id, old_data):
+        raise DatabaseClientException("Not supported on client side")
+
+    def delete_id_index(self, data):
+        raise DatabaseClientException("Not supported on client side")
+
+    def delete_indexes(self, data):
+        raise DatabaseClientException("Not supported on client side")
+
+    def single_reindex_index(self, index, data):
+        raise DatabaseClientException("Not supported on client side")
+
+    def _add_single_index(self, p, i, index):
+        raise DatabaseClientException("Not supported on client side")
+
+    def _read_index_single(self, p, ind):
+        raise DatabaseClientException("Not supported on client side")
+
+    def _read_indexes(self):
+        raise DatabaseClientException("Not supported on client side")
+Client library for CodernityDB
+==============================
+
+CodernityDB-PyClient is a client library that targets to **100%**
+compatibility between CodernityDB in embeded mode and
+CodernityDB used with CodernityDB-HTTP.
+
+The remote errors will be the same as local ones, and remote traceback will be also included in it.
+
+It will work perfectly even without CodernityDB in your system. That's possible by using python *import hook*.
+
+
+.. image:: https://bitbucket.org/codernity/codernitydb-pyclient/raw/tip/docs/CodernityDBPyClient.png
+    :align: center
+
+
+
+Install
+~~~~~~~
+
+CodernityDB-PyClient is pure Python. So you just need to::
+
+     hg clone ssh://hg@bitbucket.org/codernity/codernitydb-pyclient
+     python setup.py install
+
+.. note::
+    You don't need CodernityDB and CodernityDB-HTTP on the same machine as CodernityDB-PyClient.
+
+
+In near future we will publish packages on PyPi.
+
+
+.. note::
+    If you want to use ``msgpack`` please install ``msgpack-python``, or ``msgpack-pure`` to make use of it.
+
+

docs/CodernityDBPyClient.png

Added
New image
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+PAPER         =
+BUILDDIR      = _build
+
+# Internal variables.
+PAPEROPT_a4     = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+# the i18n builder cannot share the environment and doctrees with the others
+I18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
+
+help:
+	@echo "Please use \`make <target>' where <target> is one of"
+	@echo "  html       to make standalone HTML files"
+	@echo "  dirhtml    to make HTML files named index.html in directories"
+	@echo "  singlehtml to make a single large HTML file"
+	@echo "  pickle     to make pickle files"
+	@echo "  json       to make JSON files"
+	@echo "  htmlhelp   to make HTML files and a HTML help project"
+	@echo "  qthelp     to make HTML files and a qthelp project"
+	@echo "  devhelp    to make HTML files and a Devhelp project"
+	@echo "  epub       to make an epub"
+	@echo "  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+	@echo "  latexpdf   to make LaTeX files and run them through pdflatex"
+	@echo "  text       to make text files"
+	@echo "  man        to make manual pages"
+	@echo "  texinfo    to make Texinfo files"
+	@echo "  info       to make Texinfo files and run them through makeinfo"
+	@echo "  gettext    to make PO message catalogs"
+	@echo "  changes    to make an overview of all changed/added/deprecated items"
+	@echo "  linkcheck  to check all external links for integrity"
+	@echo "  doctest    to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+	-rm -rf $(BUILDDIR)/*
+
+html:
+	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+	@echo
+	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+	$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+	@echo
+	@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+	@echo
+	@echo "Build finished; now you can process the pickle files."
+
+json:
+	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+	@echo
+	@echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+	@echo
+	@echo "Build finished; now you can run HTML Help Workshop with the" \
+	      ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+	@echo
+	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
+	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/CodernityDB-PyClient.qhcp"
+	@echo "To view the help file:"
+	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/CodernityDB-PyClient.qhc"
+
+devhelp:
+	$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+	@echo
+	@echo "Build finished."
+	@echo "To view the help file:"
+	@echo "# mkdir -p $$HOME/.local/share/devhelp/CodernityDB-PyClient"
+	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/CodernityDB-PyClient"
+	@echo "# devhelp"
+
+epub:
+	$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+	@echo
+	@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo
+	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+	@echo "Run \`make' in that directory to run these through (pdf)latex" \
+	      "(use \`make latexpdf' here to do that automatically)."
+
+latexpdf:
+	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+	@echo "Running LaTeX files through pdflatex..."
+	$(MAKE) -C $(BUILDDIR)/latex all-pdf
+	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+	$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+	@echo
+	@echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+	$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+	@echo
+	@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+texinfo:
+	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+	@echo
+	@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
+	@echo "Run \`make' in that directory to run these through makeinfo" \
+	      "(use \`make info' here to do that automatically)."
+
+info:
+	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
+	@echo "Running Texinfo files through makeinfo..."
+	make -C $(BUILDDIR)/texinfo info
+	@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
+
+gettext:
+	$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
+	@echo
+	@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
+
+changes:
+	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+	@echo
+	@echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+	@echo
+	@echo "Link check complete; look for any errors in the above output " \
+	      "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+	@echo "Testing of doctests in the sources finished, look at the " \
+	      "results in $(BUILDDIR)/doctest/output.txt."

docs/_themes/sphinx-codernity/globaltoc.html

+{{ toctree() }}

docs/_themes/sphinx-codernity/layout.html

+<!DOCTYPE html>
+
+{%- set render_sidebar = (not embedded) and (not theme_nosidebar|tobool) and (sidebars != []) %}
+{%- set url_root = pathto('', 1) %}
+{%- if url_root == '#' %}{% set url_root = '' %}{% endif %}
+
+{%- macro relbar(bottom=False) %}
+<div class="related{% if bottom %} related-bottom{% endif %}">
+    <ul class="nav">
+        {%- block rootrellink %}
+        <li class=""><a class="brand" href="{{ pathto(master_doc) }}">{{ project|e }}</a></li>
+        {%- endblock %}
+        {%- for rellink in rellinks %}
+        <li>
+            <a href="{{ pathto(rellink[0]) }}" title="{{ rellink[1]|striptags|e }}" {{ accesskey(rellink[2]) }}>{{ rellink[3] }}</a>
+            {%- if not loop.first %}{% endif %}
+        </li>
+        {%- endfor %}
+        {%- for parent in parents %}
+        <li><a href="{{ parent.link|e }}"
+               {% if loop.last %}{{ accesskey("U") }}{% endif %}>{{ parent.title }}</a>
+        </li>
+        {%- endfor %}
+        {%- block relbaritems %} {% endblock %}
+    </ul>
+</div>
+{%- endmacro %}
+
+{%- macro sidebar() %}
+    {%- if render_sidebar %}
+            {%- if sidebars != None %}
+                {#- new style sidebar: explicitly include/exclude templates #}
+                {%- for sidebartemplate in sidebars %}
+                    {%- include sidebartemplate %}
+                {%- endfor %}
+            {%- else %}
+                {#- old style sidebars: using blocks -- should be deprecated #}
+                {%- block sidebartoc %}
+                    {%- include "localtoc.html" %}
+                {%- endblock %}
+                {%- block sidebarrel %}
+                    {%- include "relations.html" %}
+                {%- endblock %}
+                {%- block sidebarsourcelink %}
+                    {%- include "sourcelink.html" %}
+                {%- endblock %}
+                {%- if customsidebar %}
+                    {%- include customsidebar %}
+                {%- endif %}
+            {%- endif %}
+    {%- endif %}
+{%- endmacro -%}
+
+{%- macro seachbox() %}
+    {%- if pagename != "search" %}
+        <form class="navbar-search" action="{{ pathto('search') }}"
+              method="get">
+            <input type="text" name="q" placeholder="search"/>
+            <input type="hidden" name="check_keywords" value="yes"/>
+            <input type="hidden" name="area" value="default"/>
+        </form>
+    {%- endif %}
+{%- endmacro -%}
+
+<!DOCTYPE html>
+<html lang="en">
+    <head>
+        <meta charset="utf-8">
+        {{ metatags }}
+        {%- if not embedded and docstitle %}
+            {%- set titlesuffix = " &mdash; "|safe + docstitle|e %}
+        {%- else %}
+            {%- set titlesuffix = "" %}
+        {%- endif %}
+        {%- block htmltitle %}
+        <title>{{ title|striptags|e }}{{ titlesuffix }}</title>
+        {%- endblock %}
+        <meta name="viewport" content="width=device-width, initial-scale=1.0">
+        <meta name="description" content="">
+        <meta name="author" content="">
+        <link rel="shortcut icon" href="{{ pathto('_static/favicon.ico', 1) }}">
+        <!-- Le styles -->
+        <link href="{{ pathto('_static/bootstrap.css', 1) }}" rel="stylesheet">
+        <link href="{{ pathto('_static/bootstrap-responsive.css', 1) }}" rel="stylesheet">
+        <link href="{{ pathto('_static/labs.css', 1) }}" rel="stylesheet">
+        <link href="{{ pathto('_static/docs.css', 1) }}" rel="stylesheet">
+        <link href="{{ pathto('_static/pygments.css', 1) }}" rel="stylesheet">
+        {%- for cssfile in css_files %}
+            <link rel="stylesheet" href="{{ pathto(cssfile, 1) }}" type="text/css"/>
+        {%- endfor %}
+        <!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
+        <!--[if lt IE 9]>
+          <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
+        <![endif]-->
+
+        {%- if not embedded %}
+            <script type="text/javascript">
+                var DOCUMENTATION_OPTIONS = {
+                    URL_ROOT:'{{ url_root }}',
+                    VERSION:'{{ release|e }}',
+                    COLLAPSE_INDEX:false,
+                    FILE_SUFFIX:'{{ '' if no_search_suffix else file_suffix }}',
+                    HAS_SOURCE:  {{ has_source|lower }}
+                };
+            </script>
+            {%- set script_files = ["_static/jquery.js", "_static/underscore.js", "_static/bootstrap-dropdown.js"] %}
+            {%- for scriptfile in script_files %}
+                <script type="text/javascript" src="{{ pathto(scriptfile, 1) }}"></script>
+            {%- endfor %}
+
+            {%- if use_opensearch %}
+                <link rel="search" type="application/opensearchdescription+xml"
+                      title="{% trans docstitle=docstitle|e %}Search within {{ docstitle }}{% endtrans %}"
+                      href="{{ pathto('_static/opensearch.xml', 1) }}"/>
+            {%- endif %}
+            {%- if favicon %}
+                <link rel="shortcut icon" href="{{ pathto('_static/' + favicon, 1) }}"/>
+            {%- endif %}
+        {%- endif %}
+        {%- block linktags %}
+            {%- if hasdoc('about') %}
+                <link rel="author" title="{{ _('About these documents') }}"
+                      href="{{ pathto('about') }}"/>
+            {%- endif %}
+            {%- if hasdoc('genindex') %}
+                <link rel="index" title="{{ _('Index') }}"
+                      href="{{ pathto('genindex') }}"/>
+            {%- endif %}
+            {%- if hasdoc('search') %}
+                <link rel="search" title="{{ _('Search') }}" href="{{ pathto('search') }}"/>
+            {%- endif %}
+            {%- if hasdoc('copyright') %}
+                <link rel="copyright" title="{{ _('Copyright') }}" href="{{ pathto('copyright') }}"/>
+            {%- endif %}
+            <link rel="top" title="{{ docstitle|e }}" href="{{ pathto('index') }}"/>
+            {%- if parents %}
+                <link rel="up" title="{{ parents[-1].title|striptags|e }}" href="{{ parents[-1].link|e }}"/>
+            {%- endif %}
+            {%- if next %}
+                <link rel="next" title="{{ next.title|striptags|e }}" href="{{ next.link|e }}"/>
+            {%- endif %}
+            {%- if prev %}
+                <link rel="prev" title="{{ prev.title|striptags|e }}" href="{{ prev.link|e }}"/>
+            {%- endif %}
+        {%- endblock %}
+        {%- block extrahead %} {% endblock %}
+    </head>
+<body>
+
+
+{%- block header %}
+        <div class="masterhead hero-unit">
+            <div class="container">
+                <a href="http://codernity.com"><img src="{{ pathto('_static/logo_subpage.png', 1) }}"></a>
+                <h1><img src="{{ pathto('_static/erlenmeyer-flask-small.png', 1) }}"> <a href="http://labs.codernity.com">Labs</a></h1>
+            </div>
+        </div>
+        <div class="horizontal-bar">
+            <div class="container">
+                <h1><a href="index.html">{{ shorttitle|e }}</a></h1>
+            </div>
+        </div>
+        <!--{{ seachbox() }}-->
+{% endblock %}
+
+{%- block content %}
+<div class="content container">
+    <div class="row-fluid">
+        <div class="docs-sidebar span3">
+            {%- block sidebar %}{{ sidebar() }}{% endblock %}
+        </div>
+        <div class="docs-content span9">
+            {%- block document %}
+                {% block body %} {% endblock %}
+            {%- endblock %}
+        </div>
+    </div>
+</div>
+{%- endblock %}
+
+{%- block footer %}
+<div class="footer">
+    <div class="container">
+        <div class="row-fluid">
+            <div class="span4">
+                <address>
+                    <strong>Codernity</strong><br>
+                    Ostrowskiego 30 St.<br>
+                    Wroclaw, 53-238, Poland<br>
+                </address>
+                {%- if last_updated %}
+                {% trans last_updated=last_updated|e %}Last updated
+                on {{ last_updated }}.{% endtrans %}
+                {%- endif %}
+            </div>
+            <div class="span4 offset4 right">
+                <address>
+                    +48 71 70 70 934 <abbr title="Phone number"><i class="icon-headphones"></i></abbr><br/>
+                    <a href="mailto:contact@codernity.com">contact@codernity.com</a> <abbr title="Email address"><i class="icon-envelope"></i></abbr><br/>
+                    <a href="http://codernity.com">http://codernity.com</a> <abbr title="Website"><i class="icon-home"></i></abbr>
+                </address>
+            </div>
+        </div>
+    </div>
+</div>
+{%- endblock %}
+
+{% if theme_disqus_shortname %}
+{% endif %}
+
+<script src="{{ pathto('_static/bootstrap-scrollspy.js', 1) }}"></script>
+<script src="{{ pathto('_static/bootstrap-affix.js', 1) }}"></script>
+<script src="{{ pathto('_static/labs.js', 1) }}"></script>
+
+<!-- Piwik -->
+    <script type="text/javascript">
+    var pkBaseURL = (("https:" == document.location.protocol) ? "https://piwik.codernity.com/" : "http://piwik.codernity.com/");
+    document.write(unescape("%3Cscript src='" + pkBaseURL + "piwik.js' type='text/javascript'%3E%3C/script%3E"));
+    </script><script type="text/javascript">
+    try {
+    var piwikTracker = Piwik.getTracker(pkBaseURL + "piwik.php", 2);
+    piwikTracker.trackPageView();
+    piwikTracker.enableLinkTracking();
+    } catch( err ) {}
+    </script><noscript><p><img src="http://piwik.codernity.com/piwik.php?idsite=2" style="border:0" alt="" /></p></noscript>
+<!-- End Piwik Tracking Code -->
+
+</body>
+</html>

docs/_themes/sphinx-codernity/static/background.jpg

Added
New image

docs/_themes/sphinx-codernity/static/bootstrap-affix.js

+/* ==========================================================
+ * bootstrap-affix.js v2.1.0
+ * http://twitter.github.com/bootstrap/javascript.html#affix
+ * ==========================================================
+ * Copyright 2012 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ========================================================== */
+
+
+!function ($) {
+
+  "use strict"; // jshint ;_;
+
+
+ /* AFFIX CLASS DEFINITION
+  * ====================== */
+
+  var Affix = function (element, options) {
+    this.options = $.extend({}, $.fn.affix.defaults, options)
+    this.$window = $(window).on('scroll.affix.data-api', $.proxy(this.checkPosition, this))
+    this.$element = $(element)
+    this.checkPosition()
+  }
+
+  Affix.prototype.checkPosition = function () {
+    if (!this.$element.is(':visible')) return
+
+    var scrollHeight = $(document).height()
+      , scrollTop = this.$window.scrollTop()
+      , position = this.$element.offset()
+      , offset = this.options.offset
+      , offsetBottom = offset.bottom
+      , offsetTop = offset.top
+      , reset = 'affix affix-top affix-bottom'
+      , affix
+
+    if (typeof offset != 'object') offsetBottom = offsetTop = offset
+    if (typeof offsetTop == 'function') offsetTop = offset.top()
+    if (typeof offsetBottom == 'function') offsetBottom = offset.bottom()
+
+    affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ?
+      false    : offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ?
+      'bottom' : offsetTop != null && scrollTop <= offsetTop ?
+      'top'    : false
+
+    //temporary workaround
+    this.$element.css('width', $('div.docs-sidebar').css('width'))
+
+    if (this.affixed === affix) return
+
+    this.affixed = affix
+    this.unpin = affix == 'bottom' ? position.top - scrollTop : null
+    this.$element.removeClass(reset).addClass('affix' + (affix ? '-' + affix : ''))
+  }
+
+
+ /* AFFIX PLUGIN DEFINITION
+  * ======================= */
+
+  $.fn.affix = function (option) {
+    return this.each(function () {
+      var $this = $(this)
+        , data = $this.data('affix')
+        , options = typeof option == 'object' && option
+      if (!data) $this.data('affix', (data = new Affix(this, options)))
+      if (typeof option == 'string') data[option]()
+    })
+  }
+
+  $.fn.affix.Constructor = Affix
+
+  $.fn.affix.defaults = {
+    offset: 0
+  }
+
+
+ /* AFFIX DATA-API
+  * ============== */
+
+  $(window).on('load', function () {
+    $('[data-spy="affix"]').each(function () {
+      var $spy = $(this)
+        , data = $spy.data()
+
+      data.offset = data.offset || {}
+
+      data.offsetBottom && (data.offset.bottom = data.offsetBottom)
+      data.offsetTop && (data.offset.top = data.offsetTop)
+
+      $spy.affix(data)
+    })
+  })
+
+
+}(window.jQuery);

docs/_themes/sphinx-codernity/static/bootstrap-dropdown.js

+/* ============================================================
+ * bootstrap-dropdown.js v2.0.0
+ * http://twitter.github.com/bootstrap/javascript.html#dropdown
+ * ============================================================
+ * Copyright 2011 Twitter, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============================================================ */
+
+
+!function( $ ){
+
+  "use strict"
+
+ /* DROPDOWN CLASS DEFINITION
+  * ========================= */
+
+  var toggle = '[data-toggle="dropdown"]'
+    , Dropdown = function ( element ) {
+        $(element).bind('click', this.toggle)
+      }
+
+  Dropdown.prototype = {
+
+    constructor: Dropdown
+
+  , toggle: function ( e ) {
+      var $this = $(this)
+        , selector = $this.attr('data-target') || $this.attr('href')
+        , $parent = $(selector)
+
+      $parent.length || ($parent = $this.parent())
+
+      clearMenus()
+
+      !$parent.hasClass('open') && $parent.toggleClass('open')
+
+      return false
+    }
+
+  }
+
+  function clearMenus() {
+    $(toggle).parent().removeClass('open')
+  }
+
+
+  /* DROPDOWN PLUGIN DEFINITION
+   * ========================== */
+
+  $.fn.dropdown = function ( option ) {
+    return this.each(function () {
+      var $this = $(this)
+        , data = $this.data('dropdown')
+      if (!data) $this.data('dropdown', (data = new Dropdown(this)))
+      if (typeof option == 'string') data[option].call($this)
+    })
+  }
+
+  $.fn.dropdown.Constructor = Dropdown
+
+
+  /* APPLY TO STANDARD DROPDOWN ELEMENTS
+   * =================================== */
+
+  $(function () {
+    $('html').on('click.dropdown.data-api', clearMenus)
+    $('body').on('click.dropdown.data-api', toggle, Dropdown.prototype.toggle)
+  })
+
+}( window.jQuery )

docs/_themes/sphinx-codernity/static/bootstrap-responsive.css

+/*!
+ * Bootstrap Responsive v2.1.0
+ *
+ * Copyright 2012 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ */
+
+.clearfix {
+  *zoom: 1;
+}
+
+.clearfix:before,
+.clearfix:after {
+  display: table;
+  line-height: 0;
+  content: "";
+}
+
+.clearfix:after {
+  clear: both;
+}
+
+.hide-text {
+  font: 0/0 a;
+  color: transparent;
+  text-shadow: none;
+  background-color: transparent;
+  border: 0;
+}
+
+.input-block-level {
+  display: block;
+  width: 100%;
+  min-height: 30px;
+  -webkit-box-sizing: border-box;
+     -moz-box-sizing: border-box;
+          box-sizing: border-box;
+}
+
+.hidden {
+  display: none;
+  visibility: hidden;
+}
+
+.visible-phone {
+  display: none !important;
+}
+
+.visible-tablet {
+  display: none !important;
+}
+
+.hidden-desktop {
+  display: none !important;
+}
+
+.visible-desktop {
+  display: inherit !important;
+}
+
+@media (min-width: 768px) and (max-width: 979px) {
+  .hidden-desktop {
+    display: inherit !important;
+  }
+  .visible-desktop {
+    display: none !important ;
+  }
+  .visible-tablet {
+    display: inherit !important;
+  }
+  .hidden-tablet {
+    display: none !important;
+  }
+}
+
+@media (max-width: 767px) {
+  .hidden-desktop {
+    display: inherit !important;
+  }
+  .visible-desktop {
+    display: none !important;
+  }
+  .visible-phone {
+    display: inherit !important;
+  }
+  .hidden-phone {
+    display: none !important;
+  }
+}
+
+@media (min-width: 1200px) {
+  .row {
+    margin-left: -30px;
+    *zoom: 1;
+  }
+  .row:before,
+  .row:after {
+    display: table;
+    line-height: 0;
+    content: "";
+  }
+  .row:after {
+    clear: both;
+  }
+  [class*="span"] {
+    float: left;
+    margin-left: 30px;
+  }
+  .container,
+  .navbar-static-top .container,
+  .navbar-fixed-top .container,
+  .navbar-fixed-bottom .container {
+    width: 1170px;
+  }
+  .span12 {
+    width: 1170px;
+  }
+  .span11 {
+    width: 1070px;
+  }
+  .span10 {
+    width: 970px;
+  }
+  .span9 {
+    width: 870px;
+  }
+  .span8 {
+    width: 770px;
+  }
+  .span7 {
+    width: 670px;
+  }
+  .span6 {
+    width: 570px;
+  }
+  .span5 {
+    width: 470px;
+  }
+  .span4 {
+    width: 370px;
+  }
+  .span3 {
+    width: 270px;
+  }
+  .span2 {
+    width: 170px;
+  }
+  .span1 {
+    width: 70px;
+  }
+  .offset12 {
+    margin-left: 1230px;
+  }
+  .offset11 {
+    margin-left: 1130px;
+  }
+  .offset10 {
+    margin-left: 1030px;
+  }
+  .offset9 {
+    margin-left: 930px;
+  }
+  .offset8 {
+    margin-left: 830px;
+  }
+  .offset7 {
+    margin-left: 730px;
+  }
+  .offset6 {
+    margin-left: 630px;
+  }
+  .offset5 {
+    margin-left: 530px;
+  }
+  .offset4 {
+    margin-left: 430px;
+  }
+  .offset3 {
+    margin-left: 330px;
+  }
+  .offset2 {
+    margin-left: 230px;
+  }
+  .offset1 {
+    margin-left: 130px;
+  }
+  .row-fluid {
+    width: 100%;
+    *zoom: 1;
+  }
+  .row-fluid:before,
+  .row-fluid:after {
+    display: table;
+    line-height: 0;
+    content: "";
+  }
+  .row-fluid:after {
+    clear: both;
+  }
+  .row-fluid [class*="span"] {
+    display: block;
+    float: left;
+    width: 100%;
+    min-height: 30px;
+    margin-left: 2.564102564102564%;
+    *margin-left: 2.5109110747408616%;
+    -webkit-box-sizing: border-box;
+       -moz-box-sizing: border-box;
+            box-sizing: border-box;
+  }
+  .row-fluid [class*="span"]:first-child {
+    margin-left: 0;
+  }
+  .row-fluid .span12 {
+    width: 100%;
+    *width: 99.94680851063829%;
+  }
+  .row-fluid .span11 {
+    width: 91.45299145299145%;
+    *width: 91.39979996362975%;
+  }
+  .row-fluid .span10 {
+    width: 82.90598290598291%;
+    *width: 82.8527914166212%;
+  }
+  .row-fluid .span9 {
+    width: 74.35897435897436%;
+    *width: 74.30578286961266%;
+  }
+  .row-fluid .span8 {
+    width: 65.81196581196582%;
+    *width: 65.75877432260411%;
+  }
+  .row-fluid .span7 {
+    width: 57.26495726495726%;
+    *width: 57.21176577559556%;
+  }
+  .row-fluid .span6 {
+    width: 48.717948717948715%;
+    *width: 48.664757228587014%;
+  }
+  .row-fluid .span5 {
+    width: 40.17094017094017%;
+    *width: 40.11774868157847%;
+  }
+  .row-fluid .span4 {
+    width: 31.623931623931625%;
+    *width: 31.570740134569924%;
+  }
+  .row-fluid .span3 {
+    width: 23.076923076923077%;
+    *width: 23.023731587561375%;
+  }
+  .row-fluid .span2 {
+    width: 14.52991452991453%;
+    *width: 14.476723040552828%;
+  }
+  .row-fluid .span1 {
+    width: 5.982905982905983%;
+    *width: 5.929714493544281%;
+  }
+  .row-fluid .offset12 {
+    margin-left: 105.12820512820512%;
+    *margin-left: 105.02182214948171%;
+  }
+  .row-fluid .offset12:first-child {
+    margin-left: 102.56410256410257%;
+    *margin-left: 102.45771958537915%;
+  }
+  .row-fluid .offset11 {
+    margin-left: 96.58119658119658%;
+    *margin-left: 96.47481360247316%;
+  }
+  .row-fluid .offset11:first-child {
+    margin-left: 94.01709401709402%;
+    *margin-left: 93.91071103837061%;
+  }
+  .row-fluid .offset10 {
+    margin-left: 88.03418803418803%;
+    *margin-left: 87.92780505546462%;
+  }
+  .row-fluid .offset10:first-child {
+    margin-left: 85.47008547008548%;
+    *margin-left: 85.36370249136206%;
+  }
+  .row-fluid .offset9 {
+    margin-left: 79.48717948717949%;
+    *margin-left: 79.38079650845607%;
+  }
+  .row-fluid .offset9:first-child {
+    margin-left: 76.92307692307693%;
+    *margin-left: 76.81669394435352%;
+  }
+  .row-fluid .offset8 {
+    margin-left: 70.94017094017094%;
+    *margin-left: 70.83378796144753%;
+  }
+  .row-fluid .offset8:first-child {
+    margin-left: 68.37606837606839%;
+    *margin-left: 68.26968539734497%;
+  }
+  .row-fluid .offset7 {
+    margin-left: 62.393162393162385%;
+    *margin-left: 62.28677941443899%;
+  }
+  .row-fluid .offset7:first-child {
+    margin-left: 59.82905982905982%;
+    *margin-left: 59.72267685033642%;
+  }
+  .row-fluid .offset6 {
+    margin-left: 53.84615384615384%;
+    *margin-left: 53.739770867430444%;
+  }
+  .row-fluid .offset6:first-child {
+    margin-left: 51.28205128205128%;
+    *margin-left: 51.175668303327875%;
+  }
+  .row-fluid .offset5 {
+    margin-left: 45.299145299145295%;
+    *margin-left: 45.1927623204219%;
+  }
+  .row-fluid .offset5:first-child {
+    margin-left: 42.73504273504273%;
+    *margin-left: 42.62865975631933%;
+  }
+  .row-fluid .offset4 {
+    margin-left: 36.75213675213675%;
+    *margin-left: 36.645753773413354%;
+  }
+  .row-fluid .offset4:first-child {
+    margin-left: 34.18803418803419%;
+    *margin-left: 34.081651209310785%;
+  }
+  .row-fluid .offset3 {
+    margin-left: 28.205128205128204%;
+    *margin-left: 28.0987452264048%;
+  }
+  .row-fluid .offset3:first-child {
+    margin-left: 25.641025641025642%;
+    *margin-left: 25.53464266230224%;
+  }
+  .row-fluid .offset2 {
+    margin-left: 19.65811965811966%;
+    *margin-left: 19.551736679396257%;
+  }
+  .row-fluid .offset2:first-child {
+    margin-left: 17.094017094017094%;
+    *margin-left: 16.98763411529369%;
+  }
+  .row-fluid .offset1 {
+    margin-left: 11.11111111111111%;
+    *margin-left: 11.004728132387708%;
+  }
+  .row-fluid .offset1:first-child {
+    margin-left: 8.547008547008547%;
+    *margin-left: 8.440625568285142%;
+  }
+  input,
+  textarea,
+  .uneditable-input {
+    margin-left: 0;
+  }
+  .controls-row [class*="span"] + [class*="span"] {
+    margin-left: 30px;
+  }
+  input.span12,
+  textarea.span12,
+  .uneditable-input.span12 {
+    width: 1156px;
+  }
+  input.span11,
+  textarea.span11,
+  .uneditable-input.span11 {
+    width: 1056px;
+  }
+  input.span10,
+  textarea.span10,
+  .uneditable-input.span10 {
+    width: 956px;
+  }
+  input.span9,
+  textarea.span9,
+  .uneditable-input.span9 {
+    width: 856px;
+  }
+  input.span8,
+  textarea.span8,
+  .uneditable-input.span8 {
+    width: 756px;
+  }
+  input.span7,
+  textarea.span7,
+  .uneditable-input.span7 {
+    width: 656px;
+  }
+  input.span6,
+  textarea.span6,
+  .uneditable-input.span6 {
+    width: 556px;
+  }
+  input.span5,
+  textarea.span5,
+  .uneditable-input.span5 {
+    width: 456px;
+  }
+  input.span4,
+  textarea.span4,
+  .uneditable-input.span4 {
+    width: 356px;
+  }
+  input.span3,
+  textarea.span3,
+  .uneditable-input.span3 {
+    width: 256px;
+  }
+  input.span2,
+  textarea.span2,
+  .uneditable-input.span2 {
+    width: 156px;
+  }
+  input.span1,
+  textarea.span1,
+  .uneditable-input.span1 {
+    width: 56px;
+  }
+  .thumbnails {
+    margin-left: -30px;
+  }
+  .thumbnails > li {
+    margin-left: 30px;
+  }
+  .row-fluid .thumbnails {
+    margin-left: 0;
+  }
+}
+
+@media (min-width: 768px) and (max-width: 979px) {
+  .row {
+    margin-left: -20px;
+    *zoom: 1;
+  }
+  .row:before,
+  .row:after {
+    display: table;
+    line-height: 0;
+    content: "";
+  }
+  .row:after {
+    clear: both;
+  }
+  [class*="span"] {
+    float: left;
+    margin-left: 20px;
+  }
+  .container,
+  .navbar-static-top .container,
+  .navbar-fixed-top .container,
+  .navbar-fixed-bottom .container {
+    width: 724px;
+  }
+  .span12 {
+    width: 724px;
+  }
+  .span11 {
+    width: 662px;
+  }
+  .span10 {
+    width: 600px;
+  }
+  .span9 {
+    width: 538px;