Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

migrate to gen.coroutine #42

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,7 @@ data
.mr.developer.cfg

#PyCharm
.idea/
.idea/

#Sublime
*.sublime-*
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ The next steps are provide support to:
* authentication
* nearest preference in replica sets
* gridfs
* all python versions (2.5, 2.6, 2.7, 3.2 and PyPy), only python 2.7 is tested now
* all python versions (2.6, 2.7, 3.2 and PyPy), only python 2.7 is tested now

## Documentation

Expand Down Expand Up @@ -67,7 +67,7 @@ class Handler(tornado.web.RequestHandler):
def get(self):
user = {'_id': ObjectId(), 'name': 'User Name'}
yield gen.Task(self.db.user.insert, user)

yield gen.Task(self.db.user.update, user['_id'], {"$set": {'name': 'New User Name'}})

user_found = yield gen.Task(self.db.user.find_one, user['_id'])
Expand Down Expand Up @@ -98,10 +98,10 @@ class Handler(tornado.web.RequestHandler):
@gen.engine
def get(self):
user = {'_id': ObjectId()}

# write on primary
yield gen.Task(self.db.user.insert, user)

# wait for replication
time.sleep(2)

Expand Down Expand Up @@ -191,4 +191,4 @@ make test

## Issues

Please report any issues via [github issues](https://github.com/marcelnicolay/mongotor/issues)
Please report any issues via [github issues](https://github.com/marcelnicolay/mongotor/issues)
8 changes: 5 additions & 3 deletions docs/source/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ MongoTor supports installation using standard Python "distutils" or
Install via easy_install or pip
-------------------------------

When ``easy_install`` or ``pip`` is available, the distribution can be
When ``easy_install`` or ``pip`` is available, the distribution can be
downloaded from Pypi and installed in one step::

easy_install mongotor
Expand All @@ -39,17 +39,19 @@ Python prompt like this:

.. sourcecode:: python

>>> import mongotor
>>> import mongotor
>>> mongotor.version # doctest: +SKIP

Requirements
------------

Python version 2.6+ is required. But only version 2.7 is tested for now.

The following three python libraries are required.

* `pymongo <http://github.com/mongodb/mongo-python-driver>`_ version 1.9+ for bson library
* `tornado <http://github.com/facebook/tornado>`_

.. note::
The above requirements are automatically managed when installed using
any of the supported installation methods
any of the supported installation methods
2 changes: 1 addition & 1 deletion mongotor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

version = "0.1.0"
version = "0.1.5"
12 changes: 6 additions & 6 deletions mongotor/cursor.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ def find(self, callback=None):
else:
callback((response['data'], None))

@gen.engine
def count(self, callback):
@gen.coroutine
def count(self):
"""Get the size of the results set for this query.

Returns the number of documents in the results set for this query. Does
Expand All @@ -110,10 +110,10 @@ def count(self, callback):
if response and len(response) > 0 and 'n' in response:
total = int(response['n'])

callback(total)
raise gen.Return(total)

@gen.engine
def distinct(self, key, callback):
@gen.coroutine
def distinct(self, key):
"""Get a list of distinct values for `key` among all documents
in the result set of this query.

Expand All @@ -131,7 +131,7 @@ def distinct(self, key, callback):
response, error = yield gen.Task(self._database.command,
'distinct', self._collection, **command)

callback(response['values'])
raise gen.Return(response['values'])

def _query_options(self):
"""Get the query options string to use for this query."""
Expand Down
6 changes: 4 additions & 2 deletions mongotor/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
def initialized(fn):
@wraps(fn)
def wrapped(self, *args, **kwargs):
if not hasattr(self, '_initialized'):
if not hasattr(self, '_initialized') or not self._initialized:
raise DatabaseError("you must be initialize database before perform this action")

return fn(self, *args, **kwargs)
Expand All @@ -45,6 +45,7 @@ class Database(object):
def __new__(cls):
if not cls._instance:
cls._instance = super(Database, cls).__new__(cls)
cls._initialized = False

return cls._instance

Expand Down Expand Up @@ -147,7 +148,8 @@ def disconnect(cls):
>>> Database.disconnect()

"""
if not cls._instance or not hasattr(cls._instance, '_initialized'):
if (not cls._instance or not hasattr(cls._instance, '_initialized')
or not cls._instance._initialized):
raise ValueError("Database isn't initialized")

for node in cls._instance._nodes:
Expand Down
4 changes: 2 additions & 2 deletions mongotor/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
from mongotor.errors import InvalidOperationError


__ZERO = "\x00\x00\x00\x00"
__ZERO = b"\x00\x00\x00\x00"


def __last_error(args):
Expand Down Expand Up @@ -57,7 +57,7 @@ def insert(collection_name, docs, check_keys, safe, last_error_args):
"""
data = __ZERO
data += bson._make_c_string(collection_name)
bson_data = "".join([bson.BSON.encode(doc, check_keys) for doc in docs])
bson_data = b"".join([bson.BSON.encode(doc, check_keys) for doc in docs])
if not bson_data:
raise InvalidOperationError("cannot do an empty bulk insert")
data += bson_data
Expand Down
42 changes: 24 additions & 18 deletions mongotor/orm/collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import re
import logging
from tornado import gen
from mongotor.client import Client
Expand All @@ -32,6 +33,9 @@
class CollectionMetaClass(type):

def __new__(cls, name, bases, attrs):
if not attrs.get('__collection__'):
attrs['__collection__'] = re.sub(
r'\B([A-Z]+)', r'_\1', name).lower()
global __lazy_classes__

# Add the document's fields to the _data
Expand Down Expand Up @@ -65,6 +69,10 @@ class Collection(object):
>>> class Users(collection.Collection):
>>> __collection__ = 'users'
>>> name = field.StringField()

If you do not specify `__collection__` attribute, it is
auto-generated from class name. Camel case is converted
to snake case. For example: CamelCase -> camel_case.
"""
__metaclass__ = CollectionMetaClass

Expand Down Expand Up @@ -133,8 +141,11 @@ def create(cls, dictionary, cleaned=False):

return instance

@gen.engine
def save(self, safe=True, check_keys=True, callback=None):
def get_client(self):
return Client(Database(), self.__collection__)

@gen.coroutine
def save(self, safe=True, check_keys=True):
"""Save a document

>>> user = Users()
Expand All @@ -150,19 +161,18 @@ def save(self, safe=True, check_keys=True, callback=None):
"""
pre_save.send(instance=self)

client = Client(Database(), self.__collection__)
client = self.get_client()
response, error = yield gen.Task(client.insert, self.as_dict(),
safe=safe, check_keys=check_keys)

self.clean_fields()

post_save.send(instance=self)

if callback:
callback((response, error))
raise gen.Return((response, error))

@gen.engine
def remove(self, safe=True, callback=None):
@gen.coroutine
def remove(self, safe=True):
"""Remove a document

:Parameters:
Expand All @@ -171,17 +181,15 @@ def remove(self, safe=True, callback=None):
"""
pre_remove.send(instance=self)

client = Client(Database(), self.__collection__)
client = self.get_client()
response, error = yield gen.Task(client.remove, self._id, safe=safe)

post_remove.send(instance=self)

if callback:
callback((response, error))
raise gen.Return((response, error))

@gen.engine
def update(self, document=None, upsert=False, safe=True, multi=False,
callback=None, force=False):
@gen.coroutine
def update(self, document=None, upsert=False, safe=True, multi=False, force=False):
"""Update a document

:Parameters:
Expand All @@ -190,8 +198,7 @@ def update(self, document=None, upsert=False, safe=True, multi=False,
- `force`: if True will overide full document
"""
if not document and not self.dirty_fields:
callback(tuple())
return
raise gen.Return(tuple())

pre_update.send(instance=self)

Expand All @@ -201,7 +208,7 @@ def update(self, document=None, upsert=False, safe=True, multi=False,
else:
document = {"$set": self.as_dict(self.dirty_fields)}

client = Client(Database(), self.__collection__)
client = self.get_client()
spec = {'_id': self._id}

response, error = yield gen.Task(client.update, spec, document,
Expand All @@ -211,5 +218,4 @@ def update(self, document=None, upsert=False, safe=True, multi=False,

post_update.send(instance=self)

if callback:
callback((response, error))
raise gen.Return((response, error))
12 changes: 10 additions & 2 deletions mongotor/orm/field.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def __init__(self, regex=None, *args, **kwargs):

def _validate(self, value):
value = super(StringField, self)._validate(value)
if self.regex is not None and self.regex.match(value) is None:
if value is not None and self.regex is not None and self.regex.match(value) is None:
raise(TypeError("Value did not match regex"))

return value
Expand Down Expand Up @@ -116,11 +116,19 @@ def __init__(self, field_type, min_value=None, max_value=None,

def _validate(self, value):
value = super(NumberField, self)._validate(value)
if value is None:
if self.min_value is not None:
value = self.min_value
elif self.max_value is not None:
value = self.max_value
else:
value = self.field_type()

if self.min_value is not None and value < self.min_value:
raise(TypeError("Value can not be less than %s" % (self.min_value)))

if self.max_value is not None and value > self.max_value:
raise(TypeError("Value can not be more than %s" & (self.max_value)))
raise(TypeError("Value can not be more than %s" % (self.max_value)))

return value

Expand Down
Loading