Skip to content

Commit e587bf6

Browse files
committed
Merge branch 'master' of github.com:MongoEngine/mongoengine into Winedays-feature/query_set_more_support_for_collation_hint_comment
2 parents ccaa42d + fd9955c commit e587bf6

File tree

11 files changed

+79
-90
lines changed

11 files changed

+79
-90
lines changed

.github/workflows/start_mongo.sh

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@ mongodb_dir=$(find ${PWD}/ -type d -name "mongodb-linux-x86_64*")
66

77
mkdir $mongodb_dir/data
88

9-
$mongodb_dir/bin/mongod --dbpath $mongodb_dir/data --logpath $mongodb_dir/mongodb.log --fork --replSet mongoengine
9+
args=(--dbpath $mongodb_dir/data --logpath $mongodb_dir/mongodb.log --fork --replSet mongoengine)
10+
if (( $(echo "$MONGODB > 3.8" | bc -l) )); then
11+
args+=(--setParameter maxTransactionLockRequestTimeoutMillis=1000)
12+
fi
13+
14+
$mongodb_dir/bin/mongod "${args[@]}"
15+
1016
if (( $(echo "$MONGODB < 6.0" | bc -l) )); then
1117
mongo --verbose --eval "rs.initiate()"
1218
mongo --quiet --eval "rs.status().ok"

docs/changelog.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ Development
1212
- make sure to read https://www.mongodb.com/docs/manual/core/transactions-in-applications/#callback-api-vs-core-api
1313
- run_in_transaction context manager relies on Pymongo coreAPI, it will retry automatically in case of `UnknownTransactionCommitResult` but not `TransientTransactionError` exceptions
1414
- Using .count() in a transaction will always use Collection.count_document (as estimated_document_count is not supported in transactions)
15+
- Further to the deprecation warning, remove ability to use an unpacked list to `Queryset.aggregate(*pipeline)`, a plain list must be provided instead `Queryset.aggregate(pipeline)`, as it's closer to pymongo interface
16+
- Further to the deprecation warning, remove `full_response` from `QuerySet.modify` as it wasn't supported with Pymongo 3+
17+
- Fixed stacklevel of many warnings (to point places emitting the warning more accurately)
1518

1619
Changes in 0.29.0
1720
=================

entrypoint.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
mongod --replSet mongoengine --fork --logpath=/var/log/mongodb.log
44
mongo db --eval "rs.initiate()"
55
mongod --shutdown
6-
mongod --replSet mongoengine --bind_ip 0.0.0.0
6+
mongod --replSet mongoengine --bind_ip 0.0.0.0 --setParameter maxTransactionLockRequestTimeoutMillis=1000

mongoengine/base/document.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,7 @@ def to_json(self, *args, **kwargs):
455455
"representation to use. This will be changed to "
456456
"uuid_representation=UNSPECIFIED in a future release.",
457457
DeprecationWarning,
458+
stacklevel=2,
458459
)
459460
kwargs["json_options"] = LEGACY_JSON_OPTIONS
460461
return json_util.dumps(self.to_mongo(use_db_field), *args, **kwargs)
@@ -486,6 +487,7 @@ def from_json(cls, json_data, created=False, **kwargs):
486487
"representation to use. This will be changed to "
487488
"uuid_representation=UNSPECIFIED in a future release.",
488489
DeprecationWarning,
490+
stacklevel=2,
489491
)
490492
kwargs["json_options"] = LEGACY_JSON_OPTIONS
491493
return cls._from_son(json_util.loads(json_data, **kwargs), created=created)

mongoengine/base/metaclasses.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ def __new__(mcs, name, bases, attrs):
307307
and not parent_doc_cls._meta.get("abstract", True)
308308
):
309309
msg = "Trying to set a collection on a subclass (%s)" % name
310-
warnings.warn(msg, SyntaxWarning)
310+
warnings.warn(msg, SyntaxWarning, stacklevel=2)
311311
del attrs["_meta"]["collection"]
312312

313313
# Ensure abstract documents have abstract bases

mongoengine/connection.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ def _get_connection_settings(
211211
"older drivers in those languages. This will be changed to "
212212
"'unspecified' in a future release.",
213213
DeprecationWarning,
214+
stacklevel=3,
214215
)
215216
kwargs["uuidRepresentation"] = "pythonLegacy"
216217

mongoengine/queryset/base.py

Lines changed: 11 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -697,7 +697,6 @@ def update_one(
697697
def modify(
698698
self,
699699
upsert=False,
700-
full_response=False,
701700
remove=False,
702701
new=False,
703702
array_filters=None,
@@ -709,15 +708,7 @@ def modify(
709708
parameter. If no documents match the query and `upsert` is false,
710709
returns ``None``. If upserting and `new` is false, returns ``None``.
711710
712-
If the full_response parameter is ``True``, the return value will be
713-
the entire response object from the server, including the 'ok' and
714-
'lastErrorObject' fields, rather than just the modified document.
715-
This is useful mainly because the 'lastErrorObject' document holds
716-
information about the command's execution.
717-
718711
:param upsert: insert if document doesn't exist (default ``False``)
719-
:param full_response: return the entire response object from the
720-
server (default ``False``, not available for PyMongo 3+)
721712
:param remove: remove rather than updating (default ``False``)
722713
:param new: return updated rather than original document
723714
(default ``False``)
@@ -741,9 +732,6 @@ def modify(
741732
sort = queryset._ordering
742733

743734
try:
744-
if full_response:
745-
msg = "With PyMongo 3+, it is not possible anymore to get the full response."
746-
warnings.warn(msg, DeprecationWarning)
747735
if remove:
748736
result = queryset._collection.find_one_and_delete(
749737
query, sort=sort, session=_get_session(), **self._cursor_args
@@ -768,12 +756,8 @@ def modify(
768756
except pymongo.errors.OperationFailure as err:
769757
raise OperationError("Update failed (%s)" % err)
770758

771-
if full_response:
772-
if result["value"] is not None:
773-
result["value"] = self._document._from_son(result["value"])
774-
else:
775-
if result is not None:
776-
result = self._document._from_son(result)
759+
if result is not None:
760+
result = self._document._from_son(result)
777761

778762
return result
779763

@@ -1244,7 +1228,7 @@ def snapshot(self, enabled):
12441228
:param enabled: whether or not snapshot mode is enabled
12451229
"""
12461230
msg = "snapshot is deprecated as it has no impact when using PyMongo 3+."
1247-
warnings.warn(msg, DeprecationWarning)
1231+
warnings.warn(msg, DeprecationWarning, stacklevel=2)
12481232
queryset = self.clone()
12491233
queryset._snapshot = enabled
12501234
return queryset
@@ -1353,6 +1337,7 @@ def to_json(self, *args, **kwargs):
13531337
"representation to use. This will be changed to "
13541338
"uuid_representation=UNSPECIFIED in a future release.",
13551339
DeprecationWarning,
1340+
stacklevel=2,
13561341
)
13571342
kwargs["json_options"] = LEGACY_JSON_OPTIONS
13581343
return json_util.dumps(self.as_pymongo(), *args, **kwargs)
@@ -1362,24 +1347,18 @@ def from_json(self, json_data):
13621347
son_data = json_util.loads(json_data)
13631348
return [self._document._from_son(data) for data in son_data]
13641349

1365-
def aggregate(self, pipeline, *suppl_pipeline, **kwargs):
1350+
def aggregate(self, pipeline, **kwargs):
13661351
"""Perform an aggregate function based on your queryset params
13671352
13681353
:param pipeline: list of aggregation commands,
13691354
see: https://www.mongodb.com/docs/manual/core/aggregation-pipeline/
1370-
:param suppl_pipeline: unpacked list of pipeline (added to support deprecation of the old interface)
1371-
parameter will be removed shortly
13721355
:param kwargs: (optional) kwargs dictionary to be passed to pymongo's aggregate call
13731356
See https://pymongo.readthedocs.io/en/stable/api/pymongo/collection.html#pymongo.collection.Collection.aggregate
13741357
"""
1375-
using_deprecated_interface = isinstance(pipeline, dict) or bool(suppl_pipeline)
1376-
user_pipeline = [pipeline] if isinstance(pipeline, dict) else list(pipeline)
1377-
1378-
if using_deprecated_interface:
1379-
msg = "Calling .aggregate() with un unpacked list (*pipeline) is deprecated, it will soon change and will expect a list (similar to pymongo.Collection.aggregate interface), see documentation"
1380-
warnings.warn(msg, DeprecationWarning)
1381-
1382-
user_pipeline += suppl_pipeline
1358+
if not isinstance(pipeline, (tuple, list)):
1359+
raise TypeError(
1360+
f"Starting from 1.0 release pipeline must be a list/tuple, received: {type(pipeline)}"
1361+
)
13831362

13841363
initial_pipeline = []
13851364
if self._none or self._empty:
@@ -1402,7 +1381,7 @@ def aggregate(self, pipeline, *suppl_pipeline, **kwargs):
14021381
if self._skip is not None:
14031382
initial_pipeline.append({"$skip": self._skip})
14041383

1405-
final_pipeline = initial_pipeline + user_pipeline
1384+
final_pipeline = initial_pipeline + pipeline
14061385

14071386
collection = self._collection
14081387
if self._read_preference is not None or self._read_concern is not None:
@@ -1725,7 +1704,7 @@ def _cursor_args(self):
17251704
# TODO: evaluate similar possibilities using modifiers
17261705
if self._snapshot:
17271706
msg = "The snapshot option is not anymore available with PyMongo 3+"
1728-
warnings.warn(msg, DeprecationWarning)
1707+
warnings.warn(msg, DeprecationWarning, stacklevel=3)
17291708

17301709
cursor_args = {}
17311710
if not self._timeout:

tests/queryset/test_modify.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import unittest
22

3-
import pytest
4-
53
from mongoengine import (
64
Document,
75
IntField,
@@ -32,13 +30,6 @@ def test_modify(self):
3230
assert old_doc.to_json() == doc.to_json()
3331
self._assert_db_equal([{"_id": 0, "value": 0}, {"_id": 1, "value": -1}])
3432

35-
def test_modify_full_response_raise_value_error_for_recent_mongo(self):
36-
Doc(id=0, value=0).save()
37-
Doc(id=1, value=1).save()
38-
39-
with pytest.raises(ValueError):
40-
Doc.objects(id=1).modify(set__value=-1, full_response=True)
41-
4233
def test_modify_with_new(self):
4334
Doc(id=0, value=0).save()
4435
doc = Doc(id=1, value=1).save()

tests/queryset/test_queryset.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -466,8 +466,7 @@ class A(Document):
466466

467467
A.drop_collection()
468468

469-
for i in range(100):
470-
A.objects.create(s=str(i))
469+
A.objects.insert([A(s=str(i)) for i in range(100)], load_bulk=True)
471470

472471
# test iterating over the result set
473472
cnt = 0
@@ -1275,8 +1274,7 @@ def __repr__(self):
12751274

12761275
Doc.drop_collection()
12771276

1278-
for i in range(1000):
1279-
Doc(number=i).save()
1277+
Doc.objects.insert([Doc(number=i) for i in range(1000)], load_bulk=True)
12801278

12811279
docs = Doc.objects.order_by("number")
12821280

@@ -5430,8 +5428,9 @@ class Person(Document):
54305428
name = StringField()
54315429

54325430
Person.drop_collection()
5433-
for i in range(100):
5434-
Person(name="No: %s" % i).save()
5431+
5432+
persons = [Person(name="No: %s" % i) for i in range(100)]
5433+
Person.objects.insert(persons, load_bulk=True)
54355434

54365435
with query_counter() as q:
54375436
assert q == 0
@@ -5461,8 +5460,9 @@ class Person(Document):
54615460
name = StringField()
54625461

54635462
Person.drop_collection()
5464-
for i in range(100):
5465-
Person(name="No: %s" % i).save()
5463+
5464+
persons = [Person(name="No: %s" % i) for i in range(100)]
5465+
Person.objects.insert(persons, load_bulk=True)
54665466

54675467
with query_counter() as q:
54685468
assert q == 0
@@ -5529,17 +5529,20 @@ def __unicode__(self):
55295529
assert 1 == len(users._result_cache)
55305530

55315531
def test_no_cache(self):
5532-
"""Ensure you can add meta data to file"""
5532+
"""Ensure you can add metadata to file"""
55335533

55345534
class Noddy(Document):
55355535
fields = DictField()
55365536

55375537
Noddy.drop_collection()
5538+
5539+
noddies = []
55385540
for i in range(100):
55395541
noddy = Noddy()
55405542
for j in range(20):
55415543
noddy.fields["key" + str(j)] = "value " + str(j)
5542-
noddy.save()
5544+
noddies.append(noddy)
5545+
Noddy.objects.insert(noddies, load_bulk=True)
55435546

55445547
docs = Noddy.objects.no_cache()
55455548

@@ -5758,8 +5761,9 @@ class Person(Document):
57585761
name = StringField()
57595762

57605763
Person.drop_collection()
5761-
for i in range(100):
5762-
Person(name="No: %s" % i).save()
5764+
5765+
persons = [Person(name="No: %s" % i) for i in range(100)]
5766+
Person.objects.insert(persons, load_bulk=True)
57635767

57645768
with query_counter() as q:
57655769
if Person.objects:

tests/queryset/test_queryset_aggregation.py

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import unittest
2-
import warnings
32

3+
import pytest
44
from pymongo.read_preferences import ReadPreference
55

66
from mongoengine import Document, IntField, PointField, StringField
@@ -280,7 +280,7 @@ class Person(Document):
280280

281281
assert list(data) == [{"_id": p3.pk, "name": "SANDRA MARA"}]
282282

283-
def test_queryset_aggregation_deprecated_interface(self):
283+
def test_queryset_aggregation_old_interface_not_working(self):
284284
class Person(Document):
285285
name = StringField()
286286

@@ -291,27 +291,20 @@ class Person(Document):
291291
p3 = Person(name="Sandra Mara")
292292
Person.objects.insert([p1, p2, p3])
293293

294-
pipeline = [{"$project": {"name": {"$toUpper": "$name"}}}]
295-
296-
# Make sure a warning is emitted
297-
with warnings.catch_warnings():
298-
warnings.simplefilter("error", DeprecationWarning)
299-
with self.assertRaises(DeprecationWarning):
300-
Person.objects.order_by("name").limit(2).aggregate(*pipeline)
294+
_1_step_pipeline = [{"$project": {"name": {"$toUpper": "$name"}}}]
301295

302-
# Make sure old interface works as expected with a 1-step pipeline
303-
data = Person.objects.order_by("name").limit(2).aggregate(*pipeline)
296+
# Make sure old interface raises an error as we changed it >= 1.0
297+
with pytest.raises(TypeError, match="pipeline must be a list/tuple"):
298+
Person.objects.order_by("name").limit(2).aggregate(*_1_step_pipeline)
304299

305-
assert list(data) == [
306-
{"_id": p1.pk, "name": "ISABELLA LUANNA"},
307-
{"_id": p3.pk, "name": "SANDRA MARA"},
300+
_2_step_pipeline = [
301+
{"$project": {"name": {"$toUpper": "$name"}}},
302+
{"$limit": 1},
308303
]
309-
310-
# Make sure old interface works as expected with a 2-steps pipeline
311-
pipeline = [{"$project": {"name": {"$toUpper": "$name"}}}, {"$limit": 1}]
312-
data = Person.objects.order_by("name").limit(2).aggregate(*pipeline)
313-
314-
assert list(data) == [{"_id": p1.pk, "name": "ISABELLA LUANNA"}]
304+
with pytest.raises(
305+
TypeError, match="takes 2 positional arguments but 3 were given"
306+
):
307+
Person.objects.order_by("name").limit(2).aggregate(*_2_step_pipeline)
315308

316309
def test_queryset_aggregation_geonear_aggregation_on_pointfield(self):
317310
"""test ensures that $geonear can be used as a 1-stage pipeline and that
@@ -336,7 +329,7 @@ class Aggr(Document):
336329
}
337330
}
338331
]
339-
assert list(Aggr.objects.aggregate(*pipeline)) == [
332+
assert list(Aggr.objects.aggregate(pipeline)) == [
340333
{"_id": agg1.id, "c": 0.0, "name": "X"},
341334
{"_id": agg2.id, "c": 0.0, "name": "Y"},
342335
]

0 commit comments

Comments
 (0)