Модуль 5 · Урок 17 із 58

Файли й pathlib: безпечна межа I/O

Файл — зовнішня межа, де припущення програми зустрічаються з реальною файловою системою. До читання треба визначити allowed root, формат, encoding, граничний розмір і поведінку для missing/empty input; до запису — ownership, overwrite policy та спосіб не залишити частковий результат.

pathlibwithUTF-8I/O contract

Безпечна практика модуля 5

Пакет містить тільки synthetic JSON/CSV, explicit UTF-8, schema validation, deterministic output, safe logging і локальні tests. Жодних персональних чи production-даних, secrets, мережевих викликів або зовнішніх залежностей.

Завантажити практичний пакет →

Path — це дані, а не склеєний string

from pathlib import Path

workspace = Path("workspace")
source = workspace / "input" / "records.txt"
report = workspace / "output" / "report.txt"

Path виражає filesystem operation: join через /, exists(), is_file(), parent, suffix. Не будувати шлях конкатенацією з "/": separators відрізняються між platforms, а user-controlled absolute path може неочікувано вийти за intended workspace.

Boundary: external path спочатку validate-иться відносно дозволеного root; symlink і resolved-path policy треба визначати окремо для sensitive tools.

Text і bytes — різні contracts

with source.open("r", encoding="utf-8") as handle:
    text = handle.read()

with Path("image.bin").open("rb") as handle:
    payload = handle.read(1024)

Text mode повертає str і виконує decoding; binary mode повертає bytes і не приймає encoding. Явний UTF-8 робить поведінку відтворюваною між Windows і Linux. errors="ignore" не є «виправленням»: він мовчки втрачає symbols і руйнує evidence.

with закриває resource навіть при exception

Context manager гарантує виклик cleanup після виходу з block. Це важливо не лише для memory: незакритий file descriptor, незаписаний buffer або locked file створює environment-dependent failures.

with source.open(encoding="utf-8") as handle:
    first_line = handle.readline()

assert handle.closed

Не повертайте відкритий handle із helper, якщо caller не володіє його lifecycle. Простіше повернути parsed values або зробити function itself context manager у пізнішому module.

Whole-file read має size contract

read_text() і read() зручні для малих контрольованих fixtures. Для невідомих або великих inputs використовуйте iteration по lines чи chunked binary reads і встановіть maximum bytes/records. Перевірка suffix не доводить content type або безпечність payload.

def iter_nonempty_lines(path: Path):
    with path.open(encoding="utf-8") as handle:
        for number, raw in enumerate(handle, start=1):
            value = raw.strip()
            if value:
                yield number, value

Запис не повинен тихо знищувати попередній результат

Mode "w" truncates existing file. Перед overwrite визначте policy: fail if exists, explicit --force, versioned filename або atomic replace. Для важливого report спочатку пишуть sibling temporary file, flush/close, а потім замінюють target у межах same filesystem. Це не робить multi-file operation transaction.

InputRoot, kind, encoding, size, empty policy.
ReadWhole file чи stream; line numbers у errors.
OutputParent creation, overwrite і partial-write policy.
TestTemporaryDirectory, не machine-specific path.

Definition of Done

  • Paths створені через Path і не залежать від current working directory без документації.
  • Text/binary mode й encoding задані явно.
  • Resource lifecycle закрито через with.
  • Size, empty, missing, permission і overwrite cases мають очікуваний result.
  • Тести працюють лише з temporary synthetic files.

Методичні джерела

Урок, сценарії, пояснення й вправи створені SEOWORK. Посилання ведуть лише на офіційну документацію Python.

Практична перевірка · урок 17 з 47

Закріпіть матеріал уроку

Три сценарні питання. Для зарахування уроку потрібно дати щонайменше дві правильні відповіді.

1. CLI має будувати шлях до input однаково на Windows і Linux. Який підхід найкраще передає filesystem contract без ручної склейки separators?
2. Програма читає український text file на різних environments. Яка настройка прибирає залежність від platform default encoding?
3. Чому JPEG або executable треба читати в binary mode, а не як text із випадковим encoding?