Skip to content

Commit 104ec91

Browse files
committed
Merge branch 'master' of github.com:mongodb/mongo-python-driver
2 parents de7d95c + 48bdbfd commit 104ec91

File tree

68 files changed

+1524
-769
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+1524
-769
lines changed

.evergreen/run-tests.sh

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,6 @@ set -o xtrace
3131
AUTH=${AUTH:-noauth}
3232
SSL=${SSL:-nossl}
3333
TEST_ARGS="${*:1}"
34-
PYTHON=$(which python)
35-
# TODO: Remove when we drop PyPy 3.8 support.
36-
OLD_PYPY=$(python -c "import sys; print(sys.implementation.name.lower() == 'pypy' and sys.implementation.version < (7, 3, 12))")
3734

3835
export PIP_QUIET=1 # Quiet by default
3936
export PIP_PREFER_BINARY=1 # Prefer binary dists by default
@@ -113,10 +110,6 @@ fi
113110

114111
if [ "$COMPRESSORS" = "snappy" ]; then
115112
python -m pip install '.[snappy]'
116-
if [ "$OLD_PYPY" == "True" ]; then
117-
pip install "python-snappy<0.7.0"
118-
fi
119-
PYTHON=python
120113
elif [ "$COMPRESSORS" = "zstd" ]; then
121114
python -m pip install zstandard
122115
fi
@@ -237,7 +230,7 @@ if [ -n "$PERF_TEST" ]; then
237230
TEST_ARGS="test/performance/perf_test.py"
238231
fi
239232

240-
echo "Running $AUTH tests over $SSL with python $PYTHON"
233+
echo "Running $AUTH tests over $SSL with python $(which python)"
241234
python -c 'import sys; print(sys.version)'
242235

243236

@@ -246,7 +239,7 @@ python -c 'import sys; print(sys.version)'
246239

247240
# Run the tests with coverage if requested and coverage is installed.
248241
# Only cover CPython. PyPy reports suspiciously low coverage.
249-
PYTHON_IMPL=$($PYTHON -c "import platform; print(platform.python_implementation())")
242+
PYTHON_IMPL=$(python -c "import platform; print(platform.python_implementation())")
250243
if [ -n "$COVERAGE" ] && [ "$PYTHON_IMPL" = "CPython" ]; then
251244
# Keep in sync with combine-coverage.sh.
252245
# coverage >=5 is needed for relative_files=true.

doc/changelog.rst

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,22 @@
11
Changelog
22
=========
33

4+
Changes in Version 4.9.0
5+
-------------------------
6+
7+
PyMongo 4.9 brings a number of improvements including:
8+
9+
- A new asynchronous API with full asyncio support.
10+
11+
Issues Resolved
12+
...............
13+
14+
See the `PyMongo 4.9 release notes in JIRA`_ for the list of resolved issues
15+
in this release.
16+
17+
.. _PyMongo 4.9 release notes in JIRA: https://jira.mongodb.org/secure/ReleaseNote.jspa?projectId=10004&version=39940
18+
19+
420
Changes in Version 4.8.0
521
-------------------------
622

@@ -10,14 +26,21 @@ PyMongo 4.8 brings a number of improvements including:
1026

1127
- The handshake metadata for "os.name" on Windows has been simplified to "Windows" to improve import time.
1228
- The repr of ``bson.binary.Binary`` is now redacted when the subtype is SENSITIVE_SUBTYPE(8).
13-
- A new asynchronous API with full asyncio support.
1429

1530
Unavoidable breaking changes
1631
............................
1732

1833
- Since we are now using ``hatch`` as our build backend, we no longer have a ``setup.py`` file
1934
and require installation using ``pip``.
2035

36+
Issues Resolved
37+
...............
38+
39+
See the `PyMongo 4.8 release notes in JIRA`_ for the list of resolved issues
40+
in this release.
41+
42+
.. _PyMongo 4.8 release notes in JIRA: https://jira.mongodb.org/secure/ReleaseNote.jspa?projectId=10004&version=37057
43+
2144
Changes in Version 4.7.3
2245
-------------------------
2346

pymongo/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
import re
1919
from typing import List, Tuple, Union
2020

21-
__version__ = "4.8.0.dev1"
21+
__version__ = "4.9.0.dev0"
2222

2323

2424
def get_version_tuple(version: str) -> Tuple[Union[int, str], ...]:

pymongo/asynchronous/encryption.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -456,13 +456,10 @@ class Algorithm(str, enum.Enum):
456456
457457
.. versionadded:: 4.2
458458
"""
459-
RANGEPREVIEW = "RangePreview"
460-
"""RangePreview.
459+
RANGE = "Range"
460+
"""Range.
461461
462-
.. note:: Support for Range queries is in beta.
463-
Backwards-breaking changes may be made before the final release.
464-
465-
.. versionadded:: 4.4
462+
.. versionadded:: 4.8
466463
"""
467464

468465

@@ -475,11 +472,9 @@ class QueryType(str, enum.Enum):
475472
EQUALITY = "equality"
476473
"""Used to encrypt a value for an equality query."""
477474

478-
RANGEPREVIEW = "rangePreview"
475+
RANGE = "range"
479476
"""Used to encrypt a value for a range query.
480477
481-
.. note:: Support for Range queries is in beta.
482-
Backwards-breaking changes may be made before the final release.
483478
"""
484479

485480

@@ -836,10 +831,14 @@ async def encrypt(
836831
when the algorithm is :attr:`Algorithm.INDEXED`. An integer value
837832
*must* be given when the :attr:`Algorithm.INDEXED` algorithm is
838833
used.
839-
:param range_opts: Experimental only, not intended for public use.
834+
:param range_opts: Index options for `range` queries. See
835+
:class:`RangeOpts` for some valid options.
840836
841837
:return: The encrypted value, a :class:`~bson.binary.Binary` with subtype 6.
842838
839+
.. versionchanged:: 4.8
840+
Added the `range_opts` parameter.
841+
843842
.. versionchanged:: 4.7
844843
``key_id`` can now be passed in as a :class:`uuid.UUID`.
845844
@@ -888,10 +887,14 @@ async def encrypt_expression(
888887
when the algorithm is :attr:`Algorithm.INDEXED`. An integer value
889888
*must* be given when the :attr:`Algorithm.INDEXED` algorithm is
890889
used.
891-
:param range_opts: Experimental only, not intended for public use.
890+
:param range_opts: Index options for `range` queries. See
891+
:class:`RangeOpts` for some valid options.
892892
893893
:return: The encrypted expression, a :class:`~bson.RawBSONDocument`.
894894
895+
.. versionchanged:: 4.8
896+
Added the `range_opts` parameter.
897+
895898
.. versionchanged:: 4.7
896899
``key_id`` can now be passed in as a :class:`uuid.UUID`.
897900

pymongo/asynchronous/encryption_options.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -231,20 +231,20 @@ def __init__(
231231

232232

233233
class RangeOpts:
234-
"""Options to configure encrypted queries using the rangePreview algorithm."""
234+
"""Options to configure encrypted queries using the range algorithm."""
235235

236236
def __init__(
237237
self,
238238
sparsity: int,
239+
trim_factor: int,
239240
min: Optional[Any] = None,
240241
max: Optional[Any] = None,
241242
precision: Optional[int] = None,
242243
) -> None:
243-
"""Options to configure encrypted queries using the rangePreview algorithm.
244-
245-
.. note:: This feature is experimental only, and not intended for public use.
244+
"""Options to configure encrypted queries using the range algorithm.
246245
247246
:param sparsity: An integer.
247+
:param trim_factor: An integer.
248248
:param min: A BSON scalar value corresponding to the type being queried.
249249
:param max: A BSON scalar value corresponding to the type being queried.
250250
:param precision: An integer, may only be set for double or decimal128 types.
@@ -254,13 +254,15 @@ def __init__(
254254
self.min = min
255255
self.max = max
256256
self.sparsity = sparsity
257+
self.trim_factor = trim_factor
257258
self.precision = precision
258259

259260
@property
260261
def document(self) -> dict[str, Any]:
261262
doc = {}
262263
for k, v in [
263264
("sparsity", int64.Int64(self.sparsity)),
265+
("trimFactor", self.trim_factor),
264266
("precision", self.precision),
265267
("min", self.min),
266268
("max", self.max),

pymongo/asynchronous/mongo_client.py

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,7 @@ def __init__(
854854
server_monitoring_mode=options.server_monitoring_mode,
855855
)
856856

857+
self._opened = False
857858
self._init_background()
858859

859860
if _IS_SYNC and connect:
@@ -895,13 +896,14 @@ async def target() -> bool:
895896
# this closure. When the client is freed, stop the executor soon.
896897
self_ref: Any = weakref.ref(self, executor.close)
897898
self._kill_cursors_executor = executor
899+
self._opened = False
898900

899901
def _should_pin_cursor(self, session: Optional[ClientSession]) -> Optional[bool]:
900902
return self._options.load_balanced and not (session and session.in_transaction)
901903

902904
def _after_fork(self) -> None:
903905
"""Resets topology in a child after successfully forking."""
904-
self._init_background()
906+
self._init_background(self._topology._pid)
905907
# Reset the session pool to avoid duplicate sessions in the child process.
906908
self._topology._session_pool.reset()
907909

@@ -1382,7 +1384,9 @@ async def _server_property(self, attr_name: str) -> Any:
13821384
the server may change. In such cases, store a local reference to a
13831385
ServerDescription first, then use its properties.
13841386
"""
1385-
server = await self._topology.select_server(writable_server_selector, _Op.TEST)
1387+
server = await (await self._get_topology()).select_server(
1388+
writable_server_selector, _Op.TEST
1389+
)
13861390

13871391
return getattr(server.description, attr_name)
13881392

@@ -1528,9 +1532,11 @@ async def _get_topology(self) -> Topology:
15281532
If this client was created with "connect=False", calling _get_topology
15291533
launches the connection process in the background.
15301534
"""
1531-
await self._topology.open()
1532-
async with self._lock:
1533-
self._kill_cursors_executor.open()
1535+
if not self._opened:
1536+
await self._topology.open()
1537+
async with self._lock:
1538+
self._kill_cursors_executor.open()
1539+
self._opened = True
15341540
return self._topology
15351541

15361542
@contextlib.asynccontextmanager
@@ -1631,9 +1637,9 @@ async def _conn_from_server(
16311637
# always send primaryPreferred when directly connected to a repl set
16321638
# member.
16331639
# Thread safe: if the type is single it cannot change.
1634-
topology = await self._get_topology()
1635-
single = topology.description.topology_type == TOPOLOGY_TYPE.Single
1636-
1640+
# NOTE: We already opened the Topology when selecting a server so there's no need
1641+
# to call _get_topology() again.
1642+
single = self._topology.description.topology_type == TOPOLOGY_TYPE.Single
16371643
async with self._checkout(server, session) as conn:
16381644
if single:
16391645
if conn.is_repl and not (session and session.in_transaction):
@@ -1652,7 +1658,6 @@ async def _conn_for_reads(
16521658
operation: str,
16531659
) -> AsyncContextManager[tuple[Connection, _ServerMode]]:
16541660
assert read_preference is not None, "read_preference must not be None"
1655-
_ = await self._get_topology()
16561661
server = await self._select_server(read_preference, session, operation)
16571662
return self._conn_from_server(read_preference, server, session)
16581663

pymongo/asynchronous/pool.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -216,13 +216,14 @@ def _set_keepalive_times(sock: socket.socket) -> None:
216216
"version": platform.mac_ver()[0],
217217
}
218218
elif sys.platform == "win32":
219+
_ver = sys.getwindowsversion()
219220
_METADATA["os"] = {
220-
"type": platform.system(),
221-
# "Windows XP", "Windows 7", "Windows 10", etc.
222-
"name": " ".join((platform.system(), platform.release())),
223-
"architecture": platform.machine(),
224-
# Windows patch level (e.g. 5.1.2600-SP3)
225-
"version": "-".join(platform.win32_ver()[1:3]),
221+
"type": "Windows",
222+
"name": "Windows",
223+
# Avoid using platform calls, see PYTHON-4455.
224+
"architecture": os.environ.get("PROCESSOR_ARCHITECTURE") or platform.machine(),
225+
# Windows patch level (e.g. 10.0.17763-SP0).
226+
"version": ".".join(map(str, _ver[:3])) + f"-SP{_ver[-1] or '0'}",
226227
}
227228
elif sys.platform.startswith("java"):
228229
_name, _ver, _arch = platform.java_ver()[-1]

pymongo/ssl_support.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"""Support for SSL in PyMongo."""
1616
from __future__ import annotations
1717

18+
import warnings
1819
from typing import Optional
1920

2021
from pymongo.errors import ConfigurationError
@@ -23,7 +24,17 @@
2324

2425
try:
2526
import pymongo.pyopenssl_context as _ssl
26-
except ImportError:
27+
except (ImportError, AttributeError) as exc:
28+
if isinstance(exc, AttributeError):
29+
warnings.warn(
30+
"Failed to use the installed version of PyOpenSSL. "
31+
"Falling back to stdlib ssl, disabling OCSP support. "
32+
"This is likely caused by incompatible versions "
33+
"of PyOpenSSL < 23.2.0 and cryptography >= 42.0.0. "
34+
"Try updating PyOpenSSL >= 23.2.0 to enable OCSP.",
35+
UserWarning,
36+
stacklevel=2,
37+
)
2738
try:
2839
import pymongo.ssl_context as _ssl # type: ignore[no-redef]
2940
except ImportError:

pymongo/synchronous/encryption.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -454,13 +454,10 @@ class Algorithm(str, enum.Enum):
454454
455455
.. versionadded:: 4.2
456456
"""
457-
RANGEPREVIEW = "RangePreview"
458-
"""RangePreview.
457+
RANGE = "Range"
458+
"""Range.
459459
460-
.. note:: Support for Range queries is in beta.
461-
Backwards-breaking changes may be made before the final release.
462-
463-
.. versionadded:: 4.4
460+
.. versionadded:: 4.8
464461
"""
465462

466463

@@ -473,11 +470,9 @@ class QueryType(str, enum.Enum):
473470
EQUALITY = "equality"
474471
"""Used to encrypt a value for an equality query."""
475472

476-
RANGEPREVIEW = "rangePreview"
473+
RANGE = "range"
477474
"""Used to encrypt a value for a range query.
478475
479-
.. note:: Support for Range queries is in beta.
480-
Backwards-breaking changes may be made before the final release.
481476
"""
482477

483478

@@ -834,10 +829,14 @@ def encrypt(
834829
when the algorithm is :attr:`Algorithm.INDEXED`. An integer value
835830
*must* be given when the :attr:`Algorithm.INDEXED` algorithm is
836831
used.
837-
:param range_opts: Experimental only, not intended for public use.
832+
:param range_opts: Index options for `range` queries. See
833+
:class:`RangeOpts` for some valid options.
838834
839835
:return: The encrypted value, a :class:`~bson.binary.Binary` with subtype 6.
840836
837+
.. versionchanged:: 4.8
838+
Added the `range_opts` parameter.
839+
841840
.. versionchanged:: 4.7
842841
``key_id`` can now be passed in as a :class:`uuid.UUID`.
843842
@@ -886,10 +885,14 @@ def encrypt_expression(
886885
when the algorithm is :attr:`Algorithm.INDEXED`. An integer value
887886
*must* be given when the :attr:`Algorithm.INDEXED` algorithm is
888887
used.
889-
:param range_opts: Experimental only, not intended for public use.
888+
:param range_opts: Index options for `range` queries. See
889+
:class:`RangeOpts` for some valid options.
890890
891891
:return: The encrypted expression, a :class:`~bson.RawBSONDocument`.
892892
893+
.. versionchanged:: 4.8
894+
Added the `range_opts` parameter.
895+
893896
.. versionchanged:: 4.7
894897
``key_id`` can now be passed in as a :class:`uuid.UUID`.
895898

0 commit comments

Comments
 (0)