Skip to content

Commit 8d8ea20

Browse files
authored
Merge branch 'main' into PYTHON-6016
2 parents 776ff36 + 02963f6 commit 8d8ea20

41 files changed

Lines changed: 870 additions & 1097 deletions

Some content is hidden

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

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ package that is incompatible with PyMongo.
9898

9999
## Dependencies
100100

101-
PyMongo supports CPython 3.9+ and PyPy3.9+.
101+
PyMongo supports CPython 3.9+ and PyPy3.9+. PyPy support is deprecated and will be removed in a future release.
102102

103103
Required dependencies:
104104

bson/__init__.py

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -597,18 +597,32 @@ def _element_to_dict(
597597
_T = TypeVar("_T", bound=MutableMapping[str, Any])
598598

599599

600-
def _raw_to_dict(
601-
data: Any,
602-
position: int,
603-
obj_end: int,
604-
opts: CodecOptions[RawBSONDocument],
605-
result: _T,
606-
raw_array: bool = False,
607-
) -> _T:
608-
data, view = get_data_and_view(data)
609-
return cast(
610-
_T, _elements_to_dict(data, view, position, obj_end, opts, result, raw_array=raw_array)
611-
)
600+
if _USE_C:
601+
602+
def _raw_to_dict(
603+
data: Any,
604+
position: int,
605+
obj_end: int,
606+
opts: CodecOptions[RawBSONDocument],
607+
result: _T,
608+
raw_array: bool = False,
609+
) -> _T:
610+
return cast(_T, _cbson._raw_to_dict(data, position, obj_end, opts, result, raw_array))
611+
612+
else:
613+
614+
def _raw_to_dict(
615+
data: Any,
616+
position: int,
617+
obj_end: int,
618+
opts: CodecOptions[RawBSONDocument],
619+
result: _T,
620+
raw_array: bool = False,
621+
) -> _T:
622+
data, view = get_data_and_view(data)
623+
return cast(
624+
_T, _elements_to_dict(data, view, position, obj_end, opts, result, raw_array=raw_array)
625+
)
612626

613627

614628
def _elements_to_dict(

bson/_cbsonmodule.c

Lines changed: 94 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2867,9 +2867,6 @@ static int _element_to_dict(PyObject* self, const char* string,
28672867
}
28682868

28692869
static PyObject* _cbson_element_to_dict(PyObject* self, PyObject* args) {
2870-
/* TODO(PYTHON-6038): buffer-protocol inputs are copied
2871-
* upstream in get_data_and_view. Native RawBSONDocument inflation in C
2872-
* should accept the buffer directly. */
28732870
char* string;
28742871
PyObject* bson;
28752872
PyObject* options_obj = NULL;
@@ -3094,6 +3091,98 @@ static PyObject* _prepare_input_buffer(PyObject* self, PyObject* bson,
30943091
return PyBytes_FromObject(bson);
30953092
}
30963093

3094+
/* Decode all elements of a raw BSON document into `result`. */
3095+
static PyObject* _cbson_raw_to_dict(PyObject* self, PyObject* args) {
3096+
PyObject* data;
3097+
unsigned position;
3098+
unsigned obj_end;
3099+
PyObject* options_obj;
3100+
PyObject* result;
3101+
int raw_array = 0;
3102+
codec_options_t options;
3103+
PyObject* bson = NULL;
3104+
Py_buffer view = {0};
3105+
PyObject* ret = NULL;
3106+
const char* string;
3107+
unsigned end;
3108+
3109+
if (!(PyArg_ParseTuple(args, "OIIOOp", &data, &position, &obj_end,
3110+
&options_obj, &result, &raw_array) &&
3111+
convert_codec_options(self, options_obj, &options))) {
3112+
return NULL;
3113+
}
3114+
3115+
bson = _prepare_input_buffer(self, data, &options);
3116+
if (!bson) {
3117+
destroy_codec_options(&options);
3118+
return NULL;
3119+
}
3120+
3121+
if (!_get_buffer(bson, &view)) {
3122+
Py_DECREF(bson);
3123+
destroy_codec_options(&options);
3124+
return NULL;
3125+
}
3126+
3127+
string = (char*)view.buf;
3128+
/* obj_end must be the index of the document's eoo byte. The eoo check
3129+
* also protects against field scans running past the end of the buffer. */
3130+
if (obj_end < 1 || (Py_ssize_t)obj_end >= view.len || position > obj_end ||
3131+
string[obj_end]) {
3132+
PyObject* InvalidBSON = _error("InvalidBSON");
3133+
if (InvalidBSON) {
3134+
PyErr_SetString(InvalidBSON, "bad object or element length");
3135+
Py_DECREF(InvalidBSON);
3136+
}
3137+
goto done;
3138+
}
3139+
3140+
options.buffer_owner = bson;
3141+
3142+
end = obj_end - 1;
3143+
while (position < end) {
3144+
PyObject* name = NULL;
3145+
PyObject* value = NULL;
3146+
int new_position = _element_to_dict(
3147+
self, string, position, obj_end, &options, raw_array, &name, &value);
3148+
if (new_position < 0) {
3149+
goto done;
3150+
}
3151+
position = (unsigned)new_position;
3152+
3153+
if (PyDict_CheckExact(result)) {
3154+
if (PyDict_SetItem(result, name, value) < 0) {
3155+
Py_DECREF(name);
3156+
Py_DECREF(value);
3157+
goto done;
3158+
}
3159+
} else {
3160+
if (PyObject_SetItem(result, name, value) < 0) {
3161+
Py_DECREF(name);
3162+
Py_DECREF(value);
3163+
goto done;
3164+
}
3165+
}
3166+
Py_DECREF(name);
3167+
Py_DECREF(value);
3168+
}
3169+
if (position != obj_end) {
3170+
PyObject* InvalidBSON = _error("InvalidBSON");
3171+
if (InvalidBSON) {
3172+
PyErr_SetString(InvalidBSON, "bad object or element length");
3173+
Py_DECREF(InvalidBSON);
3174+
}
3175+
goto done;
3176+
}
3177+
Py_INCREF(result);
3178+
ret = result;
3179+
done:
3180+
PyBuffer_Release(&view);
3181+
Py_DECREF(bson);
3182+
destroy_codec_options(&options);
3183+
return ret;
3184+
}
3185+
30973186
static PyObject* _cbson_bson_to_dict(PyObject* self, PyObject* args) {
30983187
int32_t size;
30993188
Py_ssize_t total_size;
@@ -3423,6 +3512,8 @@ static PyMethodDef _CBSONMethods[] = {
34233512
"convert binary data to a sequence of documents."},
34243513
{"_element_to_dict", _cbson_element_to_dict, METH_VARARGS,
34253514
"Decode a single key, value pair."},
3515+
{"_raw_to_dict", _cbson_raw_to_dict, METH_VARARGS,
3516+
"Decode all elements of a raw BSON document into a result mapping."},
34263517
{"_array_of_documents_to_buffer", _cbson_array_of_documents_to_buffer, METH_VARARGS, "Convert raw array of documents to a stream of BSON documents"},
34273518
{"_test_long_long_to_str", _test_long_long_to_str, METH_VARARGS, "Test conversion of extreme and common Py_ssize_t values to str."},
34283519
{NULL, NULL, 0, NULL}

doc/changelog.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ PyMongo 4.18 brings a number of changes including:
88

99
- Dropped support for MongoDB 4.2.
1010
- Added support for MongoDB 9.0.
11+
- PyPy support is deprecated and will be removed in a future release.
1112
- Improved TLS connection performance by reusing TLS sessions across connections
1213
to the same server, avoiding a full handshake on each new connection.
1314
Session resumption is supported on all Python versions for synchronous clients
@@ -37,6 +38,8 @@ PyMongo 4.18 brings a number of changes including:
3738
:class:`bytearray` are always :class:`bytes` copies.
3839
- :func:`bson.get_data_and_view` now returns a view of a private :class:`bytes` copy
3940
for buffer-protocol inputs other than :class:`bytes` or :class:`bytearray`.
41+
- Improved the performance of lazily decoding a
42+
:class:`~bson.raw_bson.RawBSONDocument` when the C extension is available.
4043
- Fixed a potential out-of-bounds read in the C extension when decoding an
4144
array of BSON documents. An embedded document whose declared length exceeds
4245
the bytes remaining in the array now raises

pymongo/asynchronous/bulk.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
from bson.raw_bson import RawBSONDocument
3434
from pymongo import _csot, common
3535
from pymongo._telemetry import _generate_op_id_or_none
36-
from pymongo.asynchronous.client_session import AsyncClientSession, _validate_session_write_concern
36+
from pymongo.asynchronous.client_session import AsyncClientSession
3737
from pymongo.asynchronous.command_runner import (
3838
run_bulk_write_command,
3939
)
@@ -45,6 +45,7 @@
4545
_raise_bulk_write_error,
4646
_Run,
4747
)
48+
from pymongo.client_session_shared import _validate_session_write_concern
4849
from pymongo.common import (
4950
validate_is_document_type,
5051
validate_ok_for_replace,

pymongo/asynchronous/client_bulk.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,7 @@
3333
from bson.raw_bson import RawBSONDocument
3434
from pymongo import _csot, common
3535
from pymongo._telemetry import _generate_op_id_or_none
36-
from pymongo.asynchronous.client_session import (
37-
AsyncClientSession,
38-
_validate_session_write_concern,
39-
)
36+
from pymongo.asynchronous.client_session import AsyncClientSession
4037
from pymongo.asynchronous.collection import AsyncCollection
4138
from pymongo.asynchronous.command_cursor import AsyncCommandCursor
4239
from pymongo.asynchronous.command_runner import (
@@ -52,6 +49,7 @@
5249
_merge_command,
5350
_throw_client_bulk_write_exception,
5451
)
52+
from pymongo.client_session_shared import _validate_session_write_concern
5553
from pymongo.common import (
5654
validate_is_document_type,
5755
validate_ok_for_replace,

0 commit comments

Comments
 (0)