Skip to content

Commit

Permalink
Added untar progress similar to existing unzip (#17519)
Browse files Browse the repository at this point in the history
* Added untar progress similar to existing unzip

* Python 3.6 compatible

* Refactor FileProgress class and use it on uncompress

* Restored prev FileProgress location and use with

* Removed tell() call and added size limitation
  • Loading branch information
perseoGI authored Jan 17, 2025
1 parent aee9710 commit 4bb0bcc
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 42 deletions.
24 changes: 3 additions & 21 deletions conan/tools/files/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
import platform
import shutil
import subprocess
import sys
from contextlib import contextmanager
from fnmatch import fnmatch
from shutil import which


from conans.client.downloaders.caching_file_downloader import SourcesCachingDownloader
from conan.errors import ConanException
from conans.client.rest.file_uploader import FileProgress
from conans.util.files import rmdir as _internal_rmdir, human_size, check_with_algorithm_sum


Expand Down Expand Up @@ -261,7 +261,6 @@ def chdir(conanfile, newdir):
finally:
os.chdir(old_path)


def unzip(conanfile, filename, destination=".", keep_permissions=False, pattern=None,
strip_root=False, extract_filter=None):
"""
Expand Down Expand Up @@ -305,20 +304,7 @@ def unzip(conanfile, filename, destination=".", keep_permissions=False, pattern=
import zipfile
full_path = os.path.normpath(os.path.join(os.getcwd(), destination))

if hasattr(sys.stdout, "isatty") and sys.stdout.isatty():
def print_progress(the_size, uncomp_size):
the_size = (the_size * 100.0 / uncomp_size) if uncomp_size != 0 else 0
txt_msg = "Unzipping %d %%"
if the_size > print_progress.last_size + 1:
output.rewrite_line(txt_msg % the_size)
print_progress.last_size = the_size
if int(the_size) == 99:
output.rewrite_line(txt_msg % 100)
else:
def print_progress(_, __):
pass

with zipfile.ZipFile(filename, "r") as z:
with FileProgress(filename, msg="Unzipping", mode="r") as file, zipfile.ZipFile(file) as z:
zip_info = z.infolist()
if pattern:
zip_info = [zi for zi in zip_info if fnmatch(zi.filename, pattern)]
Expand All @@ -343,19 +329,16 @@ def print_progress(_, __):
output.info("Unzipping %s" % human_size(uncompress_size))
extracted_size = 0

print_progress.last_size = -1
if platform.system() == "Windows":
for file_ in zip_info:
extracted_size += file_.file_size
print_progress(extracted_size, uncompress_size)
try:
z.extract(file_, full_path)
except Exception as e:
output.error(f"Error extract {file_.filename}\n{str(e)}", error_type="exception")
else: # duplicated for, to avoid a platform check for each zipped file
for file_ in zip_info:
extracted_size += file_.file_size
print_progress(extracted_size, uncompress_size)
try:
z.extract(file_, full_path)
if keep_permissions:
Expand All @@ -367,11 +350,10 @@ def print_progress(_, __):
output.error(f"Error extract {file_.filename}\n{str(e)}", error_type="exception")
output.writeln("")


def untargz(filename, destination=".", pattern=None, strip_root=False, extract_filter=None):
# NOT EXPOSED at `conan.tools.files` but used in tests
import tarfile
with tarfile.TarFile.open(filename, 'r:*') as tarredgzippedFile:
with FileProgress(filename, msg="Uncompressing") as fileobj, tarfile.TarFile.open(fileobj=fileobj, mode='r:*') as tarredgzippedFile:
f = getattr(tarfile, f"{extract_filter}_filter", None) if extract_filter else None
tarredgzippedFile.extraction_filter = f or (lambda member_, _: member_)
if not pattern and not strip_root:
Expand Down
42 changes: 21 additions & 21 deletions conans/client/rest/file_uploader.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import io
import os
import time

Expand Down Expand Up @@ -81,27 +82,7 @@ def upload(self, url, abs_path, auth=None, dedup=False, retry=None, retry_wait=N
time.sleep(retry_wait)

def _upload_file(self, url, abs_path, headers, auth, ref):
class FileProgress: # Wrapper just to provide an upload progress every 10 seconds
def __init__(self, f, total_size):
self._f = f
self._total = total_size
self._name = os.path.basename(f.name)
self._t = TimedOutput(interval=10)
self._read = 0

def __getattr__(self, item):
return getattr(self._f, item)

def read(self, n=-1):
read_bytes = self._f.read(n)
self._read += len(read_bytes)
self._t.info(f"{ref}: Uploading {self._name}: {int(self._read*100/self._total)}%")
return read_bytes

filesize = os.path.getsize(abs_path)
with open(abs_path, mode='rb') as file_handler:
big_file = filesize > 100000000 # 100 MB
file_handler = FileProgress(file_handler, filesize) if big_file else file_handler
with FileProgress(abs_path, mode='rb', msg=f"{ref}: Uploading") as file_handler:
try:
response = self._requester.put(url, data=file_handler, verify=self._verify_ssl,
headers=headers, auth=auth,
Expand All @@ -113,3 +94,22 @@ def read(self, n=-1):
raise
except Exception as exc:
raise ConanException(exc)


class FileProgress(io.FileIO):
def __init__(self, path: str, msg: str = "Uploading", report_interval: float = 1 , *args, **kwargs):
super().__init__(path, *args, **kwargs)
self._total_size = os.path.getsize(path)
self._filename = os.path.basename(path)
# Report only on big sizes (>100MB)
self._reporter = TimedOutput(interval=report_interval) if self._total_size > 100_000_000 else None
self._bytes_read = 0
self.msg = msg

def read(self, size: int = -1) -> bytes:
block = super().read(size)
self._bytes_read += len(block)
if self._reporter:
current_percentage = int(self._bytes_read * 100.0 / self._total_size) if self._total_size != 0 else 0
self._reporter.info(f"{self.msg} {self._filename}: {current_percentage}%")
return block

0 comments on commit 4bb0bcc

Please sign in to comment.