-
Notifications
You must be signed in to change notification settings - Fork 4
Add spec test functionality #15
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
Changes from 10 commits
de6f2cc
a597592
59525cd
ef6c5a1
803e27a
b8ffa0f
16fe2a5
c8f973b
3ebe9b0
e6aa287
6ed0717
8cc3923
4f230bb
e332c4e
dd09b41
3bd6bdf
69ad0d9
075c0c1
618ae94
756c4fc
9248101
12ea3c0
f7b03b6
12aaf05
5a419d9
83063cb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,6 +20,7 @@ | |
|
|
||
| from bson.son import SON | ||
| from pymongo.collection import Collection | ||
| from pymongo.helpers import _index_document | ||
| from .utils import convert_to_camelcase | ||
|
|
||
|
|
||
|
|
@@ -39,18 +40,25 @@ def command_name(self): | |
| def get_SON(self): | ||
| cmd = SON([(self.command_name, self.collection)]) | ||
| cmd.update(self.command_document) | ||
| if self.command_document == {}: | ||
| return {} | ||
| return cmd | ||
|
|
||
|
|
||
| class UpdateCommand(BaseCommand): | ||
| def __init__(self, collection: Collection, filter, update, | ||
| kwargs): | ||
| super().__init__(collection.name) | ||
| return_document = {"updates":[{"q": filter, "u": update}]} | ||
| for key in kwargs: | ||
| value = kwargs[key] | ||
| return_document = { | ||
| "updates":[{"q": filter, "u": update}] | ||
| } | ||
| for key, value in kwargs.items(): | ||
| if key == "bypass_document_validation": | ||
| return_document[key] = value | ||
| elif key == "hint": | ||
| if value is not {} and value is not None: | ||
|
||
| return_document["updates"][0]["hint"] = value if \ | ||
| isinstance(value, str) else _index_document(value) | ||
| else: | ||
| return_document["updates"][0][key] = value | ||
| self.command_document = convert_to_camelcase(return_document) | ||
|
|
@@ -81,7 +89,12 @@ def __init__(self, collection: Collection, pipeline, session, | |
| super().__init__(collection.name) | ||
| self.command_document = {"pipeline": pipeline, "cursor": cursor_options} | ||
| for key, value in kwargs.items(): | ||
| self.command_document[key] = value | ||
| if key == "batchSize": | ||
| if value == 0: | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to check for None too?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. None values are already removed in the converting to snakecase step |
||
| continue | ||
| self.command_document["cursor"]["batchSize"] = value | ||
| else: | ||
| self.command_document[key] = value | ||
|
|
||
| self.command_document = convert_to_camelcase( | ||
| self.command_document, exclude_keys=exclude_keys) | ||
|
|
@@ -108,6 +121,8 @@ class FindCommand(BaseCommand): | |
| def __init__(self, collection: Collection, | ||
| kwargs): | ||
| super().__init__(collection.name) | ||
| if kwargs["filter"] == {}: | ||
| self.command_document = {} | ||
|
||
| for key, value in kwargs.items(): | ||
| self.command_document[key] = value | ||
| self.command_document = convert_to_camelcase(self.command_document) | ||
|
|
@@ -116,12 +131,21 @@ def __init__(self, collection: Collection, | |
| def command_name(self): | ||
| return "find" | ||
|
|
||
|
|
||
| class FindAndModifyCommand(BaseCommand): | ||
| def __init__(self, collection: Collection, | ||
| kwargs): | ||
| super().__init__(collection.name) | ||
| for key, value in kwargs.items(): | ||
| self.command_document[key] = value | ||
| if key == "update" and kwargs.get("replacement", None) is not None: | ||
|
||
| continue | ||
| if key == "hint": | ||
| self.command_document["hint"] = value if \ | ||
| isinstance(value, str) else _index_document(value) | ||
| elif key == "replacement": | ||
| self.command_document["update"] = value | ||
| else: | ||
| self.command_document[key] = value | ||
| self.command_document = convert_to_camelcase(self.command_document) | ||
|
|
||
| @property | ||
|
|
@@ -133,9 +157,15 @@ class DeleteCommand(BaseCommand): | |
| def __init__(self, collection: Collection, filter, | ||
| limit, collation, kwargs): | ||
| super().__init__(collection.name) | ||
| self.command_document = {"deletes": [SON({"q": filter, "limit": limit})]} | ||
| self.command_document = {"deletes": [SON({"q": filter, "limit": | ||
| limit, "collation": collation})]} | ||
| for key, value in kwargs.items(): | ||
| self.command_document[key] = value | ||
| if key == "hint": | ||
| self.command_document["deletes"][0]["hint"] = value if \ | ||
| isinstance(value, str) else _index_document(value) | ||
| else: | ||
| self.command_document[key] = value | ||
|
|
||
| self.command_document = convert_to_camelcase(self.command_document) | ||
|
|
||
| @property | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,20 +16,25 @@ | |
| from typing import Union, List, Dict | ||
|
|
||
| import pymongo | ||
| from pymongo.collection import Collection | ||
| from bson.son import SON | ||
|
|
||
| from .commands import AggregateCommand, FindCommand, CountCommand, \ | ||
| UpdateCommand, DistinctCommand, DeleteCommand, FindAndModifyCommand | ||
|
|
||
| Document = Union[dict, SON] | ||
|
|
||
|
|
||
| class ExplainCollection(): | ||
| def __init__(self, collection): | ||
| self.collection = collection | ||
| self.last_cmd_payload = None | ||
|
|
||
| def _explain_command(self, command): | ||
| command_son = command.get_SON() | ||
| if command_son == {}: | ||
| self.last_cmd_payload = {} | ||
| return {} | ||
|
||
| explain_command = SON([("explain", command_son)]) | ||
| explain_command["verbosity"] = "queryPlanner" | ||
| self.last_cmd_payload = command_son | ||
|
|
@@ -63,7 +68,7 @@ def distinct(self, key: str, filter: Document=None, session=None, **kwargs): | |
|
|
||
| def aggregate(self, pipeline: List[Document], session=None, **kwargs): | ||
| command = AggregateCommand(self.collection, pipeline, session, | ||
| {},kwargs) | ||
| {}, kwargs) | ||
| return self._explain_command(command) | ||
|
|
||
| def estimated_document_count(self, | ||
|
|
@@ -76,7 +81,7 @@ def count_documents(self, filter: Document, session=None, | |
| **kwargs): | ||
|
|
||
| command = AggregateCommand(self.collection, [{'$match': filter}, | ||
| {'$group': {'n': {'$sum': 1}, '_id': 1}}], | ||
| {'$group': {'n': {'$sum': 1}, '_id': 1}}], | ||
| session, {}, kwargs, | ||
| exclude_keys=filter.keys()) | ||
|
||
| return self._explain_command(command) | ||
|
|
@@ -95,7 +100,7 @@ def delete_many(self, filter: Document, collation=None, | |
| limit = 0 | ||
| kwargs["session"] = session | ||
| command = DeleteCommand(self.collection, filter, limit, collation, | ||
| kwargs) | ||
| kwargs) | ||
| return self._explain_command(command) | ||
|
|
||
| def watch(self, pipeline: Document = None, full_document: Document = None, | ||
|
|
@@ -124,7 +129,8 @@ def find(self, filter: Document = None, | |
| kwargs.update(locals()) | ||
| del kwargs["self"], kwargs["kwargs"] | ||
| command = FindCommand(self.collection, | ||
| kwargs) | ||
| kwargs) | ||
|
|
||
| return self._explain_command(command) | ||
|
|
||
| def find_one(self, filter: Document = None, **kwargs: Dict[str, | ||
|
|
@@ -149,29 +155,30 @@ def find_one_and_delete(self, filter: Document, projection: list = None, | |
| kwargs) | ||
| return self._explain_command(command) | ||
|
|
||
| def find_one_and_replace(self, filter: Document, replacement: Document, | ||
| def find_one_and_replace(self, filter: Document, update: | ||
|
||
| Document={}, | ||
| projection: list = None, sort=None, | ||
| return_document=pymongo.ReturnDocument.BEFORE, | ||
| session=None, **kwargs): | ||
| kwargs["query"] = filter | ||
| kwargs["fields"] = projection | ||
| kwargs["sort"] = sort | ||
| kwargs["new"] = False | ||
| kwargs["update"] = replacement | ||
| kwargs["update"] = update | ||
| kwargs["session"] = session | ||
| command = FindAndModifyCommand(self.collection, | ||
| kwargs) | ||
| return self._explain_command(command) | ||
|
|
||
| def find_one_and_update(self, filter: Document, replacement: Document, | ||
| def find_one_and_update(self, filter: Document, update: Document, | ||
| projection: list = None, sort=None, | ||
| return_document=pymongo.ReturnDocument.BEFORE, | ||
| session=None, **kwargs): | ||
| kwargs["query"] = filter | ||
| kwargs["fields"] = projection | ||
| kwargs["sort"] = sort | ||
| kwargs["upsert"] = False | ||
| kwargs["update"] = replacement | ||
| kwargs["update"] = update | ||
| kwargs["session"] = session | ||
|
|
||
| command = FindAndModifyCommand(self.collection, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,8 +26,8 @@ | |
| 'console_scripts': [ | ||
| 'pymongoexplain=pymongoexplain.cli_explain:cli_explain'], | ||
| }, | ||
| tests_require=["pymongo==3.10.1"], | ||
| install_requires=['pymongo==3.10.1'], | ||
| tests_require=["pymongo==3.11.0rc0"], | ||
| install_requires=['pymongo==3.11.0rc0'], | ||
|
||
| python_requires='>=3.5', | ||
| license="Apache License, Version 2.0", | ||
| classifiers=[ | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why was this change needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There were a few cases where the filter was {} and the expected command payload in that case was also {}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should not be possible for a command to be empty. Could you post those cases?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is in the following cases:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Which spec test is this specifically? Can you post the entire test failure output?