Python Quiz
Что выведет этот код? ⚙️🐍
Чему учит: поведение __eq__ и __hash__ при добавлении объектов в set.
Зачем важно: нужно для корректной дедупликации, проектирования типов и на собеседованиях про хэш‑контракт.
Подсказка:set сначала смотрит __hash__, затем вызывает __eq__ для объектов с одинаковым хэшем.
Зачем важно: нужно для корректной дедупликации, проектирования типов и на собеседованиях про хэш‑контракт.
Подсказка:
Что выведет этот код? 🧠🤔
Anonymous Quiz
0%
[(1, 2), (3, 4)]
50%
[(0, 1), (2, 3), (4, 5)]
50%
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]
0%
[(1, 2), (2, 3), (3, 4)]
Python Quiz
Что выведет этот код? 🧠🤔
Учит: поведение генераторов, состояние итератора после next() и как zip обрабатывает один и тот же итератор.
Зачем важно: помогает не допускать едва заметных багов в проде и блеснуть на собеседовании знанием итераторов.
next(gen) сдвигает итератор на один; zip(gen, gen) берёт элемент…
Зачем важно: помогает не допускать едва заметных багов в проде и блеснуть на собеседовании знанием итераторов.
Python Quiz
Что выведет этот код? 💡🧠
Чему учит: f‑строки с динамической шириной — как подставлять спецификатор формата через { } и контролировать заполнение.
Зачем важно: полезно для аккуратного вывода чисел в логах/отчётах и на собеседованиях, где проверяют синтаксис. 😺
'0' перед шириной — символ заполнителя нулями; {w…
Зачем важно: полезно для аккуратного вывода чисел в логах/отчётах и на собеседованиях, где проверяют синтаксис. 😺
JIT в Python могут выпилить — и не потому что он глючит
Полгода никто не чесался — JIT спокойно жил в main с 3.13, разгонял x86-64/Linux в среднем на 8-9% и должен был стать одной из витрин 3.15 (тот уже во freeze, релиз в октябре). А 5 июня Steering Council внезапно нажал стоп.
Причина не техническая. Всё это держится на PEP 744, а он informational, не standards-track — то есть юридически никого ни к чему не обязывает: ни к поддержке, ни к метрикам успеха, ни к обещаниям совместимости. Совет сам признал: "мы не были достаточно строги к процессу для изменения такого масштаба".
Дали полгода: либо нормальный normative PEP с критериями принятия, либо код едет из main. Баги и security чинят как обычно, новые фичи — заморожены. Томас Воутерс из совета: "we're not unreasonable, but we do want this to be taken seriously" — не расстрельный дедлайн, но и не формальность для галочки.
Мне это скорее нравится, хоть и с опозданием. Три релиза фича жила без единого письменного обязательства на случай, если что-то пойдёт не так. Спохватились бы раньше — меньше народу сейчас зависело бы от undocumented behavior.
Полгода никто не чесался — JIT спокойно жил в main с 3.13, разгонял x86-64/Linux в среднем на 8-9% и должен был стать одной из витрин 3.15 (тот уже во freeze, релиз в октябре). А 5 июня Steering Council внезапно нажал стоп.
Причина не техническая. Всё это держится на PEP 744, а он informational, не standards-track — то есть юридически никого ни к чему не обязывает: ни к поддержке, ни к метрикам успеха, ни к обещаниям совместимости. Совет сам признал: "мы не были достаточно строги к процессу для изменения такого масштаба".
Дали полгода: либо нормальный normative PEP с критериями принятия, либо код едет из main. Баги и security чинят как обычно, новые фичи — заморожены. Томас Воутерс из совета: "we're not unreasonable, but we do want this to be taken seriously" — не расстрельный дедлайн, но и не формальность для галочки.
Мне это скорее нравится, хоть и с опозданием. Три релиза фича жила без единого письменного обязательства на случай, если что-то пойдёт не так. Спохватились бы раньше — меньше народу сейчас зависело бы от undocumented behavior.
👍1
В comprehension наконец завезли распаковку через *
Все путаются в
PEP 798 чинит это через
Забавная деталь из самого PEP: авторов натолкнул письменный экзамен по Python — студенты в ответах уже писали такую звёздочку, будучи уверены, что она существует. Не существовала — до сих пор.
Фича заморожена в 3.15.0b1, релиз в октябре. Двойной for никуда не денется, но хотя бы будет чем его заменить, когда не хочешь гадать про порядок циклов.
Все путаются в
[x for row in matrix for x in row] — кажется, что for идут как во вложенном цикле, а на деле наоборот, второй for относится к первому. Классический баг на собеседованиях.PEP 798 чинит это через
*: [*row for row in matrix] — то же самое, но без риска перепутать порядок. Работает и для {*row for row in matrix}, и для словарей через **.Забавная деталь из самого PEP: авторов натолкнул письменный экзамен по Python — студенты в ответах уже писали такую звёздочку, будучи уверены, что она существует. Не существовала — до сих пор.
Фича заморожена в 3.15.0b1, релиз в октябре. Двойной for никуда не денется, но хотя бы будет чем его заменить, когда не хочешь гадать про порядок циклов.
Ваш free-threaded питон может тихо врать про GIL
Поставил 3.14.0t (free-threaded сборку), жду параллелизма — а профиль как будто однопоточный.
Оказалось, это норм поведение, просто нигде не орёт об этом. Любое C-расширение, не помеченное как поддерживающее free-threading, при импорте тихо включает GIL обратно. Без варнинга, без ошибки — просто твой параллелизм молча схлопывается в обычный GIL-режим.
Проверяется одной строкой после всех импортов:
Есть ещё жёсткий вариант —
Перед тем как постить в чат бенчмарки с линейным ускорением на 3.14t — сначала эта проверка, потом бенчмарк.
Поставил 3.14.0t (free-threaded сборку), жду параллелизма — а профиль как будто однопоточный.
Оказалось, это норм поведение, просто нигде не орёт об этом. Любое C-расширение, не помеченное как поддерживающее free-threading, при импорте тихо включает GIL обратно. Без варнинга, без ошибки — просто твой параллелизм молча схлопывается в обычный GIL-режим.
Проверяется одной строкой после всех импортов:
python3.14 -c "import sys; print(sys._is_gil_enabled())"True после импорта нужных тебе либ — значит что-то из зависимостей откатило GIL обратно, и весь выигрыш от free-threaded сборки ты потерял, просто об этом не узнал.Есть ещё жёсткий вариант —
PYTHON_GIL=0 или -X gil=0: тогда откат запрещён, и несовместимое расширение уронит процесс вместо тихого отката. Для CI это честнее, чем молчаливая деградация.Перед тем как постить в чат бенчмарки с линейным ускорением на 3.14t — сначала эта проверка, потом бенчмарк.
Что выведет этот код с lazy import в Python 3.15?
В 3.15 beta 4 подвезли
Взял классику:
Прогони в голове, без REPL. Порядок печати — в опросе.
В 3.15 beta 4 подвезли
lazy import (PEP 810) — модуль реально не грузится, пока не тронешь его атрибут. Звучит как мелочь, а ломает интуицию с ходу.Взял классику:
import this печатает дзен прямо при загрузке модуля, это все знают. Обернул в lazy import и написал zen = this. Был уверен, что присваивание — это уже «использование». Не угадал.Прогони в голове, без REPL. Порядок печати — в опросе.
Python Quiz
Что выведет этот код с lazy import в Python 3.15? В 3.15 beta 4 подвезли lazy import (PEP 810) — модуль реально не грузится, пока не тронешь его атрибут. Звучит как мелочь, а ломает интуицию с ходу. Взял классику: import this печатает дзен прямо при загрузке…
В каком порядке напечатается вывод?
Final Results
100%
start → bound → zen → end
0%
zen → start → bound → end
0%
start → zen → bound → end
0%
start → bound → end, zen не печатается
В Django чужая сессия могла прилететь вам в общем кэше
Django тихо чинит баг, из-за которого чужая сессионная кука могла закэшироваться и уйти следующему юзеру.
Дальше классика cache poisoning: если перед Django стоит Redis, Memcached или CDN — следующий, кто попал в тот же ключ кэша, мог получить в ответе чужую сессию.
Бесит не сама дыра, а то, как она написана — условие «если у запроса нет X» почти всегда придумано под один тестовый сценарий, а не под реальный трафик, где у половины пользователей уже висит кука с языком интерфейса или темой.
Пофиксили в 6.0.7 и 5.2.16 (заодно и в 6.1-бете), CVE-2026-48588, severity формально low — нужны shared cache и
Если у вас
Django тихо чинит баг, из-за которого чужая сессионная кука могла закэшироваться и уйти следующему юзеру.
UpdateCacheMiddleware и cache_page() пропускали ответ с Set-Cookie в кэш, если проверяли только один случай: запрос пришёл вообще без кук. Логика была «нет кук на входе — значит и Set-Cookie можно смело кэшировать». Стоило браузеру принести любую левую куку — тему, язык, A/B-флаг — и проверка молча выключалась. Сессионный Set-Cookie улетал в общий кэш как ни в чём не бывало.Дальше классика cache poisoning: если перед Django стоит Redis, Memcached или CDN — следующий, кто попал в тот же ключ кэша, мог получить в ответе чужую сессию.
Бесит не сама дыра, а то, как она написана — условие «если у запроса нет X» почти всегда придумано под один тестовый сценарий, а не под реальный трафик, где у половины пользователей уже висит кука с языком интерфейса или темой.
Пофиксили в 6.0.7 и 5.2.16 (заодно и в 6.1-бете), CVE-2026-48588, severity формально low — нужны shared cache и
Vary: Cookie вместе. Но именно так у многих и настроено.Если у вас
cache_page или UpdateCacheMiddleware рядом с сессиями — это тот случай, когда апдейт не терпит «на следующем спринте».Python откатил архитектуру сборщика мусора прямо в патч-релизе
Python 3.14.0 в октябре сменил сборщик мусора — с generational на incremental, обещали паузы на больших кучах короче на порядок. В 3.14.5 в мае это просто откатили назад. Патч-релиз отменяет архитектурное решение целого мажора — в CPython такое почти не бывает.
Что пошло не так: на долгоживущих процессах (веб-серверы, пайплайны) резидентная память росла без остановки. По разбору pydevtools, счётчик инкрементальной работы мог уходить в минус — и полный проход сборщика попросту не запускался: в одном из кейсов первый full collection случился только на 20 000-й итерации, а в устойчивом состоянии в памяти висело больше 90 тысяч непрочищенных циклов.
Core team не стала ждать плановый 3.14.6 — ускорили релиз и откатили incremental GC сразу в 3.14 и 3.15, вернув generational из 3.13.
Смущает не сам баг — memory-менеджмент штука тонкая. Смущает то, что фичу пять месяцев гоняли через альфы, беты и rc — и всё равно поймали только когда реальный прод начал жаловаться на RSS. Если вы успели обновиться до 3.14.0–3.14.4 и мониторили память — вы только что бесплатно побетатестили GC для всего коммьюнити.
🔗 discuss.python.org, pydevtools.com
Python 3.14.0 в октябре сменил сборщик мусора — с generational на incremental, обещали паузы на больших кучах короче на порядок. В 3.14.5 в мае это просто откатили назад. Патч-релиз отменяет архитектурное решение целого мажора — в CPython такое почти не бывает.
Что пошло не так: на долгоживущих процессах (веб-серверы, пайплайны) резидентная память росла без остановки. По разбору pydevtools, счётчик инкрементальной работы мог уходить в минус — и полный проход сборщика попросту не запускался: в одном из кейсов первый full collection случился только на 20 000-й итерации, а в устойчивом состоянии в памяти висело больше 90 тысяч непрочищенных циклов.
Core team не стала ждать плановый 3.14.6 — ускорили релиз и откатили incremental GC сразу в 3.14 и 3.15, вернув generational из 3.13.
Смущает не сам баг — memory-менеджмент штука тонкая. Смущает то, что фичу пять месяцев гоняли через альфы, беты и rc — и всё равно поймали только когда реальный прод начал жаловаться на RSS. Если вы успели обновиться до 3.14.0–3.14.4 и мониторили память — вы только что бесплатно побетатестили GC для всего коммьюнити.
🔗 discuss.python.org, pydevtools.com
Discussions on Python.org
Reverting the incremental GC in Python 3.14 and 3.15
Python 3.14 shipped with a new incremental garbage collector. However, we’ve had a number of reports of significant memory pressure in production environments. We’ve decided to revert it in both 3.14 and 3.15, and go back to the generational GC from 3.13.…
Гитхаб был чистый. Пакет — нет
В mrmustard 0.7.4 — квантовой библиотеке Xanadu на PyPI — три дня назад нашли стилер. Тащит с диска SSH-ключи, AWS- и Kubernetes-креды и ставит три способа зацепиться в системе на будущее.
Но бесит не сам стилер, а то, как его спрятали.
У версии 0.7.4 нет ни тега, ни релиза, ни коммита в GitHub-репозитории. Открываешь исходники на гитхабе — там всё чисто, ревью ничего не найдёт. Вредонос добавили только в артефакт, который улетает через
Отдельная деталь для тех, кто вообще думает про такие атаки: пейлоад сначала проверяет, что он не в CI и не в контейнере, и только потом стартует. Целились именно в ноутбуки разрабов и исследователей, а не в одноразовые раннеры.
После такого фраза «да я гитхаб пролистал, там нормально» перестаёт что-либо значить. Гитхаб и то, что реально прилетает по
Нашёл всё не Xanadu, а сторонний исследователь из Aikido Security. PyPI пакет после этого отправил в карантин.
🔗 stepsecurity.io, safedep.io
В mrmustard 0.7.4 — квантовой библиотеке Xanadu на PyPI — три дня назад нашли стилер. Тащит с диска SSH-ключи, AWS- и Kubernetes-креды и ставит три способа зацепиться в системе на будущее.
Но бесит не сам стилер, а то, как его спрятали.
У версии 0.7.4 нет ни тега, ни релиза, ни коммита в GitHub-репозитории. Открываешь исходники на гитхабе — там всё чисто, ревью ничего не найдёт. Вредонос добавили только в артефакт, который улетает через
pip install: угнали токен CI из аккаунта мейнтейнера (это был ziofil, автор проекта) и залили пакет в PyPI напрямую, мимо обычного пайплайна.Отдельная деталь для тех, кто вообще думает про такие атаки: пейлоад сначала проверяет, что он не в CI и не в контейнере, и только потом стартует. Целились именно в ноутбуки разрабов и исследователей, а не в одноразовые раннеры.
После такого фраза «да я гитхаб пролистал, там нормально» перестаёт что-либо значить. Гитхаб и то, что реально прилетает по
pip install, — два разных артефакта, и связывает их только честность мейнтейнера в момент публикации. Если пиньте пакеты по хэшу wheel — вот ровно тот случай, когда это спасает, а не паранойя.Нашёл всё не Xanadu, а сторонний исследователь из Aikido Security. PyPI пакет после этого отправил в карантин.
🔗 stepsecurity.io, safedep.io
www.stepsecurity.io
Compromised PyPI Package: mrmustard 0.7.4 Steals SSH, Cloud, and Kubernetes Credentials - StepSecurity
mrmustard 0.7.4 on PyPI was a credential stealer that ran on import, exfiltrating SSH keys, AWS, and Kubernetes credentials. Here is the analysis, IOCs, and how to respond.
PyPI перестал пускать в старые релизы
Запушил докрутку — забытое wheel под 3.14, например — в релиз, которому две недели? С 22 июля PyPI такое просто не примет.
Правило простое: через 14 дней после публикации релиз закрывается для новых файлов насовсем, без исключений. Тема всплывала ещё в PEP 740 в 2024-м, но реально завелась в марте — после того как через скомпрометированные токены и воркфлоу закинули мусор в LiteLLM и Telnyx. Старые доверенные релизы — идеальная мишень: их качают привычно, без опаски.
Перед тем как включать, проверили, насколько больно это ударит по обычным майнтейнерам: из топ-15k пакетов только 56 хоть раз добавляли cp314-колесо релизу старше двух недель. Почти никто так не работает — а вот атакующему такая лазейка была подарком.
Шероховатость: официального способа проверить статус релиза («закрыт/не закрыт») пока нет — узнаёшь только по 403 при аплоаде. Мелочь, но для CI, который доливает артефакт постфактум, это неприятный сюрприз.
🔗 blog.pypi.org
Запушил докрутку — забытое wheel под 3.14, например — в релиз, которому две недели? С 22 июля PyPI такое просто не примет.
Правило простое: через 14 дней после публикации релиз закрывается для новых файлов насовсем, без исключений. Тема всплывала ещё в PEP 740 в 2024-м, но реально завелась в марте — после того как через скомпрометированные токены и воркфлоу закинули мусор в LiteLLM и Telnyx. Старые доверенные релизы — идеальная мишень: их качают привычно, без опаски.
Перед тем как включать, проверили, насколько больно это ударит по обычным майнтейнерам: из топ-15k пакетов только 56 хоть раз добавляли cp314-колесо релизу старше двух недель. Почти никто так не работает — а вот атакующему такая лазейка была подарком.
Шероховатость: официального способа проверить статус релиза («закрыт/не закрыт») пока нет — узнаёшь только по 403 при аплоаде. Мелочь, но для CI, который доливает артефакт постфактум, это неприятный сюрприз.
🔗 blog.pypi.org
blog.pypi.org
Releases now reject new files after 14 days - The Python Package Index Blog
PyPI no longer allows publishing new files to releases older than 14 days.
uv наконец умеет проверять типы точечно, а не всей монорепой разом
Пока весь техтвиттер спорит про JIT и Rust в CPython, Astral тихо прокачивает uv в сторону монорепо.
23 июля вышел uv 0.11.32 — добавили
До этого патча
Флаг мелкий, но по нему хорошо видно курс: после покупки Astral в марте OpenAI явно не делает из uv просто быстрый pip — это заявка на единый вход в lint/format/typecheck. Направление мне нравится: меньше
Минус один:
🔗 github.com, github.com
Пока весь техтвиттер спорит про JIT и Rust в CPython, Astral тихо прокачивает uv в сторону монорепо.
23 июля вышел uv 0.11.32 — добавили
--package и --all-packages для uv check. Сама команда check появилась только в июне: она гоняет ty, тайп-чекер Astral, прямо из uv, без отдельного pip install ty.До этого патча
uv check в воркспейсе с десятком пакетов проверял всё разом — падает один сервис, и непонятно, на что смотреть в первую очередь. Теперь можно натравить проверку на конкретный пакет флагом --package, а --all-packages явно просит пройтись по всем.Флаг мелкий, но по нему хорошо видно курс: после покупки Astral в марте OpenAI явно не делает из uv просто быстрый pip — это заявка на единый вход в lint/format/typecheck. Направление мне нравится: меньше
pip install ruff mypy black в каждом новом проекте.Минус один:
check всё ещё за preview-флагом, синтаксис может поменяться до стабильного релиза — тащить в CI прямо сейчас я бы не спешил.🔗 github.com, github.com
GitHub
Release 0.11.32 · astral-sh/uv
Release Notes
Released on 2026-07-23.
Preview features
Add --package and --all-packages selection to uv check (#20628)
Allow uv upgrade to update multiple marker-specific declarations of the same ...
Released on 2026-07-23.
Preview features
Add --package and --all-packages selection to uv check (#20628)
Allow uv upgrade to update multiple marker-specific declarations of the same ...
object() как сентинел годами тихо ломался в pickle — и почти никто не замечал
Сколько раз ты писал
В Python 3.15 завезли builtin
Мелочь, а сколько самодельных
🔗 peps.python.org
Сколько раз ты писал
MISSING = object() для дефолта, который надо отличить от None? Я — постоянно. И не знал, что если такой сентинел уедет через pickle (сессии, кэш, multiprocessing, Celery) — на другом конце restored is MISSING вернёт False. Тихий баг, всплывающий только в проде, когда объект пересекает границу процесса.В Python 3.15 завезли builtin
sentinel() (PEP 661). Он запоминает модуль и имя, поэтому pickle и copy.deepcopy восстанавливают тот же объект, а не подделку. Плюс вменяемый repr из коробки — никаких <object object at 0x7f...> в логах.Мелочь, а сколько самодельных
class _Missing: pass теперь можно выкинуть.🔗 peps.python.org
В Python 3.15 завезли нормальный сэмплирующий профайлер
Полгода таскал
Новый модуль
Частота по умолчанию 1кГц, можно гнать хоть до 1МГц. На выходе — flamegraph, pstats или html, на выбор. Профайлер и целевой процесс должны быть на одной минорной версии — из 3.14 к процессу на 3.15 не приаттачиться.
Заодно
🔗 peps.python.org, docs.python.org
Полгода таскал
py-spy в докер-образ, потому что сэмплирующего профайлера в stdlib не было — только cProfile, который считает каждый вызов и заметно тормозит прод. В 3.15 это закрыли.Новый модуль
profiling.sampling (кодовое имя Tachyon, PEP 799) цепляется к живому процессу по PID и читает стек снаружи — без инструментирования кода и без рестарта:python -m profiling.sampling attach 12345Частота по умолчанию 1кГц, можно гнать хоть до 1МГц. На выходе — flamegraph, pstats или html, на выбор. Профайлер и целевой процесс должны быть на одной минорной версии — из 3.14 к процессу на 3.15 не приаттачиться.
Заодно
cProfile переехал в profiling.tracing (алиас остался жить), а древний модуль profile объявили deprecated — выпилят в 3.17.py-spy из докерфайла выкидываю на следующем спринте: не вижу смысла тащить внешний бинарник, когда то же самое теперь есть из коробки.🔗 peps.python.org, docs.python.org
Python Enhancement Proposals (PEPs)
PEP 799 – A dedicated profiling package for organizing Python profiling tools | peps.python.org
This PEP proposes the creation of a new standard library module named profiling to organize Python’s built-in profiling tools under a single, coherent namespace.