Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
278ea02
Add support for zstd compression of binary packages
grossag Sep 6, 2023
682b0e3
Switch to include python-zstandard in the package requirements
grossag Sep 8, 2023
396815c
Add a test case to cover zstd compress and decompress
grossag Sep 8, 2023
f0b7813
Downgrade to 0.20.0 to fix CI
grossag Sep 8, 2023
a33394d
Two small improvements
grossag Sep 11, 2023
bbed1a0
Address review feedback
grossag Jul 22, 2024
ea5d948
Merge branch 'develop2' into topic/grossag/zstd3
grossag Jul 22, 2024
db87f56
Add file missed by merge
grossag Jul 22, 2024
6a109f4
Fix typo in parameter which broke tests
grossag Jul 22, 2024
e5765e6
A few more small fixes in hopes of unbreaking the build
grossag Jul 22, 2024
ff29efc
Some more improvements
grossag Jul 23, 2024
0c58aa8
Address some of the review feedback
grossag Sep 16, 2024
79afdae
Flush zstd frames around every 128MB
grossag Sep 17, 2024
890c454
Merge branch 'develop2' into topic/grossag/zstd3
grossag Sep 23, 2024
b8f4a57
Fix DeprecationWarning
grossag Sep 23, 2024
44af70b
merged develop2
memsharded Dec 1, 2025
ff9cfa3
wip
memsharded Dec 1, 2025
8b33dd9
Merge branch 'develop2' into feature/builtin_compression
memsharded Dec 2, 2025
d438303
wip
memsharded Dec 2, 2025
ca1fcb1
review
memsharded Dec 3, 2025
16671b5
Merge branch 'develop2' into feature/builtin_compression
memsharded Dec 5, 2025
06e15c3
compression for cache save/restore too
memsharded Dec 5, 2025
60ef29c
fix unit test
memsharded Dec 5, 2025
1c8780e
Merge branch 'develop2' into feature/builtin_compression
memsharded Dec 9, 2025
f79080e
fix tests
memsharded Dec 9, 2025
85f8452
fix tests
memsharded Dec 9, 2025
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
34 changes: 20 additions & 14 deletions conan/internal/api/uploader.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import gzip
import os
import shutil
import sys
import tarfile
import time

Expand Down Expand Up @@ -85,6 +86,21 @@ def __init__(self, app: ConanApp, global_conf):
self._app = app
self._global_conf = global_conf

# No compressed file exists, need to compress
compressformat = self._global_conf.get("core.upload:compression_format",
default="gz", choices=COMPRESSIONS)
if compressformat == "zst" and sys.version_info.minor < 14:
Copy link
Member Author

Choose a reason for hiding this comment

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

Add risk warnings because of experimental

raise ConanException("The 'core.upload:compression_format=zst' is only for Python>=3.14")
compresslevel = self._global_conf.get("core:compresslevel", check_type=int)
if compresslevel is None and compressformat == "gz":
compresslevel = self._global_conf.get("core.gzip:compresslevel", check_type=int)
if compresslevel is not None:
ConanOutput().warning("core.gzip:compresslevel is deprecated, "
"use core.compresslevel instead", warn_tag="deprecated")

self._compressformat = compressformat
self._compresslevel = compresslevel

def prepare(self, pkg_list, enabled_remotes):
local_url = self._global_conf.get("core.scm:local_url", choices=["allow", "block"])
for ref, packages in pkg_list.items():
Expand Down Expand Up @@ -171,7 +187,7 @@ def _prepare_package(self, pref, prev_bundle):
def _compressed_file(self, filename, files, download_folder, ref):
output = ConanOutput(scope=str(ref))

# Check if there is some existing compressed file alreday
# Check if there is some existing compressed file already
matches = []
for extension in COMPRESSIONS:
file_name = filename + extension
Expand All @@ -187,21 +203,11 @@ def _compressed_file(self, filename, files, download_folder, ref):
if len(matches) == 1:
return matches[0]

# No compressed file exists, need to compress
compressformat = self._global_conf.get("core.upload:compression_format",
default="gz", choices=COMPRESSIONS)
compresslevel = self._global_conf.get("core:compresslevel", check_type=int)
if compresslevel is None and compressformat == "gz":
compresslevel = self._global_conf.get("core.gzip:compresslevel", check_type=int)
if compresslevel is not None:
ConanOutput().warning("core.gzip:compresslevel is deprecated, "
"use core.compresslevel instead", warn_tag="deprecated")

file_name = filename + compressformat
file_name = filename + self._compressformat
package_file = os.path.join(download_folder, file_name)
compressed_path = compress_files(files, file_name, download_folder,
compresslevel=compresslevel, compressformat=compressformat,
ref=ref)
compresslevel=self._compresslevel,
compressformat=self._compressformat, ref=ref)
assert compressed_path == package_file
assert os.path.exists(package_file)
return file_name
Expand Down
2 changes: 1 addition & 1 deletion conan/internal/model/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
"core.net.http:clean_system_proxy": "If defined, the proxies system env-vars will be discarded",
# Compression for `conan upload`
"core.upload:compression_format": "The compression format used when uploading Conan packages. "
"Possible values: 'zstd', 'xz', 'gzip' (default=gzip)",
"Possible values: 'zst', 'xz', 'gz' (default=gz)",
"core.gzip:compresslevel": "The Gzip compression level for Conan artifacts (default=9)",
"core:compresslevel": "The compression level for Conan artifacts (default zstd=3, gz=9)",
# Excluded from revision_mode = "scm" dirty and Git().is_dirty() checks
Expand Down
6 changes: 5 additions & 1 deletion conan/internal/rest/remote_manager.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os
import shutil
import sys

from collections import namedtuple
from typing import List
Expand Down Expand Up @@ -191,7 +192,7 @@ def _get_package(self, layout, pref, remote, scoped_output, metadata):
f"no conan_package found")
self._signer.verify(pref, download_pkg_folder, zipped_files)

tgz_file = zipped_files.pop(package_file, None)
tgz_file = zipped_files.pop(package_file)
package_folder = layout.package()
uncompress_file(tgz_file, package_folder, scope=str(pref.ref))
mkdir(package_folder) # Just in case it doesn't exist, because uncompress did nothing
Expand Down Expand Up @@ -345,6 +346,9 @@ def _call_remote(self, remote, method, *args, **kwargs):


def uncompress_file(src_path, dest_folder, scope=None):
if sys.version_info.major < 14 and src_path.endswith(".tzst"):
raise ConanException(f"File {os.path.basename(src_path)} compressed with 'zst', "
f"unsupported for Python<3.14 ")
try:
filesize = os.path.getsize(src_path)
big_file = filesize > 10000000 # 10 MB
Expand Down
21 changes: 21 additions & 0 deletions test/integration/test_compressions.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from conan.api.model import RecipeReference, PkgReference
from conan.internal.util import load
from conan.test.assets.genconanfile import GenConanfile
from conan.test.utils.tools import TestClient


Expand Down Expand Up @@ -72,3 +73,23 @@ def package(self):
downloaded_files = os.listdir(rlayout.download_export())
assert f"conan_export.t{compress}" in downloaded_files
assert f"conan_sources.t{compress}" in downloaded_files


@pytest.mark.skipif(sys.version_info.minor >= 14, reason="validate zstd error in python<314")
def test_unsupported_zstd():
c = TestClient(default_server_user=True)
c.save({"conanfile.py": GenConanfile("pkg", "0.1").with_package_file("myfile.h", "contents")})
c.run("create")
playout = c.created_layout()
c.run("upload * -r=default -c -cc core.upload:compression_format=zst", assert_error=True)
assert "ERROR: The 'core.upload:compression_format=zst' is only for Python>=3.14" in c.out

# Lets cheat, creating a fake zstd to test download
c.run("upload * -r=default -c --dry-run")
os.rename(os.path.join(playout.download_package(), "conan_package.tgz"),
os.path.join(playout.download_package(), "conan_package.tzst"))
c.run("upload * -r=default -c")
c.run("remove * -c")
c.run("install --requires=pkg/0.1", assert_error=True)
assert ("ERROR: File conan_package.tzst compressed with 'zst', unsupported "
"for Python<3.14") in c.out