Rozdział 23 · Część IV · Wdrażamy SafeSort

Od analizy do planu działania

Plan ruchu to po prostu dane: można go wyświetlać, sprawdzać lub odrzucać bez dotykania pojedynczego pliku.

SafeSort · Część 4 z 6Wdrożenie
GitHubIssue #3 · Project SafeSort Pierwsze wydanie
Add safe organization plan
Area: SafetyPriority: High
git switch -c feat/plan-and-collisions

SafeSort ma lista znalezionych plików oraz regułę klasyfikacji — ale to jest Jeszcze nie mam planu działania. Plan jest lista ruchów konkretnych: skąd i gdzie każdy plik przesuwa się, jeśli użytkownik potwierdzi wykonanie.

System plików(obecnie)nie ruszonebuild_plan()tylko czytaPLANtylko opis
Tylko czytać
scan / plan / duplicates tylko czytać
Zmieniają pliki
apply rodzaju; undo przywraca
src/safesort/models.py
@dataclass(frozen=True)
class MoveOperation:
    source: Path
    destination: Path

@dataclass(frozen=True)
class SortPlan:
    root: Path
    operations: tuple[MoveOperation, ...]
Plan to dane, a nie działanie
SortPlan nie zawiera wywołań przenoszących pliki, tylko opis tego, co może być do zrobienia. Ta idea jest kluczowa dla całej architektury SafeSort: dopóki obiekt pozostaje danymi, może być wyświetlany, testowany, zapisywany lub po prostu odrzucany bez ryzyka użycia pojedynczego pliku użytkownika.

Funkcja build_plan() tworzy plan na podstawie listy plików z skaner bez dotykania dysku ani razu, chyba read-only sprawdzeniu, czy plik już istnieje z tą nazwą na miejscu docelowym. Co robić, jeśli jesteś, to temat kolejnego etapu.

src/safesort/planner.py
def build_plan(files: list[FileInfo], root: Path, config: Config) -> SortPlan:
    dest_root = Path(root) / config.destination
    operations = []
    for file in files:
        category = classify(file.extension, config.extensions)
        dest_dir = dest_root / category
        candidate = dest_dir / file.path.name
        destination = _resolve_collision(candidate, reserved)
        operations.append(MoveOperation(source=file.path, destination=destination))
    return SortPlan(root=root, operations=tuple(operations))

Punkt kontrolny testu: Czyste planowanie

tests/test_planner.py
def test_build_plan_describes_move_without_executing_it(tmp_path):
    source = tmp_path / "report.pdf"
    source.write_bytes(b"report")
    file = FileInfo(source, source.stat().st_size, ".pdf")

    plan = build_plan([file], tmp_path, Config())

    assert plan.operations[0].destination == tmp_path / "Sorted/documents/report.pdf"
    assert source.exists()
    assert not plan.operations[0].destination.exists()
Praktyka: Zbuduj plan na podstawie listy plików
interaktywny laptop bezpośrednio w przeglądarce – Python 3.14 przez Pyodide, bez instalacji
Otwórz praktykę →
Checkpoint · Issue #3
git commit -m "feat: add safe organization plan with collision handling"
Zlał się jak Pull Request #17. Nagłówek PR nazywa od razu obie rzeczy: ten sam PR zamknął też Issue #6 (obsługa konfliktu nazw), pokazany na osobnej stronie 23-14, ponieważ jako lekcja to dwa różne, samodzielne pojęcia, nawet jeśli w repozytorium trafiły do tej samej gałęzi.
Status prawdziwego Project: Done