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

Bezpiecznie przenoszcie pliki

Bezpośrednie sortowanie jest wykonywane przez apply_plan(): przed każdym ruchem, funkcja ponownie sprawdza konflikt. Undo pozostaje osobną operacją odwrotną.

SafeSort · Część 4 z 6
Git i GitHubPlanowanieProjektWdrożenieTesty i CIPremiera
GitHubIssue #5 · Project SafeSort Pierwsze wydanie
Add explicit apply operation
Area: SafetyPriority: High
git switch -c feat/apply-operation

Do tej pory żadna funkcja w łańcuchu bezpośredniego sortowania nie zmieniła SafeSort pliku system. Moduł executor.py wykonuje operację bezpośrednią: Funkcja apply_plan() ma gotowy plan i tylko się porusza pliki są w nim wymienione. Odwracaj działanie undo też przenosi pliki, ale przywraca je zgodnie z zarejestrowanym manifestem.

System plikówkatalog z plikamiScannerscan()Klasyfikatorclassify()Executorapply_plan()
W łańcuchu sortowania do przodu rekord zaczyna się tylko od executor; undo tworzy osobny łańcuch odwrotny.
Tak było
Downloads/
report.pdf
photo.jpg
archive.zip
Stało się
Downloads/
Sorted/
documents/
report.pdf
images/
photo.jpg
archives/
archive.zip
apply wykonuje bezpośrednie sortowanie; undo później może wykonywać ruchy odwrotne na manifestie.
src/safesort/executor.py
def apply_plan(plan: SortPlan) -> list[CompletedMove]:
    results = []
    for operation in plan.operations:
        source, destination = operation.source, operation.destination
        destination.parent.mkdir(parents=True, exist_ok=True)

        if destination.exists() or destination.is_symlink():
            results.append(CompletedMove(source, destination, completed=False,
                                          error="destination already exists"))
            continue

        shutil.move(str(source), str(destination))
        results.append(CompletedMove(source, destination, completed=True))
    return results
Ruch batch nie jest transakcją bazy danych
Jeśli jeden plik w środku partii nie uda się przenieść (np. prawa dostępu zmieniły się między skanowaniem a wykonaniem), nie powinno to zatrzymywać przetwarzania pozostałych plików i nie powinno cofać już wykonanych przeniesień — system plików nie umie tak atomowo cofać grupy operacji, jak robi to baza danych. Dlatego apply_plan() przetwarza każdy ruch niezależnie i na końcu zwraca uczciwy raport o tym, co naprawdę stało się z każdym plikiem.

Zwracaj uwagę na weryfikację destination.exists() prosty Przed przeprowadzką, choć planista już uniknął tego konfliktu podczas fazy realizacji planu. Między tworzeniem planu a jego realizacją na dysku mógł pojawić się nowy plik, na przykład, jeśli sam użytkownik w danym momencie coś tam umieścił. Brak ponownej weryfikacji shutil.move() na systemach opartych na POSIX bezszelestnie nadpisywały Taki plik jest SafeSort nie nadpisuje bezgłośnie plików pod żadnym pozorem.

~/safesort $ safesort apply ~/Downloads
Applied 9 moves.
Manifest written to:
.safesort/history/20260824T085400685280.json

punkt kontrolny testu: apply zmienia tylko podaną ścieżkę

tests/test_executor.py
def test_apply_plan_moves_one_file(tmp_path):
    source = tmp_path / "report.pdf"
    destination = tmp_path / "Sorted/documents/report.pdf"
    source.write_bytes(b"report")
    plan = SortPlan(tmp_path, (MoveOperation(source, destination),))

    [result] = apply_plan(plan)

    assert result.completed is True
    assert destination.read_bytes() == b"report"
    assert not source.exists()
Praktyka: Przenoszenie plików w katalogu tymczasowym
Potrzebny jest dostęp do prawdziwego systemu plików – uruchamianego lokalnie w VS Code, PyCharm lub Jupyter
Praktyka kursuje lokalnie
Otwórz praktykę →
Punkt kontrolny · Issue #5
git commit -m "feat: add explicit apply operation"
Zlał się jak Pull Request #18. W opisie PR wyraźnie wskazano Closes #5 w opisie, więc Issue #5 automatycznie zamyka się w momencie połączenia.
Status prawdziwego Project: Done