Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bindings/python/.evergreen/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,6 @@ EXPECTED="pkg:github/mongodb/libmongocrypt@$LIBMONGOCRYPT_VERSION"
if grep -q $EXPECTED sbom.json; then
echo "SBOM is up to date!"
else
echo "SBOM is out of date! Run the \"update-sbom.sh\" script."
echo "SBOM is out of date! Run the \"scripts/update-version.sh\" script."
exit 1
fi
12 changes: 12 additions & 0 deletions bindings/python/CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
Changelog
=========

Changes in Version 1.19.0
-------------------------

- Bundle libmongocrypt 1.20.2 in release wheels.
- Renamed the ``text_opts`` parameter of ``ExplicitEncryptOpts`` and
``ExplicitEncrypter.encrypt`` to ``string_opts``, matching the "string"
algorithm it configures. This is a breaking change.
- The "textPreview" algorithm has been removed. Use "string" instead.
- The "prefixPreview", "suffixPreview", and "substringPreview" query types are
now deprecated aliases of "prefix", "suffix", and "substring". Prefer the new
names, as a future release removes the aliases.

Changes in Version 1.18.1
-------------------------

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ async def encrypt(
contention_factor=None,
range_opts=None,
is_expression=False,
text_opts=None,
string_opts=None,
):
"""Encrypts a BSON value.

Expand All @@ -115,8 +115,8 @@ async def encrypt(
with the "range" algorithm encoded as a BSON document.
- `is_expression` (boolean): True if this is an encryptExpression()
context. Defaults to False.
- `text_opts` (bytes): Options for explicit encryption
with the "textPreview" algorithm encoded as a BSON document.
- `string_opts` (bytes): Options for explicit encryption
with the "string" algorithm encoded as a BSON document.

:Returns:
The encrypted BSON value.
Expand All @@ -127,6 +127,8 @@ async def encrypt(
Added the `range_opts` and `is_expression` parameters.
.. versionchanged:: 1.16
Added the `text_opts` parameter.
.. versionchanged:: 1.19
Renamed the `text_opts` parameter to `string_opts`.
"""
# CDRIVER-3275 key_alt_name needs to be wrapped in a bson document.
if key_alt_name is not None:
Expand All @@ -139,7 +141,7 @@ async def encrypt(
contention_factor,
range_opts,
is_expression,
text_opts,
string_opts,
)
with self.mongocrypt.explicit_encryption_context(value, opts) as ctx:
return await run_state_machine(ctx, self.callback)
Expand Down
8 changes: 5 additions & 3 deletions bindings/python/pymongocrypt/mongocrypt.py
Original file line number Diff line number Diff line change
Expand Up @@ -501,9 +501,11 @@ def __init__(self, ctx, kms_providers, value, opts):
):
self._raise_from_status()

if opts.text_opts is not None:
with MongoCryptBinaryIn(opts.text_opts) as text_opts:
if not lib.mongocrypt_ctx_setopt_algorithm_text(ctx, text_opts.bin):
if opts.string_opts is not None:
with MongoCryptBinaryIn(opts.string_opts) as string_opts:
if not lib.mongocrypt_ctx_setopt_algorithm_text(
ctx, string_opts.bin
):
self._raise_from_status()

with MongoCryptBinaryIn(value) as binary:
Expand Down
14 changes: 8 additions & 6 deletions bindings/python/pymongocrypt/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ def __init__(
contention_factor=None,
range_opts=None,
is_expression=False,
text_opts=None,
string_opts=None,
):
"""Options for explicit encryption.

Expand All @@ -187,15 +187,17 @@ def __init__(
with the "range" algorithm encoded as a BSON document.
- `is_expression` (boolean): True if this is an encryptExpression()
context. Defaults to False.
- `text_opts` (bytes): Options for explicit encryption
with the "textPreview" algorithm encoded as a BSON document.
- `string_opts` (bytes): Options for explicit encryption
with the "string" algorithm encoded as a BSON document.

.. versionchanged:: 1.3
Added the `query_type` and `contention_factor` parameters.
.. versionchanged:: 1.5
Added the `range_opts` and `is_expression` parameters.
.. versionchanged:: 1.16
Added the `text_opts` parameter.
.. versionchanged:: 1.19
Renamed the `text_opts` parameter to `string_opts`.
"""
self.algorithm = algorithm
self.key_id = key_id
Expand All @@ -217,11 +219,11 @@ def __init__(
)
self.range_opts = range_opts
self.is_expression = is_expression
if text_opts is not None and not isinstance(text_opts, bytes):
if string_opts is not None and not isinstance(string_opts, bytes):
raise TypeError(
f"text_opts must be an bytes or None, not: {type(text_opts)}"
f"string_opts must be an bytes or None, not: {type(string_opts)}"
)
self.text_opts = text_opts
self.string_opts = string_opts


class DataKeyOpts:
Expand Down
10 changes: 6 additions & 4 deletions bindings/python/pymongocrypt/synchronous/explicit_encrypter.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def encrypt(
contention_factor=None,
range_opts=None,
is_expression=False,
text_opts=None,
string_opts=None,
):
"""Encrypts a BSON value.

Expand All @@ -115,8 +115,8 @@ def encrypt(
with the "range" algorithm encoded as a BSON document.
- `is_expression` (boolean): True if this is an encryptExpression()
context. Defaults to False.
- `text_opts` (bytes): Options for explicit encryption
with the "textPreview" algorithm encoded as a BSON document.
- `string_opts` (bytes): Options for explicit encryption
with the "string" algorithm encoded as a BSON document.

:Returns:
The encrypted BSON value.
Expand All @@ -127,6 +127,8 @@ def encrypt(
Added the `range_opts` and `is_expression` parameters.
.. versionchanged:: 1.16
Added the `text_opts` parameter.
.. versionchanged:: 1.19
Renamed the `text_opts` parameter to `string_opts`.
"""
# CDRIVER-3275 key_alt_name needs to be wrapped in a bson document.
if key_alt_name is not None:
Expand All @@ -139,7 +141,7 @@ def encrypt(
contention_factor,
range_opts,
is_expression,
text_opts,
string_opts,
)
with self.mongocrypt.explicit_encryption_context(value, opts) as ctx:
return run_state_machine(ctx, self.callback)
Expand Down
2 changes: 1 addition & 1 deletion bindings/python/pymongocrypt/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.

__version__ = "1.18.2.dev0"
__version__ = "1.19.0.dev0"

_MIN_LIBMONGOCRYPT_VERSION = "1.8.0"
14 changes: 7 additions & 7 deletions bindings/python/sbom.json
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
{
"components": [
{
"bom-ref": "pkg:github/mongodb/libmongocrypt@1.20.0",
"bom-ref": "pkg:github/mongodb/libmongocrypt@1.20.2",
"externalReferences": [
{
"type": "distribution",
"url": "https://github.com/mongodb/libmongocrypt/archive/1.20.0.tar.gz"
"url": "https://github.com/mongodb/libmongocrypt/archive/1.20.2.tar.gz"
},
{
"type": "website",
"url": "https://github.com/mongodb/libmongocrypt/tree/1.20.0"
"url": "https://github.com/mongodb/libmongocrypt/tree/1.20.2"
}
],
"group": "mongodb",
"name": "libmongocrypt",
"purl": "pkg:github/mongodb/libmongocrypt@1.20.0",
"purl": "pkg:github/mongodb/libmongocrypt@1.20.2",
"type": "library",
"version": "1.20.0"
"version": "1.20.2"
}
],
"dependencies": [
{
"ref": "pkg:github/mongodb/libmongocrypt@1.20.0"
"ref": "pkg:github/mongodb/libmongocrypt@1.20.2"
}
],
"metadata": {
"timestamp": "2026-06-30T18:17:42.887189+00:00",
"timestamp": "2026-08-14T19:41:24.430909+00:00",
"tools": [
{
"externalReferences": [
Expand Down
2 changes: 1 addition & 1 deletion bindings/python/scripts/libmongocrypt-version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.20.0
1.20.2
120 changes: 116 additions & 4 deletions bindings/python/test/test_mongocrypt.py
Original file line number Diff line number Diff line change
Expand Up @@ -1025,7 +1025,7 @@ async def test_text_query(self):
)
self.addCleanup(encrypter.close)

text_opts = bson_data("fle2-text-search/textopts.json")
string_opts = bson_data("fle2-text-search/textopts.json")
expected = bson_data("fle2-text-search/encrypted-payload.json")
value = bson.encode({"v": "foo"})
encrypted = await encrypter.encrypt(
Expand All @@ -1034,10 +1034,68 @@ async def test_text_query(self):
key_id=key_id,
query_type="suffix",
contention_factor=0,
text_opts=text_opts,
string_opts=string_opts,
)
self.assertEqual(encrypted, expected)

async def test_textPreview_query_fails(self):
key_path = "keys/ABCDEFAB123498761234123456789012-local-document.json"
key_id = json_data(key_path)["_id"]
encrypter = AsyncExplicitEncrypter(
MockAsyncCallback(
key_docs=[bson_data(key_path)],
kms_reply=http_data("kms-reply.txt"),
),
self.mongo_crypt_opts(),
)
self.addCleanup(encrypter.close)

with self.assertRaisesRegex(
MongoCryptError,
"Algorithm 'textPreview' is deprecated, please use 'string'",
):
await encrypter.encrypt(
bson.encode({"v": "foo"}),
"textPreview",
key_id=key_id,
query_type="suffix",
contention_factor=0,
string_opts=bson_data("fle2-text-search/textopts.json"),
)

async def test_deprecated_preview_query_types(self):
# The "*Preview" query types remain as aliases to the GA names.
# PYTHON-5959 drops them.
key_path = "keys/ABCDEFAB123498761234123456789012-local-document.json"
key_id = json_data(key_path)["_id"]
encrypter = AsyncExplicitEncrypter(
MockAsyncCallback(
key_docs=[bson_data(key_path)], kms_reply=http_data("kms-reply.txt")
),
self.mongo_crypt_opts(),
)
self.addCleanup(encrypter.close)

string_opts = bson_data("fle2-text-search/textopts.json")
value = bson.encode({"v": "foo"})

async def encrypt(query_type):
return await encrypter.encrypt(
value,
"string",
key_id=key_id,
query_type=query_type,
contention_factor=0,
string_opts=string_opts,
)

for query_type in ["prefix", "suffix", "substring"]:
with self.subTest(query_type=query_type):
self.assertEqual(
await encrypt(f"{query_type}Preview"),
await encrypt(query_type),
)


class TestNeedKMSAzureCredentials(unittest.TestCase):
maxDiff = None
Expand Down Expand Up @@ -1495,7 +1553,7 @@ def test_text_query(self):
)
self.addCleanup(encrypter.close)

text_opts = bson_data("fle2-text-search/textopts.json")
string_opts = bson_data("fle2-text-search/textopts.json")
expected = bson_data("fle2-text-search/encrypted-payload.json")
value = bson.encode({"v": "foo"})
encrypted = encrypter.encrypt(
Expand All @@ -1504,10 +1562,64 @@ def test_text_query(self):
key_id=key_id,
query_type="suffix",
contention_factor=0,
text_opts=text_opts,
string_opts=string_opts,
)
self.assertEqual(encrypted, expected)

def test_textPreview_query_fails(self):
key_path = "keys/ABCDEFAB123498761234123456789012-local-document.json"
key_id = json_data(key_path)["_id"]
encrypter = ExplicitEncrypter(
MockCallback(
key_docs=[bson_data(key_path)], kms_reply=http_data("kms-reply.txt")
),
self.mongo_crypt_opts(),
)
self.addCleanup(encrypter.close)

with self.assertRaisesRegex(
MongoCryptError,
"Algorithm 'textPreview' is deprecated, please use 'string'",
):
encrypter.encrypt(
bson.encode({"v": "foo"}),
"textPreview",
key_id=key_id,
query_type="suffix",
contention_factor=0,
string_opts=bson_data("fle2-text-search/textopts.json"),
)

def test_deprecated_preview_query_types(self):
# The "*Preview" query types remain as aliases to the GA names.
# PYTHON-5959 drops them.
key_path = "keys/ABCDEFAB123498761234123456789012-local-document.json"
key_id = json_data(key_path)["_id"]
encrypter = ExplicitEncrypter(
MockCallback(
key_docs=[bson_data(key_path)], kms_reply=http_data("kms-reply.txt")
),
self.mongo_crypt_opts(),
)
self.addCleanup(encrypter.close)

string_opts = bson_data("fle2-text-search/textopts.json")
value = bson.encode({"v": "foo"})

def encrypt(query_type):
return encrypter.encrypt(
value,
"string",
key_id=key_id,
query_type=query_type,
contention_factor=0,
string_opts=string_opts,
)

for query_type in ["prefix", "suffix", "substring"]:
with self.subTest(query_type=query_type):
self.assertEqual(encrypt(f"{query_type}Preview"), encrypt(query_type))


def read(filename, **kwargs):
with open(os.path.join(DATA_DIR, filename), **kwargs) as fp:
Expand Down
Loading