- PVSM.RU - https://www.pvsm.ru -
Всем привет! Решил попробовать новый формат на хабре: делиться результатами своей работы за прошедший месяц. С интересными ссылками на события, проекты, обсуждения, релизы из мира питона, моего телеграм канала [1] и нашего замечательного чата [2] (куда много людей приходят пиарить свои проекты).
Давайте сразу договоримся: я не публикую ИИ слоп, пишу статью ручками (потому что уважаю своего читателя) и рассказываю интересное и актуальное из своей бесплатной работы в опенсорсе, а с вас лайк статье (если она будет для вас интересной).
Погнали смотреть, что у нас в питоне происходило!
Начнем с самого важного и большого - с питона. Мой месяц в питоне прошел под знаком frozendict [3]. Я старался активно улучшать поддержку иммутабельных типов в CPython.
Вместе с Донхи На (членом Steering Council) написали ПЕП [4] для добавления нового синтаксиса frozen контейнеров в питон.
Такого синтаксиса явно не хватает. Наша главная задача: повысить частоту использования иммутабельных типов данных перед Free-Threading era [5].
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 [6] f{**items for nums in list_of_items}
Пока идея (PEP и реализация) в процессе доработки и обсуждения. Целимся в 3.16 :)
PR: https://github.com/python/cpython/pull/153413 [7]
Сейчас создание frozendict - довольно дорогая операция. PyFrozenDict_New [8] вызывает 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 void
transfer_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 :( Надо будет такое запрещать, но для начала деприкейтить.
На основе моего АПИ Виктор Стиннер (топ2 по коммитам в CPython после Гвидо) предложил добавить два метода: dict.take_frozendict и set.take_frozenset, которые будут превращать мутабельные данные в имммутабельные данные [9] (реализация [10]). Он поделился со мной идеей, я был только за!
fs = {1, 2, 3}.take_frozenset()
assert type(fs) is frozenset
assert fs == frozenset({1, 2, 3})
У нас уже есть bytearray.take_bytes [11], который за O(1) и без копирования данных превращает bytearray в иммутабельный bytes.
Возможно, что появятся аналоги и для других типов.
PR: https://github.com/python/cpython/pull/152483 [12]
То формально было 29 июня, но какая разница!
Вот такой простой код содержит в себе 2 критичных бага:
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))
Почему?
Потому что types.MappingProxyType выставляет наружу внутренний тип (dict) при сравнении и операции |. И мы уже его можем полностью мутировать. А тут мы выставляем наружу list.__dict__, который можем мутировать таким хитрым способом. И мы мутируем ->tp_dict встроенного типа данных! Потом интерпретатор падает со стектрейсом от таких приколов.
Полный разбор бага [13] (длинный текст!)
Я просто сделал PR, который отправляет копии данных в небезопасные классы. Сейчас, однако, идет обсуждение: возможно мое изменение нужно откатывать. Некоторые участники сообщества считают, что просто так делать не надо. И бага нет. Обсуждаем дальше!
репозиторий [14] | зеркало [15]
Для тех, кто не знает: самый быстрый json / msgspack сериализатор и десериализатор в Python. Написан на C, использует просто уйму оптимизаций.
В июне я рассказывал, почему msgspec такой быстрый [16], а в июле я добавил:
Поддержку frozendict для Python 3.15+ [17]
Улучшение типов msgspec.Meta с корректными @overload типами [18]
Пофиксил всяких багов по мелочи
Ждем новый релиз, пока даем коду настояться 🌚️️
репозиторий [19] | зеркало [20]
Самый быстрый, строгий и удобный REST API слой для Django. Если не знакомы, то я публиковал статью на хабре [21] про него. В июле было сделано два новых релиза. Важные изменения:
Добавили Opaque Token аутентификацию [22]
Добавили возможность тестирования тротлинга / рейтлимитов [23]
Добавили встроенные классы для проверки корректности JWT токена [24]
Добавили всякие удобные DX фишечки для пользователей, например SyncOrAsyncAuth утилиту для объявления глобальных правил аутентификации в настройках:
>>> from dmr.settings import Settings
>>> from dmr.security import SyncOrAsyncAuth
>>> from dmr.security.http import HttpBasicAsyncAuth, HttpBasicSyncAuth
>>> DMR_SETTINGS = {
... Settings.auth: [
... SyncOrAsyncAuth(
... HttpBasicSyncAuth(),
... HttpBasicAsyncAuth(),
... ),
... ],
... }
репозиторий [25] | зеркало [26]
Самый строгий Python линтер ever! Лучший инструмент, чтобы научить вашего агента писать простой и понятный Python код.
За июль вышел новый релиз 1.7.0 [27]. В нем мы:
Запретили писать 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. Опубликую в следующем месяце.
Мониторинг работы GC в CPython: https://github.com/sergey-miryanov/gcmon [28]
Визуализатор workflow для агентов: https://github.com/Sanexxxx777/agent-graph-inspector [29]
Пишем фронтенд прямо на Go + Gox в wasm: https://github.com/graybuton/goframe [30]
Одной строкой:
Выпустил 0.8.0 релиз DI фреймворка для питона punq с поддежкой типизации: репозиторий [31] | зеркало [32]
Выпустил два новых релиза проекта dotenv-linter для проверки корректности ваших .env файлов: больше правил, лучше возможности для игнорирования конкретных проблем: репозиторий [33] | зеркало [34]
Выпустил релиз типов для Django с поддержкой mypy 2.3 (последняя версия на данный момент) репозиторий [35] | зеркало [36]
Подкаст с MoscowPython с новостями из мира питона за июнь [38]
Провел PythoNN митап в Нижнем Новгороде, если живете там (или хотите приехать на следующий), то следите за анонсами [39]. У нас круто и познавательно!
Фух, вроде бы все важное покрыли :) Расскажите, как вам формат? Было ли интересно? Было ли полезно?
Постараюсь выкладывать такие отчеты каждый месяц, если формат вам понравится.
А если вы думаете, что я делаю что-то полезное для мира, то поддержать можно по ссылкам:
Я там ничего не продаю, просто делаю свою работу, если считаете, что я делаю полезное - можно поддержать. Если нет, то я все равно продолжу ее делать.
Подписывайтесь на главный канал [1] сообщества, если вам нравится такое читать. И возможно даже захочется поучаствовать [42]! Буду рад видеть вас частью нашего опенсорс сообщеста :)
Давайте в коммментах обсудим: какой синтаксис для frozendict / frozenset вы бы хотели видеть в Python?
Автор: sobolevn
Источник [43]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/python/455861
Ссылки в тексте:
[1] моего телеграм канала: https://telegram.me/opensource_findings
[2] нашего замечательного чата: https://telegram.me/opensource_findings_chat
[3] frozendict: https://peps.python.org/pep-0814/
[4] написали ПЕП: https://pep-previews--5049.org.readthedocs.build/pep-0841/index.html
[5] перед Free-Threading era: https://www.youtube.com/watch?v=PZu6LyiZVbM&list=PL2Uw4_HvXqvYg5fYUg9tzVrY3zCT7W7Y7&index=23
[6] PEP-798: https://peps.python.org/pep-0798/
[7] https://github.com/python/cpython/pull/153413: https://github.com/python/cpython/pull/153413
[8] PyFrozenDict_New: https://github.com/python/cpython/blob/10a84540c2c2533e31f5f0649cc4e4afdd991ba2/Objects/dictobject.c#L8529
[9] превращать мутабельные данные в имммутабельные данные: https://discuss.python.org/t/add-a-dict-method-to-convert-to-a-frozendict/108102
[10] реализация: https://github.com/vstinner/cpython/pull/16
[11] bytearray.take_bytes: https://discuss.python.org/t/add-take-bytes-n-to-bytearray-providing-a-zero-copy-path-to-bytes/103804
[12] https://github.com/python/cpython/pull/152483: https://github.com/python/cpython/pull/152483
[13] Полный разбор бага: https://telegram.me/opensource_findings/973
[14] репозиторий: https://github.com/msgspec/msgspec
[15] зеркало: https://gitverse.ru/sobolevn/msgspec
[16] такой быстрый: https://telegram.me/opensource_findings/971
[17] frozendict для Python 3.15+: https://github.com/msgspec/msgspec/pull/1105
[18] Улучшение типов msgspec.Meta с корректными @overload типами: https://github.com/msgspec/msgspec/pull/700
[19] репозиторий: https://github.com/wemake-services/django-modern-rest
[20] зеркало: https://gitverse.ru/sobolevn/django-modern-rest
[21] публиковал статью на хабре: https://habr.com/ru/articles/1017036/
[22] Opaque Token аутентификацию: https://django-modern-rest.readthedocs.io/en/latest/pages/auth/token.html
[23] тестирования тротлинга / рейтлимитов: https://github.com/wemake-services/django-modern-rest/pull/1167
[24] проверки корректности JWT токена: https://github.com/wemake-services/django-modern-rest/pull/1153
[25] репозиторий: https://github.com/wemake-services/wemake-python-styleguide
[26] зеркало: https://gitverse.ru/sobolevn/wemake-python-styleguide
[27] новый релиз 1.7.0: https://github.com/wemake-services/wemake-python-styleguide/releases#release-1.7.0
[28] https://github.com/sergey-miryanov/gcmon: https://github.com/sergey-miryanov/gcmon
[29] https://github.com/Sanexxxx777/agent-graph-inspector: https://github.com/Sanexxxx777/agent-graph-inspector
[30] https://github.com/graybuton/goframe: https://github.com/graybuton/goframe
[31] репозиторий: https://github.com/bobthemighty/punq
[32] зеркало: https://gitverse.ru/sobolevn/punq
[33] репозиторий: https://github.com/wemake-services/dotenv-linter
[34] зеркало: https://gitverse.ru/sobolevn/dotenv-linter
[35] репозиторий: https://github.com/typeddjango/django-stubs
[36] зеркало: https://gitverse.ru/sobolevn/django-stubs
[37] Подкаст со Сбером про управление разработкой в век ИИ: https://vkvideo.ru/video-22522055_456247964
[38] Подкаст с MoscowPython с новостями из мира питона за июнь: https://www.youtube.com/watch?v=PZxJsmu-OwU
[39] следите за анонсами: https://telegram.me/pytho_nn_events
[40] https://boosty.to/sobolevn: https://boosty.to/sobolevn
[41] https://github.com/sponsors/wemake-services: https://github.com/sponsors/wemake-services
[42] поучаствовать: https://telegram.me/opensource_findings_python
[43] Источник: https://habr.com/ru/articles/1065404/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1065404
Нажмите здесь для печати.