| 
7 | 7 | import sys  | 
8 | 8 | from collections.abc import Generator  | 
9 | 9 | from contextlib import contextmanager  | 
 | 10 | +from pathlib import Path  | 
10 | 11 | from tempfile import NamedTemporaryFile  | 
11 | 12 | from typing import Any, BinaryIO, cast  | 
12 | 13 | 
 
  | 
@@ -150,3 +151,77 @@ def directory_size(path: str) -> int | float:  | 
150 | 151 | 
 
  | 
151 | 152 | def format_directory_size(path: str) -> str:  | 
152 | 153 |     return format_size(directory_size(path))  | 
 | 154 | + | 
 | 155 | + | 
 | 156 | +def leaf_dirs_without_files(path: Path, parents: list[Path]) -> Generator[Path]:  | 
 | 157 | +    """  | 
 | 158 | +    Yields every subdirectory of +path+ that has no files under it.  | 
 | 159 | +    If no subdirectories contain any files, also yields +path+.  | 
 | 160 | +    """  | 
 | 161 | + | 
 | 162 | +    subdirs = []  | 
 | 163 | +    for item in path.iterdir():  | 
 | 164 | +        if item.is_dir():  | 
 | 165 | +            subdirs.append(item)  | 
 | 166 | +        else:  | 
 | 167 | +            # If we find a file, we want to preserve the whole subtree,  | 
 | 168 | +            # so bail immediately.  | 
 | 169 | +            return  | 
 | 170 | + | 
 | 171 | +    # If we get to this point, we didn't find a file yet.  | 
 | 172 | + | 
 | 173 | +    parents = parents + [path]  | 
 | 174 | +    if subdirs:  | 
 | 175 | +        for subdir in subdirs:  | 
 | 176 | +            yield from subdirs_without_wheels(subdir, parents)  | 
 | 177 | +    else:  | 
 | 178 | +        yield from reversed(sorted(parents))  | 
 | 179 | + | 
 | 180 | + | 
 | 181 | +def subdirs_without_files(path: str) -> Generator[Path]:  | 
 | 182 | +    """Yields every subdirectory of +path+ that has no files under it."""  | 
 | 183 | + | 
 | 184 | +    path_obj = Path(path)  | 
 | 185 | + | 
 | 186 | +    if not path_obj.exists():  | 
 | 187 | +        return  | 
 | 188 | + | 
 | 189 | +    for subdir in path_obj.iterdir():  | 
 | 190 | +        yield from leaf_dirs_without_files(subdir, [])  | 
 | 191 | + | 
 | 192 | + | 
 | 193 | +def leaf_dirs_without_wheels(path: Path, parents: list[Path]) -> Generator[Path]:  | 
 | 194 | +    """  | 
 | 195 | +    Yields every subdirectory of +path+ that has no .whl files under it.  | 
 | 196 | +    If no subdirectories include .whl files, also yields +path+.  | 
 | 197 | +    """  | 
 | 198 | +    subdirs = []  | 
 | 199 | +    for item in path.iterdir():  | 
 | 200 | +        if item.is_dir():  | 
 | 201 | +            subdirs.append(item)  | 
 | 202 | +        else:  | 
 | 203 | +            if item.name.endswith(".whl"):  | 
 | 204 | +                # If we found a wheel, we want to preserve this whole subtree,  | 
 | 205 | +                # so we bail immediately and don't return any results.  | 
 | 206 | +                return  | 
 | 207 | + | 
 | 208 | +    # If we get to this point, we didn't find a wheel yet.  | 
 | 209 | + | 
 | 210 | +    parents = parents + [path]  | 
 | 211 | +    if subdirs:  | 
 | 212 | +        for subdir in subdirs:  | 
 | 213 | +            yield from leaf_dirs_without_wheels(subdir, parents)  | 
 | 214 | +    else:  | 
 | 215 | +        yield from reversed(sorted(parents))  | 
 | 216 | + | 
 | 217 | + | 
 | 218 | +def subdirs_without_wheels(path: str) -> Generator[Path]:  | 
 | 219 | +    """Yields every subdirectory of +path+ that has no .whl files under it."""  | 
 | 220 | + | 
 | 221 | +    path_obj = Path(path)  | 
 | 222 | + | 
 | 223 | +    if not path_obj.exists():  | 
 | 224 | +        return  | 
 | 225 | + | 
 | 226 | +    for subdir in path_obj.iterdir():  | 
 | 227 | +        yield from leaf_dirs_without_wheels(subdir, [])  | 
0 commit comments