Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added missing unittest for directory_size #5201

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
60 changes: 31 additions & 29 deletions conda_build/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@
if TYPE_CHECKING:
from conda.models.records import PrefixRecord

log = logging.getLogger(__name__)

on_win = sys.platform == "win32"
on_mac = sys.platform == "darwin"
on_linux = sys.platform == "linux"
Expand Down Expand Up @@ -122,7 +124,7 @@ def stat_file(path):
return os.stat(path)


def directory_size_slow(path):
def directory_size_slow(path: str | os.PathLike | Path) -> int:
total_size = 0
seen = set()

Expand All @@ -142,40 +144,40 @@ def directory_size_slow(path):
return total_size


def directory_size(path):
try:
if on_win:
command = 'dir /s "{}"' # Windows path can have spaces
out = subprocess.check_output(command.format(path), shell=True)
else:
command = "du -s {}"
out = subprocess.check_output(
command.format(path).split(), stderr=subprocess.PIPE
)
def directory_size(path: str | os.PathLike | Path) -> int:
if not Path(path).exists():
return 0

if hasattr(out, "decode"):
try:
out = out.decode(errors="ignore")
# This isn't important anyway so give up. Don't try search on bytes.
except (UnicodeDecodeError, IndexError):
if on_win:
return 0
else:
pass
if on_win:
# Windows can give long output, we need only 2nd to last line
out = out.strip().rsplit("\r\n", 2)[-2]
pattern = r"\s([\d\W]+).+" # Language and punctuation neutral
out = re.search(pattern, out.strip()).group(1).strip()
out = out.replace(",", "").replace(".", "").replace(" ", "")
else:
out = out.split()[0]
try:
command = ["dir", "/s", path] if on_win else ["du", "-s", path]
out = subprocess.run(
command,
check=True,
shell=on_win,
text=True,
errors="ignore",
capture_output=True,
).stdout
except subprocess.CalledProcessError:
out = directory_size_slow(path)
log.debug("Failed to get directory size using system command %s", command)
return directory_size_slow(path)

if on_win:
# Windows can give long output, we need only 2nd to last line
out = out.strip().splitlines()[-2]
pattern = r"\s([\d\W]+).+" # Language and punctuation neutral
if not (match := re.search(pattern, out.strip())):
log.debug("Failed to parse directory size from output: %s", out)
return 0
out = match.group(1).strip()
out = out.replace(",", "").replace(".", "").replace(" ", "")
else:
out = out.split()[0]

try:
return int(out) # size in bytes
except ValueError:
log.debug("Failed to parse directory size from output: %s", out)
return 0


Expand Down
44 changes: 43 additions & 1 deletion tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@
import sys
from pathlib import Path
from typing import NamedTuple
from uuid import uuid4

import filelock
import pytest
from pytest import MonkeyPatch
from pytest_mock import MockerFixture

import conda_build.utils as utils
from conda_build import utils
from conda_build.exceptions import BuildLockError


Expand Down Expand Up @@ -433,3 +435,43 @@ def test_is_conda_pkg(tmpdir, value: str, expected: bool, is_dir: bool, create:
fp.write("test")

assert utils.is_conda_pkg(value) == expected


def test_directory_size(tmp_path: Path, mocker: MockerFixture):
# mock the slow function so we can catch that it is not called
mock = mocker.patch("conda_build.utils.directory_size_slow")

assert utils.directory_size("fake") == 0

assert utils.directory_size(tmp_path) == 0

count = 1000
size = 1000
for i in range(count):
(tmp_path / f"file{i}").write_text(uuid4().hex * size)

if utils.on_win:
expected = 32 * size * count
elif utils.on_mac:
expected = 64 * count
else:
expected = 32 * count

assert utils.directory_size(tmp_path) == expected

assert not mock.called


def test_directory_size_slow(tmp_path: Path):
assert utils.directory_size_slow("fake") == 0

assert utils.directory_size_slow(tmp_path) == 0

count = 1000
size = 1000
for i in range(count):
(tmp_path / f"file{i}").write_text(uuid4().hex * size)

expected = 32 * size * count

assert utils.directory_size_slow(tmp_path) == expected