Merge branch 'master' into refactoring_poor_assertions_in_tests
This commit is contained in:
commit
8f76e1e344
@ -538,7 +538,7 @@ There are a few top level defaults for all indexes that can be set::
|
|||||||
|
|
||||||
|
|
||||||
:attr:`index_options` (Optional)
|
:attr:`index_options` (Optional)
|
||||||
Set any default index options - see the `full options list <http://docs.mongodb.org/manual/reference/method/db.collection.ensureIndex/#db.collection.ensureIndex>`_
|
Set any default index options - see the `full options list <https://docs.mongodb.com/manual/reference/method/db.collection.createIndex/#ensureindex-options>`_
|
||||||
|
|
||||||
:attr:`index_background` (Optional)
|
:attr:`index_background` (Optional)
|
||||||
Set the default value for if an index should be indexed in the background
|
Set the default value for if an index should be indexed in the background
|
||||||
|
@ -159,51 +159,69 @@ class no_sub_classes(object):
|
|||||||
|
|
||||||
|
|
||||||
class query_counter(object):
|
class query_counter(object):
|
||||||
"""Query_counter context manager to get the number of queries."""
|
"""Query_counter context manager to get the number of queries.
|
||||||
|
This works by updating the `profiling_level` of the database so that all queries get logged,
|
||||||
|
resetting the db.system.profile collection at the beginnig of the context and counting the new entries.
|
||||||
|
|
||||||
|
This was designed for debugging purpose. In fact it is a global counter so queries issued by other threads/processes
|
||||||
|
can interfere with it
|
||||||
|
|
||||||
|
Be aware that:
|
||||||
|
- Iterating over large amount of documents (>101) makes pymongo issue `getmore` queries to fetch the next batch of
|
||||||
|
documents (https://docs.mongodb.com/manual/tutorial/iterate-a-cursor/#cursor-batches)
|
||||||
|
- Some queries are ignored by default by the counter (killcursors, db.system.indexes)
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""Construct the query_counter."""
|
"""Construct the query_counter
|
||||||
self.counter = 0
|
"""
|
||||||
self.db = get_db()
|
self.db = get_db()
|
||||||
|
self.initial_profiling_level = None
|
||||||
|
self._ctx_query_counter = 0 # number of queries issued by the context
|
||||||
|
|
||||||
def __enter__(self):
|
self._ignored_query = {
|
||||||
"""On every with block we need to drop the profile collection."""
|
'ns':
|
||||||
|
{'$ne': '%s.system.indexes' % self.db.name},
|
||||||
|
'op':
|
||||||
|
{'$ne': 'killcursors'}
|
||||||
|
}
|
||||||
|
|
||||||
|
def _turn_on_profiling(self):
|
||||||
|
self.initial_profiling_level = self.db.profiling_level()
|
||||||
self.db.set_profiling_level(0)
|
self.db.set_profiling_level(0)
|
||||||
self.db.system.profile.drop()
|
self.db.system.profile.drop()
|
||||||
self.db.set_profiling_level(2)
|
self.db.set_profiling_level(2)
|
||||||
|
|
||||||
|
def _resets_profiling(self):
|
||||||
|
self.db.set_profiling_level(self.initial_profiling_level)
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
self._turn_on_profiling()
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __exit__(self, t, value, traceback):
|
def __exit__(self, t, value, traceback):
|
||||||
"""Reset the profiling level."""
|
self._resets_profiling()
|
||||||
self.db.set_profiling_level(0)
|
|
||||||
|
|
||||||
def __eq__(self, value):
|
def __eq__(self, value):
|
||||||
"""== Compare querycounter."""
|
|
||||||
counter = self._get_count()
|
counter = self._get_count()
|
||||||
return value == counter
|
return value == counter
|
||||||
|
|
||||||
def __ne__(self, value):
|
def __ne__(self, value):
|
||||||
"""!= Compare querycounter."""
|
|
||||||
return not self.__eq__(value)
|
return not self.__eq__(value)
|
||||||
|
|
||||||
def __lt__(self, value):
|
def __lt__(self, value):
|
||||||
"""< Compare querycounter."""
|
|
||||||
return self._get_count() < value
|
return self._get_count() < value
|
||||||
|
|
||||||
def __le__(self, value):
|
def __le__(self, value):
|
||||||
"""<= Compare querycounter."""
|
|
||||||
return self._get_count() <= value
|
return self._get_count() <= value
|
||||||
|
|
||||||
def __gt__(self, value):
|
def __gt__(self, value):
|
||||||
"""> Compare querycounter."""
|
|
||||||
return self._get_count() > value
|
return self._get_count() > value
|
||||||
|
|
||||||
def __ge__(self, value):
|
def __ge__(self, value):
|
||||||
""">= Compare querycounter."""
|
|
||||||
return self._get_count() >= value
|
return self._get_count() >= value
|
||||||
|
|
||||||
def __int__(self):
|
def __int__(self):
|
||||||
"""int representation."""
|
|
||||||
return self._get_count()
|
return self._get_count()
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
@ -211,10 +229,12 @@ class query_counter(object):
|
|||||||
return u"%s" % self._get_count()
|
return u"%s" % self._get_count()
|
||||||
|
|
||||||
def _get_count(self):
|
def _get_count(self):
|
||||||
"""Get the number of queries."""
|
"""Get the number of queries by counting the current number of entries in db.system.profile
|
||||||
ignore_query = {'ns': {'$ne': '%s.system.indexes' % self.db.name}}
|
and substracting the queries issued by this context. In fact everytime this is called, 1 query is
|
||||||
count = self.db.system.profile.find(ignore_query).count() - self.counter
|
issued so we need to balance that
|
||||||
self.counter += 1 # Account for the query we just fired
|
"""
|
||||||
|
count = self.db.system.profile.find(self._ignored_query).count() - self._ctx_query_counter
|
||||||
|
self._ctx_query_counter += 1 # Account for the query we just issued to gather the information
|
||||||
return count
|
return count
|
||||||
|
|
||||||
|
|
||||||
|
@ -4749,6 +4749,7 @@ class QuerySetTest(unittest.TestCase):
|
|||||||
Person(name="No: %s" % i).save()
|
Person(name="No: %s" % i).save()
|
||||||
|
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
|
try:
|
||||||
self.assertEqual(q, 0)
|
self.assertEqual(q, 0)
|
||||||
people = Person.objects.no_cache()
|
people = Person.objects.no_cache()
|
||||||
|
|
||||||
@ -4760,6 +4761,14 @@ class QuerySetTest(unittest.TestCase):
|
|||||||
|
|
||||||
people.count()
|
people.count()
|
||||||
self.assertEqual(q, 3)
|
self.assertEqual(q, 3)
|
||||||
|
except AssertionError as exc:
|
||||||
|
db = get_db()
|
||||||
|
msg = ''
|
||||||
|
for q in list(db.system.profile.find())[-50:]:
|
||||||
|
msg += str([q['ts'], q['ns'], q.get('query'), q['op']])+'\n'
|
||||||
|
msg += str(q)
|
||||||
|
raise AssertionError(str(exc) + '\n'+msg)
|
||||||
|
|
||||||
|
|
||||||
def test_cache_not_cloned(self):
|
def test_cache_not_cloned(self):
|
||||||
|
|
||||||
|
@ -209,18 +209,99 @@ class ContextManagersTest(unittest.TestCase):
|
|||||||
with no_sub_classes(User):
|
with no_sub_classes(User):
|
||||||
raise TypeError()
|
raise TypeError()
|
||||||
|
|
||||||
|
def test_query_counter_does_not_swallow_exception(self):
|
||||||
|
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
with query_counter() as q:
|
||||||
|
raise TypeError()
|
||||||
|
|
||||||
|
def test_query_counter_temporarily_modifies_profiling_level(self):
|
||||||
|
connect('mongoenginetest')
|
||||||
|
db = get_db()
|
||||||
|
|
||||||
|
initial_profiling_level = db.profiling_level()
|
||||||
|
|
||||||
|
try:
|
||||||
|
NEW_LEVEL = 1
|
||||||
|
db.set_profiling_level(NEW_LEVEL)
|
||||||
|
self.assertEqual(db.profiling_level(), NEW_LEVEL)
|
||||||
|
with query_counter() as q:
|
||||||
|
self.assertEqual(db.profiling_level(), 2)
|
||||||
|
self.assertEqual(db.profiling_level(), NEW_LEVEL)
|
||||||
|
except Exception:
|
||||||
|
db.set_profiling_level(initial_profiling_level) # Ensures it gets reseted no matter the outcome of the test
|
||||||
|
raise
|
||||||
|
|
||||||
def test_query_counter(self):
|
def test_query_counter(self):
|
||||||
connect('mongoenginetest')
|
connect('mongoenginetest')
|
||||||
db = get_db()
|
db = get_db()
|
||||||
db.test.find({})
|
|
||||||
|
collection = db.query_counter
|
||||||
|
collection.drop()
|
||||||
|
|
||||||
|
def issue_1_count_query():
|
||||||
|
collection.find({}).count()
|
||||||
|
|
||||||
|
def issue_1_insert_query():
|
||||||
|
collection.insert_one({'test': 'garbage'})
|
||||||
|
|
||||||
|
def issue_1_find_query():
|
||||||
|
collection.find_one()
|
||||||
|
|
||||||
|
counter = 0
|
||||||
|
with query_counter() as q:
|
||||||
|
self.assertEqual(q, counter)
|
||||||
|
self.assertEqual(q, counter) # Ensures previous count query did not get counted
|
||||||
|
|
||||||
|
for _ in range(10):
|
||||||
|
issue_1_insert_query()
|
||||||
|
counter += 1
|
||||||
|
self.assertEqual(q, counter)
|
||||||
|
|
||||||
|
for _ in range(4):
|
||||||
|
issue_1_find_query()
|
||||||
|
counter += 1
|
||||||
|
self.assertEqual(q, counter)
|
||||||
|
|
||||||
|
for _ in range(3):
|
||||||
|
issue_1_count_query()
|
||||||
|
counter += 1
|
||||||
|
self.assertEqual(q, counter)
|
||||||
|
|
||||||
|
def test_query_counter_counts_getmore_queries(self):
|
||||||
|
connect('mongoenginetest')
|
||||||
|
db = get_db()
|
||||||
|
|
||||||
|
collection = db.query_counter
|
||||||
|
collection.drop()
|
||||||
|
|
||||||
|
many_docs = [{'test': 'garbage %s' % i} for i in range(150)]
|
||||||
|
collection.insert_many(many_docs) # first batch of documents contains 101 documents
|
||||||
|
|
||||||
with query_counter() as q:
|
with query_counter() as q:
|
||||||
self.assertEqual(0, q)
|
self.assertEqual(q, 0)
|
||||||
|
list(collection.find())
|
||||||
|
self.assertEqual(q, 2) # 1st select + 1 getmore
|
||||||
|
|
||||||
for i in range(1, 51):
|
def test_query_counter_ignores_particular_queries(self):
|
||||||
db.test.find({}).count()
|
connect('mongoenginetest')
|
||||||
|
db = get_db()
|
||||||
|
|
||||||
self.assertEqual(50, q)
|
collection = db.query_counter
|
||||||
|
collection.insert_many([{'test': 'garbage %s' % i} for i in range(10)])
|
||||||
|
|
||||||
|
with query_counter() as q:
|
||||||
|
self.assertEqual(q, 0)
|
||||||
|
cursor = collection.find()
|
||||||
|
self.assertEqual(q, 0) # cursor wasn't opened yet
|
||||||
|
_ = next(cursor) # opens the cursor and fires the find query
|
||||||
|
self.assertEqual(q, 1)
|
||||||
|
|
||||||
|
cursor.close() # issues a `killcursors` query that is ignored by the context
|
||||||
|
self.assertEqual(q, 1)
|
||||||
|
|
||||||
|
_ = db.system.indexes.find_one() # queries on db.system.indexes are ignored as well
|
||||||
|
self.assertEqual(q, 1)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user