Находки в опенсорсе: мир питона за июль 2026

от автора

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

No AI slop

No AI slop

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

Погнали смотреть, что у нас в питоне происходило!


CPython

Начнем с самого важного и большого — с питона. Мой месяц в питоне прошел под знаком frozendict. Я старался активно улучшать поддержку иммутабельных типов в CPython.

Написал PEP и сделал Reference Implementation нового синтаксиса для frozendict / frozenset

Вместе с Донхи На (членом Steering Council) написали ПЕП для добавления нового синтаксиса frozen контейнеров в питон.

Такого синтаксиса явно не хватает. Наша главная задача: повысить частоту использования иммутабельных типов данных перед Free-Threading era.

fd = f{1: 2, 3: 4}assert isinstance(fd, frozendict)fs = f{1, 2}assert isinstance(fs, frozenset)

Абсолютно знакомый синтаксис. Ведь мы просто добавили f (как frozen) перед существующими dict и set литералами.

Поддерживаем все виды синтаксиса и comprehensions, конечно же:

  • f{1, *other, 2}

  • f{**first, **second, 'default': 0}

  • f{x for x in range(10) if x % 2 == 0}

  • f{x: x for x in range(10)}

  • f{x async for x in arange(10)}

  • PEP-798 f{**items for nums in list_of_items}

Пока идея (PEP и реализация) в процессе доработки и обсуждения. Целимся в 3.16 🙂

Предложил добавить новое C-API для оптимизации создания frozendict из dict

PR: https://github.com/python/cpython/pull/153413

Сейчас создание frozendict — довольно дорогая операция. PyFrozenDict_New вызывает dict_update_common, которые копирует данные из словаря за O(n). А мой PyDict_AsFrozenDictAndClear сделает такое за O(1).

Так как PyDictObject и PyFrozenDictObject C-структуры имеют очень похожее внутреннее устройство, то я могу просто выдрать куски из словаря и вставить их в новый frozendict.

Аллоцируем новый пустой frozendict, затем под локом — выдираем куски из старого словаря. Детали реализации (чуть упрощено для чтения):

PyObject *PyDict_AsFrozenDictAndClear(PyObject *dict){    if (dict == NULL || !PyDict_Check(dict)) {        PyErr_BadInternalCall();        return NULL;    }    PyObject *res = frozendict_new_untracked(&PyFrozenDict_Type);    if (res == NULL) {        return NULL;    }    Py_BEGIN_CRITICAL_SECTION(dict);    transfer_keys_and_values_lock_held(res, dict);    Py_END_CRITICAL_SECTION();    _PyObject_GC_TRACK(res);    assert(_PyFrozenDictObject_CAST(res)->ma_hash == -1);    return res;}

Хеш во frozendict считается лениво по запросу __hash__, а не сразу при создании; потому он остается -1.

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

static voidtransfer_keys_and_values_lock_held(PyObject *res, PyObject *dict){    PyDictObject *new = (PyDictObject *)res;     PyDictObject *old = (PyDictObject *)dict;    assert(can_modify_dict(old));    // Fast path: do nothing on an empty dict:    if (old->ma_keys == Py_EMPTY_KEYS) {        return;    }    Py_ssize_t used = old->ma_used;    PyDictKeysObject *keys = old->ma_keys;    PyDictValues *values = old->ma_values;    // Clear the old dict keys and values, but do not decref them:     clear_common(old);    set_keys(old, Py_EMPTY_KEYS);    set_values(old, NULL);    ASSERT_CONSISTENT(old);    // Transfer keys and values from dict to frozendict:    new->ma_used = used;    new->ma_keys = keys;    new->ma_values = values;    ASSERT_CONSISTENT(new);}

Прикольно, да? Такое же АПИ мы готовим для frozenset и set. Там все сложнее, к сожалению старые фукнции C-API для PySet умеют мутировать frozenset 🙁 Надо будет такое запрещать, но для начала деприкейтить.

Поддержал добавление новых двух методов для dict и set

На основе моего АПИ Виктор Стиннер (топ2 по коммитам в CPython после Гвидо) предложил добавить два метода: dict.take_frozendict и set.take_frozenset, которые будут превращать мутадельные данные в имммутабельные данные (реализация). Он поделился со мной идеей, я был только за!

fs = {1, 2, 3}.take_frozenset()assert type(fs) is frozensetassert fs == frozenset({1, 2, 3})

У нас уже есть bytearray.take_bytes, который за O(1) и без копирования данных превращает bytearray в иммутабельный bytes.

Возможно, что появятся аналоги и для других типов.

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

PR: https://github.com/python/cpython/pull/152483

То формально было 29 июня, но какая разница!

Вот такой простой код содержит в себе 2 критичных бага:

class Evil:    def __eq__(self, other):        return otherleaked = vars(list) == Evil()name = "example"leaked[name] = lambda self: "probe"print(getattr(list, name)([]))del leaked[name]print(hasattr(list, name))

Почему?

Потому что types.MappingProxyType выставляет наружу внутренний тип (dict) при сравнении и операции |. И мы уже его можем полностью мутировать. А тут мы выставляем наружу list.__dict__, который можем мутировать таким хитрым способом. И мы мутируем ->tp_dict встроенного типа данных! Потом интерпретатор падает со стектрейсом от таких приколов.

Полный разбор бага (длинный текст!)

Я просто сделал PR, который отправляет копии данных в небезопасные классы. Сейчас, однако, идет обсуждение: возможно мое изменение нужно откатывать. Некоторые участники сообщества считают, что просто так делать не надо. И бага нет. Обсуждаем дальше!

msgspec

[репозиторий] | [зеркало]

Для тех, кто не знает: самый быстрый json / msgspack сериализатор и десериализатор в Python. Написан на C, использует просто уйму оптимизаций.

В июне я рассказывал, почему msgspec такой быстрый, а в июле я добавил:

Ждем новый релиз, пока даем коду настояться 🌚️️

django-modern-rest

[репозиторий] | [зеркало]

Самый быстрый, строгий и удобный REST API слой для Django. Если не знакомы, то я публиковал статью на хабре про него. В июле было сделано два новых релиза. Важные изменения:

>>> from dmr.settings import Settings>>> from dmr.security import SyncOrAsyncAuth>>> from dmr.security.http import HttpBasicAsyncAuth, HttpBasicSyncAuth>>> DMR_SETTINGS = {...     Settings.auth: [...         SyncOrAsyncAuth(...             HttpBasicSyncAuth(),...             HttpBasicAsyncAuth(),...         ),...     ],... }

wemake-python-styleguide

[репозиторий] | [зеркало]

Самый строгий Python линтер ever! Лучший инструмент, чтобы научить вашего агента писать простой и понятный Python код.

За июль вышел новый релиз 1.7.0. В нем мы:

  • Запретили писать array[start:stop:] вместо array[start:stop] и array[start::] вместо array[start:]

  • Улучшили правила для match/case, например, такой код теперь заставят переписывать:

match state:    case EventType.REJECT:        user = 'rejected'    case _:        user = 'active'

Потому что тут у нас просто if state == EventType.REJECT: ... с else:, а не match/case. Аналогично с другими типами паттернов MatchSequence, MatchMapping, тд.

Одной строкой

Здесь будут ссылки и новости от сообщества. Хотите попасть сюда? Присылайте свои проекты в чат с тегом #opensource. Опубликую в следующем месяце.

Одной строкой:

Заключение

Фух, вроде бы все важное покрыли 🙂 Расскажите, как вам формат? Было ли интересно? Было ли полезно?

Постараюсь выкладывать такие отчеты каждый месяц, если формат вам понравится.

А если вы думаете, что я делаю что-то полезное для мира, то поддержать можно по ссылкам:

Я там ничего не продаю, просто делаю свою работу, если считаете, что я делаю полезное — можно поддержать. Если нет, то я все равно продолжу ее делать.

Подписывайтесь на главный канал сообщества, если вам нравится такое читать. И возможно даже захочется поучаствовать! Буду рад видеть вас частью нашего опенсорс сообщеста 🙂

Давайте в коммментах обсудим: какой синтаксис для frozendict / frozenset вы бы хотели видеть в Python?

ссылка на оригинал статьи https://habr.com/ru/articles/1065404/