diff --git a/src/ahriman/core/utils.py b/src/ahriman/core/utils.py index 7e4e2e1d..ec7884aa 100644 --- a/src/ahriman/core/utils.py +++ b/src/ahriman/core/utils.py @@ -431,6 +431,22 @@ def safe_filename(source: str) -> str: return re.sub(r"[^A-Za-z\d\-._~:\[\]@]", "-", source) +def safe_iterdir(path: Path) -> Iterator[Path]: + """ + wrapper around :func:`pathlib.Path.iterdir`, which suppresses :exc:`PermissionError` + + Args: + path(Path): path to iterate + + Yields: + Path: content of the directory + """ + try: + yield from path.iterdir() + except PermissionError: + pass + + def srcinfo_property(key: str, srcinfo: Mapping[str, Any], package_srcinfo: Mapping[str, Any], *, default: Any = None) -> Any: """ diff --git a/src/ahriman/models/repository_paths.py b/src/ahriman/models/repository_paths.py index e3cec7a9..25258a04 100644 --- a/src/ahriman/models/repository_paths.py +++ b/src/ahriman/models/repository_paths.py @@ -279,14 +279,16 @@ class RepositoryPaths(LazyLogging): Note, however, that this method doesn't handle any exceptions and will eventually interrupt if there will be any. """ + from ahriman.core.utils import safe_iterdir + path = path or self.root def walk(root: Path) -> Iterator[Path]: # basically walk, but skipping some content - for child in root.iterdir(): + for child in safe_iterdir(root): yield child if child in (self.chroot.parent,): - yield from child.iterdir() # we only yield top-level in chroot directory + yield from safe_iterdir(child) # we only yield top-level in chroot directory elif child.is_dir(): yield from walk(child) diff --git a/tests/ahriman/core/test_utils.py b/tests/ahriman/core/test_utils.py index 100670a9..0c920a52 100644 --- a/tests/ahriman/core/test_utils.py +++ b/tests/ahriman/core/test_utils.py @@ -11,7 +11,7 @@ from unittest.mock import call as MockCall from ahriman.core.exceptions import BuildError, CalledProcessError, OptionError, UnsafeRunError from ahriman.core.utils import check_output, check_user, dataclass_view, enum_values, extract_user, filter_json, \ full_version, minmax, package_like, parse_version, partition, pretty_datetime, pretty_interval, pretty_size, \ - safe_filename, srcinfo_property, srcinfo_property_list, trim_package, utcnow, walk + safe_filename, safe_iterdir, srcinfo_property, srcinfo_property_list, trim_package, utcnow, walk from ahriman.models.package import Package from ahriman.models.package_source import PackageSource from ahriman.models.repository_id import RepositoryId @@ -426,6 +426,14 @@ def test_safe_filename() -> None: assert safe_filename("tolua++-1.0.93-4-x86_64.pkg.tar.zst") == "tolua---1.0.93-4-x86_64.pkg.tar.zst" +def test_safe_iterdir(mocker: MockerFixture) -> None: + """ + must suppress PermissionError + """ + iterdir_mock = mocker.patch("pathlib.Path.iterdir", side_effect=PermissionError) + assert list(safe_iterdir(Path("root"))) == [] + + def test_srcinfo_property() -> None: """ must correctly extract properties