shield защищает задачу, но не ожидание
Корутина ждёт важную операцию, и в этот момент её отменяют. Если ожидание обёрнуто в
Получаются два разных состояния: ожидающий уже отменён, операция ещё идёт. Создайте задачу явно и сохраните сильную ссылку на неё. Цикл событий держит только слабые ссылки, поэтому один вызов shield не заменяет управление сроком жизни фоновой работы.
shield также не защищает от прямого
Корутина ждёт важную операцию, и в этот момент её отменяют. Если ожидание обёрнуто в
asyncio.shield(task), внутренняя задача не отменяется вместе с вызывающей корутиной. Но у вызывающей корутины await всё равно поднимет CancelledError.Получаются два разных состояния: ожидающий уже отменён, операция ещё идёт. Создайте задачу явно и сохраните сильную ссылку на неё. Цикл событий держит только слабые ссылки, поэтому один вызов shield не заменяет управление сроком жизни фоновой работы.
shield также не защищает от прямого
task.cancel() и отмены из самой операции. В документации разобраны эти случаи. Заранее решите, кто дождётся задачи и обработает её результат. Отмена ожидания не означает, что операция завершилась.Пять секунд HTTPX не равны дедлайну запроса
Сервер может долго отдавать ответ маленькими порциями, а HTTPX не прервёт чтение по истечении пяти секунд с начала запроса. Тайм-аут чтения ограничивает ожидание очередного фрагмента данных. Если фрагменты приходят вовремя, суммарное чтение может длиться дольше.
У HTTPX четыре отдельных ограничения:
По умолчанию библиотека ограничивает сетевую неактивность пятью секундами. Настройки тайм-аутов позволяют задать эти ограничения отдельно. Если нужно ограничить общую длительность операции, задайте отдельный общий дедлайн с корректной отменой и освобождением ресурсов.
Сервер может долго отдавать ответ маленькими порциями, а HTTPX не прервёт чтение по истечении пяти секунд с начала запроса. Тайм-аут чтения ограничивает ожидание очередного фрагмента данных. Если фрагменты приходят вовремя, суммарное чтение может длиться дольше.
У HTTPX четыре отдельных ограничения:
connect, read, write и pool. Последнее относится к ожиданию свободного соединения в пуле. Поэтому PoolTimeout не исправляется одним увеличением времени подключения к серверу.По умолчанию библиотека ограничивает сетевую неактивность пятью секундами. Настройки тайм-аутов позволяют задать эти ограничения отдельно. Если нужно ограничить общую длительность операции, задайте отдельный общий дедлайн с корректной отменой и освобождением ресурсов.
❤1
После commit атрибут может потребовать SQL
В асинхронном коде обычное чтение ORM-атрибута не должно неожиданно ходить в базу. Но после
В руководстве SQLAlchemy 2.0 для AsyncSession используют
Это не предзагрузка всех связей и не гарантия свежести после чужой транзакции. Ленивую связь по-прежнему надо загрузить явным способом. Для параллельных задач всё равно нужны отдельные AsyncSession.
В асинхронном коде обычное чтение ORM-атрибута не должно неожиданно ходить в базу. Но после
commit() SQLAlchemy по умолчанию помечает состояние объектов как устаревшее. Следующее обращение к полю может попытаться загрузить его заново, уже вне явного await.В руководстве SQLAlchemy 2.0 для AsyncSession используют
expire_on_commit=False. Уже загруженные значения остаются доступны после commit. Нужное поле или связь можно обновить явно через await session.refresh(obj, ["field"]).Это не предзагрузка всех связей и не гарантия свежести после чужой транзакции. Ленивую связь по-прежнему надо загрузить явным способом. Для параллельных задач всё равно нужны отдельные AsyncSession.
Исправление Ruff может сменить исключение
Замена
Первое выражение поднимет
По умолчанию Ruff применяет safe fixes. Флаг
Замена
list(values)[0] на next(iter(values)) позволяет получить первый элемент без построения всего списка. Но на пустом входе меняется тип исключения.Первое выражение поднимет
IndexError, второе StopIteration. Обработчик except IndexError больше не сработает. Именно поэтому исправление правила RUF015 в документации Ruff относится к unsafe, хотя на непустом входе оно решает ту же задачу.По умолчанию Ruff применяет safe fixes. Флаг
ruff check --fix --unsafe-fixes разрешает и изменения, способные повлиять на поведение или удалить комментарии. Перед таким запуском стоит проверить пустые входы и обработчики ошибок. Ускорение из примера в документации нельзя переносить на любой код.👍2
asdict копирует глубже, чем кажется
Вызов
Если поле содержит большой список, рассчитывать на сохранение ссылки на него нельзя. Для поверхностного представления документация предлагает собрать словарь через
Для чтения полей без копирования вложенных объектов подходит поверхностный словарь. Если нужно менять вложенный список независимо от dataclass, он не подойдёт: вложенные данные остаются общими. Ни один из этих способов сам по себе не превращает произвольное содержимое полей в готовый JSON.
Вызов
asdict(obj) не ограничивается созданием словаря полей dataclass. Функция рекурсивно обходит вложенные dataclass, словари, списки и кортежи, а прочие объекты копирует через copy.deepcopy.Если поле содержит большой список, рассчитывать на сохранение ссылки на него нельзя. Для поверхностного представления документация предлагает собрать словарь через
fields() и getattr(). На карточке видно различие: у такой сборки вложенный список остаётся общим с исходным объектом.Для чтения полей без копирования вложенных объектов подходит поверхностный словарь. Если нужно менять вложенный список независимо от dataclass, он не подойдёт: вложенные данные остаются общими. Ни один из этих способов сам по себе не превращает произвольное содержимое полей в готовый JSON.
fileno может отправить маленький файл на диск
Даже если в
У этого временного файла несколько причин перехода к дисковому файлу. При положительном лимите это превышение
Лимит здесь полезен для буферизации, но условие «размер меньше порога» не доказывает отсутствия файлового I/O. При этом слово «диск» описывает файловое поведение API и не указывает на конкретный физический носитель или задержку операционной системы.
Даже если в
SpooledTemporaryFile(max_size=1_000_000) записана всего сотня байт, запрос файлового дескриптора перенесёт содержимое из памяти в дисковый файл.У этого временного файла несколько причин перехода к дисковому файлу. При положительном лимите это превышение
max_size. Вызов fileno() делает то же самое независимо от размера, а rollover() явно запускает этот переход. Поэтому библиотека, которой передали file-like объект, может изменить способ хранения одним запросом дескриптора.Лимит здесь полезен для буферизации, но условие «размер меньше порога» не доказывает отсутствия файлового I/O. При этом слово «диск» описывает файловое поведение API и не указывает на конкретный физический носитель или задержку операционной системы.