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

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

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

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

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

Регистрация в перечне РКН: https://tprg.ru/xZOL
Download Telegram
vLLM-инференс в облаке без собственной инфраструктуры

Selectel запустил Foundation Models Catalog — управляемый сервис, где вы выбираете модель из каталога, а провайдер берёт на себя GPU, масштабирование и наблюдаемость. Движок под капотом: vLLM, питоновская библиотека поверх PyTorch, которую сейчас называют производственным стандартом для инференса.

Что это даёт на практике: вместо того чтобы поднимать свой vLLM-сервер, арендовать GPU и настраивать мониторинг, вы обращаетесь к уже готовому OpenAI-совместимому эндпоинту через openai-клиент или requests. Код тот же, что при локальном запуске. Автомасштабирование, логи и метрики включены.

В каталоге сейчас IBM Granite, Qwen, DeepSeek, Phi, Mistral; скоро Gemma и Whisper. Оплата пока за вычислительные ресурсы, следующим шагом станет тарификация по токенам.

Подробнее на Tproger.

@zen_of_python (теперь в VK и Max)
2🔥1
В Long Beach на PyCon US 2026 14 мая прошёл Typing Summit: четыре часа, восемь докладов, один трек. Несколько моментов с саммита.

Гвидо ван Россум открыл саммит докладом про то, что правило PEP 484 «никакого нового синтаксиса для типов» де-факто давно нарушено: TypedDict, ParamSpec, оператор type и прочее. Его аргумент: пора признать это формально и выбирать приоритеты по реальной боли пользователей. Опирался на Python Typing Survey 2025.

Дуглас Креагер из Astral (его слайд гласил «an OpenAI joint») показал девятистрочный пример, на котором все продакшен-чекеры (mypy, pyright, pyre, Pyrefly, ty) дают неверный ответ:

def choose[A](a1: A, a2: A) -> A:
return random.choice([a1, a2])

def partial[X, Y, Z](fn: Callable[[X, Y], Z], x: X) -> Callable[[Y], Z]: ...

p = partial(choose, None)
p(2) # все говорят: ошибка, 2 не None
p("hello") # то же самое


Прямой вызов choose(None, 2) работает корректно: A решается в None | Literal[2]. После применения partial система ломается. Креагер предлагает другую стратегию: тянуть весь набор констрейнтов как отложенный обобщённый тип, без преждевременной подстановки. ty переезжает на этот подход, внутри используется тернарная структура BDD (двоичные решающие диаграммы с третьим «неопределённым» ребром).

Коннер Нильсен из команды Pyrefly доложил результаты экспериментов с ИИ-агентами. Если код хорошо типизирован и type checker подключён к агенту через обёртку с циклом «думай, действуй, наблюдай»:

— Доля успешных решений на внутренних бенчмарках команды поднялась с 79,6 до 83,9 процента.
— На 21 процент меньше шагов до решения.
— На 14 процентов быстрее по полному времени работы.

На слабо типизированном коде из SWE-bench Verified эффекта почти нет. Type checker ловит ошибки в соседних с задачей файлах, и агент уходит чинить их вместо основной задачи. Наблюдение по моделям: Claude Sonnet 4.5 чинит каждую ошибку, GPT-5 codex игнорирует типы, пока обёртка насильно их не подсунет.

Ещё были доклады про intersection types (Jelle Zijlstra), PEP 827 от Vercel про conditional и mapped types по образцу TypeScript, tensor-shape типы в Pyrefly, и формализация подмножества Python в Lean 4. Полный обзор здесь: https://bernat.tech/posts/pycon-us-2026-typing-summit-recap/

@zen_of_python (теперь в VK и Max)
7👍4
Наткнулся тут на обсуждение, которое мне тоже периодически приходит в голову: стоит ли использовать uv, если OpenAI покупает Astral. Напомню, Astral это те ребята, которые сделали uv, ruff и ty, которые мы так любим.

Короткий вывод: для новых Python-проектов uv всё ещё выглядит самым практичным выбором.

Почему разработчики так за него держатся: uv закрывает сразу несколько бытовых задач: ставит Python-версии, создаёт venv, ставит зависимости, фиксирует lockfile, запускает команды в окружении и заменяет часть сценариев pip, pip-tools, pipx, poetry и pyenv.

Типичный flow становится проще:
uv init
uv add fastapi
uv run app.py
uv sync --frozen


Риск с OpenAI тут скорее управленческий, а не технический. Важно смотреть на лицензию, телеметрию, темп релизов и то, останется ли core-разработка открытой. На практике же зависимости живут в стандартном pyproject.toml, а Python уже движется к стандартному lockfile через PEP 751 / pylock.toml, так что миграция с uv должна быть проще, чем с более закрытых форматов.

То есть вопрос скорее не про то, использовать ли uv, а о том как использовать его без религии и без лишней привязки к одному владельцу.

А вы как думаете? Продолжаем жить на uv, всё норм?

@zen_of_python
👍43
PyPy 7.3.23 стал доступен для скачивания, а PyPy описала релиз как bugfix-обновление.

Напомню, PyPy — альтернативный интерпретатор Python с JIT. Его обычно пробуют там, где много долгоживущего CPU-bound кода на чистом Python и хочется ускорения без переписывания на C.

Что важно в 7.3.23:

🔘убрали ложные warning'и про неиспользованные корутины при доступе к cr_frame;
🔘починили множественное наследование для смешанных Python/C-extension типов, включая сценарии вокруг pybind11 и cpyext;
🔘PyPy3.11 теперь идёт со стандартной библиотекой CPython 3.11.15;
🔘дизассемблер стал ближе к CPython: интерпретатор перешёл на exception tables вместо отдельных opcodes. На скорость это пока не влияет.

Если вы уже используете PyPy, то обновление стоит поставить, API совместим с линейкой 7.3. Если только думаете попробовать — начните с прогона тестов на PyPy3.11 и проверки зависимостей.

PyPy лучше всего раскрывается на чистом Python-коде. В проектах, где основная нагрузка уходит в NumPy, pandas, torch или другие C-расширения, прирост может быть совсем другим.

@zen_of_python
Please open Telegram to view this post
VIEW IN TELEGRAM
🆒3🔥2👍1👌1
Попалось обсуждение про опенсорс. Мейнтейнер описывает ситуацию: кто-то форкает репозиторий, ссылается в коммитах на его issue — это приятно, человек заинтересовался. А открываешь ветку, и там чистый ИИ-слоп. Файлы свалены в корень вместо src, тесты с print вместо pytest (хотя в CONTRIBUTING всё расписано), импорты вида from mylib import Foo, Bar, которых в коде и документации никогда не было. Сверху сабклассы от этих галлюцинированных типов, разобрать невозможно.

Отдельная деталь: у автора такого PR обычно куча форков других репозиториев, и везде он охотится за меткой good first issue. Похоже на сбор очков на GitHub, а не на желание реально закрыть задачу. Отсюда вопрос: стоит ли тратить силы на честное ревью и как закрыть PR, не отпугнув нормальных будущих контрибьюторов.

Что советуют бывалые мейнтейнеры:

🔘Просто закрыть и заблокировать. Самый частый ответ. К остальным это не враждебно: либо человек игнорит правила проекта, либо приносит код без ценности. И то и другое повод закрыть.
🔘 Завести метку вроде «doesn't follow contribution rules» и объяснить её в гайдлайнах. Тогда на закрытие уходит три клика, а если вернётся живой человек и поправит PR, это уже сигнал, что стоит посмотреть.
🔘 Поднять минимальный порог через CI. ruff, mypy или basedpyright, pytest с требованием покрытия, проверка докстрингов. Линтеры отсекают мусор автоматически, ещё до человеческого ревью.
🔘 Не закрывать вслепую каждый случай. Люди с неродным английским всё чаще прогоняют описание и доки через ИИ. Если контрибьюция небольшая, есть смысл быстро глянуть сам код.

Был и спор про AGENTS.md. Одни считают, что инструкции для агента поднимают качество PR. Другие, что само наличие файла как бы говорит «здесь рады ИИ-коду» и только притягивает слоп.

А вы как с этим справляетесь, если ведёте свои репозитории?

@zen_of_python
Please open Telegram to view this post
VIEW IN TELEGRAM
2👍1
Осенью нас ждёт Python 3.15, набор фич для него уже заморожен в бете. Заглянул, что там приедет, и нашёл пару вещей, которые поменяют привычки.

Самое заметное: явные ленивые импорты (PEP 810). Появляется ключевое слово lazy. Модуль пишется в импорте как обычно, но реально подгружается только при первом обращении.

lazy import json
lazy from pathlib import Path

print("старт") # json и pathlib ещё не загружены
data = json.loads('{"ok": true}') # здесь json наконец подгружается


Раньше ради быстрого старта тяжёлые импорты прятали внутрь функций. Теперь их можно держать наверху файла, как и положено, без платы за импорт при запуске. Есть и глобальные переключатели: флаг -X lazy_imports и переменная окружения, если хочется включить лень разом. И это не воскрешение отклонённого когда-то PEP 690, механизм тут другой и явный.

Второе приятное: встроенный frozendict (PEP 814). Неизменяемый и хешируемый словарь прямо в языке, без импортов. То же, чем frozenset является для set.

config = frozendict({"debug": True, "retries": 3})
# поменять значение уже нельзя, будет TypeError


Ещё одно: PEP 661 даёт штатный способ делать sentinel-значения вместо самодельных MISSING = object(). Теперь это нормальный механизм с человеческим repr и поддержкой в аннотациях типов.

Под капотом тоже движение: переработанный JIT даёт около 8-9% на Linux x86-64, интерпретатор с tail-call приехал и на Windows (там плюс 15-20%), появился стабильный ABI для free-threaded сборок.

Финальный 3.15 ждём осенью, но облик версии уже ясен. Лично я больше всего жду lazy import: сколько раз приходилось прятать импорты в функции ради быстрого CLI.

А вы ждёте 3.15 или пока сидите на чём-то постарше?

@zen_of_python
🔥163👍2
Мейнтейнер проекта рассказывает, каково это, когда в твою библиотеку прилетает громкая CVE и следом волна прессы. Речь про Starlette — это ASGI-фреймворк, на котором стоят FastAPI, vLLM, LiteLLM, куча MCP-серверов. CVE-2026-48710, окрестили «BadHost». Суть короткая.

Роутинг внутри Starlette ходит по сырому HTTP-пути. А вот request.url он пересобирает строкой http://{host}{path}, где host берётся из заголовка Host. А Host контролирует клиент. И если приложение в middleware проверяет доступ так:

if request.url.path.startswith("/admin"):
return PlainTextResponse("Forbidden", status_code=403)


то достаточно дёрнуть /admin/potato с заголовком Host: example.com/? — роутер всё равно отдаст endpoint /admin/potato (он-то смотрит на сырой путь), а вот request.url станет http://example.com/?/admin/potato, и request.url.path схлопнется в просто /. Проверка не срабатывает, секрет утекает.

Что говорит сам мейнтейнер, и это самое интересное:

🔘баг живёт не во фреймворке самом по себе, а в паттерне приложения. Авторизацию вообще нельзя строить на пути, хосте или query — её ломают и трейлинг-слеши, и регистр, и percent-encoding. Host тут просто ещё один способ;
🔘если нужен реальный путь, бери request.scope["path"] — его никто из Host не пересобирает;
🔘чинится апгрейдом на Starlette 1.0.1, там Host наконец валидируется.

Но человеческая часть зашла даже сильнее технической. Исследователи сначала выкатили срок раскрытия в месяц и предложили созвон «через 2 или 5 дней», как будто у опенсорсника есть дежурная security-команда, а не вечера после основной работы. Под конец и вовсе предложили опубликовать advisory до того, как выйдет патч. Мейнтейнер прямо называет это ужасной практикой: открытое описание дыры без фикса оставляет всех уязвимыми, зато атакующие получают ровно ту же инструкцию.

Отдельно его задело, что под уязвимость подняли брендированный лендинг badhost.org с логотипом, именем и интернет-сканером. Маркетинг уязвимости: пока базы CVE не разъехались и Dependabot не прокликал алерты, человек узнаёт о дыре в своём проде из заголовка новости.

@zen_of_python
Please open Telegram to view this post
VIEW IN TELEGRAM
8👍2👎1
Polars запустил распределённый движок на Kubernetes. Раньше это жило только в их облаке на AWS, а теперь распределённый Polars можно поднять в своей инфраструктуре: AKS, GKE, EKS, голое железо, даже SLURM. Код не меняется, тот же самый Polars API, ты просто дописываешь .remote(ctx) к своему LazyFrame, и запрос уходит считаться на кластер. Хоть петабайтный join, запущенный с ноутбука.

Распределённые движки обычно ощущаются как боль: куча микросервисов, тяжёлый JVM-рантайм, кластер поднимается минуты, а то и десятки минут. У Polars один бинарник и helm-чарт, кластер стартует за секунды. То есть реально можно поднимать отдельный кластер под каждый ETL-джоб и гасить после.

Плюс завезли две приятные штуки:

🔘Профайлинг запросов. Раньше движок был чёрным ящиком: пока запрос не закончился, ты не знал, что он делает. Теперь есть фронтенд и API, которые в реальном времени показывают, какая операция выполняется, сколько строк прошло через каждый узел и сколько данных шаффлится между воркерами. Причём работает и для одного узла, редкий шанс заглянуть внутрь движка на рантайме.
🔘 Data lineage через OpenLineage. Движок умеет слать события в любой коллектор (например, Marquez), так что видно, как и когда таблица создавалась и обновлялась.

@zen_of_python
Please open Telegram to view this post
VIEW IN TELEGRAM
👍138
Steering Council внезапно нажал на тормоз перед JIT-компилятором в CPython. 5 июня совет опубликовал заявление: чтобы JIT стал поддерживаемой, а не экспериментальной частью CPython, нужен полноценный Standards Track PEP, который сообщество обсудит, а совет официально примет или отклонит.

История такая: JIT влили в main несколько лет назад как эксперимент, и единственный PEP про него, PEP 744, имеет статус Informational — то есть описывает дизайн, но ничего формально не закрепляет. Открытые вопросы из него (долгосрочные мейнтейнеры, security-ревью, поддержка отладчиков, гарантии рантайма) так и висят нерешёнными. Совет честно признаёт, что ответственность общая: сами не следили за процессом так строго, как заслуживает изменение такого масштаба.

Условия жёсткие:

🔘до принятия PEP никакой новой функциональности JIT в main: ни фич, ни оптимизаций, ни перформанс-работы. Только багфиксы и security-фиксы;
🔘на подачу и принятие PEP даётся шесть месяцев. Не успеют — JIT удаляют из main, и разработка продолжается вне основного репозитория;
🔘PEP должен ответить на неудобные вопросы: план поддержки, совместимость с free-threading, профайлерами и отладчиками, измеримые цели по скорости и памяти, отношения со сторонними JIT вроде CinderX, Numba и PyTorch.

Интересная мысль из заявления: вместо одной конкретной реализации PEP может описать JIT-инфраструктуру, на которой можно пробовать разные стратегии трассировки, не привязываясь к одной.

Напомню контекст: буквально на прошлой неделе писал, что переработанный JIT в 3.15 даёт около 8-9% на Linux x86-64. И вот теперь вся эта работа на паузе до принятия PEP. Шесть месяцев на то, чтобы формализовать проект, в который вложены годы — следим.

@zen_of_python
Please open Telegram to view this post
VIEW IN TELEGRAM
7👍4
Astral завезла в uv защиту от уязвимостей и малвари. 8 июня в их блоге анонсированы две preview-фичи, и обе бьют точно в больное место экосистемы — атаки через цепочку поставок.

Первая: команда uv audit. Сканирует зависимости проекта по базе OSV на известные уязвимости и заброшенный статус пакетов. Работает в 4-10 раз быстрее pip-audit — фирменный стиль Astral на месте.

Вторая интереснее: проверка на малварь прямо при установке. С переменной окружения UV_MALWARE_CHECK=1 команды uv add и uv sync сверяют залоченные зависимости с MAL-записями OSV и блокируют установку известной малвари до того, как её код вообще запустится. Учитывая, что свежие PyPI-кампании прячут стилеры в .pth-хуки, которые срабатывают при каждом старте интерпретатора даже без импорта пакета, проверка до установки — единственный момент, когда ещё не поздно.

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

Обе фичи в preview, но включить UV_MALWARE_CHECK в CI можно уже сейчас — бесплатная страховка. Кто на uv, попробуйте uv audit на своём проекте и расскажите, сколько насыпало.

@zen_of_python
🔥10👍5
История недели: линтер ruff в одиночку остановил supply-chain-атаку на популярный проект. 8 июня злоумышленник взломал аккаунт сооснователя GPT-Pilot (ИИ-агент для разработки, 33,7 тысячи звёзд на GitHub) и форс-пушнул прямо в main 758 КБ обфусцированного JavaScript-стилера из семейства Shai-Hulud — того самого, что осенью прокатилось по npm.

Дальше произошло прекрасное. CI проекта дважды завернула вредоносный коммит:

🔘сначала ruff format --check споткнулся о нарушение форматирования в _hooks.py;
🔘потом ruff check нашёл E402 (импорт не в начале файла) и I001 (несортированные импорты) в __init__.py.

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

Разбор инцидента опубликовала StepSecurity, и вывод из него шире смешного заголовка. Строгий линтинг в CI — это не только про красоту кода. Вредоносные инъекции почти всегда выглядят чужеродно: обфусцированные блобы, импорты посреди файла, нетипичное форматирование. Линтер с жёсткими правилами превращается в дешёвый детектор аномалий, который не обойти, не понимая кодстайл проекта.

Так что когда в следующий раз будете ворчать на красный пайплайн из-за несортированных импортов — вспомните, что ровно эта проверка спасла проект на 33 тысячи звёзд.

@zen_of_python
Please open Telegram to view this post
VIEW IN TELEGRAM
10👍7👏1😁1
Есть страница, которую я бы выдавал вместе с установщиком Python — Comprehensive Python Cheatsheet. Одна гигантская шпаргалка на весь язык: коллекции, итераторы, декораторы, ООП, файлы, async и основные библиотеки. 38 тысяч звёзд на GitHub и регулярные обновления.

Ценность не в том, что там есть что-то секретное, а в том, что всё в одном месте и с примерами в три строки. Несколько вещей оттуда, которые регулярно забываются:

🔘Counter не только считает, но и сразу выдаёт топ: Counter(words).most_common(3);
🔘defaultdict(list) избавляет от проверок if key in dict при группировке;
🔘itertools.groupby работает только на отсортированных данных — классическая ловушка;
🔘functools.partial замораживает аргументы: basetwo = partial(int, base=2);
🔘ChainMap собирает конфиг по приоритету: ChainMap(user_settings, defaults);
🔘@dataclass(frozen=True) даёт неизменяемый объект с __eq__ и __hash__ из коробки.

@zen_of_python
Please open Telegram to view this post
VIEW IN TELEGRAM
4👍3
Pyrefly активно пилят, и по свежему dev-релизу хорошо видно, куда он растёт: за один цикл 250 коммитов от 37 контрибьюторов, и упор сместился на работу в редакторе, а не только на проверку в CI.

Напомню, Pyrefly — это статический анализатор типов, конкурент mypy и pyright, написанный на Rust ради скорости. На недавнем Typing Summit показывали, что хорошо типизированный код заметно поднимает успех ИИ-агентов, так что быстрый чекер с живой обратной связью в IDE сейчас особенно к месту.

Что приехало для редактора:
🔘 автодополнение литералами: если аргумент имеет тип Literal["a", "b"], редактор предложит ровно эти значения;
🔘 рефакторинги «вынести функцию или класс в новый модуль» и «перенести символ в другой файл» с автоматическим переписыванием импортов;
🔘 навигация по pytest-фикстурам: от использования фикстуры можно прыгнуть к её определению;
🔘 новый LSP-endpoint getExpectedType: редактор спрашивает у чекера, какой тип ожидается в текущей позиции, и подсказывает точнее;
🔘 команда pyrefly coverage check показывает, насколько код покрыт аннотациями типов.

А вы уже пробуете быстрые чекеры на Rust или пока остаётесь на mypy?

@zen_of_python
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3🆒2