Zen of Python
18.8K subscribers
1.47K photos
202 videos
38 files
3.66K links
Полный Дзен Пайтона в одном канале

Разместить рекламу: @tproger_sales_bot

Правила общения: https://tprg.ru/rules

Другие каналы: @tproger_channels

Сайт: https://tprg.ru/site

Регистрация в перечне РКН: https://tprg.ru/xZOL
Download Telegram
Почему тайм-аут не всегда заканчивается ровно по часам

asyncio.TaskGroup владеет созданными в нём задачами и ждёт их завершения. Если одна падает с обычным исключением, группа запрашивает отмену остальных, дожидается очистки и собирает ошибки в ExceptionGroup.

В разборе отмены и тайм-аутов важна граница: cancel() отправляет запрос, а задача должна получить CancelledError и выполнить finally. Если она подавляет отмену или долго очищает ресурсы, завершение затянется.

wait_for() ждёт окончания отмены, поэтому фактическое время может превысить заданный timeout. Контекст asyncio.timeout() отменяет текущую задачу и преобразует свою отмену в TimeoutError снаружи блока. Для связанных операций помещайте TaskGroup внутрь timeout, чтобы дочерняя работа имела явного владельца.

#python #asyncio
Что мешает запускать Python из снимка памяти

Импорты и инициализация повторяются при каждом старте интерпретатора. Снимок уже подготовленного процесса мог бы пропускать эту работу — особенно в WebAssembly и сервисах, которые часто запускают короткие задачи.

В обсуждении снимков CPython есть неожиданный барьер: случайное начальное значение для хеширования. Python выбирает его при запуске, чтобы затруднить атаки через коллизии. Снимок зафиксирует значение, и восстановленные процессы начнут использовать одно и то же.

Худ Чатем предлагает отдельную фазу инициализации с повторной настройкой состояния после восстановления. Потребуется учитывать и библиотеки, рассчитывающие на новый запуск. Это пока обсуждаемая архитектура; просто сохранить память процесса недостаточно для безопасного ускорения старта.

#python #cpython
Как PEP 827 предлагает выводить тип выбранных полей

ORM-запрос выбирает name и email, но статический анализатор часто видит заранее заданную модель. Для отдельных операций приходится вручную поддерживать похожие типы: публичный ответ, создание записи и частичное обновление.

PEP 827 предлагает преобразования типов: условные выражения, перебор типов и доступ к членам модели. Библиотека могла бы вывести тип результата из выбранных полей или построить Update-модель с необязательными свойствами.

На Language Summit обсуждали и хранение аннотаций: Pydantic и FastAPI используют их во время выполнения, поэтому одного результата статической проверки недостаточно. Сохранение исходных выражений требует компромисса между памятью, скоростью и сложностью. Это предложение с прототипом, а не доступный синтаксис стабильного Python.

#python #типизация
👍1
Python 3.10 больше не получает исправления безопасности

Версия 3.10.22 стала последней в своей ветке. После пяти лет поддержки Python 3.10 достиг end of life: новых security updates для него не будет.

В объявлении команды Python есть и другие изменения: 3.13.16 — последний полный maintenance-релиз ветки 3.13. Дальше она будет получать только исправления безопасности.

Перед миграцией проверьте интерпретатор в контейнерах, CI, виртуальных окружениях и заданиях по расписанию. Затем убедитесь, что для выбранной версии доступны совместимые зависимости и wheels, и прогоните тесты приложения.

Обновление пакетов внутри окружения само по себе не обновляет Python. Версию runtime нужно менять отдельно.

#python #обновления
Почему lru_cache удерживает объекты в памяти

Декоратор lru_cache хранит аргументы и результат функции. Если поставить его на метод экземпляра, self станет частью ключа — кеш может удерживать объект после того, как остальной код перестал им пользоваться.

Есть и другие ловушки мемоизации. Каждый cache hit возвращает сохранённый объект: изменение закешированного списка увидят следующие вызовы. А maxsize ограничивает число записей, но не задаёт срок их жизни.

Для результатов выбирайте неизменяемые структуры, для внешних данных продумайте инвалидирование. Через cache_info() проверьте, сколько попаданий кеш вообще даёт.

Потокобезопасность кеша тоже не гарантирует один запуск функции: при одновременных промахах вычисление может повториться.

#python #производительность
👍2
Как менеджер паролей найдёт вашу форму Django

Менеджер паролей обнаружил утечку и предлагает заменить пароль. Чтобы открыть нужную страницу, он может обратиться к /.well-known/change-password.

В Django достаточно добавить RedirectView с pattern_name, указывающим на публичную форму смены пароля. URL стандарта не содержит завершающего слеша, а редирект должен быть временным.

Проверьте имя целевого маршрута тестом: Django разрешает его при запросе, поэтому опечатка может остаться незаметной до первого обращения.

Ещё одна проверка — неизвестный URL должен возвращать 404. Если catch-all выдаёт 200 на любой путь, менеджер паролей может проигнорировать этот механизм.

#python #django
Когда Path.info экономит обращения к файловой системе

При обходе большого каталога проверки is_file() и is_dir() могут создавать множество запросов метаданных. В Python 3.14 появился кеш типов файлов Path.info.

У объектов, полученных через Path.iterdir(), он может уже содержать информацию из сканирования каталога. Несколько проверок типа используют эти данные повторно.

Цена — актуальность. Если другой процесс заменил файл после обхода, path.info может описывать прежнее состояние. Для свежей проверки используйте прямые методы Path.is_file() и Path.is_dir().

Кеш нельзя очистить на месте; новый Path(path) начинает с пустым кешем. Но даже свежая проверка не устраняет гонку между проверкой и открытием файла — ошибки операции всё равно нужно обрабатывать.

#python #файлы
Почему mmap не всегда избавляет от копирования

mmap позволяет обращаться к содержимому файла по смещениям. Это удобно для бинарных форматов и повторного поиска, но срез mapped[start:end] всё равно создаёт новый объект bytes.

Чтобы работать с данными через заимствованное представление, используйте memoryview. Тогда нужно следить за временем жизни: пока представления существуют, закрытие mapping может вызвать BufferError. Освободите их до выхода из контекстного менеджера.

Отображение не означает, что весь файл сразу загружен в RAM, и не делает его неизменяемым снимком. Другой процесс всё ещё может менять исходный файл.

Для одного последовательного прохода обычное буферизованное чтение часто проще. Выбирайте mmap под характер доступа и измеряйте результат.

#python #память
Почему Rust внутри CPython предлагают начать с zlib

На Python Language Summit обсудили постепенное внедрение Rust в CPython. Первый кандидат — модуль zlib: небольшой участок с заметным применением, в том числе при работе с пакетами.

Предлагается использовать zlib-rs, а существующую реализацию сохранить как fallback. В обсуждаемом плане Rust-бэкенд мог бы стать опциональным в Python 3.16. Это предложение, а не уже принятая возможность релиза.

Перед расширением проекта нужно проверить поддержку платформ, влияние на производительность, сборку и сопровождение зависимостей.

Rust помогает исключить некоторые ошибки работы с памятью, но корректность алгоритмов по-прежнему требует тестирования. Поэтому в плане есть fuzzing и property-based tests.

#python #cpython
❤1
Почему тест проходит отдельно и падает в наборе

Один тест добавляет объект в общий список, другой рассчитывает, что список пуст. При привычном порядке запуска ошибка может долго оставаться незаметной.

В разборе плагинов pytest есть инструмент для таких случаев: pytest-randomly перемешивает порядок тестов и печатает seed. С ним можно повторить проблемный запуск:

pytest --randomly-seed=12345


Плагин также управляет состоянием генераторов случайных чисел, поэтому сбой стоит проверить и на зависимость от случайных данных.

Ищите изменяемые атрибуты класса, глобальные кеши и фикстуры, которые оставляют состояние после себя. Исправление — изолировать данные и добавить очистку. Один успешный запуск в случайном порядке ещё не доказывает независимость тестов.

#python #тестирование
Как запустить тесты самого Django через uv

Хотите исправить баг в Django? Сначала понадобится окружение для тестов фреймворка. Установки Django из PyPI для этого недостаточно: нужны исходники и тестовые зависимости.

Адам Джонсон показывает настройку через uv. После клонирования репозитория выполните из его корня:

uv venv
uv pip install -e . -r tests/requirements/py3.txt
uv run python tests/runtests.py


Editable-установка позволяет тестам использовать ваш изменённый код. Для запуска можно указать конкретные тестовые модули вместо всего набора.

Одна из возможных проблем — сборка pylibmc, которой нужны компилятор и заголовки libmemcached. Без этой зависимости остальные тесты можно запускать, но проверки, которым она нужна, будут пропущены. Учитывайте это, если правка затрагивает кеширование.

#python #django
Почему subprocess с PIPE может зависнуть

Дочерний процесс пишет в stdout, родитель вызывает wait(). На небольшом выводе всё работает. На большом буфер канала заполняется: ребёнок ждёт чтения, родитель — завершения ребёнка.

В разборе взаимной блокировки показано, почему каналы нужно читать во время работы процесса. Для ограниченного вывода удобен run():

import subprocess

result = subprocess.run(
["python", "--version"],
capture_output=True,
text=True,
timeout=10,
check=True,
)


Он использует communicate(), который обслуживает оба потока. Вывод при этом собирается в памяти, поэтому для больших логов выбирайте файл или потоковое чтение.

У Popen.communicate(timeout=...) другая тонкость: истечение тайм-аута само не убивает процесс. Нужны завершение процесса и повторный communicate() для чтения остатка вывода.

#python #процессы
⚡1