CLI-интерфейс и обработка ошибок

CLI-интерфейс и обработка ошибок

Бизнес-логика готова: классы Contact и AddressBook работают и сохраняют данные. Последний шаг — создать интерфейс командной строки, с которым будет взаимодействовать пользователь. Это превратит набор классов в готовое приложение.

Структура CLI: цикл с меню

Консольные приложения работают по одной схеме: показать меню → получить команду → выполнить → повторить:

# cli.py

from address_book import AddressBook
from contact import Contact, ContactNotFoundError

def show_menu():
    print("\n=== Телефонная книга ===")
    print("1. Показать все контакты")
    print("2. Добавить контакт")
    print("3. Найти контакт")
    print("4. Удалить контакт")
    print("0. Выход")

def run_cli(book: AddressBook):
    while True:
        show_menu()
        choice = input("Выберите действие: ").strip()

        if choice == "0":
            print("До свидания!")
            break
        elif choice == "1":
            cmd_list(book)
        elif choice == "2":
            cmd_add(book)
        elif choice == "3":
            cmd_find(book)
        elif choice == "4":
            cmd_delete(book)
        else:
            print("Неизвестная команда")

Функции команд

Каждая команда — отдельная функция. Это делает код читаемым и позволяет легко добавлять новые команды:

def cmd_list(book: AddressBook):
    contacts = book.all()
    if not contacts:
        print("Книга пуста")
        return
    print(f"\nКонтактов: {len(contacts)}")
    for c in contacts:
        print(f"  {c}")

def cmd_add(book: AddressBook):
    name  = input("Имя: ").strip()
    phone = input("Телефон: ").strip()
    email = input("Email (Enter — пропустить): ").strip() or None
    try:
        contact = Contact(name, phone, email)
        book.add(contact)
        print(f"Контакт '{contact.name}' добавлен")
    except ValueError as e:
        print(f"Ошибка: {e}")

def cmd_find(book: AddressBook):
    query = input("Имя для поиска: ").strip()
    results = book.search(query)
    if not results:
        print("Ничего не найдено")
    else:
        for c in results:
            print(f"  {c}")

def cmd_delete(book: AddressBook):
    name = input("Имя для удаления: ").strip()
    try:
        book.delete(name)
        print(f"Контакт '{name}' удалён")
    except ContactNotFoundError as e:
        print(f"Ошибка: {e}")

Точка входа: main.py

# main.py

from address_book import AddressBook
from cli import run_cli

CONTACTS_FILE = "contacts.json"

def main():
    book = AddressBook(CONTACTS_FILE)
    run_cli(book)

if __name__ == "__main__":
    main()

Принципы обработки ошибок в CLI

Хороший CLI не падает от ошибок пользователя:

  1. Ожидаемые ошибки перехватывай — пустое имя, несуществующий контакт — это предсказуемые ситуации, они перехватываются и выводятся понятным сообщением.

  2. Неожиданные ошибки позволяй всплывать — баги в коде (не в вводе пользователя) должны быть видны: не прячь их в except Exception: pass.

  3. KeyboardInterrupt обрабатывай gracefully — если пользователь нажал Ctrl+C, завершись корректно:

# main.py
def main():
    book = AddressBook(CONTACTS_FILE)
    try:
        run_cli(book)
    except KeyboardInterrupt:
        print("\nПрервано пользователем. До свидания!")

Итоговая структура проекта

phonebook/
    contact.py       — Contact, ContactNotFoundError
    address_book.py  — AddressBook (хранение, поиск, CRUD)
    cli.py           — show_menu, run_cli, cmd_*
    main.py          — точка входа, CONTACTS_FILE
    contacts.json    — создаётся автоматически при первом запуске

Запуск: python main.py

Что дальше

Телефонная книга готова! Её можно расширить:

  • Поиск по телефону или email
  • Редактирование существующего контакта
  • Экспорт в CSV
  • Сортировка по разным полям
  • Группы контактов

Каждое расширение — один-два новых метода в AddressBook и новая команда в cli.py. Архитектура позволяет расти без переписывания существующего кода.

Итог

  • Цикл с меню — стандартный паттерн для консольных приложений.
  • Одна функция = одна команда; run_cli только маршрутизирует.
  • Ожидаемые ошибки перехватываем (ValueError, ContactNotFoundError) и показываем сообщение.
  • KeyboardInterrupt обрабатываем gracefully в main().
  • if __name__ == "__main__": гарантирует, что main() вызывается только при прямом запуске.

Поздравляем с завершением курса «Основы Python»! За 13 модулей вы прошли путь от print("Hello") до полноценного объектно-ориентированного приложения с файловым хранилищем. Python — инструмент, а мастерство приходит с практикой. Пишите код!

Попробуйте интерактивную версию

Практические задачи, квизы и AI-наставник — бесплатный старт без карты

Перейти к практике