Н
Находки в опенсорсе
13.07.2026 09:04 · 👁 3.9K
Внутри питона есть ЕЩЕ виртуальные машины
Мы все знаем, что сам питон - одна большая стековая виртуальная машина, которая выполняет опкоды. Их мы можем посмотреть через dis:
>>> import dis
>>> dis.dis('x + y')
0 RESUME 0
1 LOAD_NAME 0 (x)
LOAD_NAME 1 (y)
BINARY_OP 0 (+)
RETURN_VALUE
Можем получить список всех опкодов, можем вызвать их оптимизации и посмотреть на результат с оптимизациями.
Но! Внутри CPython есть и другие виртуальные машины. Сегодня поговорим про ту, которой все мы всегда пользовались, но не знали, что она - виртуальная машина.
pickle
Да, не удивляйтесь. Встроенный протокол сериализации в питоне работает благодаря отдельной стековой виртуальной машине. Давайте посмотрим.
>>> class User:
... def __init__(self, username: str, tags: list[str]) -> None:
... self.username = username
... self.tags = tags
... def __reduce__(self) -> tuple[type['User'], tuple[Any, ...]]:
... return (type(self), (self.username, self.tags))
Создадим обычный класс и запиклим его объект:
>>> import pickle
>>> user = User('sobolevn', tags=['python', 'tg'])
>>> pickle.dumps(user, protocol=0)
b'c__main__\nUser\np0\n(Vsobolevn\np1\n(lp2\nVpython\np3\naVtg\np4\natp5\nRp6\n.'
Обратите внимание, что в разных протоколах значение будет разное:
>>> pickle.dumps(user, protocol=1)
b'c__main__\nUser\nq\x00(X\x08\x00\x00\x00sobolevnq\x01]q\x02(X\x06\x00\x00\x00pythonq\x03X\x02\x00\x00\x00tgq\x04etq\x05Rq\x06.'
Всегда необходимо тестировать, что pickle работает для всех версий от 0 до pickle.HIGHEST_PROTOCOL для ваших объектов, которые поддерживают такой способ сериализации.
Что внутри?
Можно, глядя на значения, подумать, что там просто лежит какой-то бинарный формат сериалиации. Однако, там лежат опкоды виртуальной машины для сериалиации объектов. Их можно задисить:
>>> import pickletools
>>> pickletools.dis(pickle.dumps(user, protocol=1))
0: c GLOBAL '__main__ User'
15: q BINPUT 0
17: ( MARK
18: X BINUNICODE 'sobolevn'
31: q BINPUT 1
33: ] EMPTY_LIST
34: q BINPUT 2
36: ( MARK
37: X BINUNICODE 'python'
48: q BINPUT 3
50: X BINUNICODE 'tg'
57: q BINPUT 4
59: e APPENDS (MARK at 36)
60: t TUPLE (MARK at 17)
61: q BINPUT 5
63: R REDUCE
64: q BINPUT 6
66: . STOP
highest protocol among opcodes = 1
Сравните, как будет отличаться вывод для другого протокола, например пятого.
И окажется, что все "случайные" символы на самом деле просто так же обозначают опкоды. Теперь мы умеем их читать.
Мы можем найти все опкоды и посмотреть их доки:
>>> pickletools.opcodes[25].code
']'
>>> pickletools.opcodes[25].doc
'Push an empty list.'
И мы даже можем оптимизировать байткод pickle для более быстрой сериализации / десериализации.
Прям полностью настоящая ВМ :)
Вот за счет чего мы можем с помощью pickle сериализовать любой Python объект (почти), а с помощью других средств - получается сильно сложнее.
Обсуждение: Знали о такой детали реализации? Знаете ли вы как работает pickle сам по себе? Зачем нужны протоколы и версии? Или сделать отдельный пост про детали работы? Знаете ли вы, что pickle - фундаментально небезопасный протокол? И нельзя запускать чужие дампы, только свои доверенные?
Загадка: кстати, какие еще виртуальные машины внутри CPython вы знаете? Я назвал только одну из нескольких. Заходите в комменты за ответами, правильные - покажу завтра.
Н
Находки в опенсорсе
10.07.2026 09:15 · 👁 5.4K
Подкаст про управление разработкой в новой реальности
ВК | ТГ
Меня пригласили поучаствовать в подкасте, чтобы обсудить разработку с ИИ и управление разработкой с ИИ.
Участники:
- Кирилл Меньшов, старший вице-президент и руководитель блока «Технологии» Сбера
- Дмитрий Иванов, руководитель SourceCraft в Яндексе
- Я, опенсорс разработчик
Поговорили про важные вопросы:
- Сможем ли программировать на русском? Или его тоже потребуется формально верифицировать?
- Как обеспечить должный уровень ИБ, когда софт пишут агенты?
- Как оставить себе "план б", если код все-таки придется смотреть?
- Как расти в новом мире как инженер?
- Откуда брать новых senior разработчиков? И нужны ли они?
- Как починить найм? И почему резюме скорее всего скоро будет не особо нужно
Как вы знаете, я занимаю довольно нейтрально-реалистичную позицию в ИИ вопросах: есть такие-то плюсы и минусы, такие-то риски, такие-то косты, такие-то процессы. В подкасте старался как раз доносить её :)
Полезные ссылки из выпуска:
- Концепция AI-Disrupt PDLC: https://aipdlc.ru/ru
- Язык формальной верификации: https://lean-lang.org
- Tokenmaxxing: https://tokenmaxxing.com
- Прибыльны ли ИИ компании? https://isaiprofitable.com
- Наши скиллы для агентов в DMR
Обсуждение: Что вы думаете? Какие у вас есть страхи / предвкушения от нового ИИ мира разработки? Готовы ли вы программировать на русском языке? Потребуется ли нам смотреть в код?
Буду рад услышать все самые полярные мнения!
erid: 2VfnxvSxct3 Реклама. ПАО "СБЕРБАНК", ИНН 7707083893, 18+
Н
Находки в опенсорсе
06.07.2026 11:50 · 👁 7.4K
Наконец-то полезные фичи в питоне
Многие знают, что я почти не добавляю новые фичи в CPython, я стараюсь выпилить существующие и править баге в тех, что у меня не получается убирать. Новые если и добавляю, то без масштабных обсуждений.
Некоторые новые фичи встречают у меня сильный оптимизм: как новый встроенный sampling profiler. Вот тут видео про него кстати с прошедшего PyCon.
Некоторые фичи встречают у меня понимание: как например typing.disjoint_base. Простая штука, решает понятную проблему.
Некоторые фичи встречают у меня лютое подгорание: как например lazy imports. Вот доклад с пайкона и про них, кстати.
Я думаю, что мое понимание хорошей фичи очень сильно расходится с таким пониманием у других питонистов, мое понимание "хорошего питона" можно найти в моем wemake-python-styleguide.
Зная такую вводную, я решил сделать "большую" новую фичу. На один символ в грамматике.
Иммутабельному питону - быть!
У нас была довольно большая проблема: создавать мутабельные словари и множества - можно довольно легко. {1: 2} и {1, 2, 3}
Чтобы создать frozendict и frozenset нам уже нужен вызов функции: frozendict({1: 2}) и frozenset({1, ,2, 3}). Почему так делать не очень?
1. Потому что писать долго, мало людей будут заморачиваться. Зачем, когда проще создать мутабельную структуру?
2. Потому что frozendict и frozenset тупо медленнее. frozendict пока вообще имеет 0 оптимизаций для работы и просто в тупую копирует всю память из dict, который мы отправляем. Получая буквально O(n * 2) по памяти и времени работы. Делает лишний CALL. А frozenset({1, 2, 3}) немного оптимизирован через INTRINSIC_BUILD_FROZENSET опкод, который генерируется только для set в качестве входного аргумента
3. Неудобно писать comprehensions. Они получается сильно менее читаемые, чем их мутабельные версии
Мое предложение (пока только в формате обсуждения): https://discuss.python.org/t/frozenset-and-frozendict-comprehensions/101584/9
Мой PR с добавлением данной фичи в CPython: https://github.com/python/cpython/pull/152820 (он нужен для написания ПЕПа)
Как оно выглядит?
>>> ${1: 2}
frozendict({1: 2})
>>> ${1, 2, 3}
frozenset({1, 2, 3})
>>> ${x: x for x in range(3)}
frozendict({0: 0, 1: 1, 2: 2})
>>> ${x for x in range(2)}
frozenset({0, 1})
Что важно? Оно уже умеет все то, что умеют привычные нам конструкции без $:
- ${} - пустой frozendict, как {} - пустой dict
- ${1, *other} - распаковка внутри frozenset
- ${**d for d in list_of_dicts} - распаковка внутри frozendict comprehension + PEP-798
- ${x async for x in async_iterable if x >0} - async frozenset comprehension с условием
Почему $?
- Потому что $ - is the real deal 😎💸
- Потому что $ не имеет смысла сейчас: но будет значить "иммутабельность"
- Потому что $ может легко в дальнейшем использовать для других иммутабельных штуках: $(x for x in range(1)) для нативного tuple comprehension, для PEP-805 с __freeze__ и тд
И другие новости моих опенсорс проектов одной строкой
- Новый релиз django-modern-rest с новыми DX фичами
- Добавляем Token auth в следующий релиз DMR
- msgspec готов к релизу новой версии с поддержкой frozendict
- Улучшаем поддержку match/case в wemake-python-stylguide
- Предлагаю улучшенное C-API для создания frozendict
- Выпустил релиз punq с поддежкой типизации
Если вы хотите поддержать мою работу в опенсорсе:
- https://boosty.to/sobolevn
- https://github.com/sponsors/wemake-services
Обсуждение: Что вы думаете, нужен ли такой синтаксис? Удобнее ли будет пользоваться иммутабельными структурами после добавления такого синтаксиса?
Н
Находки в опенсорсе
29.06.2026 09:08 · 👁 8.3K
Пользуясь случаем: у нас 10 июля митап в Нижнем Новгороде.
https://pytho-nn.timepad.ru/event/4050146/
В программе 4 крутейших юбилейных доклада от гостей города и (даже!) нижегородца:
- Артем Пашков, Нижний Новгород, Сообщество "Опенсорсеры", Как сообщество Опенсорсеры помогает open source проектам и разработчикам?
Отвечу на главный вопрос: нужно ли лично вам занимать опенсорсом? И как начать?
Расскажу про проблемы, знакомые многим: недостаток внимания к своему проекту, где искать контрибьюторов, куда самому законтрибьютить, а также как получить полезный фидбек.
- Илья Солин, Уфа, ТБанк, https://t.me/http_418_i_am_a_teapot, Алгебраические эффекты: понять, полюбить и никогда не тащить в прод
Разберём концепцию теоретически (почему это классно), попробуем реализовать её на Python и поймём, стоило ли оно того.
- Георгий Бородин, Москва, Как я перестал бояться и полюбил бойлерплейт
Благие намерения далеко не всегда приводят к хорошему, но если не опускать лапки – наверняка получится сделать систему мечты. Цена этого – вопрос, о котором и хочется рассказать (собираюсь рассказать об одном очень грязном способе якобы оптимизации деливери, который заставил меня ползать по флеймграфам и проклинать себя же и об одном, который позволил мне перестать ждать фронтовых задач).
- Евгений Блинов, The Mutating Company, Из чего состоит фреймворк мутационного тестирования?
В процессе создания своего фреймворка МТ мне потребовалось создать некоторое количество промежуточных библиотек. Подробнее о них расскажу в докладе.
Спикеров можно и нужно мучать вопросами.
Ну а после: афте-пати в баре до закрытия, афте-афте-пати до самого утра.
Ждем всех 10 июля по адресу Алексеевская, 6/16, ИТ Лекторий «Горький Тех»
Сбор гостей с 18:00, стартуем в 18:30
Приходите, приезжайте :)
Н
Находки в опенсорсе
29.06.2026 08:51 · 👁 13.4K
Если вы понимаете данный баг, то вы знаете питон лучше 95% людей
А если нет, то вы многое узнаете про то, как работает память и почему мутабельностью стоит пользоваться с осторожностью.
Недавно я увидел один из лучших багов в CPython за долгое время. А я видел много багов 🌚️️
Вот код, который делает две критичные безумные вещи (попробуйте их найти прежде, чем читать дальше):
class Evil:
def __eq__(self, other):
return other
leaked = vars(list) == Evil()
name = "example"
leaked[name] = lambda self: "probe"
print(getattr(list, name)([]))
del leaked[name]
print(hasattr(list, name))
Разбор бага
Во-первых, что произойдет?
1. Мы мутируем встроенный и иммутабельный тип list, хотя такое должно быть невозможно
2. Интерпретатор закрашится; не упадет с исключением, а словит core dump на уровне C кода
Но почему? Пройдемся по каждой строке. Со ссылками на исходники: кликайте и читайте!
1. Сначала мы создадим класс Evil, который просто возвращает из __eq__ второй объект, который ему передали. Так можно делать, тут нет ничего сломанного.
2. Далее, мы сравниваем vars(list) с Evil, и вот тут как раз в leaked попадет второй объект из Evil.__eq__, в нашем случае vars(list)
3. vars возвращает вам list.__dict__, который является не обычным dict, а types.MappingProxyType, то есть иммутабельным маппингом поверх оригинального значения. Добавлять в него ключи нельзя. Потому что мы не хотим, чтобы в список или другие типы нам подкидывали какие-то новые методы во время работы программы
4. Как работает сравнение для mappingproxy? mappingproxy хранит в себе оригинальный мутабельный словарь, который он "проксирует" или "защищает от изменений". И сравнивает на самом деле не себя, а оригинальный объект
5. В случае с list.__dict__ мы получаем PyDictProxy_New(self->tp_dict), где хранится тот самый настоящий и защищенный __dict__ из типа list, который обычно не доступен вне C кода
6. При сравнении mappingproxy разворачивается и достает из себя ->mapping, тот самый чистый и мутабельный ->tp_dict
7. Теперь у нас есть ->tp_dict, мы можем в него добавлять методы: leaked[name] = lambda self: "probe". Они будут работать. Мы только что достигли пункта 1. и мутировали встроенный Python тип без единого импорта
8. Далее происходит еще более дикое. Мы удаляем метод, который добавили через del leaked[name]
9. Питон не ожидает такого: методы у встроенных типов не могут появляться и исчезать. И при следующем обращении к hasattr(list, name) крашится вот тут на обращении к уже освобожденной памяти. EXC_BAD_ACCESS, пункт 2. пал
пу-пу-пу
Фикс
Баг: https://github.com/python/cpython/issues/152405
Как такое чинить?
1. Нужно сохранить обратную совместимость для всех видов сравнений. Менять типы или значения нельзя
2. Необходимо убрать креш и мутацию типа
3. Сильно раздувать потребление памяти / время работы тоже нельзя
Мой PR: https://github.com/python/cpython/pull/152483
Что он делает? Если мы сравниваем прокси поверх обычного словаря, но не с известными нам безопасными типами, то мы делаем копию словаря и сравниваем ее:
if (
PyDict_CheckExact(v->mapping) &&
!(PyAnyDict_CheckExact(w) || PyODict_CheckExact(w))
) {
// So, instead we send a copy:
PyObject *copy = PyDict_Copy(v->mapping);
if (copy == NULL) {
return NULL;
}
PyObject *res = PyObject_RichCompare(copy, w, op);
Py_DECREF(copy);
return res;
}
Таким образом - все ошибки выше уходят. Любая мутация останется в копии. Доп память не тратится в большом количестве популярных случаев.
Данный баг все еще есть на всех версиях питона. Я вам его не показывал, вы ничего не видели.
Обсуждение: какие у вас были самые кринжовые / прикольные баги?
| Поддержать | YouTube | GitHub | Чат |
Н
Находки в опенсорсе
25.06.2026 13:14 · 👁 9K
Генерируем Rust код из Python и становимся крабами
Проект "острый краб": https://github.com/kushaldas/spicycrab
Вы же знаете, что вы узнаете про все важные штуки в питоне первыми? На ближайшем Language Summit в июле Кушал Дас - core-разработчик CPython - представит свой новый проект. Но зачем ждать июля, когда код открыт? Давайте смотреть и пробовать!
В чем главная идея?
- Пишем на типизированном Python
- Получаем на выходе Rust код, который работает в десятки или сотни раз быстрее
- Можем использовать в Python крейты Rust и Python пакеты, что? 🙀
(проект еще не просто в альфе, а в пре-альфе, но мы тут просто любим странное, ставь 🕊, если просто заходишь сюда почитать про непонятное и удивительное)
Начнем с простого: print('Hello world')
Запустим: crabpy transpile ex.py и получим:
fn main() {
println!("Hello world");
}
Прикол! Давайте сделаем сложнее. Возьмем clap (популярная библиотека для парсинга CLI параметров в расте) и сделаем мини CLI с ее помощью ... на питоне.
1. Скачиваем Rust зависимость и генерим из нее Python стабы: cookcrab generate clap -o rust-stubs/
2. Смотрим, что там внутри правда Python стабы, удивляемся
3. Устанавливаем стабы: pip install -e ./rust-stubs/clap_builder ./rust-stubs/clap
4. Пишем на питоне:
from spicycrab_clap import Command, Arg, ArgMatches
def main() -> None:
matches: ArgMatches = (
Command.new("myapp")
.arg(Arg.new("name").required(True))
.get_matches()
)
name: str = matches.get_one("name").unwrap().clone()
print(f"Hello, {name}!")
5. Транспилим: crabpy transpile ex.py
6. Получаем
pub fn main() {
let matches: clap_builder::ArgMatches = clap::Command::new("myapp")
.arg(clap::Arg::new("name").required(true))
.get_matches();
let name: String = matches.get_one::<String>("name").cloned().unwrap().to_string();
println!("{}", format!("Hello, {}!", name));
}
7. Запускаем:
» cargo run -- Nikita
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.01s
Running `/Users/sobolev/Desktop/spicycrab/rusty/target/debug/ex Nikita`
Hello, Nikita!
Теперь у вас нет уважительных причин, чтобы говорить "я не знаю раст" 🌚️️
Зачем?
А если серьезно, то не совсем пока понятно - какую нишу будет занимать данный проект.
Сам автор говорит:
> Write typed Python and generate working Rust code via spicycrab. This currently includes part of stdlib, async (via tokio), actix-web examples. Slowly more and more Rust crates are available as stub typed Python modules, which we can use like normal Python code while developing and then compiling the generated Rust code as final output. The final goal is to be able to write smaller production code using spicycrab.
Кажется, что ниша довольно маленькая. Если вам реально хочется писать Python + Rust код вместе (что вообще-то лютейшая база, например ruff и uv ровно так и написаны), то есть уже готовые проекты:
- https://github.com/pyo3/pyo3 - для использования Rust вместе с CPython биндингами
- https://github.com/pyo3/maturin - система сборки для такие проектов
Есть проекты чуть менее универсальные, например:
- https://github.com/RustPython/RustPython - интерпретатор Python на Rust, там тоже можно писать модули на расте для питона своим особым способом
use rustpython::vm::pymodule;
#[pymodule]
mod test_module {
#[pyfunction]
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
}
- https://github.com/youknowone/pyre - новый интерпретатор Python на Rust (от того же автора) но с Free-Threading и JIT из PyPy, в некоторых случаях в 45 раз быстрее CPython, goes brrrr
- Поддержка Rust напрямую в CPython: https://t.me/opensource_findings/941 Ждете? :)
И еще куча всего другого.
Но, будет интересно посмотреть, что выйдет из такого довольно необычного опыта.
Обсуждение: Знаете ли вы раст? Хотите ли изучить? Видите ли применения у себя на работе?
| Поддержать | YouTube | GitHub | Чат |