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

PYTHON-1316 Remove Database.eval and database.SystemJS #542

Merged
merged 5 commits into from
Jan 12, 2021
Merged
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
4 changes: 0 additions & 4 deletions doc/api/pymongo/database.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,3 @@
.. autoattribute:: read_preference
.. autoattribute:: write_concern
.. autoattribute:: read_concern


.. autoclass:: pymongo.database.SystemJS
:members:
3 changes: 3 additions & 0 deletions doc/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ Breaking Changes in 4.0
.......................

- Removed support for Python 2.7, 3.4, and 3.5. Python 3.6+ is now required.
- Removed :meth:`~pymongo.database.Database.eval`,
:data:`~pymongo.database.Database.system_js` and
:class:`~pymongo.database.SystemJS`.
- Removed :mod:`~pymongo.thread_util`.

Notable improvements
Expand Down
21 changes: 20 additions & 1 deletion doc/migrate-to-pymongo4.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ PyMongo 4 Migration Guide

from pymongo import MongoClient, ReadPreference
client = MongoClient()
collection = client.my_database.my_collection
database = client.my_database
collection = database.my_collection

PyMongo 4.0 brings a number of improvements as well as some backward breaking
changes. This guide provides a roadmap for migrating an existing application
Expand Down Expand Up @@ -51,3 +52,21 @@ Warnings can also be changed to errors::

Removed features with no migration path
---------------------------------------

Database.eval, Database.system_js, and SystemJS are removed
...........................................................

Removed :meth:`~pymongo.database.Database.eval`,
:data:`~pymongo.database.Database.system_js` and
:class:`~pymongo.database.SystemJS`. The eval command was deprecated in
MongoDB 3.0 and removed in MongoDB 4.2. There is no replacement for eval with
MongoDB 4.2+.

However, on MongoDB <= 4.0, code like this::

>>> result = database.eval('function (x) {return x;}', 3)

can be changed to this::

>>> from bson.code import Code
>>> result = database.command('eval', Code('function (x) {return x;}'), args=[3]).get('retval')
82 changes: 0 additions & 82 deletions pymongo/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,14 +145,6 @@ def method_overwritten(instance, method):
if method_overwritten(manipulator, "transform_outgoing"):
self.__outgoing_manipulators.insert(0, manipulator)

@property
def system_js(self):
"""**DEPRECATED**: :class:`SystemJS` helper for this :class:`Database`.

See the documentation for :class:`SystemJS` for more details.
"""
return SystemJS(self)

@property
def client(self):
"""The client instance for this :class:`Database`."""
Expand Down Expand Up @@ -1547,77 +1539,3 @@ def dereference(self, dbref, session=None, **kwargs):
self.__name))
return self[dbref.collection].find_one(
{"_id": dbref.id}, session=session, **kwargs)

def eval(self, code, *args):
"""**DEPRECATED**: Evaluate a JavaScript expression in MongoDB.

:Parameters:
- `code`: string representation of JavaScript code to be
evaluated
- `args` (optional): additional positional arguments are
passed to the `code` being evaluated

.. warning:: the eval command is deprecated in MongoDB 3.0 and
will be removed in a future server version.
"""
warnings.warn("Database.eval() is deprecated",
DeprecationWarning, stacklevel=2)

if not isinstance(code, Code):
code = Code(code)

result = self.command("$eval", code, args=args)
return result.get("retval", None)

def __call__(self, *args, **kwargs):
"""This is only here so that some API misusages are easier to debug.
"""
raise TypeError("'Database' object is not callable. If you meant to "
"call the '%s' method on a '%s' object it is "
"failing because no such method exists." % (
self.__name, self.__client.__class__.__name__))


class SystemJS(object):
"""**DEPRECATED**: Helper class for dealing with stored JavaScript.
"""

def __init__(self, database):
"""**DEPRECATED**: Get a system js helper for the database `database`.

SystemJS will be removed in PyMongo 4.0.
"""
warnings.warn("SystemJS is deprecated",
DeprecationWarning, stacklevel=2)

if not database.write_concern.acknowledged:
database = database.client.get_database(
database.name, write_concern=DEFAULT_WRITE_CONCERN)
# can't just assign it since we've overridden __setattr__
object.__setattr__(self, "_db", database)

def __setattr__(self, name, code):
self._db.system.js.replace_one(
{"_id": name}, {"_id": name, "value": Code(code)}, True)

def __setitem__(self, name, code):
self.__setattr__(name, code)

def __delattr__(self, name):
self._db.system.js.delete_one({"_id": name})

def __delitem__(self, name):
self.__delattr__(name)

def __getattr__(self, name):
return lambda *args: self._db.eval(Code("function() { "
"return this[name].apply("
"this, arguments); }",
scope={'name': name}), *args)

def __getitem__(self, name):
return self.__getattr__(name)

def list(self):
"""Get a list of the names of the functions stored in this database."""
return [x["_id"] for x in self._db.system.js.find(projection=["_id"])]
71 changes: 0 additions & 71 deletions test/test_database.py
Original file line number Diff line number Diff line change
Expand Up @@ -806,35 +806,6 @@ def test_deref_kwargs(self):
db.dereference(DBRef("test", 4),
projection={"_id": False}))

@client_context.require_no_auth
@client_context.require_version_max(4, 1, 0)
def test_eval(self):
db = self.client.pymongo_test
db.test.drop()

with ignore_deprecations():
self.assertRaises(TypeError, db.eval, None)
self.assertRaises(TypeError, db.eval, 5)
self.assertRaises(TypeError, db.eval, [])

self.assertEqual(3, db.eval("function (x) {return x;}", 3))
self.assertEqual(3, db.eval(u"function (x) {return x;}", 3))

self.assertEqual(None,
db.eval("function (x) {db.test.save({y:x});}", 5))
self.assertEqual(db.test.find_one()["y"], 5)

self.assertEqual(5, db.eval("function (x, y) {return x + y;}", 2, 3))
self.assertEqual(5, db.eval("function () {return 5;}"))
self.assertEqual(5, db.eval("2 + 3;"))

self.assertEqual(5, db.eval(Code("2 + 3;")))
self.assertRaises(OperationFailure, db.eval, Code("return i;"))
self.assertEqual(2, db.eval(Code("return i;", {"i": 2})))
self.assertEqual(5, db.eval(Code("i + 3;", {"i": 2})))

self.assertRaises(OperationFailure, db.eval, "5 ++ 5;")

# TODO some of these tests belong in the collection level testing.
def test_insert_find_one(self):
db = self.client.pymongo_test
Expand Down Expand Up @@ -910,48 +881,6 @@ def test_delete(self):
db.test.delete_many({})
self.assertFalse(db.test.find_one())

@client_context.require_no_auth
@client_context.require_version_max(4, 1, 0)
def test_system_js(self):
db = self.client.pymongo_test
db.system.js.delete_many({})

self.assertEqual(0, db.system.js.count_documents({}))
db.system_js.add = "function(a, b) { return a + b; }"
self.assertEqual('add', db.system.js.find_one()['_id'])
self.assertEqual(1, db.system.js.count_documents({}))
self.assertEqual(6, db.system_js.add(1, 5))
del db.system_js.add
self.assertEqual(0, db.system.js.count_documents({}))

db.system_js['add'] = "function(a, b) { return a + b; }"
self.assertEqual('add', db.system.js.find_one()['_id'])
self.assertEqual(1, db.system.js.count_documents({}))
self.assertEqual(6, db.system_js['add'](1, 5))
del db.system_js['add']
self.assertEqual(0, db.system.js.count_documents({}))
self.assertRaises(OperationFailure, db.system_js.add, 1, 5)

# TODO right now CodeWScope doesn't work w/ system js
# db.system_js.scope = Code("return hello;", {"hello": 8})
# self.assertEqual(8, db.system_js.scope())

self.assertRaises(OperationFailure, db.system_js.non_existant)

def test_system_js_list(self):
db = self.client.pymongo_test
db.system.js.delete_many({})
self.assertEqual([], db.system_js.list())

db.system_js.foo = "function() { return 'blah'; }"
self.assertEqual(["foo"], db.system_js.list())

db.system_js.bar = "function() { return 'baz'; }"
self.assertEqual(set(["foo", "bar"]), set(db.system_js.list()))

del db.system_js.foo
self.assertEqual(["bar"], db.system_js.list())

def test_command_response_without_ok(self):
# Sometimes (SERVER-10891) the server's response to a badly-formatted
# command document will have no 'ok' field. We should raise
Expand Down