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
9 changes: 9 additions & 0 deletions .evergreen/generated_configs/variants.yml
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,15 @@ buildvariants:
- rhel87-small
expansions:
COMPRESSOR: zstd
- name: compression-zstd-ubuntu-22
tasks:
- name: .test-standard !.server-4.2 !.server-4.4 !.server-5.0 .python-3.14
- name: .test-standard !.server-4.2 !.server-4.4 !.server-5.0 .python-3.14t
display_name: Compression zstd Ubuntu-22
run_on:
- ubuntu2204-small
expansions:
COMPRESSOR: ztsd

# Coverage report tests
- name: coverage-report
Expand Down
16 changes: 16 additions & 0 deletions .evergreen/scripts/generate_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,22 @@ def create_compression_variants():
expansions=expansions,
)
)
# Add explicit tests with compression.zstd support on linux.
host = HOSTS["ubuntu22"]
expansions = dict(COMPRESSOR="ztsd")
tasks = [
".test-standard !.server-4.2 !.server-4.4 !.server-5.0 .python-3.14",
".test-standard !.server-4.2 !.server-4.4 !.server-5.0 .python-3.14t",
]
display_name = get_variant_name(f"Compression {compressor}", host)
variants.append(
create_variant(
tasks,
display_name,
host=host,
expansions=expansions,
)
)
return variants


Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,8 @@ python -m pip install "pymongo[snappy]"
```

Wire protocol compression with zstandard requires
[zstandard](https://pypi.org/project/zstandard):
[backports.zstd](https://pypi.org/project/backports.zstd)
when used with Python versions before 3.14:

```bash
python -m pip install "pymongo[zstd]"
Expand Down
1 change: 1 addition & 0 deletions doc/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ PyMongo 4.16 brings a number of changes including:
Python 3.10+. The minimum version is ``2.6.1`` to account for `CVE-2023-29483 <https://www.cve.org/CVERecord?id=CVE-2023-29483>`_.
- Removed support for Eventlet.
Eventlet is actively being sunset by its maintainers and has compatibility issues with PyMongo's dnspython dependency.
- Use Zstandard support from the standard library for Python 3.14+, and use ``backports.zstd`` for older versions.

Changes in Version 4.15.3 (2025/10/07)
--------------------------------------
Expand Down
4 changes: 2 additions & 2 deletions pymongo/asynchronous/mongo_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -422,8 +422,8 @@ def __init__(
with the server. Currently supported options are "snappy", "zlib"
and "zstd". Support for snappy requires the
`python-snappy <https://pypi.org/project/python-snappy/>`_ package.
zlib support requires the Python standard library zlib module. zstd
requires the `zstandard <https://pypi.org/project/zstandard/>`_
zlib support requires the Python standard library zlib module. For
Python before 3.14 zstd requires the `backports.zstd <https://pypi.org/project/backports.zstd/>`_
package. By default no compression is used. Compression support
must also be enabled on the server. MongoDB 3.6+ supports snappy
and zlib compression. MongoDB 4.2+ adds support for zstd.
Expand Down
42 changes: 27 additions & 15 deletions pymongo/compression_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# limitations under the License.
from __future__ import annotations

import sys
import warnings
from typing import Any, Iterable, Optional, Union

Expand Down Expand Up @@ -44,7 +45,10 @@ def _have_zlib() -> bool:

def _have_zstd() -> bool:
try:
import zstandard # noqa: F401
if sys.version_info >= (3, 14):
from compression import zstd
else:
from backports import zstd # noqa: F401

return True
except ImportError:
Expand Down Expand Up @@ -79,11 +83,18 @@ def validate_compressors(dummy: Any, value: Union[str, Iterable[str]]) -> list[s
)
elif compressor == "zstd" and not _have_zstd():
compressors.remove(compressor)
warnings.warn(
"Wire protocol compression with zstandard is not available. "
"You must install the zstandard module for zstandard support.",
stacklevel=2,
)
if sys.version_info >= (3, 14):
warnings.warn(
"Wire protocol compression with zstandard is not available. "
"The compression.zstd module is not available.",
stacklevel=2,
)
else:
warnings.warn(
"Wire protocol compression with zstandard is not available. "
"You must install the backports.zstd module for zstandard support.",
stacklevel=2,
)
return compressors


Expand Down Expand Up @@ -144,12 +155,12 @@ class ZstdContext:

@staticmethod
def compress(data: bytes) -> bytes:
# ZstdCompressor is not thread safe.
# TODO: Use a pool?

import zstandard
if sys.version_info >= (3, 14):
from compression import zstd
else:
from backports import zstd

return zstandard.ZstdCompressor().compress(data)
return zstd.compress(data)


def decompress(data: bytes | memoryview, compressor_id: int) -> bytes:
Expand All @@ -166,10 +177,11 @@ def decompress(data: bytes | memoryview, compressor_id: int) -> bytes:

return zlib.decompress(data)
elif compressor_id == ZstdContext.compressor_id:
# ZstdDecompressor is not thread safe.
# TODO: Use a pool?
import zstandard
if sys.version_info >= (3, 14):
from compression import zstd
else:
from backports import zstd

return zstandard.ZstdDecompressor().decompress(data)
return zstd.decompress(data)
else:
raise ValueError("Unknown compressorId %d" % (compressor_id,))
4 changes: 2 additions & 2 deletions pymongo/synchronous/mongo_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -422,8 +422,8 @@ def __init__(
with the server. Currently supported options are "snappy", "zlib"
and "zstd". Support for snappy requires the
`python-snappy <https://pypi.org/project/python-snappy/>`_ package.
zlib support requires the Python standard library zlib module. zstd
requires the `zstandard <https://pypi.org/project/zstandard/>`_
zlib support requires the Python standard library zlib module. For
Python before 3.14 zstd requires the `backports.zstd <https://pypi.org/project/backports.zstd/>`_
package. By default no compression is used. Compression support
must also be enabled on the server. MongoDB 3.6+ supports snappy
and zlib compression. MongoDB 4.2+ adds support for zstd.
Expand Down
2 changes: 1 addition & 1 deletion requirements/zstd.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
zstandard
backports.zstd>=1.0.0;python_version<'3.14'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer we keep using zstandard to limit the scope of these changes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Rogdham what are the benefits to using backports.zstd vs zstandard? I'd prefer to avoid changing our dependency unless there is a strong motivation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main benefit is to be sure to have the same exact API as compressor.zstd, so switching one for the other based on Python version will always work. This is guarantied because it is the exact same code as in CPython, with only the needed modifications to support older versions.

Other benefits are less clear and depends on the use cases:

  • Free threading support on Python 3.13
  • Wheels are smaller (e.g. for Linux 64bits on 3.13 490.8kB vs 5.5 MB)
  • Adds support for Zstandard in other stdlib modules such as tarfile or zipfile

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay thanks. I'm fine either way here. I'll defer to @blink1073.

Loading
Loading