Motor 3.0 Migration Guide¶
Motor 3.0 brings a number of changes to Motor 2.0’s API. The major version is required in order to bring support for PyMongo 4.0+. To add compatibility with PyMongo 4, several methods were removed, as detailed below. Some of the underlying behaviors and method arguments have changed in PyMongo 4.0 as well.
Follow this guide to migrate an existing application that had used Motor 2.x.
Check compatibility¶
Read the Requirements page and ensure your MongoDB server and Python interpreter are compatible, and your Tornado version if you are using Tornado.
Upgrade to Motor 2.5¶
The first step in migrating to Motor 3.0 is to upgrade to at least Motor 2.5.
If your project has a
requirements.txt
file, add the line:
motor >= 2.5, < 3.0
Python 3.7+¶
Motor 3.0 drops support for Python 3.5 and 3.6. Users who wish to upgrade to 3.x must first upgrade to Python 3.7+.
Enable Deprecation Warnings¶
A DeprecationWarning
is raised by most changes made in PyMongo 4.0.
Make sure you enable runtime warnings to see where
deprecated functions and methods are being used in your application:
python -Wd <your application>
Warnings can also be changed to errors:
python -Wd -Werror <your application>
Note that there are some deprecation warnings raised by Motor itself for
APIs that are deprecated but not yet removed, like fetch_next()
.
MotorClient¶
directConnection
defaults to False¶
directConnection
URI option and keyword argument to MotorClient
defaults to False
instead of None
,
allowing for the automatic discovery of replica sets. This means that if you
want a direct connection to a single server you must pass
directConnection=True
as a URI option or keyword argument.
Renamed URI options¶
Several deprecated URI options have been renamed to the standardized option names defined in the URI options specification. The old option names and their renamed equivalents are summarized in the table below. Some renamed options have different semantics from the option being replaced as noted in the ‘Migration Notes’ column.
Old URI Option |
Renamed URI Option |
Migration Notes |
---|---|---|
ssl_pem_passphrase |
tlsCertificateKeyFilePassword |
|
ssl_ca_certs |
tlsCAFile |
|
ssl_crlfile |
tlsCRLFile |
|
ssl_match_hostname |
tlsAllowInvalidHostnames |
|
ssl_cert_reqs |
tlsAllowInvalidCertificates |
Instead of |
ssl_certfile |
tlsCertificateKeyFile |
Instead of using |
ssl_keyfile |
||
j |
journal |
|
wtimeout |
wTimeoutMS |
MotorClient.fsync is removed¶
Removed fsync()
. Run the
fsync command directly with command()
instead. For example:
await client.admin.command('fsync', lock=True)
MotorClient.unlock is removed¶
Removed unlock()
. Run the
fsyncUnlock command directly with
command()
instead. For example:
await client.admin.command('fsyncUnlock')
MotorClient.max_bson_size/max_message_size/max_write_batch_size are removed¶
Removed max_bson_size
,
max_message_size
, and
max_write_batch_size
. These helpers
were incorrect when in loadBalanced=true mode
and ambiguous in clusters
with mixed versions. Use the hello command to get the authoritative
value from the remote server instead. Code like this:
max_bson_size = client.max_bson_size
max_message_size = client.max_message_size
max_write_batch_size = client.max_write_batch_size
can be changed to this:
doc = await client.admin.command('hello')
max_bson_size = doc['maxBsonObjectSize']
max_message_size = doc['maxMessageSizeBytes']
max_write_batch_size = doc['maxWriteBatchSize']
MotorClient.event_listeners and other configuration option helpers are removed¶
The following client configuration option helpers are removed:
event_listeners
.max_pool_size
.min_pool_size
.max_idle_time_ms
.local_threshold_ms
.server_selection_timeout
.retry_writes
.retry_reads
.
These helpers have been replaced by
options
. Code like this:
client.event_listeners
client.local_threshold_ms
client.server_selection_timeout
client.max_pool_size
client.min_pool_size
client.max_idle_time_ms
can be changed to this:
client.options.event_listeners
client.options.local_threshold_ms
client.options.server_selection_timeout
client.options.pool_options.max_pool_size
client.options.pool_options.min_pool_size
client.options.pool_options.max_idle_time_seconds
tz_aware
defaults to False
¶
tz_aware
, an argument for JSONOptions
,
now defaults to False
instead of True
. json_util.loads
now
decodes datetime as naive by default.
MotorClient cannot execute operations after close()
¶
MotorClient
cannot execute any operations
after being closed. The previous behavior would simply reconnect. However,
now you must create a new instance.
MotorClient raises exception when given more than one URI¶
MotorClient
now raises a ConfigurationError
when more than one URI is passed into the hosts
argument.
MotorClient raises exception when given unescaped percent sign in login info¶
MotorClient
now raises an
InvalidURI
exception
when it encounters unescaped percent signs in username and password.
Database¶
MotorDatabase.current_op is removed¶
Removed current_op()
. Use
aggregate()
instead with the
$currentOp aggregation pipeline stage. Code like
this:
ops = client.admin.current_op()['inprog']
can be changed to this:
ops = await client.admin.aggregate([{'$currentOp': {}}]).to_list()
MotorDatabase.profiling_level is removed¶
Removed profiling_level()
which was deprecated in
PyMongo 3.12. Use the profile command instead. Code like this:
level = db.profiling_level()
Can be changed to this:
profile = await db.command('profile', -1)
level = profile['was']
MotorDatabase.set_profiling_level is removed¶
Removed set_profiling_level()
which was deprecated in
PyMongo 3.12. Use the profile command instead. Code like this:
db.set_profiling_level(pymongo.ALL, filter={'op': 'query'})
Can be changed to this:
res = await db.command('profile', 2, filter={'op': 'query'})
MotorDatabase.profiling_info is removed¶
Removed profiling_info()
which was deprecated in
PyMongo 3.12. Query the ‘system.profile’ collection instead. Code like this:
profiling_info = db.profiling_info()
Can be changed to this:
profiling_info = await db['system.profile'].find().to_list()
MotorDatabase.__bool__ raises NotImplementedError¶
MotorDatabase
now raises an error upon evaluating as a
Boolean. Code like this:
if database:
Can be changed to this:
if database is not None:
You must now explicitly compare with None.
MotorCollection¶
MotorCollection.map_reduce and MotorCollection.inline_map_reduce are removed¶
Removed map_reduce()
and
inline_map_reduce()
.
Migrate to aggregate()
or run the
mapReduce command directly with command()
instead. For more guidance on this migration see:
MotorCollection.reindex is removed¶
Removed motor.motor_tornado.MotorCollection.reindex()
. Run the
reIndex command directly instead. Code like this:
>>> result = await database.my_collection.reindex()
can be changed to this:
>>> result = await database.command('reIndex', 'my_collection')
The modifiers parameter is removed¶
Removed the modifiers
parameter from
find()
,
find_one()
,
find_raw_batches()
, and
MotorCursor()
. Pass the options directly to the method
instead. Code like this:
cursor = await coll.find({}, modifiers={
"$comment": "comment",
"$hint": {"_id": 1},
"$min": {"_id": 0},
"$max": {"_id": 6},
"$maxTimeMS": 6000,
"$returnKey": False,
"$showDiskLoc": False,
})
can be changed to this:
cursor = await coll.find(
{},
comment="comment",
hint={"_id": 1},
min={"_id": 0},
max={"_id": 6},
max_time_ms=6000,
return_key=False,
show_record_id=False,
)
The hint parameter is required with min/max¶
The hint
option is now required when using min
or max
queries
with find()
to ensure the query utilizes
the correct index. For example, code like this:
cursor = await coll.find({}, min={'x', min_value})
can be changed to this:
cursor = await coll.find({}, min={'x', min_value}, hint=[('x', ASCENDING)])
MotorCollection.__bool__ raises NotImplementedError¶
MotorCollection
now raises an error upon evaluating
as a Boolean. Code like this:
if collection:
Can be changed to this:
if collection is not None:
You must now explicitly compare with None.
MotorCollection.find returns entire document with empty projection¶
Empty projections (eg {} or []) for
find()
, and
find_one()
are passed to the server as-is rather than the previous behavior which
substituted in a projection of {"_id": 1}
. This means that an empty
projection will now return the entire document, not just the "_id"
field.
To ensure that behavior remains consistent, code like this:
await coll.find({}, projection={})
Can be changed to this:
await coll.find({}, projection={"_id":1})
SONManipulator is removed¶
PyMongo 4.0 removed pymongo.son_manipulator
.
Motor 3.0 removed motor.MotorDatabase.add_son_manipulator()
,
motor.MotorDatabase.outgoing_copying_manipulators
,
motor.MotorDatabase.outgoing_manipulators
,
motor.MotorDatabase.incoming_copying_manipulators
, and
motor.MotorDatabase.incoming_manipulators
.
Removed the manipulate
parameter from
find()
,
find_one()
, and
MotorCursor()
.
The pymongo.son_manipulator.SONManipulator
API has limitations as a
technique for transforming your data and was deprecated in PyMongo 3.0.
Instead, it is more flexible and straightforward to transform outgoing
documents in your own code before passing them to PyMongo, and transform
incoming documents after receiving them from PyMongo.
Alternatively, if your application uses the SONManipulator
API to convert
custom types to BSON, the TypeCodec
and
TypeRegistry
APIs may be a suitable alternative.
For more information, see the
custom type example.
GridFS changes¶
disable_md5 parameter is removed¶
Removed the disable_md5 option for MotorGridFSBucket
and
MotorGridFS
. GridFS no longer generates checksums.
Applications that desire a file digest should implement it outside GridFS
and store it with other file metadata. For example:
import hashlib
my_db = MotorClient().test
fs = GridFSBucket(my_db)
grid_in = fs.open_upload_stream("test_file")
file_data = b'...'
sha356 = hashlib.sha256(file_data).hexdigest()
await grid_in.write(file_data)
grid_in.sha356 = sha356 # Set the custom 'sha356' field
await grid_in.close()
Note that for large files, the checksum may need to be computed in chunks to avoid the excessive memory needed to load the entire file at once.
Removed features with no migration path¶
Encoding a UUID raises an error by default¶
The default uuid_representation for CodecOptions
,
JSONOptions
, and
MotorClient
has been changed from
bson.binary.UuidRepresentation.PYTHON_LEGACY
to
bson.binary.UuidRepresentation.UNSPECIFIED
. Attempting to encode a
uuid.UUID
instance to BSON or JSON now produces an error by default.
See Handling UUID Data for details.
Upgrade to Motor 3.0¶
Once your application runs without deprecation warnings with Motor 2.5, upgrade to Motor 3.0.