Skip to content

Commit 8875123

Browse files
committed
fix: correct return type annotation for find_one_and_* methods to include None
1 parent 44a58f1 commit 8875123

File tree

4 files changed

+51
-8
lines changed

4 files changed

+51
-8
lines changed

doc/changelog.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ PyMongo 4.16 brings a number of changes including:
1717
- Removed support for Eventlet.
1818
Eventlet is actively being sunset by its maintainers and has compatibility issues with PyMongo's dnspython dependency.
1919
- Use Zstandard support from the standard library for Python 3.14+, and use ``backports.zstd`` for older versions.
20+
- Fixed return type annotation for ``find_one_and_*`` methods on :class:`~pymongo.asynchronous.collection.AsyncCollection`
21+
and :class:`~pymongo.synchronous.collection.Collection` to include ``None``.
2022

2123
Changes in Version 4.15.4 (2025/10/21)
2224
--------------------------------------

doc/contributors.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,4 @@ The following is a list of people who have contributed to
107107
- Jeffrey A. Clark (aclark4life)
108108
- Steven Silvester (blink1073)
109109
- Noah Stapp (NoahStapp)
110+
- Cal Jacobson (cj81499)

pymongo/asynchronous/collection.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3310,7 +3310,7 @@ async def find_one_and_delete(
33103310
let: Optional[Mapping[str, Any]] = None,
33113311
comment: Optional[Any] = None,
33123312
**kwargs: Any,
3313-
) -> _DocumentType:
3313+
) -> Optional[_DocumentType]:
33143314
"""Finds a single document and deletes it, returning the document.
33153315
33163316
>>> await db.test.count_documents({'x': 1})
@@ -3320,6 +3320,10 @@ async def find_one_and_delete(
33203320
>>> await db.test.count_documents({'x': 1})
33213321
1
33223322
3323+
Returns ``None`` if no document matches the filter.
3324+
3325+
>>> await db.test.find_one_and_delete({'_exists': False})
3326+
33233327
If multiple documents match *filter*, a *sort* can be applied.
33243328
33253329
>>> async for doc in db.test.find({'x': 1}):
@@ -3402,10 +3406,22 @@ async def find_one_and_replace(
34023406
let: Optional[Mapping[str, Any]] = None,
34033407
comment: Optional[Any] = None,
34043408
**kwargs: Any,
3405-
) -> _DocumentType:
3409+
) -> Optional[_DocumentType]:
34063410
"""Finds a single document and replaces it, returning either the
34073411
original or the replaced document.
34083412
3413+
>>> await db.test.find_one({'x': 1})
3414+
{'_id': 0, 'x': 1}
3415+
>>> await db.test.find_one_and_replace({'x': 1}, {'y': 2})
3416+
{'_id': 0, 'x': 1}
3417+
>>> await db.test.find_one({'x': 1})
3418+
>>> await db.test.find_one({'y': 2})
3419+
{'_id': 0, 'y': 2}
3420+
3421+
Returns ``None`` if no document matches the filter.
3422+
3423+
>>> await db.test.find_one_and_replace({'_exists': False}, {'x': 1})
3424+
34093425
The :meth:`find_one_and_replace` method differs from
34103426
:meth:`find_one_and_update` by replacing the document matched by
34113427
*filter*, rather than modifying the existing document.
@@ -3510,13 +3526,17 @@ async def find_one_and_update(
35103526
let: Optional[Mapping[str, Any]] = None,
35113527
comment: Optional[Any] = None,
35123528
**kwargs: Any,
3513-
) -> _DocumentType:
3529+
) -> Optional[_DocumentType]:
35143530
"""Finds a single document and updates it, returning either the
35153531
original or the updated document.
35163532
3533+
>>> await db.test.find_one({'_id': 665})
3534+
{'_id': 665, 'done': False, 'count': 25}
35173535
>>> await db.test.find_one_and_update(
35183536
... {'_id': 665}, {'$inc': {'count': 1}, '$set': {'done': True}})
3519-
{'_id': 665, 'done': False, 'count': 25}}
3537+
{'_id': 665, 'done': False, 'count': 25}
3538+
>>> await db.test.find_one({'_id': 665})
3539+
{'_id': 665, 'done': True, 'count': 26}
35203540
35213541
Returns ``None`` if no document matches the filter.
35223542

pymongo/synchronous/collection.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3303,7 +3303,7 @@ def find_one_and_delete(
33033303
let: Optional[Mapping[str, Any]] = None,
33043304
comment: Optional[Any] = None,
33053305
**kwargs: Any,
3306-
) -> _DocumentType:
3306+
) -> Optional[_DocumentType]:
33073307
"""Finds a single document and deletes it, returning the document.
33083308
33093309
>>> db.test.count_documents({'x': 1})
@@ -3313,6 +3313,10 @@ def find_one_and_delete(
33133313
>>> db.test.count_documents({'x': 1})
33143314
1
33153315
3316+
Returns ``None`` if no document matches the filter.
3317+
3318+
>>> db.test.find_one_and_delete({'_exists': False})
3319+
33163320
If multiple documents match *filter*, a *sort* can be applied.
33173321
33183322
>>> for doc in db.test.find({'x': 1}):
@@ -3395,10 +3399,22 @@ def find_one_and_replace(
33953399
let: Optional[Mapping[str, Any]] = None,
33963400
comment: Optional[Any] = None,
33973401
**kwargs: Any,
3398-
) -> _DocumentType:
3402+
) -> Optional[_DocumentType]:
33993403
"""Finds a single document and replaces it, returning either the
34003404
original or the replaced document.
34013405
3406+
>>> db.test.find_one({'x': 1})
3407+
{'_id': 0, 'x': 1}
3408+
>>> db.test.find_one_and_replace({'x': 1}, {'y': 2})
3409+
{'_id': 0, 'x': 1}
3410+
>>> db.test.find_one({'x': 1})
3411+
>>> db.test.find_one({'y': 2})
3412+
{'_id': 0, 'y': 2}
3413+
3414+
Returns ``None`` if no document matches the filter.
3415+
3416+
>>> db.test.find_one_and_replace({'_exists': False}, {'x': 1})
3417+
34023418
The :meth:`find_one_and_replace` method differs from
34033419
:meth:`find_one_and_update` by replacing the document matched by
34043420
*filter*, rather than modifying the existing document.
@@ -3503,13 +3519,17 @@ def find_one_and_update(
35033519
let: Optional[Mapping[str, Any]] = None,
35043520
comment: Optional[Any] = None,
35053521
**kwargs: Any,
3506-
) -> _DocumentType:
3522+
) -> Optional[_DocumentType]:
35073523
"""Finds a single document and updates it, returning either the
35083524
original or the updated document.
35093525
3526+
>>> db.test.find_one({'_id': 665})
3527+
{'_id': 665, 'done': False, 'count': 25}
35103528
>>> db.test.find_one_and_update(
35113529
... {'_id': 665}, {'$inc': {'count': 1}, '$set': {'done': True}})
3512-
{'_id': 665, 'done': False, 'count': 25}}
3530+
{'_id': 665, 'done': False, 'count': 25}
3531+
>>> db.test.find_one({'_id': 665})
3532+
{'_id': 665, 'done': True, 'count': 26}
35133533
35143534
Returns ``None`` if no document matches the filter.
35153535

0 commit comments

Comments
 (0)