k08. Исключения, логирование, отладка
UAV Python Pro · Конспект K08 · Модуль M1. Python intensive: исключения, журналирование и отладка · Редакция 2.0 · После CHECKPOINT-K07
На уроке K07 вы научились читать и записывать файлы. На практике файл может отсутствовать, текст JSON может быть повреждён, а пользователь может ввести слова вместо числа. Если программа на каждую такую ситуацию просто завершается аварийно, инженер теряет время, а в более сложных сценариях — теряет понятный след причины отказа. На этом уроке вы научитесь перехватывать ожидаемые ошибки, создавать учебное исключение предметной области, вести журнал через модуль logging и отличать журнал от простого вывода print. Практика выполняется в виртуальном окружении. Полные тексты учебных файлов вложены в аккордеоны раздела практики и совпадают с файлами в каталоге code/M01_python/.
Как устроены примеры в этой редакции. В теоретических разделах каждый важный фрагмент кода приводится дважды. Сначала вы видите блок «код целиком»: в начале указана краткая цель, далее идёт сам код. Затем в раскрывающемся аккордеоне размещён тот же фрагмент с построчным разбором: после смысловой строки добавлен комментарий преподавателя с префиксом «# →». В разделе практики для каждой задачи в аккордеоне приведён полный текст соответствующего файла k08_task*.py.
Главная мысль урока состоит в следующем. Исключение — это сигнал интерпретатора Python о том, что обычный ход программы прервать необходимо. Конструкция try/except позволяет перехватить ожидаемый тип ошибки и продолжить работу осмысленно. Модуль logging записывает ход работы с уровнем важности и отметкой времени; такой журнал удобнее print для длинных сессий и последующего разбора. Отладка в PyCharm позволяет остановить программу на выбранной строке и посмотреть значения переменных, не восстанавливая их по обрывкам вывода.
Содержание
1. Цели урока
После изучения этого урока вы будете уметь выполнять следующие действия.
- Вы будете объяснять, что такое исключение и чем оно отличается от обычного возврата значения через return.
- Вы будете писать конструкцию try/except для ожидаемых ошибок ValueError и FileNotFoundError.
- Вы будете понимать назначение веток else и finally у конструкции try.
- Вы будете создавать простой класс собственного исключения и поднимать его командой raise.
- Вы будете настраивать logging.basicConfig и записывать сообщения уровней DEBUG, INFO, WARNING и ERROR.
- Вы будете соединять загрузку JSON с перехватом ошибок и записью в журнал.
- Вы будете знать базовый порядок отладки в PyCharm: точка останова, запуск Debug и просмотр переменных.
2. Словарь урока
| Термин | Простыми словами |
|---|---|
| Исключение / Exception | Сигнал Python о том, что выполнить операцию обычным путём не удалось. |
| traceback | Текст трассировки: цепочка вызовов и строка, на которой ошибка проявилась. |
| try / except | Конструкция «попробуй выполнить блок; если случился указанный тип ошибки — обработай её». |
| raise | Команда «поднять исключение» самостоятельно, когда данные недопустимы по смыслу задачи. |
| logging | Стандартный модуль журналирования сообщений с уровнями важности. |
| Уровень журнала | Степень важности сообщения: DEBUG, INFO, WARNING, ERROR, CRITICAL. |
| Breakpoint | Точка останова в отладчике: выполнение останавливается на выбранной строке. |
| Отладчик | Инструмент среды разработки, который выполняет программу пошагово и показывает значения переменных. |
3. Зачем исключения и журнал в разработке для БПЛА
Представьте учебный сценарий, который читает JSON-файл с порогом минимального напряжения и затем печатает решение «GO» или «STOP». На диске файла может не оказаться. Внутри файла может быть опечатка. Пользователь может указать путь к другому каталогу. Если каждая такая ситуация завершает программу мгновенным аварийным остановом, вы теряете и частичный результат, и понятный след причины.
Исключения дают язык для описания ожидаемых сбоев. Журнал даёт хронологию: что программа пыталась сделать, что она получила и на чём остановилась. В модулях, посвящённых MAVLink, те же приёмы понадобятся для таймаутов, обрывов связи и разбора сессии. Сейчас вы осваиваете их на файлах и числах — на безопасной учебной почве.
↑ К оглавлению4. Конструкция try, except, else, finally
Базовый try/except
Исключение — это объект-сигнал о сбое. Когда интерпретатор Python не может продолжить обычный ход программы, например при вызове float("текст"), он создаёт исключение. Если его никто не перехватил, программа печатает traceback и завершается.
Конструкция try / except означает следующее. Сначала выполняется блок try. Если во время выполнения возникло исключение указанного типа, управление переходит в блок except, где ситуацию обрабатывают.
# Пример: перехват ValueError при преобразовании строки в число
def read_min_alt(raw: str) -> float:
"""Преобразовать строку в float. При ошибке вернуть 10.0."""
try:
value = float(raw)
except ValueError:
print(f"Не могу превратить {raw!r} в число. Беру 10.0")
return 10.0
return value
print(read_min_alt("12.5"))
print(read_min_alt("двенадцать"))
- Построчный разбор
Ниже приведён тот же фрагмент с пояснением после каждой смысловой строки. Строки, которые начинаются с последовательности символов «# →», содержат комментарий преподавателя. Эти строки не являются обязательной частью программы.
def read_min_alt(raw: str) -> float: # → Объявляем функцию: на входе строка, на выходе число float. """Преобразовать строку в float. При ошибке вернуть 10.0.""" # → Краткое описание функции (docstring). try: # → Начинается блок, в котором мы ожидаем возможную ошибку. value = float(raw) # → Пытаемся превратить строку в число. Если raw = "двенадцать", возникнет ValueError. except ValueError: # → Если возникло исключение типа ValueError, управление переходит сюда. print(f"Не могу превратить {raw!r} в число. Беру 10.0") # → Сообщаем о проблеме. !r показывает значение в кавычках. return 10.0 # → Возвращаем запасное учебное значение вместо падения программы. return value # → Если ошибки не было, возвращаем успешно полученное число. print(read_min_alt("12.5")) # → Первый вызов: строка корректна, ожидаем 12.5. print(read_min_alt("двенадцать")) # → Второй вызов: строка нечисловая, сработает except.
# Пример: FileNotFoundError при чтении файла
from pathlib import Path
def read_text_or_warn(path: Path) -> str:
try:
return path.read_text(encoding="utf-8")
except FileNotFoundError:
print(f"Файл не найден: {path}")
return ""
- Построчный разбор
Ниже приведён тот же фрагмент с пояснением после каждой смысловой строки. Строки, которые начинаются с последовательности символов «# →», содержат комментарий преподавателя. Эти строки не являются обязательной частью программы.
from pathlib import Path # → Импортируем Path для работы с путями к файлам. def read_text_or_warn(path: Path) -> str: # → Функция принимает путь и возвращает текст либо пустую строку. try: # → Пытаемся прочитать файл. return path.read_text(encoding="utf-8") # → Успешное чтение: сразу возвращаем содержимое. except FileNotFoundError: # → Если файла нет, Python возбуждает FileNotFoundError. print(f"Файл не найден: {path}") # → Сообщаем о причине. return "" # → Возвращаем пустую строку, чтобы вызывающий код мог продолжить работу.
Ветки else и finally
У конструкции try есть две дополнительные ветки, которые часто путают.
- Ветка else выполняется только в том случае, если блок try завершился без исключения.
- Ветка finally выполняется почти всегда: и после успеха, и после except. Её используют для обязательных завершающих действий. В учебном примере это служебное сообщение. В прикладном коде это может быть закрытие ресурса, если вы не использовали оператор with.
# Пример: ветки else и finally
from pathlib import Path
def load_demo(p: Path) -> str:
print("start load")
try:
text = p.read_text(encoding="utf-8")
except FileNotFoundError:
print("except: файла нет")
return ""
else:
print("else: чтение прошло без исключения")
return text
finally:
print("finally: служебный хвост")
- Построчный разбор
Ниже приведён тот же фрагмент с пояснением после каждой смысловой строки. Строки, которые начинаются с последовательности символов «# →», содержат комментарий преподавателя. Эти строки не являются обязательной частью программы.
def load_demo(p: Path) -> str: # → Учебная функция загрузки текста файла. print("start load") # → Метка начала: поможет увидеть порядок сообщений. try: # → Основная операция, которая может завершиться ошибкой. text = p.read_text(encoding="utf-8") # → Чтение файла. except FileNotFoundError: # → Обработка отсутствия файла. print("except: файла нет") # → Сообщение о перехваченной ошибке. return "" # → Возврат пустой строки. else: # → Эта ветка выполняется только если except не сработал. print("else: чтение прошло без исключения") # → Подтверждение успешного чтения. return text # → Возвращаем прочитанный текст. finally: # → Эта ветка выполняется почти всегда: и после успеха, и после except. print("finally: служебный хвост") # → Место для обязательных завершающих действий.
k08_task2_else_finally.py приведён в задаче 8.2. Внимательно прочитайте порядок печатных строк для случая, когда файл существует, и для случая, когда файла нет.5. Команда raise и собственное исключение
Иногда ошибка представляет собой не сбой средств языка Python, а нарушение правила вашей предметной области. Например, отрицательная высота в учебном фильтре телеметрии недопустима. Тогда программа может самостоятельно поднять исключение.
Команда raise создаёт исключение и прерывает обычный ход функции. Вызывающий код решает, перехватить ли его блоком except или позволить программе остановиться.
# Пример: своё исключение и raise
class TelemetryError(Exception):
"""Учебная ошибка: данные телеметрии непригодны для решения."""
def require_positive_altitude(altitude_m: float) -> float:
if altitude_m < 0:
raise TelemetryError(f"Отрицательная высота: {altitude_m}")
return altitude_m
try:
print(require_positive_altitude(-3.0))
except TelemetryError as exc:
print("domain error:", exc)
- Построчный разбор
Ниже приведён тот же фрагмент с пояснением после каждой смысловой строки. Строки, которые начинаются с последовательности символов «# →», содержат комментарий преподавателя. Эти строки не являются обязательной частью программы.
class TelemetryError(Exception): # → Объявляем собственный класс исключения. Он наследует Exception. """Учебная ошибка: данные телеметрии непригодны для решения.""" # → Краткое описание смысла ошибки. def require_positive_altitude(altitude_m: float) -> float: # → Функция проверяет высоту на допустимость. if altitude_m < 0: # → Условие нарушения предметного правила. raise TelemetryError(f"Отрицательная высота: {altitude_m}") # → raise поднимает исключение и прерывает обычный ход функции. return altitude_m # → Если высота корректна, возвращаем её как обычный результат. try: # → Вызываем функцию в защищённом блоке. print(require_positive_altitude(-3.0)) # → Передаём заведомо неверное значение. except TelemetryError as exc: # → Перехватываем именно наш тип ошибки. as exc сохраняет объект ошибки. print("domain error:", exc) # → Печатаем текст исключения.
k08_task3_custom_error.py приведён в задаче 8.3.6. Модуль logging
Уровни сообщений
logging — стандартный модуль Python для журналирования. В отличие от print, запись в журнал имеет уровень важности и обычно содержит отметку времени.
| Уровень | Типичный смысл |
|---|---|
| DEBUG | Подробности для разработчика; в рабочем режиме их часто отключают |
| INFO | Нормальный ход работы: сессия началась, файл загружен |
| WARNING | Ситуация подозрительна, но программа ещё может продолжать |
| ERROR | Операция не удалась |
| CRITICAL | Очень тяжёлый сбой; используют реже |
Настройка basicConfig
# Пример: настройка logging в файл и в консоль
import logging
from pathlib import Path
log_path = Path("k08_data") / "session_demo.log"
log_path.parent.mkdir(exist_ok=True)
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s [%(levelname)s] %(message)s",
handlers=[
logging.FileHandler(log_path, encoding="utf-8"),
logging.StreamHandler(),
],
)
log = logging.getLogger("k08.demo")
log.info("сессия началась")
log.error("не удалось прочитать учебный файл")
- Построчный разбор
Ниже приведён тот же фрагмент с пояснением после каждой смысловой строки. Строки, которые начинаются с последовательности символов «# →», содержат комментарий преподавателя. Эти строки не являются обязательной частью программы.
import logging # → Подключаем стандартный модуль журналирования. from pathlib import Path # → Нужен для пути к файлу журнала. log_path = Path("k08_data") / "session_demo.log" # → Путь к файлу журнала сессии. log_path.parent.mkdir(exist_ok=True) # → Создаём каталог, если его ещё нет. logging.basicConfig( # → Однократная настройка корневого журнала при старте программы. level=logging.DEBUG, # → Минимальный уровень: DEBUG и важнее будут обработаны. format="%(asctime)s [%(levelname)s] %(message)s", # → Формат строки: время, уровень, текст сообщения. handlers=[ # → Куда писать сообщения. logging.FileHandler(log_path, encoding="utf-8"), # → Запись в файл в кодировке UTF-8. logging.StreamHandler(), # → Дублирование в консоль. ], # → Конец списка обработчиков. ) # → Конец basicConfig. log = logging.getLogger("k08.demo") # → Именованный логгер для сообщений этого учебного модуля. log.info("сессия началась") # → Сообщение уровня INFO о нормальном ходе работы. log.error("не удалось прочитать учебный файл") # → Сообщение уровня ERROR о неудачной операции.
# Пример: JSON + try/except + logging
import json
import logging
from pathlib import Path
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
log = logging.getLogger("k08.loader")
def load_telemetry_json(path: Path) -> dict:
log.info("loading %s", path)
try:
text = path.read_text(encoding="utf-8")
data = json.loads(text)
except FileNotFoundError:
log.error("file not found: %s", path)
return {}
except json.JSONDecodeError as exc:
log.error("bad json in %s: %s", path, exc)
return {}
else:
log.info("loaded keys=%s", list(data.keys()))
return data
- Построчный разбор
Ниже приведён тот же фрагмент с пояснением после каждой смысловой строки. Строки, которые начинаются с последовательности символов «# →», содержат комментарий преподавателя. Эти строки не являются обязательной частью программы.
import json # → Модуль для разбора JSON. import logging # → Модуль журнала. from pathlib import Path # → Пути к файлам. logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s") # → Краткая настройка журнала: INFO и выше, с временем. log = logging.getLogger("k08.loader") # → Логгер для загрузчика. def load_telemetry_json(path: Path) -> dict: # → Функция загружает словарь телеметрии из JSON-файла. log.info("loading %s", path) # → Фиксируем начало операции. %s подставит path. try: # → Ожидаем возможные ошибки чтения и разбора. text = path.read_text(encoding="utf-8") # → Читаем файл как текст. data = json.loads(text) # → Превращаем текст JSON в объект Python. except FileNotFoundError: # → Файла нет на диске. log.error("file not found: %s", path) # → Пишем ошибку в журнал. return {} # → Возвращаем пустой словарь как безопасный запасной результат. except json.JSONDecodeError as exc: # → Текст есть, но JSON повреждён. log.error("bad json in %s: %s", path, exc) # → Журналируем причину разбора. return {} # → Снова безопасный пустой результат. else: # → Сюда попадаем только если try прошёл без исключений. log.info("loaded keys=%s", list(data.keys())) # → Отмечаем успех и состав ключей. return data # → Возвращаем загруженный словарь.
k08_task4_logging_basic.py и k08_task5_logged_loader.py приведены в задачах 8.4 и 8.5.7. Отладка в PyCharm
Отладчик — это инструмент среды разработки, который выполняет программу под контролем. Можно остановиться на строке, посмотреть переменные и продолжить выполнение по шагам.
Breakpoint, или точка останова, — это метка на строке исходного текста. Когда выполнение доходит до этой строки, программа переходит в режим паузы.
Базовый порядок работы в PyCharm состоит в следующем.
- Откройте нужный файл с расширением .py.
- Щёлкните слева от номера строки. Появится красная точка — breakpoint.
- Запустите не команду Run, а команду Debug.
- Когда выполнение остановится, откройте панель Variables и просмотрите значения.
- Клавишами Step Over и Step Into продвигайтесь по коду.
- Когда закончите, нажмите Stop.
8. Типичные ошибки
| Ошибка | Что происходит | Как следует рассуждать |
|---|---|---|
| Блок except без указания типа | Глотаются даже программные дефекты | Указывайте конкретный тип исключения |
| Ошибку перехватили и промолчали | Позднее невозможно понять причину | Запишите хотя бы log.error или log.exception |
| basicConfig вызывают многократно без системы | Конфигурация журнала становится непредсказуемой | Настраивайте журнал один раз при старте программы |
| Путают return и raise | Либо появляется тихий None, либо неожиданный traceback | return отдаёт нормальный результат; raise сигнализирует о сбое |
| Отладку ведут только через print в десяти местах | Много шума, нет уровней, нет файла сессии | Используйте logging и точечный запуск Debug |
9. Практика
В каждой карточке задачи ниже в раскрывающемся блоке приведён полный текст соответствующего учебного файла. Этот текст совпадает с файлами в каталоге code/M01_python/. Рекомендуется такой порядок работы. Сначала внимательно прочитайте условие. Затем раскройте аккордеон, скопируйте код в файл проекта под тем же именем, запустите его в виртуальном окружении и сверьте вывод с ожидаемым результатом. Каталог k08_data/ учебные сценарии создадут самостоятельно.
Задача 8.1. Конструкция try и except
Файл практики: k08_task1_try_except.py.
Реализуйте перехват ValueError при преобразовании строки в число и перехват FileNotFoundError при чтении отсутствующего файла. В обоих случаях программа не должна завершаться аварийно.
Ожидаемый результат. Для строки «12.5» будет напечатано число 12.5. Для строки «двенадцать» появится предупреждение и значение 10.0. Для отсутствующего файла функция вернёт пустую строку.
- Полный текст файла k08_task1_try_except.py
Скопируйте приведённое ниже содержимое в файл
code/M01_python/k08_task1_try_except.pyв проекте PyCharm (виртуальное окружение) и запустите программу. Ниже приведён полный учебный текст файла с комментариями.# k08_task1_try_except.py # Цель: перехватить ValueError при преобразовании строки в число # и FileNotFoundError при чтении несуществующего файла. from pathlib import Path HERE = Path(__file__).resolve().parent def read_min_alt(raw: str) -> float: """Преобразовать ввод пользователя в float. Если строка не является числом, перехватываем ValueError и возвращаем запасное учебное значение. """ try: value = float(raw) except ValueError: print(f"Не могу превратить {raw!r} в число. Беру 10.0") return 10.0 return value def read_text_or_warn(path: Path) -> str: """Прочитать текст файла. Если файла нет — вернуть пустую строку.""" try: return path.read_text(encoding="utf-8") except FileNotFoundError: print(f"Файл не найден: {path}") return "" print("case1:", read_min_alt("12.5")) print("case2:", read_min_alt("двенадцать")) missing = HERE / "k08_data" / "no_such_file.txt" print("file content empty?:", read_text_or_warn(missing) == "")
Задача 8.2. Ветки else и finally
Файл практики: k08_task2_else_finally.py.
Проследите порядок сообщений start load, except или else, finally для существующего файла и для отсутствующего файла.
Ожидаемый результат. При наличии файла вы увидите else и finally. При отсутствии файла вы увидите except и finally. В обоих случаях finally выполняется.
- Полный текст файла k08_task2_else_finally.py
Скопируйте приведённое ниже содержимое в файл
code/M01_python/k08_task2_else_finally.pyв проекте PyCharm (виртуальное окружение) и запустите программу. Ниже приведён полный учебный текст файла с комментариями.# k08_task2_else_finally.py # Цель: понять ветки else и finally у try. from pathlib import Path HERE = Path(__file__).resolve().parent OUT = HERE / "k08_data" OUT.mkdir(exist_ok=True) path = OUT / "demo.txt" path.write_text("ok\n", encoding="utf-8") def load_demo(p: Path) -> str: """Показать try / except / else / finally на чтении файла.""" print("start load") try: text = p.read_text(encoding="utf-8") except FileNotFoundError: print("except: файла нет") return "" else: # else выполняется только если except НЕ сработал print("else: чтение прошло без исключения") return text finally: # finally выполняется почти всегда: и при успехе, и при ошибке print("finally: этот блок служебный (закрытие ресурсов, метка в лог)") print("result:", repr(load_demo(path))) print("---") print("result missing:", repr(load_demo(OUT / "missing.txt")))
Задача 8.3. Собственное исключение
Файл практики: k08_task3_custom_error.py.
Объявите класс TelemetryError, поднимайте его при отрицательной высоте и перехватывайте в вызывающем коде.
Ожидаемый результат. Для значения 12.0 будет напечатан успешный результат. Для значения -3.0 будет напечатано domain error с текстом об отрицательной высоте.
- Полный текст файла k08_task3_custom_error.py
Скопируйте приведённое ниже содержимое в файл
code/M01_python/k08_task3_custom_error.pyв проекте PyCharm (виртуальное окружение) и запустите программу. Ниже приведён полный учебный текст файла с комментариями.# k08_task3_custom_error.py # Цель: своё исключение для учебной «ошибки предметной области». class TelemetryError(Exception): """Учебная ошибка: данные телеметрии непригодны для решения.""" def require_positive_altitude(altitude_m: float) -> float: """Вернуть высоту, если она корректна; иначе поднять TelemetryError.""" if altitude_m < 0: # raise создаёт исключение и прерывает обычный ход функции raise TelemetryError(f"Отрицательная высота: {altitude_m}") return altitude_m for value in (12.0, -3.0): try: alt = require_positive_altitude(value) print("ok altitude:", alt) except TelemetryError as exc: # as exc — сохранить объект ошибки, чтобы напечатать текст print("domain error:", exc)
Задача 8.4. Журнал logging
Файл практики: k08_task4_logging_basic.py.
Настройте basicConfig так, чтобы сообщения писались и в файл session_demo.log, и в консоль. Запишите сообщения нескольких уровней.
Ожидаемый результат. В консоли и в файле k08_data/session_demo.log появятся строки с временем, уровнем и текстом сообщений.
- Полный текст файла k08_task4_logging_basic.py
Скопируйте приведённое ниже содержимое в файл
code/M01_python/k08_task4_logging_basic.pyв проекте PyCharm (виртуальное окружение) и запустите программу. Ниже приведён полный учебный текст файла с комментариями.# k08_task4_logging_basic.py # Цель: модуль logging — уровни и вывод в файл + консоль. import logging from pathlib import Path HERE = Path(__file__).resolve().parent OUT = HERE / "k08_data" OUT.mkdir(exist_ok=True) log_path = OUT / "session_demo.log" # basicConfig настраивает корневой логгер ОДИН раз при старте программы logging.basicConfig( level=logging.DEBUG, format="%(asctime)s [%(levelname)s] %(message)s", handlers=[ logging.FileHandler(log_path, encoding="utf-8"), logging.StreamHandler(), # дублировать в консоль ], ) log = logging.getLogger("k08.demo") log.debug("подробности для разработчика") log.info("сессия началась") log.warning("напряжение учебное ниже комфортного порога") log.error("не удалось прочитать учебный файл") print("log file:", log_path) print("log tail:") print(log_path.read_text(encoding="utf-8"))
Задача 8.5. JSON, except и журнал
Файл практики: k08_task5_logged_loader.py.
Загрузите корректный JSON, повреждённый JSON и отсутствующий файл. Для ошибок используйте log.error, для успеха — log.info.
Ожидаемый результат. Для good будет словарь с mode и altitude_m. Для bad и missing будет пустой словарь, а в журнале — сообщения об ошибках.
- Полный текст файла k08_task5_logged_loader.py
Скопируйте приведённое ниже содержимое в файл
code/M01_python/k08_task5_logged_loader.pyв проекте PyCharm (виртуальное окружение) и запустите программу. Ниже приведён полный учебный текст файла с комментариями.# k08_task5_logged_loader.py # Цель: соединить JSON-загрузку (K07) с try/except и logging (K08). import json import logging from pathlib import Path HERE = Path(__file__).resolve().parent OUT = HERE / "k08_data" OUT.mkdir(exist_ok=True) logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s", ) log = logging.getLogger("k08.loader") def load_telemetry_json(path: Path) -> dict: """Загрузить JSON-словарь телеметрии с журналированием ошибок.""" log.info("loading %s", path) try: text = path.read_text(encoding="utf-8") data = json.loads(text) except FileNotFoundError: log.error("file not found: %s", path) return {} except json.JSONDecodeError as exc: log.error("bad json in %s: %s", path, exc) return {} else: log.info("loaded keys=%s", list(data.keys())) return data # Готовим корректный файл good = OUT / "telemetry_ok.json" good.write_text('{"mode": "GUIDED", "altitude_m": 11.0}\n', encoding="utf-8") # Готовим битый JSON bad = OUT / "telemetry_bad.json" bad.write_text("{mode: GUIDED, broken}\n", encoding="utf-8") print("good:", load_telemetry_json(good)) print("bad:", load_telemetry_json(bad)) print("missing:", load_telemetry_json(OUT / "nope.json"))
Задача 8.6. Отладка в PyCharm
Откройте файл k08_task5_logged_loader.py. Поставьте точку останова внутри блока except json.JSONDecodeError или на строке с log.error для повреждённого JSON. Запустите программу командой Debug и просмотрите путь к файлу и объект ошибки в панели Variables.
Задача 8.7. Сохранение в Git
Сохраните практику в истории Git. Каталог k08_data/ с выходными учебными файлами в репозиторий обычно не включают.
git add code/M01_python/k08_*.py
git commit -m "K08: try except raise logging practice"
10. Проверьте себя
- Чем исключение отличается от обычного return None?
- Зачем в блоке except указывают конкретный тип, а не перехватывают все исключения подряд?
- Когда выполняется ветка else у конструкции try?
- Зачем нужна ветка finally, если для файлов часто используют оператор with?
- В каком случае уместна команда raise для собственного TelemetryError?
- Чем вызов logging.info отличается от print с точки зрения последующего разбора сессии?
- Что означает уровень WARNING по сравнению с уровнем ERROR?
- Какой минимальный порядок действий в PyCharm Debug вы назовёте достаточным для учебной задачи?
- Зачем в уроке один и тот же пример кода показан дважды: целиком и с построчным разбором?
11. Чек-лист самопроверки
- Я объясняю исключение и traceback своими словами законченными предложениями.
- Я пишу try/except для ValueError и FileNotFoundError.
- Я понимаю назначение веток else и finally.
- Я создаю класс исключения и выполняю raise.
- Я настраиваю logging.basicConfig и пишу сообщения в файл.
- Я различаю уровни DEBUG, INFO, WARNING и ERROR.
- Я связал загрузку JSON с except и записями в журнал.
- Я поставил breakpoint и хотя бы один раз запускал Debug.
- Я открывал аккордеоны практики и сверял полный код с запуском в виртуальном окружении.
- Я запускал k08_task1 … k08_task5 в venv.
- Я сделал git commit по K08.
K08 чек-лист закрыт. Далее следует урок K09, посвящённый классам, dataclasses и перечислениям Enum.12. Что дальше
- Предыдущий урок: K07 · Файлы, JSON, CSV, YAML
- Текущий урок: K08 · Исключения, журналирование и отладка (редакция 2.0)
- Следующий урок: K09 · Классы, dataclasses, Enum
- Горизонт модуля M1: K12 и контрольная точка G1
docs/05_EDITOR_STYLE_GUIDE.md.