Rozdział 23 · Część IV · Wdrażamy SafeSort
Zeskanuj katalog
scan() czyta system plików ściśle — przechodzi przez katalog rekurencyjnie i nie zmienia ani jednego pliku.
SafeSort · Część 4 z 6Wdrożenie
Add directory scanner
Area: FilesystemPriority: High
git switch -c feat/directory-scanner
Pierwszy krok SafeSort — przejrzeć katalog i sporządzić lista plików. Funkcja
scan() — jedna z trzech komend wyłącznie odczytujących SafeSort: ona
tylko wywołuje iterdir() oraz stat(),
Nigdy nie tworząc, nie przenosząc ani nie usuwając niczego z dysku.
Downloads/
report.pdf
cat.jpg
archive.zip
src/safesort/scanner.py
def scan(root: Path, config: Config) -> list[FileInfo]:
root = Path(root)
excluded = config.excluded_names()
results: list[FileInfo] = []
_scan_dir(root, excluded, results)
return results
Samo przejście jest rekurencyjne: _scan_dir() wchodzi w
każdy podkatalog i nazywa się ponownie:
src/safesort/scanner.py
for entry in entries:
if entry.is_symlink():
continue # symboliczne linki pominięte – zobacz następną stronę
if entry.is_dir():
if entry.name in excluded:
continue # Katalogi wykluczone są pomijane – zobacz następną stronę
_scan_dir(entry, excluded, results)
elif entry.is_file():
size = entry.stat().st_size
results.append(FileInfo(path=entry, size=size, extension=entry.suffix.lower()))
Katalog, którego nie można odczytać, nie powinien zatrzymywać całego skanowania
Jeśli nie ma wystarczających praw dostępu do podkatalogu,
iterdir() przyczyny PermissionError. Implementacja _scan_dir() przechwytuje ten błąd dla każdego podkatalogu osobno, zapisuje ostrzeżenie w dzienniku i kontynuuje skanowanie pozostałych katalogów — dzięki temu jeden niedostępny podkatalog nie przerywa całego przeglądu. Później omówimy to szczegółowo wraz z innymi błędami systemu plików.Skaner można sprawdzić teraz — rzeczywisty wydruk:
Punkt kontrolny testu: Wymagania stają się regresją
tests/test_scanner.py
def test_scan_finds_file_without_changing_it(tmp_path):
source = tmp_path / "report.pdf"
source.write_bytes(b"report")
files = scan(tmp_path, Config())
assert [item.path for item in files] == [source]
assert source.read_bytes() == b"report"
Najpierw wymaganie jest obserwowane na jednym przykładzie, następnie test zapisuje je jako kontrakt. Późniejsza sekcja testowania rozszerzy tę podstawę fixtures i edge cases.
Praktyka: Skanowanie prawdziwego katalogu
Potrzebny jest dostęp do prawdziwego systemu plików – uruchamianego lokalnie w VS Code, PyCharm lub Jupyter
Praktyka kursuje lokalnie