Глава 23 · Часть IV · Реализуем SafeSort

Что делать, если имя уже занято

Ни на диске, ни внутри одного плана два файла никогда не получат одинаковое имя назначения.

SafeSort · Часть 4 из 6Реализация
GitHubIssue #6 · Project «SafeSort — первый релиз»
Handle destination name collisions
Area: SafetyPriority: Medium
git switch -c feat/plan-and-collisions

Два файла с одинаковым именем могут попасть в одну и ту же категорию — например, два разных notes.txt из разных подкаталогов исходного каталога. Если бы SafeSort просто перемещал файл поверх существующего, второй файл исчез бы молча — в каталоге назначения осталось бы одно и то же имя notes.txt, но с содержимым только одного из двух файлов, без единого предупреждения об этом.

~/safesort $ safesort apply ~/Downloads
Applied 1 moves.
Sorted/documents/
notes.txt
notes (1).txtНОВОЕ
Файл, который уже был в Sorted/documents/, остался нетронутым; новый получил свободное имя.

SafeSort никогда не решает конфликт имён перезаписью: вместо этого он находит свободное имя по понятной схеме.

src/safesort/planner.py
def _resolve_collision(candidate: Path, reserved: set[Path]) -> Path:
    if candidate not in reserved and not candidate.exists():
        return candidate

    stem, suffix, parent = candidate.stem, candidate.suffix, candidate.parent
    counter = 1
    while True:
        alternative = parent / f"{stem} ({counter}){suffix}"
        if alternative not in reserved and not alternative.exists():
            return alternative
        counter += 1
Пример
otchet.pdf уже существует в Sorted/documents/
→ следующий otchet.pdf получит имя otchet (1).pdf
→ если и оно занято — otchet (2).pdf, и так далее
reserved — конфликты внутри одного плана, не только на диске
Проверки not candidate.exists() достаточно для конфликта с уже существующим файлом на диске — но не для случая, когда два файла из одного и того же плана претендуют на одинаковое имя одновременно, пока ни один из них ещё не перемещён. Множество reserved запоминает уже занятые в этом плане имена, поэтому второй файл получит otchet (1).pdf, даже если на диске пока нет ни одного otchet.pdf.
Практика: безопасное имя при конфликте
Интерактивный ноутбук прямо в браузере — Python 3.14 через Pyodide, без установки
Открыть практику →
Чекпойнт · Issue #6
git commit -m "feat: add safe organization plan with collision handling"
Та же ветка и тот же Pull Request #17, который закрыл Issue #3 на странице 23-11. план и обработка конфликта имён были одной, тесно связанной задачей в терминах кода, но остаются двумя разными Issues и двумя разными уроками.
Статус в реальном Project: Done