tgindex
Находки в опенсорсе

Находки в опенсорсе

Статистика

Привет! Меня зовут Никита Соболев. Я занимаюсь опенсорс разработкой полный рабочий день. Тут я рассказываю про #python, #c, #opensource и тд. Поддержать: https://boosty.to/sobolevn РКН: https://vk.cc/cOzn36 Связь: @sobolev_nikita

Последний пост
14 авг.
Последнее чтение
13:41
Постов за неделю
1
Всего постов
24
Тип
открытый
Язык
русский
Категория
Технологии
В каталоге с
12 авг.
Подписчики
12 106
+469 за 3 дн.
Сутки
+66
+0,55%
Неделя
 
Месяц
 
Просмотров на пост
10,4 тыс
24 постов
Вовлечённость
85,6%
к подписчикам
Постов в день
0,1
всего 24
Упоминаний
18
каналов
Охват размещения
оценка
1/24сутки в ленте
2 931
1/48двое суток
3 358
1/72трое суток
3 622

Оценка по просмотрам недавних постов: пост набирает почти всё за первые сутки.

Посты

  • 14 авг.3 30512340

    Какие уникальные фичи есть в django-modern-rest? Иногда, когда я добавляю какие-то фичи в мой https://github.com/wemake-services/django-modern-rest ⭐, то я думаю про себя: почему таких фичей больше нет нигде? Давайте сегодня посмотрим на них. А вы мне расскажите свое мнение в комментах. Семантическая схема Допустим, вы навесили на какой-то свой endpoint auth: class UserController(Controller[MsgspecSerializer]): @modify(auth=[JWTAsyncAuth()]) async def get(self) -> User: ... В OpenAPI автоматически появятся все коды ошибок, которые могут случиться в auth (401). Ничего не надо допом писать. И так происходит со всеми частями фреймворка: добавил throttling=[SyncThrottle(1, Rate.minute)]? Теперь у тебя в ответах автоматом 429. Если нужно, можно отключить любые семантические статусы. Не должно ли такое быть дефолтом везде? Умные типы ошибок Не уходя далеко: как кастомизировать формат ошибки, например, в FastAPI? Через боль. Как поменять в спеке формат? Руками. В DMR мы просто добавили везде error_model как параметр. Можно заменять любые ошибки, все автоматом сконвертится и покажет правильную схему. Зачем? Хочешь Problem Details - используешь. Хочешь свой формат - реализуешь. Можно даже content negotiation на ошибки навесить. Почему никто о таком не думает в других фреймворках? Нормальный throttling Фича, которая принесла мне больше всех боли. Я прочитал throttling реализации во всех фреймворках. В Litestar даже фиксы присылал. 1. Почти нигде из коробки нет поддержки разных алгоритмов, бекендов, иногда даже cache-keys. Очень жаль, есть только обычный counter с бекендом в памяти 2. Нигде (пришлите в комменты контр-пример) нет разделения на throttling до auth и после. Почему такое вообще важно? Чтобы не заддосить auth. И чтобы иметь возможность выдавать per-user правила. Нужны и важны оба варианта 3. Кастомизация заголовков ответа? Ха! Что? Почему? Простое переиспользование кода Когда я смотрю на АПИ разных DRF проектов или FastAPI, мне становится больно. FastAPI строит все на view функциях, которые нельзя нормально кастомизировать. А DRF строит все на импортах строк внутри настроек. А как на счет классов и наследования? У нас подобное сделано как абстрактные generic классы. Например: получить JWT. Можно выбрать любой сериализатор, можно выбрать любые модели для запроса и ответа: class RequestPayload(pydantic.BaseModel): username: str password: str class ResponsePayload(pydantic.BaseModel): access: str refresh: str class ObtainAccessAndRefreshSyncController( ObtainTokensSyncController[ PydanticSerializer, RequestPayload, ResponsePayload, ], ): ... # надо еще переопределить 2 метода Все типизировано, документировано, очевидно. Как вы думаете, почему так больше никто не делает? Внешние вьюхи Интегрировать один фреймворк в другой - крайне сложно. Вот мы недавно даже стрим проводили, потому что не могли использовать dj-rest-auth из DRF. Так быть не должно. Теперь в DMR можно использовать любые внешние Django View. Хоть DRF, хоть django-ninja, хоть ванильные вьюхи. И отображать любой внешний OpenAPI. Вот настолько просто: raw_schema = read_openapi_yaml('openapi.yml') router = Router( urls=[ external_path( 'number/', number, name='number', openapi=load_schema(raw_schema['paths']['/api/number'], PathItem), ), ], ) Почему другие фреймворки не стараются вписать существующие решения? Одной строкой - У нас есть еще куча других крутых фичей! Заглядывайте в наш чатик по DMR @django_modern_rest - Релизнули django-stubs@6.1 с поддержкой django@6.1 - Сделали папку с крутыми каналами ребят из нашего Python сообщества. Смело можно закидывать коллегам как базовую папку "на кого подписаться в тг по питону". Внутри все мои друзья и коллеги, советую! Поддержать мою работу над опенсорсом и данным каналом можно на бусти и github sponsors. Знание, что люди тебя поддерживают, мотивирует работать дальше!

  • 4 авг.7 525201190

    Я попробовал uv, обплевался и вернулся на poetry Если вы последние полгода не читали чат, то вы не знаете, как сильно у меня горело после перехода на uv. Сейчас расскажу! Данный пост специально написан, чтобы и у вас тоже сгорело. Если к концу вы подойдете без боли в ... душе, то все было зря. Как все было? Обычно, я первый прыгаю пробовать все новые и модные штуки. Так было с Pipenv, когда он только появился. Что там творилось! Так было с poetry, которым я начал пользоваться через год, в 2018м. Но вот с uv так не было, потому что я не знал, зачем бы мне хотелось переехать. Быстрее? Но ладно, мы решили попробовать его на 3х проектах. Быстро же! Ух 🌚 - https://github.com/typeddjango/django-stubs (терпим) - https://github.com/wemake-services/django-modern-rest (сложно съехать) - https://github.com/wemake-services/wemake-django-template (откатили) Почему? Ох, даже не знаю с чего начать! Я напишу только про то, с чем столкнулся лично. - У uv нет своей системы сборки для бинарников, uv_build умеет собирать только Python пакеты. DMR компилирует часть своих исходников, нам приходится доставлять hatch (другой пакетный менеджер), чтобы вообще иметь возможность работать. Топ кек - Полное игнорирование стандартов. PEP 751? pip.conf? Нет, не слышали. - uv публиковал нам сломанные README в PyPI, пришлось ставить плагин - uv ставит вам совсем не тот питон, который вы думаете. pyenv ставит вам ровно то, что вы попросили, то uv тянет "relocatable" сборку: питон собранный на одной платформе для других платформ. Будет ли все работать? Конечно же нет. Сборка с ним пакетов локально просто не работает. Разбираться в C стектрейсе я не стал, просто поставил нужный питон через pyenv - Сломанные дефолты. uv run автоматически ставит зависимости, что скрывало от нас баг в CI (нужно ставить UV_NO_SYNC=1. uv sync создает виртуально окружение без pip (нужно указывать UV_VENV_SEED=1), из-за чего pip install package ставит пакет в глобальный pip, даже с включенным venv. Ну то есть: вирутальное окружение начинает протекать :( - Для шаблонов uv не подходит, потому что добавляет имя пакета в uv.lock (даже когда ты явно говоришь, что сам пакет ставить не надо), если оно меняется, то лок разваливается. Нам пришлось убрать параметризацию имени пакета в шаблоне. Потом откатили - uv записывает текущую версию проекта (не пакета) в uv.lock, что ломает релиз тулы вроде semantic-release - uv add package добавляет его в dependencies как "package>=current.version", что ломает будущие вызовы uv sync -U, когда к нам прилетают ломающие изменений из новых мажорных версий пакетов - Нет возможности посмотреть устаревшие пакеты как в poetry show --outdated, есть внешний плагин - Не работает uv sync без pyproject.toml, что нужно для кеша докер слоев. Уже джва года - uv допускает дубликаты зависимостей с разными версиями (!), что вызвало у нас странный баг - uv self update не работал в punq из-за наличия tox-uv в зависимостях - uv не работает с dependabot, если используется workspace, пришлось переходить на renovate. PRов просто не было. uv sync -U с workspaces крайне плохо работает, какие-то пакеты приходилось руками обновлять - Для продвижения ty добавили более длинный алиас uvx ty: uv check Ну и самое главное: теперь uv принадлежит OpenAI. Можно ли им доверять важную часть инфраструктуры? Я сильно сомневаюсь. Получил ли я что-то взамен на все мои новые страдания? Пример из wemake-django-template: - poetry: 20 секунд - uv: 11.4 секунды Я понимаю, что для каких-то больших проектов разница может быть значительной. Использовать? Если вы пофиксите дефолты у себя в конфиге, у вас стандартный проект, у вас есть линтер на номера версий, бот для обновления зависимостей, и вы реально замечаете время установки пакетов локально / в CI с другими тулами. Ключевой вопрос: может стоило просто переписать резолвер и скачивание пакетов в poetry на rust? Обсуждение: Какой ваш опыт? Заметили ли вы, что последнее время инструменты хайпят только за счет "скорости"?

  • 3 авг.6 62213581

    Вайбкодим security компоненты в django-modern-rest (или нет) https://www.youtube.com/watch?v=laJQcNAqxc0 В django-modern-rest появилась большая задача: полностью мигрировать в себя dj-rest-auth. Ссылочка (мы ищем контрибьюторов, особенно специалистов в…

  • 1 авг.7 269144100

    Вайбкодим security компоненты в django-modern-rest (или нет) https://www.youtube.com/watch?v=laJQcNAqxc0 В django-modern-rest появилась большая задача: полностью мигрировать в себя dj-rest-auth. Ссылочка (мы ищем контрибьюторов, особенно специалистов в безопасности OAuth / MFA / Passkeys!): https://github.com/wemake-services/django-modern-rest/issues/1193 На что @fastnewsdev (как главный пропагандист ИИИ = "инженерный искусственный интеллект" ) сказал, что он мне такое заваншотит клодом еще до того, как я успею выпить бутылку пива. Ну вот и посмотрим, пиво я пью быстро 🌚 Завтра стрим в 19:00 на канале вялых питонов: - Никита будет вайбкодить Django код (он не знает джангу) - Никита будет ревьюить и пытаться дотащить код хоть до какого-то вменяемого состояния Приходите смотреть, на что реально способен ИИ. Сбивайте нас с толку своими остроумными комментариями, задавайте провокационные вопросы. Обсуждение: На какой результат ставите? Что в итоге получится? За какого Никиту будете болеть лично вы? Сколько Никит будет на стриме? | Поддержать | YouTube | GitHub | Чат |

  • 31 июл.11,6 тыс247100

    id() в питоне не выдает уникальные значения Наверняка, многие из вас, когда изучали питон читали, что id(obj) дает уникальный идентификатор объекта. Его даже назвали, блин, id 🌚️️ Но, все не совсем так. Сегодня будем срывать покровы с одной из самых простых и сложных механик в питоне. Что вообще такое id? Сначала, давайте посмотрим на то, как id() устроен: static PyObject * builtin_id_impl(PyModuleDef *self, PyObject *v) { PyObject *id = PyLong_FromVoidPtr(v); if (id && PySys_Audit("builtins.id", "O", id) < 0) { Py_DECREF(id); return NULL; } return id; } Что тут происходит? 1. Объявляем C builtin функцию id 2. Получаем указатель на объект v, id которого хотим узнать 3. Получаем число из указателя (???) 4. Вызываем событие аудита для id 5. Возвращает результат (или NULL вместе с исключением) Вроде бы все понятно, кроме пункта 3. Как можно из указателя получить обычный int? Сначала разберемся с указателем в C. int x = 0; int *y = &x; Здесь x - значение в ячейке памяти. А *y - указатель на ячейку x. *y хранит в себе числовой адрес оригинальной ячейки. Указывает на оригинальное значение в памяти. Как в известном меме. По сути - работает как одна lea инструкция из x86_64. https://godbolt.org/z/9xco3j8Y4 Возвращаемся к нашей C-API функции: PyObject * PyLong_FromVoidPtr(void *p) { // тут еще есть код унификации разных размерностей для разных платформ, но я его выкинул для простоты return PyLong_FromUnsignedLongLong((unsigned long long)(uintptr_t)p); } А что за касты? - uintptr_t - числовой тип, в который гарантировано поместится любой адрес - unsigned long long - достаточно большой числовой тип, который поддерживает C-API Вот так мы и получили int из указателя. Потому что указатель и есть числовое значение изначально. Так почему не уникальный? Потому что ячейки памяти можно и нужно переиспользовать! Простой пример: x, y, z = 1, 2, 3 t1 = (x, y, z) id1 = id(t1) del t1 a, b, c = 4, 5, 6 t2 = (a, b, c) assert id(t2) == id1 Создаем кортеж, сохраняем его id, доводим его счетчик ссылок до 0, он удаляется, создаем новый такого же размера. Другой объект, другие значения. Получаем такой же id. Пу-пу-пу. Почему так? Потому что в питоне есть freelist оптимизация: мы не выкидываем участки памяти под контейнеры частых размеров, а просто переиспользуем ту же память под новые контейнеры. Потому что аллокация памяти - очень дорогая. Одинаковый участок памяти = одинаковый адрес указателя = одинаковый id. Итого, правильная формулировка: id выдает уникальные значения для одновременно-живущих объектов. Обсуждение: Узнали ли вы что-то новое сегодня? Какие еще части питона вы хотели бы разобрать? | Поддержать | YouTube | GitHub | Чат |

  • 13 июл.9 00914762

    Внутри питона есть ЕЩЕ виртуальные машины Мы все знаем, что сам питон - одна большая стековая виртуальная машина, которая выполняет опкоды. Их мы можем посмотреть через 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 июл.8 151102146

    Подкаст про управление разработкой в новой реальности ВК | ТГ Меня пригласили поучаствовать в подкасте, чтобы обсудить разработку с ИИ и управление разработкой с ИИ. Участники: - Кирилл Меньшов, старший вице-президент и руководитель блока «Технологии» Сбера - Дмитрий Иванов, руководитель 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+

  • 6 июл.9 85720762

    Наконец-то полезные фичи в питоне Многие знают, что я почти не добавляю новые фичи в 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 июн.9 6365234

    Пользуясь случаем: у нас 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 июн.14,9 тыс144300

    Если вы понимаете данный баг, то вы знаете питон лучше 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 июн.10,2 тыс245170

    Генерируем 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 | Чат |

  • 16 июн.8 94816862

    Почему msgspec такой быстрый? Несколько дней назад я решил разобраться в устройстве msgspec. Получилось како бычно: я напал на него со своими PRами, мне через день выдали права на merge и release. Но самое главное: теперь я могу рассказать вам про внутреннее устройство самого быстрого сериализатора для json в питоне. Как быстро распарсить json? Традиционные парсеры json делают так: - Парсим весь json документ - Используем промежуточный слой для хранения json как примитивных Python объектов: dict, list, int, str, None, тд - Превращаем Python объекты в финальный вариант: датаклассы, модели, более сложные типы, тд msgspec использует несколько важных хитростей, чтобы парсить json наиболее быстрым способом. Пример: >>> import msgspec >>> class User(msgspec.Struct): ... username: str ... email: str ... >>> decoder = msgspec.json.Decoder(User) >>> decoder.decode(b'{"username": "example", "email": "email@example.com"}') User(username='example', email='email@example.com') Все самое интересное происходит в JSONDecoder_decode и в json_decode: 1. Мы используем TypeNode *type для мета информации о том, что мы будем парсить. В нашем случае там будет struct User с двумя str полями 2. Далее мы проваливаемся в функцию json_decode_nocustom, она очень красивая: static MS_INLINE PyObject * json_decode_nocustom( JSONDecoderState *self, TypeNode *type, PathNode *path ) { // ... switch (c) { case 'n': return json_decode_none(self, type, path); case 't': return json_decode_true(self, type, path); case 'f': return json_decode_false(self, type, path); case '[': return json_decode_array(self, type, path); case '{': return json_decode_object(self, type, path); case '"': return json_decode_string(self, type, path); default: return json_maybe_decode_number(self, type, path); } } Буквально по первому символу, мы можем парсить нужные части. Хитрый json_decode_object посмотрит, что type у нас MS_TYPE_STRUCT и будет парсить сразу msgspec.Struct. Что еще более хитро, то парситься будут только те ключи, которые явно указаны в User, остальные будут просто пропускаться через вызов json_skip. То есть: ключ в C мы конечно обязаны прочитать в виде char *, чтобы сравнить его с существующими ключами User. Но вот создавать дорогие промежуточные Python объекты мы не будем. Если ключ нам не нужен, то и значение его мы парсить не будем. На выходе получим сразу объект User без промежуточных слоев и их аллокаций. Быстро? Быстро. Минусы На данный момент у msgspec есть главный минус: плохая поддержка Union типов. То есть: некоторые комбинации данных вообще не получится распарсить. Например: str | bytes. Или два датакласса. Или два тайпдикта. Почему? Потому что оптимизации пока мешают работе 🌚 Но, вопрос решаем. Сделаем. Второй минус: мало всего можно выразить. pydantic умеет куда больше. Потому я в django-modern-rest и сделал выбор сериализатора для каждого отдельного контроллера. Чтобы точечно выбирать скорость vs функциональность. Что будет с msgspec дальше? Новые релизы добавят кучу новых фичей. Поддержку pyrefly, heap types, поддержку subinterpreters, FT, более гибкие правила проверок значений и тд. А еще я параллельно добавил поддержку frozendict для Python 3.15+ и предложил сделать новое АПИ для него: PyFrozenDict_FromDictSteal, потому что текущее АПИ работает за O(n * 2), когда можно за O(n). Обсуждение: а вы пробовали msgspec? Какие впечатления? | Поддержать | YouTube | GitHub | Чат |

  • 8 июн.13,3 тыс262256

    Зеркало PyPI На нескольких проектах последние несколько дней сталкиваемся с проблемами с доступом к PyPI: как локально, так и в CI. Печально. All attempts to connect to pypi.org failed. Probable Causes: - the server is not responding to requests at the moment - the hostname cannot be resolved by your DNS - your network is not connected to the internet Если у вас есть такая же проблема, можете воспользоваться PyPI зеркалом от GitVerse: https://gitverse.ru/docs/artifactory/registry-mirrors/pypi-mirror?utm_source=tg&utm_medium=fix&utm_campaign=bloggers&utm_content=post&utm_term=nikitasobolev&utm_erid=2VfnxxwjcVp Все пакеты и все версии, которые есть на PyPI - оттуда тоже доступны. Перевел консалтинговые проекты - заработало. Как настроить? pip, документация: # Установка одного пакета: pip install attrs --extra-index-url https://pypi-mirror.gitverse.ru/simple/ # Настройка для всех команд: pip config --user set global.index-url https://pypi-mirror.gitverse.ru/simple/ pip config --user set global.trusted-host pypi-mirror.gitverse.ru Можно настроить как альтернативный, а не главный индекс: вместо global.index-url используйте global.extra-index-url. poetry, документация: # pyproject.toml [[tool.poetry.source]] name = "pypi" priority = "primary" [[tool.poetry.source]] name = "gitverse" url = "https://pypi-mirror.gitverse.ru/simple/" priority = "supplemental" Сначала пробуем pypi, если не вышло - идем в зеркало. Можно повернуть priority в зависимости от ваших задач. uv, документация: # pyproject.toml [[tool.uv.index]] url = "https://pypi.org/simple/" name = "pypi" default = true [[tool.uv.index]] url = "https://pypi-mirror.gitverse.ru/simple/" name = "gitverse" Здесь аналогично, default имеет самый низкий приоритет. Важно: обратите внимание, чтобы при использовании любых зеркал, у вас были корректные хеши пакетов при установке. poetry и uv делают такое по-умолчанию. А вот pip требует явного --require-hashes параметра. Сам pip тем временем не умеет дампить хеши, но pip-tools умеет 🌚 Пример, корректной работы: » uv sync --default-index https://pypi-mirror.gitverse.ru/simple/ Resolved 171 packages in 20ms Checked 102 packages in 13ms Еще есть зеркала для: - DockerHub - NPM - Maven Обсуждение: вас затронула проблема? Реклама. ПАО "СБЕРБАНК", ИНН 7707083893. erid: 2VfnxxwjcVp

  • 5 июн.10,4 тыс14116

    Анонс стрима: "работаем над lazy import'ами в CPython и плачем под аниме" (на превью - я на стриме) Мы с @nkhitrov_blog, @fastnewsdev и Денисом Аникиным (в 2026 и без тг канала!) решили замутить стрим по питону и ... новый канал на ютюбе под названием "Вялые…

  • 3 июн.9 4926220

    Анонс стрима: "работаем над lazy import'ами в CPython и плачем под аниме" (на превью - я на стриме) Мы с @nkhitrov_blog, @fastnewsdev и Денисом Аникиным (в 2026 и без тг канала!) решили замутить стрим по питону и ... новый канал на ютюбе под названием "Вялые…

  • 29 мая15,9 тыс14976

    Анонс стрима: "работаем над lazy import'ами в CPython и плачем под аниме" (на превью - я на стриме) Мы с @nkhitrov_blog, @fastnewsdev и Денисом Аникиным (в 2026 и без тг канала!) решили замутить стрим по питону и ... новый канал на ютюбе под названием "Вялые Питоны". Подписаться уже можно вот тут: https://www.youtube.com/@SluggishPythons О чем будет канал? - Менее душный и более мемный чем мой основной - Все еще про питон и всякие хардкорные штуки внутри - Шутки, пиво, лень, слезы - Разные новые форматы, которые мы будем анонсировать постепенно - Разные интересные коллабы с веселыми и умными людьми Контент на старом канале останется таким же, каким и был. Я как раз вернулся из творческого отпуска. Скоро будет завоз по adaptix и django-modern-rest. И финал по vscode. О чем будет первый стрим? - Обсудим мотивацию и устройство PEP-810, потестим разные странные случае, Никита побомбит - Я запилю каких-нибудь пару тасочек в CPython, например https://github.com/python/cpython/issues/150459 - Если я буду плохо рассказывать, что там происходят - пацаны будут меня душить своими любимыми аниме - Если хватит времени, то еще починим setuptools / distutils, а то я все сломал - Выпьем пива со всеми желающими 🍻 Народ в чате проголосовал за время стрима в будний вечер, так что - записываем дату и время: Среда, 3 июня, 19:00 https://www.youtube.com/watch?v=W9Hd5dfxjIU Приходите задавать свои ответы и хорошо проводить время!

  • 26 мая13,7 тыс10354

    PEP 810: Explicit lazy imports На обсуждение вышел новый PEP, который предлагает добавить в Python 3.15 новый вид импортов. https://peps.python.org/pep-0810/ lazy import json lazy from json import dumps Как будет работать? Импорты не будут подгружаться…

  • 20 мая9 95110121

    Free-Threading и итераторы: что могло пойти так? Недавно в питоне появилась новая фича (которую я вам пока не покажу), а я решил сделать новый формат – вопрос-загадку. Мы все знаем, что Free-Threading работает совсем по-другому, вместо одного глобального…

  • 20 мая8 3845035

    Free-Threading и итераторы: что могло пойти так? Недавно в питоне появилась новая фича (которую я вам пока не покажу), а я решил сделать новый формат – вопрос-загадку. Мы все знаем, что Free-Threading работает совсем по-другому, вместо одного глобального GIL, у нас множество критических секций per-object и атомарных операций. Тут обычный питоновский код на тредах. Создаем 10 тредов и идем по итератору, складываем его объекты в одну общую сумму с локом. В итоге должно получиться значение равное sum(range(limit)). Получится ли? import threading import time from test.support import threading_helper limit = 10_000 workers_count = 10 result = 0 result_lock = threading.Lock() start = threading.Event() def producer(limit): for x in range(limit): yield x def consumer(iterator): global result start.wait() total = 0 for x in iterator: total += x with result_lock: result += total iterator = producer(limit) # 🤔 workers = [ threading.Thread(target=consumer, args=(iterator,)) for _ in range(workers_count) ] with threading_helper.wait_threads_exit(): for worker in workers: worker.start() for worker in workers: # Wait for the worker thread to actually start. while worker.ident is None: time.sleep(0.1) start.set() for worker in workers: worker.join() Перед запуском подумайте сначала сами: - Что вообще может произойти? - Как поправить текущую ситуацию в теории? - Как можно поменять код сейчас без каких-либо новых фичей, чтоб заработало? - Что было бы идеально увидеть в качестве решения из коробки? - Где бы вы хотели увидеть такое решение в модулях питона? Ответ и ссылки будут вечером. В комментах - обсуждаем! | Поддержать | YouTube | GitHub | Чат |

  • 14 мая19,1 тыс199430

    ИИ переписал Bun с Zig на Rust PR: https://github.com/oven-sh/bun/pull/30412 (он настолько большой, что гитхаб его не открывает у меня) Последние несколько дней в чате очень плотно обсуждали последнюю ИИ новость. Один из альтернативных JS рантаймов bun полность переписали с zig на #rust. Переписывали, конечно же, используя исключительно агентов и ИИ (от компании Anthropic) . На все про все ушло 10 дней, тесты прошли, перформанс остался такой же. Звучит красиво? Красиво. Таймлайн истории 1. 2 декабря 2025 года Anthropic покупает bun и всю команду: https://bun.com/blog/bun-joins-anthropic 2. Команда Zig известна своим "No AI Slop" policy (прямо как django-modern-rest), некоторые люди сразу предсказывали конфликт интересов между Bun + Anthropic и Zig 3. 26 апреля 2026 года, команда bun форкает zig и добавляет туда поддержку параллельного семантического анализа https://x.com/bunjavascript/status/2048427636414923250 4. 9 мая открывается тот самый PR 5. 14 мая он успешно смерджен Важные детали А вот тут начинается интересное. - Для начала авторы Zig объяснили, что подход форка с семаналом некорректный, и что они сами работают над данной фичей, скоро она будет доступна: https://ziggit.dev/t/bun-s-zig-fork-got-4x-faster-compilation-times/15183/19 - Билды получились недетерминированные, о чем им и рассказала кор-команда. Тогда форк пришлось закопать, видимо Теперь посмотрим на качество PR. - Качество кода там примерно вот такое: https://github.com/oven-sh/bun/commit/d144fa6e20ab65d55add82ef3241609dcbb04cdc (то есть - никакое) - Файлы в нем даже были неотформатированы встроенным cargo fmt, что делается буквально в каждом Rust проекте: https://github.com/oven-sh/bun/pull/30695 - Ревью не было, потому что внутри PRа +1 009 257, -4 024 и 6000+ коммитов - unsafe в коде встречает 10487 раз (да, там много ffi, но все равно). Для сравнения в uv (кода правда меньше в 2 раза) - всего 73 раза - "Скорость работы осталось такой же" - довольно странный тезис, учитывая что zig и rust оба генерят код через LLVM, часто практически идентичный, заслуги ИИ здесь нет Выводы - Прикольно, что такое вообще можно сделать (с неограниченными токенами) - Как теперь bun будет владеть своей базой кода, кто сможет в ней разобраться и что-то пофиксить - вопрос открытый - Какой смысл во всем действии (кроме очевидного маркетинга) - вопрос открытый - Брать ли теперь bun в прод? Конечно нет Обсуждение: что вы думаете по данному вопросу? Стали бы использовать bun у себя в проекте в новом виде? | Поддержать | YouTube | GitHub | Чат |