This media is not supported in your browser
VIEW IN TELEGRAM
⚙️ Подменяй любые импорты в Python “на лету” — без изменения кода
Если ты хочешь протестировать модуль, подменить зависимость, замокать внешний сервис или обмануть импорт — не обязательно редактировать исходники. Python позволяет перехватывать импорты прямо во время выполнения, через sys.modules.
Вот минимальный приём, который делает это прозрачно:
import sys
import types
# Создаём фейковый модуль
fake = types.SimpleNamespace()
fake.get_data = lambda: "подмена работает"
# Подменяем импорт
sys.modules['external_service'] = fake
# Теперь даже import будет работать
import external_service
print(external_service.get_data()) # → "подмена работает"
@Python_Community_ru
Если ты хочешь протестировать модуль, подменить зависимость, замокать внешний сервис или обмануть импорт — не обязательно редактировать исходники. Python позволяет перехватывать импорты прямо во время выполнения, через sys.modules.
Вот минимальный приём, который делает это прозрачно:
import sys
import types
# Создаём фейковый модуль
fake = types.SimpleNamespace()
fake.get_data = lambda: "подмена работает"
# Подменяем импорт
sys.modules['external_service'] = fake
# Теперь даже import будет работать
import external_service
print(external_service.get_data()) # → "подмена работает"
@Python_Community_ru
🚀 Быстрый HTTP‑сервер на базе asyncio.Protocol
В этом гайде показано как создать минималистичный HTTP‑сервер на Python, используя низкоуровневый API asyncio.Protocol, что делает его быстрее и гибче, чем решения на основе asyncio.Streams или фреймворков типа FastAPI.
Основные пункты:
1. Сокет-обработчик (`ConnectionHandler`)
- Наследуется от asyncio.Protocol — реакции на события connection_made, data_received, connection_lost.
- Хранит буфер, транспорт и маппинг маршрутов.
2. Парсинг HTTP-запросов
- В data_received накапливаются байты.
- Запрос считается полным при обнаружении \r\n\r\n.
- Затем из заголовков извлекаются method, path, другие поля.
3. Маршрутизация через декоратор
- Класс HTTPServer собирает функции-обработчики для путей.
- Обработчик вызывает либо корутины, либо возвращает статический HTML.
4. Отправка ответа
- Используется asyncio.create_task внутри колбэка, чтобы запустить send_response — где формируется ответ с HTTP-кодом, заголовками и телом.
- После write() соединение закрывается.
5. Простой запуск сервера
- Через loop.create_server() создаётся экземпляр ConnectionHandler.
- serve_forever() запускает обработку соединений.
6. Высокая производительность
Сервер обрабатывает ~100 000 запросов за ~4.2 с при нагрузке 100 одновременных соединений, в то время как FastAPI справляется за ~32 с :contentReference[oaicite:1]{index=1}.
Зачем это полезно:
- Полный контроль над TCP-соединениями и буферами.
- Существенно выше скорость при простых HTTP-API.
- Отличная демонстрация возможностей, которые остаются скрытыми за высокоуровневыми фреймворками.
Поддержка сообщества:
На Reddit статья получила высокую оценку как "отличный материал по Asyncio Protocols"
https://jacobpadilla.com/articles/asyncio-protocols
@Python_Community_ru
В этом гайде показано как создать минималистичный HTTP‑сервер на Python, используя низкоуровневый API asyncio.Protocol, что делает его быстрее и гибче, чем решения на основе asyncio.Streams или фреймворков типа FastAPI.
Основные пункты:
1. Сокет-обработчик (`ConnectionHandler`)
- Наследуется от asyncio.Protocol — реакции на события connection_made, data_received, connection_lost.
- Хранит буфер, транспорт и маппинг маршрутов.
2. Парсинг HTTP-запросов
- В data_received накапливаются байты.
- Запрос считается полным при обнаружении \r\n\r\n.
- Затем из заголовков извлекаются method, path, другие поля.
3. Маршрутизация через декоратор
- Класс HTTPServer собирает функции-обработчики для путей.
- Обработчик вызывает либо корутины, либо возвращает статический HTML.
4. Отправка ответа
- Используется asyncio.create_task внутри колбэка, чтобы запустить send_response — где формируется ответ с HTTP-кодом, заголовками и телом.
- После write() соединение закрывается.
5. Простой запуск сервера
- Через loop.create_server() создаётся экземпляр ConnectionHandler.
- serve_forever() запускает обработку соединений.
6. Высокая производительность
Сервер обрабатывает ~100 000 запросов за ~4.2 с при нагрузке 100 одновременных соединений, в то время как FastAPI справляется за ~32 с :contentReference[oaicite:1]{index=1}.
Зачем это полезно:
- Полный контроль над TCP-соединениями и буферами.
- Существенно выше скорость при простых HTTP-API.
- Отличная демонстрация возможностей, которые остаются скрытыми за высокоуровневыми фреймворками.
Поддержка сообщества:
На Reddit статья получила высокую оценку как "отличный материал по Asyncio Protocols"
https://jacobpadilla.com/articles/asyncio-protocols
@Python_Community_ru
🔥2
🐍 20 ещё более продвинутых однострочников на Python — часть 3
Если ты уже пишешь на Python профессионально, вот ещё приёмы, которые реально экономят строки и читаются на лету:
1. 🗃️ Группировка по ключу без pandas
groups = defaultdict(list); [groups[k].append(v) for k, v in data]
2. 🪄 Быстрое глубокое копирование объекта
copy = pickle.loads(pickle.dumps(obj, -1))
3. 📤 Отправка JSON через POST
r = requests.post(url, json=payload)
4. 📌 Проверка, что файл — это символическая ссылка
is_symlink = Path(p).is_symlink()
5. 🧩 Быстрое объединение нескольких словарей
merged = {**d1, **d2, **d3}
6. 🧪 Проверить, что список отсортирован
is_sorted = all(a
@Python_Community_ru
Если ты уже пишешь на Python профессионально, вот ещё приёмы, которые реально экономят строки и читаются на лету:
1. 🗃️ Группировка по ключу без pandas
groups = defaultdict(list); [groups[k].append(v) for k, v in data]
2. 🪄 Быстрое глубокое копирование объекта
copy = pickle.loads(pickle.dumps(obj, -1))
3. 📤 Отправка JSON через POST
r = requests.post(url, json=payload)
4. 📌 Проверка, что файл — это символическая ссылка
is_symlink = Path(p).is_symlink()
5. 🧩 Быстрое объединение нескольких словарей
merged = {**d1, **d2, **d3}
6. 🧪 Проверить, что список отсортирован
is_sorted = all(a
@Python_Community_ru
🖥 Полезный, но редко используемый приём: динамическое управление множеством контекст-менеджеров через `contextlib.ExitStack`
Если вам нужно открыть *N* файлов, захватить *M* блокировок или временно изменить кучу настроек, а их количество известно только во время выполнения, традиционный with … as …: не подойдёт. Вместо «пирамиды» вложенных with воспользуйтесь ExitStack:
from contextlib import ExitStack
filenames = ["a.log", "b.log", "c.log"]
with ExitStack() as stack:
files = [stack.enter_context(open(name)) for name in filenames]
# теперь у вас список открытых файлов, с которыми можно работать
for f in files:
print(f.readline())
# здесь ExitStack автоматически закроет все файлы, даже если их было 1000
Почему это круто
- Управляет произвольным числом контекстов: добавляете их в цикл, условно, через функции-фабрики.
- Гарантирует корректный rollback при исключениях: всё, что добавлено в ExitStack, будет закрыто в обратном порядке.
- Упрощает сложную инициализацию: можно динамически «подключать» то, что нужно именно сейчас (файлы, блокировки, сетевые соединения).
🔧 Где пригодится
- Пакетная обработка файлов и архивов.
- Тестовые стенды с кучей временных ресурсов.
- Плагины, которые могут регистрировать собственные контекст-менеджеры.
Теперь никакого «каскада из with» — один аккуратный ExitStack.
@Python_Community_ru
Если вам нужно открыть *N* файлов, захватить *M* блокировок или временно изменить кучу настроек, а их количество известно только во время выполнения, традиционный with … as …: не подойдёт. Вместо «пирамиды» вложенных with воспользуйтесь ExitStack:
from contextlib import ExitStack
filenames = ["a.log", "b.log", "c.log"]
with ExitStack() as stack:
files = [stack.enter_context(open(name)) for name in filenames]
# теперь у вас список открытых файлов, с которыми можно работать
for f in files:
print(f.readline())
# здесь ExitStack автоматически закроет все файлы, даже если их было 1000
Почему это круто
- Управляет произвольным числом контекстов: добавляете их в цикл, условно, через функции-фабрики.
- Гарантирует корректный rollback при исключениях: всё, что добавлено в ExitStack, будет закрыто в обратном порядке.
- Упрощает сложную инициализацию: можно динамически «подключать» то, что нужно именно сейчас (файлы, блокировки, сетевые соединения).
🔧 Где пригодится
- Пакетная обработка файлов и архивов.
- Тестовые стенды с кучей временных ресурсов.
- Плагины, которые могут регистрировать собственные контекст-менеджеры.
Теперь никакого «каскада из with» — один аккуратный ExitStack.
@Python_Community_ru
📊 Python for Nonprofits — книга и код для анализа данных в НКО
Это полноценное учебное пособие по Python, заточенное под нужды некоммерческих организаций.
Автор — Kenneth Burchfiel — собрал в одном репозитории и книгу, и рабочие блокноты для реального анализа данных.
▪ Импорт и очистка данных (CSV, API, Google Sheets)
▪ Описательная статистика, линейные регрессии
▪ Визуализация: графики, интерактивные карты (Plotly, Folium)
▪ Dash-приложения и публикация дашбордов
▪ Всё оформлено как Jupyter-книга: можно читать, запускать, менять
Кому подойдёт:
– начинающим дата-аналитикам, которые хотят делать полезные проекты
– НКО, работающим с открытыми данными и отчётами
– преподавателям, ищущим учебный курс по Python с практикой
💡 Можно читать онлайн или скачать как книгу. Лицензия MIT — можно использовать где угодно.
📘 Подробнее и исходники: https://github.com/kburchfiel/pfn
@Python_Community_ru
Это полноценное учебное пособие по Python, заточенное под нужды некоммерческих организаций.
Автор — Kenneth Burchfiel — собрал в одном репозитории и книгу, и рабочие блокноты для реального анализа данных.
▪ Импорт и очистка данных (CSV, API, Google Sheets)
▪ Описательная статистика, линейные регрессии
▪ Визуализация: графики, интерактивные карты (Plotly, Folium)
▪ Dash-приложения и публикация дашбордов
▪ Всё оформлено как Jupyter-книга: можно читать, запускать, менять
Кому подойдёт:
– начинающим дата-аналитикам, которые хотят делать полезные проекты
– НКО, работающим с открытыми данными и отчётами
– преподавателям, ищущим учебный курс по Python с практикой
💡 Можно читать онлайн или скачать как книгу. Лицензия MIT — можно использовать где угодно.
📘 Подробнее и исходники: https://github.com/kburchfiel/pfn
@Python_Community_ru
👍1
This media is not supported in your browser
VIEW IN TELEGRAM
🛠 Вышел новый генератор CAD‑моделей по чертежам — и он реально работает!
GenCAD умеет превращать чертежи в 3D‑модели и сразу генерирует параметрический CAD‑код. Для сложных и детализированных объектов он пока не подойдет, но с простыми деталями вроде винтиков и креплений справляется отлично.
📎 Идеально для быстрого прототипирования стандартных элементов.
📄 Paper: https://openreview.net/pdf?id=e817c1wEZ6
🔗 Website: https://gencad.github.io
💻 Code: https://github.com/ferdous-alam/GenCAD
@Python_Community_ru
GenCAD умеет превращать чертежи в 3D‑модели и сразу генерирует параметрический CAD‑код. Для сложных и детализированных объектов он пока не подойдет, но с простыми деталями вроде винтиков и креплений справляется отлично.
📎 Идеально для быстрого прототипирования стандартных элементов.
📄 Paper: https://openreview.net/pdf?id=e817c1wEZ6
🔗 Website: https://gencad.github.io
💻 Code: https://github.com/ferdous-alam/GenCAD
@Python_Community_ru
📊 OpenBB (https://github.com/OpenBB-finance/OpenBB#) — это платформа для инвестиционных исследований, предоставляющая доступ к акциям, опционам, криптовалютам, форексу и макроэкономическим данным. Платформа ориентирована на пользователей всех уровней и включает в себя как командную строку, так и графический интерфейс. 🌟 OpenBB предлагает бесплатный и открытый доступ к аналитическим инструментам и может быть расширен через различные плагины. Проект также поддерживает автоматизацию процессов и интеграцию с внешними сервисами
🔥 Инструмент является бесплатным аналогом Bloomberg terminal (подписка на который стоит $20 тыс. в год!)
👩💻 Язык: Python
🔐 Лицензия: GNU
▪ Github (https://github.com/OpenBB-finance/OpenBB#)
@Python_Community_ru
🔥 Инструмент является бесплатным аналогом Bloomberg terminal (подписка на который стоит $20 тыс. в год!)
👩💻 Язык: Python
🔐 Лицензия: GNU
▪ Github (https://github.com/OpenBB-finance/OpenBB#)
@Python_Community_ru
This media is not supported in your browser
VIEW IN TELEGRAM
🐍 Хитрый совет по Python: как ускорить in в 1000 раз
Когда ты проверяешь, есть ли элемент в списке, Python делает это медленно — он пробегает по списку полностью.
А если ты проверяешь элемент в множестве (set), это работает в сотни раз быстрее.
📌 Пример:
data = [1, 2, 3, ..., 1000000] # большой список
queries = [42, 9999, 123456]
# Медленно:
for q in queries:
if q in data:
print(q)
🔁 Каждый q in data проходит весь список → медленно.
✅ Правильный способ:
Преобразуй список в set один раз — и все станет быстро:
data_set = set(data)
for q in queries:
if q in data_set:
print(q)
💡 Проверка in в set работает мгновенно (как в словаре).
Такой трюк особенно полезен, если:
-у тебя много данных
-ты делаешь много проверок на вхождение
📌 Запомни: if x in список: — медленно
Лучше: if x in set(список): — быстро!
@Python_Community_ru
Когда ты проверяешь, есть ли элемент в списке, Python делает это медленно — он пробегает по списку полностью.
А если ты проверяешь элемент в множестве (set), это работает в сотни раз быстрее.
📌 Пример:
data = [1, 2, 3, ..., 1000000] # большой список
queries = [42, 9999, 123456]
# Медленно:
for q in queries:
if q in data:
print(q)
🔁 Каждый q in data проходит весь список → медленно.
✅ Правильный способ:
Преобразуй список в set один раз — и все станет быстро:
data_set = set(data)
for q in queries:
if q in data_set:
print(q)
💡 Проверка in в set работает мгновенно (как в словаре).
Такой трюк особенно полезен, если:
-у тебя много данных
-ты делаешь много проверок на вхождение
📌 Запомни: if x in список: — медленно
Лучше: if x in set(список): — быстро!
@Python_Community_ru
💡 STUMPY — библиотека для анализа временных рядов. Этот проект предлагает мощный инструмент для работы с временными последовательностями через вычисление matrix profile — специальной метрики, которая автоматически находит схожие паттерны в данных.
Инструмент поддерживает распределённые вычисления через Dask и GPU-ускорение через Numba. Технология особенно полезна для обнаружения аномалий, повторяющихся фрагментов и семантической сегментации. Библиотека одинаково хорошо работает как на небольших наборах данных, так и на временных рядах длиной в миллионы точек.
🤖 GitHub (https://github.com/stumpy-dev/stumpy)
@Python_Community_ru
Инструмент поддерживает распределённые вычисления через Dask и GPU-ускорение через Numba. Технология особенно полезна для обнаружения аномалий, повторяющихся фрагментов и семантической сегментации. Библиотека одинаково хорошо работает как на небольших наборах данных, так и на временных рядах длиной в миллионы точек.
🤖 GitHub (https://github.com/stumpy-dev/stumpy)
@Python_Community_ru
👩💻 MegaParse (https://github.com/QuivrHQ/MegaParse) — высокопроизводительный парсер, который способен преобразовывать всевозможные типы документов (Word документы, PDF-файлы или даже презентации PowerPoint)!
🌟 Его основная цель — преобразовывать информацию из документов в текст для LLM, при этом минимизируя различные потери данных.
🔐 Лицензия: Apache-2.0
🖥 Github (https://github.com/QuivrHQ/MegaParse)
@Python_Community_ru
🌟 Его основная цель — преобразовывать информацию из документов в текст для LLM, при этом минимизируя различные потери данных.
🔐 Лицензия: Apache-2.0
🖥 Github (https://github.com/QuivrHQ/MegaParse)
@Python_Community_ru
🖥 Что выведет код ниже?
def append_to_list(val, my_list=[]):
my_list.append(val)
return my_list
print(append_to_list(1))
print(append_to_list(2))
print(append_to_list(3))
🤯 Подвох
Многие думают, что каждый вызов append_to_list() создаёт новый список, и ожидают вывод:
[1]
[2]
[3]
Но на самом деле Python выведет:
[1]
[1, 2]
[1, 2, 3]
🧠 Почему так происходит?
В Python значения аргументов по умолчанию вычисляются один раз — при определении функции, а не при каждом вызове.
Значение my_list=[] создаётся один раз и сохраняется между вызовами. Это работает как статическая переменная внутри функции.
✅ Как это исправить?
Используй None как значение по умолчанию:
def append_to_list(val, my_list=None):
if my_list is None:
my_list = []
my_list.append(val)
return my_list
print(append_to_list(1)) # [1]
print(append_to_list(2)) # [2]
print(append_to_list(3)) # [3]
💡 Вывод
Не используйте изменяемые объекты (например, list, dict, set) как значения по умолчанию для аргументов функций в Python.
@Python_Community_ru
def append_to_list(val, my_list=[]):
my_list.append(val)
return my_list
print(append_to_list(1))
print(append_to_list(2))
print(append_to_list(3))
🤯 Подвох
Многие думают, что каждый вызов append_to_list() создаёт новый список, и ожидают вывод:
[1]
[2]
[3]
Но на самом деле Python выведет:
[1]
[1, 2]
[1, 2, 3]
🧠 Почему так происходит?
В Python значения аргументов по умолчанию вычисляются один раз — при определении функции, а не при каждом вызове.
Значение my_list=[] создаётся один раз и сохраняется между вызовами. Это работает как статическая переменная внутри функции.
✅ Как это исправить?
Используй None как значение по умолчанию:
def append_to_list(val, my_list=None):
if my_list is None:
my_list = []
my_list.append(val)
return my_list
print(append_to_list(1)) # [1]
print(append_to_list(2)) # [2]
print(append_to_list(3)) # [3]
💡 Вывод
Не используйте изменяемые объекты (например, list, dict, set) как значения по умолчанию для аргументов функций в Python.
@Python_Community_ru
🐍 dlt — библиотека для загрузки данных на Python. Этот open-source инструмент упрощает работу с данными — от простых скриптов до сложных ETL-пайплайнов. Автоматически определяет структуру данных и адаптируется под разные источники и хранилища.
Проект удобен для быстрого прототипирования: можно начать в Colab-ноутбуке, а затем масштабировать до production-решения. Поддерживает инкрементальную загрузку и интеграцию с Airflow.
🤖 GitHub (https://github.com/dlt-hub/dlt)
@Python_Community_ru
Проект удобен для быстрого прототипирования: можно начать в Colab-ноутбуке, а затем масштабировать до production-решения. Поддерживает инкрементальную загрузку и интеграцию с Airflow.
🤖 GitHub (https://github.com/dlt-hub/dlt)
@Python_Community_ru
🖥 Полезный трюк в Python: как отсортировать список по нескольким условиям одновременно
Если вы хотите отсортировать список объектов (например, словарей или кортежей) сразу по нескольким критериям — например, сначала по возрасту по возрастанию, а затем по имени по убыванию — не нужно писать громоздкие функции.
Используйте sorted() с key, комбинируя несколько полей с нужной логикой сортировки. Вот как:
people = [
{"name": "Alice", "age": 30},
{"name": "Bob", "age": 25},
{"name": "Charlie", "age": 25},
{"name": "David", "age": 30},
]
# Сортировка: сначала по age (по возрастанию), затем по name (по убыванию)
sorted_people = sorted(people, key=lambda p: (p["age"], -ord(p["name"][0])))
for person in sorted_people:
print(person)
💡 Работает и с объектами, и с кортежами — главное, правильно составить key. Особенно полезно для фильтрации списков в табличных данных, при выводе результатов или генерации отчётов.
#python
Больше коротких уроков тут (https://www.youtube.com/shorts/VFmdZOYI8qc)
@Python_Community_ru
Если вы хотите отсортировать список объектов (например, словарей или кортежей) сразу по нескольким критериям — например, сначала по возрасту по возрастанию, а затем по имени по убыванию — не нужно писать громоздкие функции.
Используйте sorted() с key, комбинируя несколько полей с нужной логикой сортировки. Вот как:
people = [
{"name": "Alice", "age": 30},
{"name": "Bob", "age": 25},
{"name": "Charlie", "age": 25},
{"name": "David", "age": 30},
]
# Сортировка: сначала по age (по возрастанию), затем по name (по убыванию)
sorted_people = sorted(people, key=lambda p: (p["age"], -ord(p["name"][0])))
for person in sorted_people:
print(person)
💡 Работает и с объектами, и с кортежами — главное, правильно составить key. Особенно полезно для фильтрации списков в табличных данных, при выводе результатов или генерации отчётов.
#python
Больше коротких уроков тут (https://www.youtube.com/shorts/VFmdZOYI8qc)
@Python_Community_ru
YouTube
🔥 Полезный трюк в Python: как отсортировать список по нескольким условиям одновременно #python #ии
Если вы хотите отсортировать список объектов (например, словарей или кортежей) сразу по нескольким критериям — например, сначала по возрасту по возрастанию, ...
🐧 Pynguin — генератор юнит-тестов для Python. Инструмент автоматически создаёт тесты для Python-кода, экономя время разработчиков и при этом адаптирован под особенности динамической типизации Python.
Проект пока находится в стадии исследования и требует осторожности — он исполняет тестируемый код, поэтому разработчики рекомендуют использовать изолированные среды вроде Docker. Установка через pip install pynguin, минимальная конфигурация требует указания пути к проекту и модулям для тестирования.
🤖 GitHub (https://github.com/se2p/pynguin)
@Python_Community_ru
Проект пока находится в стадии исследования и требует осторожности — он исполняет тестируемый код, поэтому разработчики рекомендуют использовать изолированные среды вроде Docker. Установка через pip install pynguin, минимальная конфигурация требует указания пути к проекту и модулям для тестирования.
🤖 GitHub (https://github.com/se2p/pynguin)
@Python_Community_ru
👍2
🐍 Python Gotcha: как правильно логировать необработанные исключения
В этой статье разбирают частую проблему: программа падает, но в логах — тишина. Необработанные исключения отправляются в stderr, а не в лог-файл, и вы не знаете, что пошло не так.
Пример кода:
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
handler = logging.FileHandler("app.log")
handler.setFormatter(logging.Formatter("%(asctime)s %(name)s %(levelname)s %(message)s"))
logger.addHandler(handler)
def divide(a, b):
return a / b
def main():
logger.info("Start")
logger.info(divide(10, 0)) # ZeroDivisionError
logger.info("End")
if __name__ == "__main__":
main()
📄 В логе:
2025-07-24 12:00:00 __main__ INFO Start
❗ А сам ZeroDivisionError — только в консоли. Лог молчит.
✅ Решение — использовать sys.excepthook:
import sys
def handle_uncaught_exception(exc_type, exc_value, exc_traceback):
logger.critical("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback))
sys.excepthook = handle_uncaught_exception
Теперь, если произойдёт ошибка, она попадёт в лог:
CRITICAL Uncaught exception
Traceback (most recent call last):
...
ZeroDivisionError: division by zero
📌 Вывод:
— Необработанные исключения не попадут в лог, если явно не подключить sys.excepthook
— Это особенно важно в продакшене: лог покажет, где и почему всё сломалось
— Просто и надёжно
🔗 Подробнее (https://andrewwegner.com/python-gotcha-logging-uncaught-exception.html)
@Python_Community_ru
В этой статье разбирают частую проблему: программа падает, но в логах — тишина. Необработанные исключения отправляются в stderr, а не в лог-файл, и вы не знаете, что пошло не так.
Пример кода:
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
handler = logging.FileHandler("app.log")
handler.setFormatter(logging.Formatter("%(asctime)s %(name)s %(levelname)s %(message)s"))
logger.addHandler(handler)
def divide(a, b):
return a / b
def main():
logger.info("Start")
logger.info(divide(10, 0)) # ZeroDivisionError
logger.info("End")
if __name__ == "__main__":
main()
📄 В логе:
2025-07-24 12:00:00 __main__ INFO Start
❗ А сам ZeroDivisionError — только в консоли. Лог молчит.
✅ Решение — использовать sys.excepthook:
import sys
def handle_uncaught_exception(exc_type, exc_value, exc_traceback):
logger.critical("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback))
sys.excepthook = handle_uncaught_exception
Теперь, если произойдёт ошибка, она попадёт в лог:
CRITICAL Uncaught exception
Traceback (most recent call last):
...
ZeroDivisionError: division by zero
📌 Вывод:
— Необработанные исключения не попадут в лог, если явно не подключить sys.excepthook
— Это особенно важно в продакшене: лог покажет, где и почему всё сломалось
— Просто и надёжно
🔗 Подробнее (https://andrewwegner.com/python-gotcha-logging-uncaught-exception.html)
@Python_Community_ru
Ponderings of an Andy
Python Gotcha: Logging an uncaught exception
Uncaught exceptions will crash an application. If you don't know how to log these, it can be difficult to troubleshoot such a crash. Let's walk through this gotcha and see how to fix it.