Обстановка в опенсорсе прямо сейчас: https://github.com/wemake-services/wemake-python-styleguide/issues/3596
А пока - все вместе ждем релиза
А пока - все вместе ждем релиза
django_modern_rest, уже скоро.3😁226🤡37❤13🔥8👏6🤔6😢5
PEP-814: frozendict
В Python 3.15 появится полноценный иммутабельный словарь.
PEP: https://peps.python.org/pep-0814
Обсуждение: https://discuss.python.org/t/pep-814-add-frozendict-built-in-type/104854
Оригинальный PR: https://github.com/python/cpython/pull/144757
Исходники (да, они с
Зачем?
Главный вопрос: зачем питону вдруг через 35 лет понадобился иммутабельный словарь? Мотивации в ПЕПе явно не очень хватает. Но я докину:
1.
2. С иммутабельными объектами куда проще работать в режиме Free-Threading
3. Многие другие новые идеи вроде Виртуальных Потоков тоже хотели бы иметь аналог иммутабельного словаря
Ну а
И вот у нас появилась точная копия обычного
Примеры
Но не умеет ничего из
Зато умеет в
Как его менять? А вот так, создавая новые:
Детали реализации
Чтобы вы понимали, насколько они похожи:
Интересно, как работает
В C-API тоже добавили функций для работы с новым словарем:
А еще половина stdlib поменяет константы с
Отличный ПЕП, простая реализация, крутая фича. Питон победа!
Обсуждение: как вы относитесь к иммутабельности в питоне и вообще?
| Поддержать | YouTube | GitHub | Чат |
В Python 3.15 появится полноценный иммутабельный словарь.
PEP: https://peps.python.org/pep-0814
Обсуждение: https://discuss.python.org/t/pep-814-add-frozendict-built-in-type/104854
Оригинальный PR: https://github.com/python/cpython/pull/144757
Исходники (да, они с
dict лежат в одном файле на 8к строк)Зачем?
Главный вопрос: зачем питону вдруг через 35 лет понадобился иммутабельный словарь? Мотивации в ПЕПе явно не очень хватает. Но я докину:
1.
frozendict можно будет шарить между разными интерпретаторами без какого-либо оверхеда2. С иммутабельными объектами куда проще работать в режиме Free-Threading
3. Многие другие новые идеи вроде Виртуальных Потоков тоже хотели бы иметь аналог иммутабельного словаря
Ну а
types.MappingProxyType(mapping) был только surface-immutable. Все равно можно было поменять оригинальный объект mapping.И вот у нас появилась точная копия обычного
dict, только иммутабельная:Примеры
frozendict является collections.abc.Mapping и таким же Generic с двумя параметрами:
>>> frozendict.__mro__
(<class 'frozendict'>, <class 'object'>)
>>> obj = frozendict({'a': 1})
>>> frozendict[str, int]
frozendict[str, int]
Но не умеет ничего из
collections.abc.MutableMapping:
>>> obj['a'] = 2
TypeError: 'frozendict' object does not support item assignment
>>> obj.update
AttributeError: 'frozendict' object has no attribute 'update'
Зато умеет в
hash, если все ключи и значения умеют в hash:
>>> hash(obj)
6343282633043897990
>>> hash(frozendict({1: []}))
TypeError: unhashable type: 'list'
Как его менять? А вот так, создавая новые:
>>> obj = frozendict({'a': 1})
>>> id(obj)
4352339472
>>> obj |= {'b': 2} # <- тут мы создали новый frozendict
>>> obj
frozendict({'a': 1, 'b': 2})
>>> id(obj)
4352341680
Детали реализации
Чтобы вы понимали, насколько они похожи:
frozendict просто переиспользует clinic макросы dict для определения своих методов (=использует те же методы):
static PyMethodDef frozendict_methods[] = {
DICT___CONTAINS___METHODDEF
{"__getitem__", dict_subscript, METH_O | METH_COEXIST, getitem__doc__},
DICT___SIZEOF___METHODDEF
DICT_GET_METHODDEF
DICT_KEYS_METHODDEF
DICT_ITEMS_METHODDEF
DICT_VALUES_METHODDEF
DICT_FROMKEYS_METHODDEF
DICT_COPY_METHODDEF
DICT___REVERSED___METHODDEF
{"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")},
{NULL, NULL} /* sentinel */
};
Интересно, как работает
hash: он полностью дублирует алгоритм хеша из frozenset.В C-API тоже добавили функций для работы с новым словарем:
PyFrozenDict_New, PyAnyDict_Check проверяет на dict, frozendict или их подтипы.А еще половина stdlib поменяет константы с
dict на frozendict.Отличный ПЕП, простая реализация, крутая фича. Питон победа!
Обсуждение: как вы относитесь к иммутабельности в питоне и вообще?
| Поддержать | YouTube | GitHub | Чат |
Python Enhancement Proposals (PEPs)
PEP 814 – Add frozendict built-in type | peps.python.org
A new public immutable type frozendict is added to the builtins module.
3👍169🔥45❤11🎉5😢2🥰1😁1👌1
PEP-747: TypeForm, или "аннотируем аннотации"
PEP: https://peps.python.org/747
Реализация: https://github.com/python/cpython/pull/145034
Что и зачем?
Представьте, что вам нужно описать, что какая-то функция может принять в качестве входного аргумента любую аннотацию. Например для валидации как в пидантике. Как бы вы такое сделали?
Но, на самом деле у нас тут есть ошибка.
Например,
Пример в pyright.
Данная фича реально нужна авторам библиотек, кто строит свою логику работы на типах.
Реальный пример: в django-modern-rest (нативная интеграция 🌚️️️️) мы создаем метаданные об ответе
На данный момент
Но в идеале мы будем использовать
Как и многие другие:
• pydantic
• msgspec
• dishka (использует аннотации для DI)
• Даже некоторые места stdlib, например: dataclasses.fields
Итоговый пример:
Текущее состояние
В
В pyright поддержка уже есть полтора года как.
В Python3.15 будет нативно в
Еще одна хорошая фича.
Обсуждение: какие варианты использования
| Поддержать | YouTube | GitHub | Чат |
PEP: https://peps.python.org/747
Реализация: https://github.com/python/cpython/pull/145034
Что и зачем?
Представьте, что вам нужно описать, что какая-то функция может принять в качестве входного аргумента любую аннотацию. Например для валидации как в пидантике. Как бы вы такое сделали?
type[T]?
from typing import Any
def validate[T](typ: type[T], value: Any) -> None: ...
validate(int, 1)
Но, на самом деле у нас тут есть ошибка.
typ будет ожидать любой объект класса type, а не любую аннотацию.Например,
int | str не является объектом класса type, но является валидной аннотацией. Так же как и: None, Literal[1], Self, T, тд.Пример в pyright.
Данная фича реально нужна авторам библиотек, кто строит свою логику работы на типах.
Реальный пример: в django-modern-rest (нативная интеграция 🌚️️️️) мы создаем метаданные об ответе
ResponseSpec(return_type=int | str, status_code=200) для такого кода пользователя:
from dmr import Controller
from dmr.plugins.msgspec import MsgspecSerializer
class UserController(Controller[MsgspecSerializer]):
async def get(self) -> int | str: ...
На данный момент
ResponseSpec.return_type имеет аннотацию Any, как единственный рабочий вариант. Но в идеале мы будем использовать
TypeForm[Any] в ближайшее время.Как и многие другие:
• pydantic
• msgspec
• dishka (использует аннотации для DI)
• Даже некоторые места stdlib, например: dataclasses.fields
Итоговый пример:
from typing_extensions import TypeForm, Any
def validate[T](typ: TypeForm[T], value: Any) -> T: ...
reveal_type(validate(int, 1)) # int
reveal_type(validate(int | str, 1)) # int | str
Текущее состояние
В
typing_extensions код уже есть, поддержка в mypy будет в версии 1.20 (следующей).В pyright поддержка уже есть полтора года как.
В Python3.15 будет нативно в
typing. Еще одна хорошая фича.
Обсуждение: какие варианты использования
TypeForm есть у вас? | Поддержать | YouTube | GitHub | Чат |
Python Enhancement Proposals (PEPs)
PEP 747 – Annotating Type Forms | peps.python.org
Type expressions provide a standardized way to specify types in the Python type system. When a type expression is evaluated at runtime, the resulting type form object encodes the information supplied in the type expression. This enables a variety of use...
2❤68🔥26👍22💩4👌1
PEP-827: Самое интересное, что случалось с типами в питоне!
Текст: https://peps.python.org/pep-0827/
Обсуждение: https://discuss.python.org/t/pep-827-type-manipulation/106353
Если вы когда-то писали на TypeScript (одобряем) или на каких-то других языках с продвинутой системой типов, вам всегда должно было быть больно от того, что происходит в Python.
Да, тут можно выразить некоторые простые вещи. Но, как например типизировать такой код?
Никак, обидно. Я даже 100 лет назад делал такую поделку: https://github.com/wemake-services/mypy-extras
Чтобы хоть как-то решать проблему выше.
Предложение
И вот Юрий Селиванов (автор asyncio и edge-db) предлагает добавить в питон специальные действия над типами.
Чтобы было как в TS, где есть условные и рекурсивные типы, готовые операторы как
Вот что предлагают добавить:
А еще:
– Типовые операторы:
– Методы для интроспекции объектов в типах:
– Создание типов внутри аннотаций:
Пример
Показать детали работы всего я, конечно, не смогу. Но смогу показать один пример из ПЕПа.
Понятная проблема: есть какая-то модель пользователя. При создании данной модели - мы указываем все поля, кроме
Сейчас мы делаем что-то типа
Но, мы можем создавать такие модели при помощи типов.
Полный код: https://github.com/vercel/python-typemap/blob/main/tests/test_fastapilike_2.py
Данная страшная конструкция будет спрятана внутри
А внутри уже:
– Полная типизация всех полей
– Новая корректная модель, которая всегда актуальна
Круто?
Мое мнение: в детали данного предложения я пока не вникал, но в целом - направление правильное.
Обсуждение: а что вы думаете про такое развитие типизации в питоне?
P.S. Из телеги и ютюба не перекатываемся. Рекламы на канале и так почти не было, для меня - мало что меняется.
Если вы хотите поддерживать мою работу в опенсорсе и контент без рекламы скам-курсов и вечных прогревов, то всегда можно закинуть на бусти: https://boosty.to/sobolevn
| Поддержать | YouTube | GitHub | Чат |
Текст: https://peps.python.org/pep-0827/
Обсуждение: https://discuss.python.org/t/pep-827-type-manipulation/106353
Если вы когда-то писали на TypeScript (одобряем) или на каких-то других языках с продвинутой системой типов, вам всегда должно было быть больно от того, что происходит в Python.
Да, тут можно выразить некоторые простые вещи. Но, как например типизировать такой код?
@dataclass
class User:
username: str
age: int
def get_field(obj: Any, field_name: str) -> Any:
return getattr(obj, field_name)
user = User('example', 18)
username = get_field(user, 'username')
# ^ мы знаем, что тут `str`, но никак не можем такое выразить, кроме КУЧИ `@overload` для конкретного типа
# а для общего случая - вообще никак
Никак, обидно. Я даже 100 лет назад делал такую поделку: https://github.com/wemake-services/mypy-extras
Чтобы хоть как-то решать проблему выше.
Предложение
И вот Юрий Селиванов (автор asyncio и edge-db) предлагает добавить в питон специальные действия над типами.
Чтобы было как в TS, где есть условные и рекурсивные типы, готовые операторы как
keyof и куча дополнительных типов в npm.Вот что предлагают добавить:
<type> = ...
# Type booleans are all valid types too
| <type-bool>
# Conditional types
| <type> if <type-bool> else <type>
# Types with variadic arguments can have
# *[... for t in ...] arguments
| <ident>[<variadic-type-arg> +]
# Type member access
| <type>.<name>
| GenericCallable[<type>, lambda <args>: <type>]
А еще:
– Типовые операторы:
IsAssignable, IsEquivalent, GetArg, FromUnion, тд– Методы для интроспекции объектов в типах:
Members, Attrs, GetMember, тд– Создание типов внутри аннотаций:
NewProtocol, NewTypedDictПример
Показать детали работы всего я, конечно, не смогу. Но смогу показать один пример из ПЕПа.
Понятная проблема: есть какая-то модель пользователя. При создании данной модели - мы указываем все поля, кроме
primary_key. Но показывать мы будем наружу все поля, кроме password.Сейчас мы делаем что-то типа
class UserBase(SQLModel):
name: str = Field(index=True)
age: int | None = Field(default=None, index=True)
class User(UserBase, table=True):
id: int | None = Field(default=None, primary_key=True)
password: str = Field(hidden=True)
class UserPublic(UserBase):
id: int
class UserCreate(UserBase):
password: str
Но, мы можем создавать такие модели при помощи типов.
Полный код: https://github.com/vercel/python-typemap/blob/main/tests/test_fastapilike_2.py
# Extract the default type from an Init field.
# If it is a Field, then we try pulling out the "default" field,
# otherwise we return the type itself.
type GetDefault[Init] = (
GetFieldItem[Init, Literal["default"]]
if typing.IsAssignable[Init, Field]
else Init
)
# Create takes everything but the primary key and preserves defaults
type Create[T] = typing.NewProtocol[
*[
typing.Member[
p.name,
p.type,
p.quals,
GetDefault[p.init],
]
for p in typing.Iter[typing.Attrs[T]]
if not typing.IsAssignable[
Literal[True],
GetFieldItem[p.init, Literal["primary_key"]],
]
]
]
Данная страшная конструкция будет спрятана внутри
SQLModel, а мы будем писать просто:
UserCreate = Create[User]
А внутри уже:
– Полная типизация всех полей
– Новая корректная модель, которая всегда актуальна
Круто?
Мое мнение: в детали данного предложения я пока не вникал, но в целом - направление правильное.
Обсуждение: а что вы думаете про такое развитие типизации в питоне?
P.S. Из телеги и ютюба не перекатываемся. Рекламы на канале и так почти не было, для меня - мало что меняется.
Если вы хотите поддерживать мою работу в опенсорсе и контент без рекламы скам-курсов и вечных прогревов, то всегда можно закинуть на бусти: https://boosty.to/sobolevn
| Поддержать | YouTube | GitHub | Чат |
Python Enhancement Proposals (PEPs)
PEP 827 – Type Manipulation | peps.python.org
We propose adding powerful type-level introspection and construction facilities to Python’s type system. This design is inspired largely by TypeScript’s conditional and mapped types, but is adapted to the distinct semantics and constraints of Python’s t...
4🔥101👍31🤔19💩13❤12👎10🤮4🎉2👌1
django-modern-rest@0.1.0 – первый публичный релиз!
Исходники: https://github.com/wemake-services/django-modern-rest
Подробнейшая документация: https://django-modern-rest.readthedocs.io
Пример настоящего приложения: https://github.com/wemake-services/wemake-django-template
Первый анонс был уже какое-то время назад.
Так что давайте повторять, что у нас тут происходит.
Во-первых, у нас рекорд: еще нет ни одного релиза, а уже 560+ ⭐ на Гитхабе (сходите поставьте, кто еще не).
Вижу, что люди ждут, вижу интерес. Спасибо!
Фичи
– Главная фича, которая вообще подтолкнула меня к такому проекту: инфраструктура Джанги. Тут есть буквально все пакеты на все случаи жизни. Но не было нормального REST фреймворка. В комментах я регулярно наблюдал, как люди ненавидят Джангу, но почти всегда говорят про DRF. Да, он был ужасен – но теперь он на свалке истории!
– Все существующие плагины к родной Джанге должны работать
– Официальная поддержка Джанго в одном файле, да, Джанга может быть настолько простой
– Работаем с любыми моделями: pydantic, msgspec, TypedDict, dataclass, тд. Сериализация и валидация не прибиты гвоздями. А значит можно выбирать сериализатор под контроллер. Где-то msgspec + TypedDict для скорости. Где-то pydantic для более широких возможностей валидации. Можно писать свои
– Скорость. Мы довольно быстрые. Самый быстрый Python фреймворк для REST в Django. По скорости можно сравнивать с FastAPI, мы всего лишь на 30% медленнее. Но у нас и Джанга вообще-то. Скорость будет улучшаться, есть разные интересные идеи
– Типизация: типизировано всё! Но самое важное, типизацию не пихают вам в лицо. Нет огромных и сложных типов. Все просто, надежно и удобно. Поддерживаем
– Поддержка
– SSE! Без дополнительных костылей: просто работает (с валидацией сообщений и возможностью строить бизнесовые ADT поверх типов сообщений и крутейшей схемой)
– Семантика. Одна из ключевых фичей: мы очень сильно упоролись по генерации схемы. Добавил
– Swagger, Scalar, Redoc из коробки, легко настраивать
– Работаем не только с json, поддерживаем content negotiation, можно писать свои парсеры и рендереры
– JWT и DjangoSessionAuth из коробки, есть возможность отзыва токенов и сессий
– Возможность писать заготовки контроллеров и полностью переиспользовать код. Писать плагины под
– Загрузка и отдача файлов (но на питоне такое очень осторожно надо делать, лучше на Rust)
– Нет привязки к логике или DI (берите любой, например dishka). Мы просто парсим данные и возвращаем их. То есть: код не превратится в кашу из логики и фреймворка уже через 10 бизнес фичей
– Удобная обработка ошибок на многих уровнях
– Полная возможность для кастомизации. Можно даже поменять формат внутренних ошибок в рамках контроллера
– Удобные тесты:
– Скилы для LLM для написания кода по OpenAPI спеке,
– Но никакого нейрослопа внутри!
Исходники: https://github.com/wemake-services/django-modern-rest
Подробнейшая документация: https://django-modern-rest.readthedocs.io
Пример настоящего приложения: https://github.com/wemake-services/wemake-django-template
Первый анонс был уже какое-то время назад.
Так что давайте повторять, что у нас тут происходит.
Во-первых, у нас рекорд: еще нет ни одного релиза, а уже 560+ ⭐ на Гитхабе (сходите поставьте, кто еще не).
Вижу, что люди ждут, вижу интерес. Спасибо!
import uuid
import msgspec
from dmr import Body, Controller
from dmr.plugins.msgspec import MsgspecSerializer
class UserCreateModel(msgspec.Struct):
email: str
class UserModel(UserCreateModel):
uid: uuid.UUID
class UserController(
Controller[MsgspecSerializer],
Body[UserCreateModel],
):
def post(self) -> UserModel:
return UserModel(uid=uuid.uuid4(), email=self.parsed_body.email)
Фичи
– Главная фича, которая вообще подтолкнула меня к такому проекту: инфраструктура Джанги. Тут есть буквально все пакеты на все случаи жизни. Но не было нормального REST фреймворка. В комментах я регулярно наблюдал, как люди ненавидят Джангу, но почти всегда говорят про DRF. Да, он был ужасен – но теперь он на свалке истории!
– Все существующие плагины к родной Джанге должны работать
– Официальная поддержка Джанго в одном файле, да, Джанга может быть настолько простой
– Работаем с любыми моделями: pydantic, msgspec, TypedDict, dataclass, тд. Сериализация и валидация не прибиты гвоздями. А значит можно выбирать сериализатор под контроллер. Где-то msgspec + TypedDict для скорости. Где-то pydantic для более широких возможностей валидации. Можно писать свои
– Скорость. Мы довольно быстрые. Самый быстрый Python фреймворк для REST в Django. По скорости можно сравнивать с FastAPI, мы всего лишь на 30% медленнее. Но у нас и Джанга вообще-то. Скорость будет улучшаться, есть разные интересные идеи
– Типизация: типизировано всё! Но самое важное, типизацию не пихают вам в лицо. Нет огромных и сложных типов. Все просто, надежно и удобно. Поддерживаем
mypy, pyright, pyrefly в самых строгих вариантах– Поддержка
async везде. От вьюх и моделей до SSE. Никаких sync_to_async внутри– SSE! Без дополнительных костылей: просто работает (с валидацией сообщений и возможностью строить бизнесовые ADT поверх типов сообщений и крутейшей схемой)
– Семантика. Одна из ключевых фичей: мы очень сильно упоролись по генерации схемы. Добавил
auth= в контроллер? В списке ответов появился 401 статус код автоматически. Возвращаешь ответ, заголовок, куку, которой нет в спеке? Во время дебага – случится ошибка валидации. На проде валидацию нужно отключать для скорости. Так мы гарантируем точность ответов и схемы. Не нравится схема? Все легко переопределить или вообще отключить– Swagger, Scalar, Redoc из коробки, легко настраивать
– Работаем не только с json, поддерживаем content negotiation, можно писать свои парсеры и рендереры
– JWT и DjangoSessionAuth из коробки, есть возможность отзыва токенов и сессий
– Возможность писать заготовки контроллеров и полностью переиспользовать код. Писать плагины под
dmr будет просто и удобно– Загрузка и отдача файлов (но на питоне такое очень осторожно надо делать, лучше на Rust)
– Нет привязки к логике или DI (берите любой, например dishka). Мы просто парсим данные и возвращаем их. То есть: код не превратится в кашу из логики и фреймворка уже через 10 бизнес фичей
– Удобная обработка ошибок на многих уровнях
– Полная возможность для кастомизации. Можно даже поменять формат внутренних ошибок в рамках контроллера
– Удобные тесты:
polyfactory, pytest, schemathesis (проходим все правила из коробки)– Скилы для LLM для написания кода по OpenAPI спеке,
llms-full.txt, Context7 для контекста– Но никакого нейрослопа внутри!
GitHub
GitHub - wemake-services/django-modern-rest: Modern REST framework for Django with types and async support!
Modern REST framework for Django with types and async support! - wemake-services/django-modern-rest
86🔥235👏34❤21👍11🎉8💩4🤔3😢1🤮1
Начало: https://xn--r1a.website/opensource_findings/950
Что будет дальше?
– Доработка доки. Я хочу, чтобы люди заново открывали для себя Джангу (лучший фреймворк для веба на питоне, имхо). Изучали лучшие практики, думали про архитектуру. Сейчас дока в хорошем состоянии, но нет предела совершенству
– Мы еще даже не пробовали значительно ускорить проект. В рамках идей: переписывания кусков на Rust, Cython, компиляция кода mypyc
– Поддержка WebSocket без
– Поддержка других форматов стриминга кроме SSE, например
– Поддержка
– Скилы для LLM для автоматизации перехода с
– Поддержка
Благодарности
Данный проект не стал бы возможен без:
– Александра и Алексея – соавторов проекта, они затащили гигантский объем работы
– Виктора, кто сделал нам офигительную интерактивную доку!
– А так же 51 других контрибьюторов, кто внес неоценимый вклад в проект
Большое спасибо всем за помощь, обратную связи и поддержку, без вас – ничего бы не вышло.
Лучшее сообщество! 🫶
Ну а я – делаю небольшой перерыв, отдыхаю и работаю дальше!
Обсуждение: какие фичи вы бы хотели увидеть в дальнейших релизах?
P.S. Если у вас есть подкаст / канал / тд, и вы хотите поговорить со мной про веб фреймворки на питоне – пишите в личку! Сделаем интересное :)
| Поддержать | YouTube | GitHub | Чат |
Что будет дальше?
– Доработка доки. Я хочу, чтобы люди заново открывали для себя Джангу (лучший фреймворк для веба на питоне, имхо). Изучали лучшие практики, думали про архитектуру. Сейчас дока в хорошем состоянии, но нет предела совершенству
– Мы еще даже не пробовали значительно ускорить проект. В рамках идей: переписывания кусков на Rust, Cython, компиляция кода mypyc
– Поддержка WebSocket без
django-channels (фу, Артём, без негатива)– Поддержка других форматов стриминга кроме SSE, например
JsonL– Поддержка
cattrs и adaptix– Скилы для LLM для автоматизации перехода с
django-rest-framework и django-ninja– Поддержка
tyБлагодарности
Данный проект не стал бы возможен без:
– Александра и Алексея – соавторов проекта, они затащили гигантский объем работы
– Виктора, кто сделал нам офигительную интерактивную доку!
– А так же 51 других контрибьюторов, кто внес неоценимый вклад в проект
Большое спасибо всем за помощь, обратную связи и поддержку, без вас – ничего бы не вышло.
Лучшее сообщество! 🫶
Ну а я – делаю небольшой перерыв, отдыхаю и работаю дальше!
Обсуждение: какие фичи вы бы хотели увидеть в дальнейших релизах?
P.S. Если у вас есть подкаст / канал / тд, и вы хотите поговорить со мной про веб фреймворки на питоне – пишите в личку! Сделаем интересное :)
| Поддержать | YouTube | GitHub | Чат |
Telegram
Находки в опенсорсе
django-modern-rest@0.1.0 – первый публичный релиз!
Исходники: https://github.com/wemake-services/django-modern-rest
Подробнейшая документация: https://django-modern-rest.readthedocs.io
Пример настоящего приложения: https://github.com/wemake-services/wemake…
Исходники: https://github.com/wemake-services/django-modern-rest
Подробнейшая документация: https://django-modern-rest.readthedocs.io
Пример настоящего приложения: https://github.com/wemake-services/wemake…
8👍132❤48👏21🔥10🤔2💩2😢1
Находки в опенсорсе pinned «django-modern-rest@0.1.0 – первый публичный релиз! Исходники: https://github.com/wemake-services/django-modern-rest Подробнейшая документация: https://django-modern-rest.readthedocs.io Пример настоящего приложения: https://github.com/wemake-services/wemake…»
PEP 828: async yield from и состояние асинхронных генераторов в питоне
PEP: https://peps.python.org/pep-0828
Обсуждение: https://discuss.python.org/t/pep-828-supporting-yield-from-in-asynchronous-generators/106459
Код: https://github.com/python/cpython/pull/145716
В питон хотят добавить
Во-первых, оно реально иногда удобно. Во-вторых, реально консистентно визуально и синтаксически с синхронными генераторами:
С другой стороны: https://pyfound.blogspot.com/2024/06/python-language-summit-2024-limiting-yield-in-async-generators.html
> Guido van Rossum lamented that this was "yet another demonstration that async generators were a bridge too far. Could we have a simpler PEP that proposes to deprecate and eventually remove from the language asynchronous generators, just because they're a pain and tend to spawn more complexity".
> Zac had no objections to a PEP deprecating async generators¹. Zac continued, "while static analysis is helpful in some cases, there are inevitably cases that it misses which kept biting us... until we banned all async generators in our codebase".
И вроде бы на примере выше оно выглядит нормально. Но давайте чуть глубже посмотрим.
Кстати, недавно наш коллега – Сергей Мирянов – добавил секцию "Async generators best practices" в доку asyncio.
Всем советую: https://docs.python.org/3.15/library/asyncio-dev.html#asynchronous-generators-best-practices
Если вы можете найти в данном коде 4 ошибки, то можете не читать доку. Остальным обязательно.
Какие проблемы там подсвечены?
1. Явное использование
2. Порядок очистки ресурсов в асинхронных генераторах может быть не таким, как вы думаете
3. Запуск асинхронных генераторов без event loop - плохая идея
4. Итерация асинхронного генератора из двух разных тасок = ошибка
Так вот! Стоит ли углубляться туда?
Обсуждение: что вы думаете про асинхронные генераторы и их развитие? Можете ли честно сказать, что понимаете, как они работают? Можете найти баги с
| Поддержать | YouTube | GitHub | Чат |
PEP: https://peps.python.org/pep-0828
Обсуждение: https://discuss.python.org/t/pep-828-supporting-yield-from-in-asynchronous-generators/106459
Код: https://github.com/python/cpython/pull/145716
В питон хотят добавить
async yield from. И у меня есть много разных мыслей.Во-первых, оно реально иногда удобно. Во-вторых, реально консистентно визуально и синтаксически с синхронными генераторами:
async def agenerator():
yield 1
yield 2
async def main():
async yield from agenerator()
С другой стороны: https://pyfound.blogspot.com/2024/06/python-language-summit-2024-limiting-yield-in-async-generators.html
> Guido van Rossum lamented that this was "yet another demonstration that async generators were a bridge too far. Could we have a simpler PEP that proposes to deprecate and eventually remove from the language asynchronous generators, just because they're a pain and tend to spawn more complexity".
> Zac had no objections to a PEP deprecating async generators¹. Zac continued, "while static analysis is helpful in some cases, there are inevitably cases that it misses which kept biting us... until we banned all async generators in our codebase".
И вроде бы на примере выше оно выглядит нормально. Но давайте чуть глубже посмотрим.
Кстати, недавно наш коллега – Сергей Мирянов – добавил секцию "Async generators best practices" в доку asyncio.
Всем советую: https://docs.python.org/3.15/library/asyncio-dev.html#asynchronous-generators-best-practices
Если вы можете найти в данном коде 4 ошибки, то можете не читать доку. Остальным обязательно.
import asyncio
work_done = False
async def cursor():
try:
yield 1
finally:
assert work_done
async def rows():
global work_done
try:
yield 2
finally:
await asyncio.sleep(0.1) # immitate some async work
work_done = True
async def main():
async for c in cursor():
async for r in rows():
break
break
asyncio.run(main())
Какие проблемы там подсвечены?
1. Явное использование
aclosing(agen) контекста для закрытия AsyncGenerator, иначе может пропасть стадия "уборки за собой", а сам генератор может остаться живым2. Порядок очистки ресурсов в асинхронных генераторах может быть не таким, как вы думаете
3. Запуск асинхронных генераторов без event loop - плохая идея
4. Итерация асинхронного генератора из двух разных тасок = ошибка
Так вот! Стоит ли углубляться туда?
Обсуждение: что вы думаете про асинхронные генераторы и их развитие? Можете ли честно сказать, что понимаете, как они работают? Можете найти баги с
asyncio.CanceledError и очисткой состояния без запуска кода? Я - нет.| Поддержать | YouTube | GitHub | Чат |
1❤31👍16😢6🔥1🤔1
И сразу бонусом хочу напомнить, что такое обычное выражение
Источник: https://peps.python.org/pep-0380
Никогда не спрашивайте такое на собесах, будьте людьми 🌚
Что будет тут?
Страшно. Очень страшно.
P.S. Два поста в один день, когда такое было?!
RESULT = yield from EXPR в CPython.
_i = iter(EXPR)
try:
_y = next(_i)
except StopIteration as _e:
_r = _e.value
else:
while 1:
try:
_s = yield _y
except GeneratorExit as _e:
try:
_m = _i.close
except AttributeError:
pass
else:
_m()
raise _e
except BaseException as _e:
_x = sys.exc_info()
try:
_m = _i.throw
except AttributeError:
raise _e
else:
try:
_y = _m(*_x)
except StopIteration as _e:
_r = _e.value
break
else:
try:
if _s is None:
_y = next(_i)
else:
_y = _i.send(_s)
except StopIteration as _e:
_r = _e.value
break
RESULT = _r
Источник: https://peps.python.org/pep-0380
Никогда не спрашивайте такое на собесах, будьте людьми 🌚
Что будет тут?
async def agenerator():
yield 1
return 2
async def main():
result = async yield from agenerator()
assert result == 2
Страшно. Очень страшно.
P.S. Два поста в один день, когда такое было?!
Python Enhancement Proposals (PEPs)
PEP 380 – Syntax for Delegating to a Subgenerator | peps.python.org
A syntax is proposed for a generator to delegate part of its operations to another generator. This allows a section of code containing ‘yield’ to be factored out and placed in another generator. Additionally, the subgenerator is allowed to return with ...
2😁57🤯19❤12
tracecov: считаем покрытие АПИ через спецификацию OpenAPI
Вышла новая версия
И там мы выпустили поддержку
В чем суть? Там мы считаем не "покрытие кода", а намного более важную метрику: "покрытие тестами нашего АПИ". Ну то есть буквально:
• Какие операции были вызваны?
• С какими телами и параметрами?
• Какие ответы получены по статусам?
• Какие схемы возвращены?
• Работают ли примеры из доки?
Так как мы используем очень строгую схему - у нас такой подход хорошо работает.
Мы интегрировали поддержку
Один запуск
В
И тогда тесты будут падать при низком покрытии АПИ. Вот куда можно развиваться, если у вас - как у нас - уже 100% обычного покрытия.
Одной строкой
• Добавили поддержку
• Добавили
• Добавили
• Переработали несколько апишек, стало значительно удобнее. Спасибо первым пользователям за обратную связь!
Обсуждение: Воспользовались бы такой метрикой? И какое покрытие вы считаете оптимальным? И почему 100%?
P.S. Выпустил большую статью про
| Поддержать | YouTube | GitHub | Чат |
Вышла новая версия
0.4.0 https://github.com/wemake-services/django-modern-restИ там мы выпустили поддержку
tracecov. Инструмент новый, такого в других фреймворках я не видел.В чем суть? Там мы считаем не "покрытие кода", а намного более важную метрику: "покрытие тестами нашего АПИ". Ну то есть буквально:
• Какие операции были вызваны?
• С какими телами и параметрами?
• Какие ответы получены по статусам?
• Какие схемы возвращены?
• Работают ли примеры из доки?
Так как мы используем очень строгую схему - у нас такой подход хорошо работает.
Мы интегрировали поддержку
tracecov в наш dmr_client, который используется для всех интеграционных тестов. И schemathesis, который мы используем для property-based тестирования OpenAPI спецификации - тоже поддерживает такое.Один запуск
schemathesis позволяет добиться примерно 85+% покрытия всего АПИ. Вау! То есть: тесты можно почти не писать с таким походом.В
pyproject.toml можно добавить:
# Tracecov:
"--tracecov-format=text,html,markdown",
"--tracecov-fail-under-operations=100",
"--tracecov-fail-under-examples=100",
# TODO: set value to 100
"--tracecov-fail-under-parameters=90",
"--tracecov-fail-under-keywords=90",
"--tracecov-fail-under-responses=50",
И тогда тесты будут падать при низком покрытии АПИ. Вот куда можно развиваться, если у вас - как у нас - уже 100% обычного покрытия.
Одной строкой
• Добавили поддержку
attrs для моделей• Добавили
msgpack как протокол для АПИ, он значительно быстрее json• Добавили
JsonLines для стриминга событий• Переработали несколько апишек, стало значительно удобнее. Спасибо первым пользователям за обратную связь!
Обсуждение: Воспользовались бы такой метрикой? И какое покрытие вы считаете оптимальным? И почему 100%?
P.S. Выпустил большую статью про
django-modern-rest на Хабру: https://habr.com/ru/articles/1017036 Если есть плюсики - буду очень благодарен за помощь в продвижении!| Поддержать | YouTube | GitHub | Чат |
21🔥105🎉16❤12👍7💩2
Нас всех заменят!
Сегодня я открыл для себя вайбкодинг. Да, модели действительно пишут код лучше людей.
За несколько часов я смог сделать больше, чем за месяц до.
Конечно, потребовались некоторые изменения рабочего процесса.
Из самого важного:
• Описывать контекст по частям
• Делать строгие
• Использовать последние SOTA модели
• Пользоваться скилами готовыми под нужные технологии
Еще я заметил, что некоторые языки подходят лучше, чем другие.
Пока остановился на Go. Язык очень приятно выглядит. Он простой, но выразительный.
Из-за его продвинутой статической типизации и универсальности писать на нем большие проекты будет очень удобно.
Как быстро меняется мир!
Обсуждение: В комментах посоветуйте своё любимое аниме? Смотреть нечего!
Хорошего праздника! #ironid:
UPD: Первое апреля закончилось :(
| Поддержать | YouTube | GitHub | Чат |
Сегодня я открыл для себя вайбкодинг. Да, модели действительно пишут код лучше людей.
За несколько часов я смог сделать больше, чем за месяц до.
Конечно, потребовались некоторые изменения рабочего процесса.
Из самого важного:
• Описывать контекст по частям
• Делать строгие
AGENT.md• Использовать последние SOTA модели
• Пользоваться скилами готовыми под нужные технологии
Еще я заметил, что некоторые языки подходят лучше, чем другие.
Пока остановился на Go. Язык очень приятно выглядит. Он простой, но выразительный.
Из-за его продвинутой статической типизации и универсальности писать на нем большие проекты будет очень удобно.
Как быстро меняется мир!
Обсуждение: В комментах посоветуйте своё любимое аниме? Смотреть нечего!
Хорошего праздника! #ironid:
c435ff72UPD: Первое апреля закончилось :(
| Поддержать | YouTube | GitHub | Чат |
3😁323🤡41🔥14❤12🎉11💩6🤯5😢3👎1🤔1🤩1
cibuildwheel: делаем колеса в промышленных масштабах
Ссылка: https://github.com/pypa/cibuildwheel
Привет! Вы наверняка когда-нибудь задумывались, откуда берутся все те замечательные wheel пакеты под разные системы и архитектуры для наших любимых зависимостей с бинарными частями. Например: mypy, black, тд.
Вот и я - нет!
Но, когда мне для релиза
Как оно работает?
Пакет - достаточно простой, обычная клиха (украл слово у @diementros). При запуске - указываем какой
Соберет вам текущий пакет для
Как будет проходить сборка? Полный лог: https://github.com/wemake-services/django-modern-rest/actions/runs/24023507014/job/70057082696#step:4:195
1. Сначала устанавливается нужный питон из готовых образов
2. Подготавливаем окружение
3. Запускаем
Например, у нас она выглядит так (мы используем
Вторая часть задачи: запустить сам
Тут мы указываем: что билдим, что по-умолчанию билд с mypyc выключен, какие зависимости для билда нужны и что нужно поставить рантайм зависимости для билда. Билдить с mypyc будем только если есть специальный флаг:
Только когда он есть (или мы билдим с
4. Запускаем тесты собранного
5. Замеряем, что наши скомпилированные части реально стали работать быстрее
Готово!
Запускаем в CI
Последняя часть: нужно как-то запустить CI с 50+ разных вариантов конфигураций.
Далее дело техники, собираем матрицу всех задач для нужной CI и запускаем такую матрицу:
Самая хитрая часть тут в
Получается удобно и довольно просто.
Последним шагом мы просто загружаем данные пакеты при
И вот так - к вам приехал новый релиз
Анонс митапа в Нижнем
Кстати, у нас скоро будет PythoNN митап в Нижнем Новгороде со всеми вашими любимыми спикерами: @diementros @pymineral, а еще Роман Фролов и Михаил Васильев.
17 апреля, начало в 18:30.
Регистрация: https://pytho-nn.timepad.ru/event/3880099
Приезжайте, приходите. Будет много пива, настолок, разговоров про питон.
Обсуждение: чем вы билдите колеса на работе? Нужно ли вообще такое где-то, кроме опенсорса?
| Поддержать | YouTube | GitHub | Чат |
Ссылка: https://github.com/pypa/cibuildwheel
Привет! Вы наверняка когда-нибудь задумывались, откуда берутся все те замечательные wheel пакеты под разные системы и архитектуры для наших любимых зависимостей с бинарными частями. Например: mypy, black, тд.
Вот и я - нет!
Но, когда мне для релиза
django-modern-rest@0.5.0 потребовалось компилировать части фреймворка с mypyc для получения перформанса на ровном месте - мне пришлось разобраться. Так давайте и вам расскажу.Как оно работает?
Пакет - достаточно простой, обычная клиха (украл слово у @diementros). При запуске - указываем какой
wheel нужно собрать. Например:
cibuildwheel --only cp313-macosx_arm64 --config-file pyproject.toml
Соберет вам текущий пакет для
3.13 и macos с arm64 архитектурой. А вот конфигурация:
[tool.cibuildwheel]
build = "cp3{11,12,13,14}-*"
build-frontend = "uv"
test-command = 'your_test_command'
Как будет проходить сборка? Полный лог: https://github.com/wemake-services/django-modern-rest/actions/runs/24023507014/job/70057082696#step:4:195
1. Сначала устанавливается нужный питон из готовых образов
2. Подготавливаем окружение
3. Запускаем
build систему. Она берется из вашего pyproject.toml / setup.pyНапример, у нас она выглядит так (мы используем
uv, у которого нет родной билд системы для бинарных зависимостей, потому используем `hatch`):
[build-system]
requires = ["hatchling", "hatch", "hatch-fancy-pypi-readme"]
build-backend = "hatchling.build"
Вторая часть задачи: запустить сам
mypyc на нужных файлах. К hatch есть плагинчик hatch-mypy. Его надо тоже настроить:
[tool.hatch.build.targets.wheel.hooks.mypyc]
enable-by-default = false
dependencies = ["hatch-mypyc", "mypy==1.19.1"]
include = ["dmr/_compiled"]
require-runtime-dependencies = true
Тут мы указываем: что билдим, что по-умолчанию билд с mypyc выключен, какие зависимости для билда нужны и что нужно поставить рантайм зависимости для билда. Билдить с mypyc будем только если есть специальный флаг:
[tool.cibuildwheel.environment]
HATCH_BUILD_HOOKS_ENABLE = "1"
Только когда он есть (или мы билдим с
cibuildwheel), то сборка пакета запустится. Такое нужно нам, чтобы иметь возможность делать нативные python-only сборки без .so частей.4. Запускаем тесты собранного
wheel пакета с test-command, проверяем, что собранный пакет работает5. Замеряем, что наши скомпилированные части реально стали работать быстрее
Готово!
Запускаем в CI
Последняя часть: нужно как-то запустить CI с 50+ разных вариантов конфигураций.
cibuildwheel тут снова поможет. Он умеет выплевывать такие конфигурации для CI командой: CIBW_BUILD="cp313-*" cibuildwheel --print-build-identifiers --platform macos.Далее дело техники, собираем матрицу всех задач для нужной CI и запускаем такую матрицу:
mypyc:
name: mypyc wheels ${{ matrix.only }}
needs: configure
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include: ${{ fromJson(needs.configure.outputs.include) }}
Самая хитрая часть тут в
include: там мы как раз динамически подставляем конфигурации от cibuildwheel.Получается удобно и довольно просто.
Последним шагом мы просто загружаем данные пакеты при
release, используя PyPI Trusted Publisher.И вот так - к вам приехал новый релиз
django-modern-rest с опциональными бинарными частями для СКОРОСТИ: https://github.com/wemake-services/django-modern-rest/releases/tag/0.5.0Анонс митапа в Нижнем
Кстати, у нас скоро будет PythoNN митап в Нижнем Новгороде со всеми вашими любимыми спикерами: @diementros @pymineral, а еще Роман Фролов и Михаил Васильев.
17 апреля, начало в 18:30.
Регистрация: https://pytho-nn.timepad.ru/event/3880099
Приезжайте, приходите. Будет много пива, настолок, разговоров про питон.
Обсуждение: чем вы билдите колеса на работе? Нужно ли вообще такое где-то, кроме опенсорса?
| Поддержать | YouTube | GitHub | Чат |
GitHub
GitHub - pypa/cibuildwheel: 🎡 Build Python wheels for all the platforms with minimal configuration.
🎡 Build Python wheels for all the platforms with minimal configuration. - pypa/cibuildwheel
1🔥52👍15❤4😱1
PEP-830: Добавление времени к трейсбекам ошибок
PEP: https://peps.python.org/pep-0830/
Реализация: https://github.com/python/cpython/pull/129337
Обсуждение: https://discuss.python.org/t/pep-830-add-timestamps-to-exceptions-and-tracebacks/106942
Идея - просто отличная. Практически все внешние трекеры ошибок вроде Сентри - уже давно добавляют время ошибки к самому исключению.
А сейчас в 3.15+ такое будет делаться нативно.
Особенно полезно оно будет в
С переменной окружения
Обратная совместимость
Данная фича будет включаться только при использовании переменной окружения
Поддерживаемые форматы:
•
•
Так же будет добавлен новый атрибут
Перф
Никаких значимых изменений замечено не было. Кроме случаев с control flow, которые будут исключены из данной логики.
Оффтоп
Если вы думаете, какой пакет уже попал в
Обсуждение: Какие у вас лучшие практики логгирования исключений? Как-то работаете с группами по-особому? Видите ли вы применение у себя в проекте?
Update: в обсуждении вскрылось много подводных камней, заходите читать комменты.
| Поддержать | YouTube | GitHub | Чат |
PEP: https://peps.python.org/pep-0830/
Реализация: https://github.com/python/cpython/pull/129337
Обсуждение: https://discuss.python.org/t/pep-830-add-timestamps-to-exceptions-and-tracebacks/106942
Идея - просто отличная. Практически все внешние трекеры ошибок вроде Сентри - уже давно добавляют время ошибки к самому исключению.
А сейчас в 3.15+ такое будет делаться нативно.
Особенно полезно оно будет в
ExceptionGroup для сортировки группы. Пример:
import asyncio
async def fetch_user(uid):
await asyncio.sleep(0.5)
raise ConnectionError(f"User service timeout for {uid}")
async def fetch_orders(uid):
await asyncio.sleep(0.1)
raise ValueError(f"Invalid user_id format: {uid}")
async def fetch_recommendations(uid):
await asyncio.sleep(2.3)
raise TimeoutError("Recommendation service timeout")
async def get_dashboard(uid):
results = await asyncio.gather(
fetch_user(uid),
fetch_orders(uid),
fetch_recommendations(uid),
return_exceptions=True,
)
errors = [r for r in results if isinstance(r, Exception)]
if errors:
raise ExceptionGroup("dashboard fetch failed", errors)
asyncio.run(get_dashboard("usr_12@34"))
С переменной окружения
PYTHON_TRACEBACK_TIMESTAMPS=iso питон будет выводить:
+ Exception Group Traceback (most recent call last):
| File "service.py", line 26, in <module>
| asyncio.run(get_dashboard("usr_12@34"))
| ...
| File "service.py", line 24, in get_dashboard
| raise ExceptionGroup("dashboard fetch failed", errors)
| ExceptionGroup: dashboard fetch failed (3 sub-exceptions) <@2026-04-19T07:24:31.102431Z>
+-+---------------- 1 ----------------
| Traceback (most recent call last):
| File "service.py", line 5, in fetch_user
| raise ConnectionError(f"User service timeout for {uid}")
| ConnectionError: User service timeout for usr_12@34 <@2026-04-19T07:24:29.300461Z>
+---------------- 2 ----------------
| Traceback (most recent call last):
| File "service.py", line 9, in fetch_orders
| raise ValueError(f"Invalid user_id format: {uid}")
| ValueError: Invalid user_id format: usr_12@34 <@2026-04-19T07:24:28.899918Z>
+---------------- 3 ----------------
| Traceback (most recent call last):
| File "service.py", line 13, in fetch_recommendations
| raise TimeoutError("Recommendation service timeout")
| TimeoutError: Recommendation service timeout <@2026-04-19T07:24:31.102394Z>
+----------------------------------—
Обратная совместимость
Данная фича будет включаться только при использовании переменной окружения
PYTHON_TRACEBACK_TIMESTAMPS или флага сборки питона -X traceback_timestamps=<format>.Поддерживаемые форматы:
•
ns для отображения таймстампов с точностью до наносекунд <@1776017178.687320256>•
iso для отображения datetime в iso форматеТак же будет добавлен новый атрибут
BaseException.__timestamp_ns__, который будет хранить непосредственное время для отображения. Он будет записываться всегда и всегда в формате наносекунд. Но, значение будет не 0, только с проставленной конфигурацией:
>>> try:
... raise ValueError('demo')
... except ValueError as exc:
... saved = exc
>>> # Без флага
>>> saved.__timestamp_ns__
0
>>> # С флагом `PYTHON_TRACEBACK_TIMESTAMPS=iso`
>>> saved.__timestamp_ns__
1776935887649972000
Перф
Никаких значимых изменений замечено не было. Кроме случаев с control flow, которые будут исключены из данной логики.
Оффтоп
Если вы думаете, какой пакет уже попал в
awesome-python в секцию Web APIs для Django наравне с django-rest-framework, то да, можно поздравить :) И в awesome-django тоже 😊Обсуждение: Какие у вас лучшие практики логгирования исключений? Как-то работаете с группами по-особому? Видите ли вы применение у себя в проекте?
Update: в обсуждении вскрылось много подводных камней, заходите читать комменты.
| Поддержать | YouTube | GitHub | Чат |
Python Enhancement Proposals (PEPs)
PEP 830 – Add timestamps to exceptions and tracebacks | peps.python.org
This PEP adds an optional __timestamp_ns__ attribute to BaseException that records when the exception was instantiated with no observable overhead. When enabled via environment variable or command-line flag, formatted tracebacks display this timestamp a...
3🔥72👍29❤22
Нерегулярная рубрика "посмотрите, что творится!". Как вы знаете, рынок найма http клиентов полностью сломан! Сегодня мы постараемся решить данную проблему.
zapros - modern and extensible python http client
Звезды ставить сюда: https://github.com/kap-sh/zapros
Документация: https://zapros.dev
Сообщество: @pythonzapros
Недавно мне написал Карен Петросян (кстати, заходите к нам в чат, где все события и происходят) – топ3 мейнтейнер библиотеки HTTPX по количеству коммитов, автор httpx-aiohttp и hishel. И говорит: я сделал новый крутой клиент для HTTP для питона. И я такой: офигеть! Дайте два!
В чем фишка?
А ситуация на рынке такова.
Главная особенность
- HTTP/1, HTTP/2 и HTTP/3 - независимость от транспортного уровня позволяет использовать интерфейс
- Rust - поддерживает транспортную реализацию поверх Rust-библиотеки reqwest
- Работа в браузере (через Pyodide) - ещё раз, транспортный уровень `Zapros`-а полностью независим от самого клиента, и из коробки поддерживает работу в браузере, используя fetch API.
Идея независимости от транспортного уровня появилась у автора во время работы над проектом httpx-aiohttp, который был создан, чтобы «спасти» HTTPX от багнутой реализации транспортного уровня, подменяя его на aiohttp. В итоге проект вырос в полноценную библиотеку, используемую в SDK от OpenAI и Anthropic.
Уделяя особое внимание расширяемости,
- Моков - позволяет мокать запросы без необходимости в сторонних библиотеках.
- Кеширования - позволяет кешировать запросы в памяти или на диске (работает поверх библиотеки `hishel`).
- Ретраев - позволяет автоматически повторять запросы при неудаче с помощью настраиваемой логики.
- Кук - автоматически управляет куками.
- Кассет - позволяет записывать и воспроизводить HTTP-взаимодействия, что полезно для тестирования и отладки (аналог vcr).
- Редиректов - автоматически обрабатывает HTTP-редиректы согласно стандарту HTTP (RFC 9111).
Обсуждение: Каким HTTP клиентом пользуетесь вы? Какие у вас с ним проблемы? Чего не хватает? Какой Python HTTP клиент считаете лучшим на данный момент?
zapros - modern and extensible python http client
Звезды ставить сюда: https://github.com/kap-sh/zapros
Документация: https://zapros.dev
Сообщество: @pythonzapros
Недавно мне написал Карен Петросян (кстати, заходите к нам в чат, где все события и происходят) – топ3 мейнтейнер библиотеки HTTPX по количеству коммитов, автор httpx-aiohttp и hishel. И говорит: я сделал новый крутой клиент для HTTP для питона. И я такой: офигеть! Дайте два!
В чем фишка?
А ситуация на рынке такова.
requests морально устарел 10 лет назад. На фоне умирающего HTTPX, у которого не было релиза больше года, и автор которого не хочет релизить новые версии и даже заблокировал возможность создавать новые задачи, автор Zapros попытался написать аналог, способный не только заменить HTTPX, но и предложить кучу новых интересных фич.
from zapros import AsyncClient
async def main() -> None:
async with AsyncClient() as client:
response = await client.get("https://httpbin.org/get")
print(response.status, response.json)
Главная особенность
Zapros - его дизайн: вместо того чтобы зависеть от конкретных имплементаций транспортного уровня, Zapros работает с абстракциями, благодаря которым он может поддерживать:- HTTP/1, HTTP/2 и HTTP/3 - независимость от транспортного уровня позволяет использовать интерфейс
Zapros поверх любых транспортных реализаций.- Rust - поддерживает транспортную реализацию поверх Rust-библиотеки reqwest
- Работа в браузере (через Pyodide) - ещё раз, транспортный уровень `Zapros`-а полностью независим от самого клиента, и из коробки поддерживает работу в браузере, используя fetch API.
Идея независимости от транспортного уровня появилась у автора во время работы над проектом httpx-aiohttp, который был создан, чтобы «спасти» HTTPX от багнутой реализации транспортного уровня, подменяя его на aiohttp. В итоге проект вырос в полноценную библиотеку, используемую в SDK от OpenAI и Anthropic.
Zapros имеет всего лишь 3 зависимости: h11, pywhatwgurl и typing-extensions. Поддерживает Python 3.10 и выше.Уделяя особое внимание расширяемости,
Zapros был спроектирован с удобным механизмом расширения клиента с помощью миддлварей. Из коробки идут миддлвари для:- Моков - позволяет мокать запросы без необходимости в сторонних библиотеках.
- Кеширования - позволяет кешировать запросы в памяти или на диске (работает поверх библиотеки `hishel`).
- Ретраев - позволяет автоматически повторять запросы при неудаче с помощью настраиваемой логики.
- Кук - автоматически управляет куками.
- Кассет - позволяет записывать и воспроизводить HTTP-взаимодействия, что полезно для тестирования и отладки (аналог vcr).
- Редиректов - автоматически обрабатывает HTTP-редиректы согласно стандарту HTTP (RFC 9111).
from zapros import CacheMiddleware, Client, RetryMiddleware
with (
Client().wrap_with_middleware(
lambda next: RetryMiddleware(next) # wrap with the retry middleware
).wrap_with_middleware(
lambda next: CacheMiddleware(next) # wrap with the cache middleware
) as client
):
# automatically retries failed requests and caches responses
client.get("https://zapros.dev")
Zapros не принуждает использовать ни одну из данных миддлварей: сам класс клиента отвечает только за отправку HTTP-запросов, всё остальное — уже миддлвари, которые вы можете использовать по своему усмотрению. И хотя основные миддлвари написаны так, чтобы покрывать большинство случаев использования, вы можете использовать и свои кастомные решения.Zapros поддерживает как синхронный, так и асинхронный интерфейс, и использует улучшенную версию механизма unasync, который используется в httpx для поддержки обоих интерфейсов.Обсуждение: Каким HTTP клиентом пользуетесь вы? Какие у вас с ним проблемы? Чего не хватает? Какой Python HTTP клиент считаете лучшим на данный момент?
GitHub
GitHub - kap-sh/zapros: Modern and extensible HTTP client for Python
Modern and extensible HTTP client for Python. Contribute to kap-sh/zapros development by creating an account on GitHub.
2❤112🔥61👍14🤔8🤡2
PEP-661: sentinel объекты
PEP: https://peps.python.org/pep-0661/
Код: https://github.com/python/cpython/pull/148831
Обсуждение: https://discuss.python.org/t/pep-661-sentinel-values/9126
В питон 3.15 добавляют новый
Проблема достаточно понятная, например: нам нужно создать какое-то значение по-умолчанию, чтобы мы знали, что аргумент не был передан. Но
Данная логика встречается буквально везде:
• dataclasses
• django_modern_rest
• msgspec (тоже самое но на C)
Однако, теперь можно упростить АПИ для создания таких объектов до:
А вот пример PR, где в
Как оно примерно внутри устроено
Как и все билтины,
Но я приведу примерную версию на питоне (из PEP), чтобы было понятнее, как он работает внутри:
Хороший пример синглтона ^
Что здесь важно?
1.
2. Объект должны быть внешне иммутабельным
3. Копирование объекта должно возвращать тот же самый синглтон
Не только для Питона
Ну и конечно же: есть две новые функции в C-API для создания таких объектов в C-extensions (как
•
•
Вот такая фича. Довольно просто, закрывает понятную проблему. Но не очень ясно, почему builtin.
Обсуждение: Приходилось ли пользоваться чем-то подобным? Какую реализацию синглтона в питоне вы считаете лучшей? Согласны с добавлением нового builtin?
| Поддержать | YouTube | GitHub | Чат |
PEP: https://peps.python.org/pep-0661/
Код: https://github.com/python/cpython/pull/148831
Обсуждение: https://discuss.python.org/t/pep-661-sentinel-values/9126
В питон 3.15 добавляют новый
builtin – sentinel, чтобы создавать значения по-умолчанию.Проблема достаточно понятная, например: нам нужно создать какое-то значение по-умолчанию, чтобы мы знали, что аргумент не был передан. Но
None является валидным значением в нашей логике. Потому нужно создать новое особое "пустое" значение.Данная логика встречается буквально везде:
• dataclasses
• django_modern_rest
• msgspec (тоже самое но на C)
Однако, теперь можно упростить АПИ для создания таких объектов до:
_SENTINEL_VALUE = sentinel('_SENTINEL_VALUE')
А вот пример PR, где в
dataclasses уже используют новое АПИ: https://github.com/python/cpython/commit/16952218d0535904236e8a39851133688c9ce1f0Как оно примерно внутри устроено
Как и все билтины,
sentinel написан на C, его исходники вот тут: https://github.com/python/cpython/blob/main/Objects/sentinelobject.cНо я приведу примерную версию на питоне (из PEP), чтобы было понятнее, как он работает внутри:
class sentinel:
__slots__ = ("__name__", "_module_name")
def __init_subclass__(cls):
raise TypeError("type 'sentinel' is not an acceptable base type")
def __init__(self, name, /):
if not isinstance(name, str):
raise TypeError("sentinel name must be a string")
self.__name__ = name
self._module_name = sys._getframemodulename(1)
@property
def __module__(self):
return self._module_name
def __repr__(self):
return self.__name__
def __reduce__(self):
return self.__name__
def __copy__(self):
return self
def __deepcopy__(self, memo):
return self
def __or__(self, other):
return typing.Union[self, other]
Хороший пример синглтона ^
Что здесь важно?
1.
pickle должен корректно работать, для того имя sentinel('NAME') должно совпадать с именем объекта на уровне модуля: NAME = 2. Объект должны быть внешне иммутабельным
3. Копирование объекта должно возвращать тот же самый синглтон
Не только для Питона
Ну и конечно же: есть две новые функции в C-API для создания таких объектов в C-extensions (как
msgspec например): •
PyObject *PySentinel_New(const char *name, const char *module_name) для создания•
bool PySentinel_Check(PyObject *obj) для проверкиВот такая фича. Довольно просто, закрывает понятную проблему. Но не очень ясно, почему builtin.
Обсуждение: Приходилось ли пользоваться чем-то подобным? Какую реализацию синглтона в питоне вы считаете лучшей? Согласны с добавлением нового builtin?
| Поддержать | YouTube | GitHub | Чат |
Python Enhancement Proposals (PEPs)
PEP 661 – Sentinel Values | peps.python.org
Unique placeholder values, commonly known as “sentinel values”, are common in programming. They have many uses, such as for:
2👍56🔥8❤7🤔2🤯1
Вышел mypy 2.0
Changelog: https://github.com/python/mypy/blob/master/CHANGELOG.md#mypy-20
Что изменилось?
По-умолчанию
Включили
Теперь можно переопределять переменные, даже разных типов с
Данная фича раньше была под флагом
Самое интересное
Добавили
Против режима с одним воркером (как было до 2.0):
А теперь еще убираем
Вот такой прирост производительности. Версия с
Очень радостно, что mypy становится быстрее. Дальнейшее развитие mypyc приведет к еще большему перфу. И не только для mypy.
Обсуждение: Как быстро вы обновляете mypy на своих проектах? Насколько сурово настраиваете? Будет ли профит от нескольких воркеров?
| Поддержать | YouTube | GitHub | Чат |
Changelog: https://github.com/python/mypy/blob/master/CHANGELOG.md#mypy-20
Что изменилось?
По-умолчанию
--local-partial-types теперь всегда включен. Он нужен для корректной типизации в разных скоупах.
a = [] # Needs type annotation when using `local-partial-types`
def func() -> None:
a.append(1)
Включили
--strict-bytes по-умолчанию. Раньше тип bytes разрешал передавать memoryview и bytearray. Теперь с новым поведением bytes разрешает только bytes, все остальные типы нужно указывать отдельно.Теперь можно переопределять переменные, даже разных типов с
--allow-redefinition
def foo(cond: bool) -> None:
if cond:
for x in ["a", "b"]:
# Type of "x" is "str" here
...
else:
for x in [1, 2]:
# Type of "x" is "int" here
...
Данная фича раньше была под флагом
--allow-redefinition-new, а теперь включена по-умолчанию.Самое интересное
Добавили
--num-workers, который позволяет ускорить mypy кратно на больших кодовых базах. Я буду запускать mypy прямо на кодовой базе mypy (без mypyc, без кеша, но с orjson и `sqlite_cache`):
» rm -rf .mypy_cache && time mypy --config-file mypy_self_check.ini -p mypy -p mypyc --num-workers=8
7.090 total
Против режима с одним воркером (как было до 2.0):
» rm -rf .mypy_cache && time mypy --config-file mypy_self_check.ini -p mypy -p mypyc
25.335 total
А теперь еще убираем
orjson и sqlite_cache:
» rm -rf .mypy_cache && time mypy --config-file mypy_self_check.ini -p mypy -p mypyc
28.108 total
Вот такой прирост производительности. Версия с
mypyc (то есть та, которую мы скачиваем из pip) будет еще быстрее.Очень радостно, что mypy становится быстрее. Дальнейшее развитие mypyc приведет к еще большему перфу. И не только для mypy.
Обсуждение: Как быстро вы обновляете mypy на своих проектах? Насколько сурово настраиваете? Будет ли профит от нескольких воркеров?
| Поддержать | YouTube | GitHub | Чат |
GitHub
mypy/CHANGELOG.md at master · python/mypy
Optional static typing for Python. Contribute to python/mypy development by creating an account on GitHub.
1🔥75👍22❤6
Находки в опенсорсе: хлеб
> The sourdough framework is an open-source book dedicated to helping you to make the best possible sourdough bread at home.
https://github.com/hendricius/the-sourdough-framework
Наконец-то нормальные проекты!
Люблю домашний хлеб. Делимся рецептами вкусной еды / хлеба в комментах!
> The sourdough framework is an open-source book dedicated to helping you to make the best possible sourdough bread at home.
https://github.com/hendricius/the-sourdough-framework
Наконец-то нормальные проекты!
Люблю домашний хлеб. Делимся рецептами вкусной еды / хлеба в комментах!
2🔥105❤20🥰9😁4😱2🤯1
ИИ переписал 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 (то есть - никакое)
- Файлы в нем даже были неотформатированы встроенным
- Ревью не было, потому что внутри PRа
-
- "Скорость работы осталось такой же" - довольно странный тезис, учитывая что zig и rust оба генерят код через LLVM, часто практически идентичный, заслуги ИИ здесь нет
Выводы
- Прикольно, что такое вообще можно сделать (с неограниченными токенами)
- Как теперь bun будет владеть своей базой кода, кто сможет в ней разобраться и что-то пофиксить - вопрос открытый
- Какой смысл во всем действии (кроме очевидного маркетинга) - вопрос открытый
- Брать ли теперь bun в прод? Конечно нет
Обсуждение: что вы думаете по данному вопросу? Стали бы использовать bun у себя в проекте в новом виде?
| Поддержать | YouTube | GitHub | Чат |
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 | Чат |
GitHub
Rewrite Bun in Rust by Jarred-Sumner · Pull Request #30412 · oven-sh/bun
Blog post with details coming soon.
It passes Bun's pre-existing test suite on all platforms (and fixes several memory leaks and flaky tests), the binary size shrinks by 3 MB - 8 MB, the be...
It passes Bun's pre-existing test suite on all platforms (and fixes several memory leaks and flaky tests), the binary size shrinks by 3 MB - 8 MB, the be...
1👍73😁61❤19💩13🤯9🤔6🤡3🔥2👎1
Free-Threading и итераторы: что могло пойти так?
Недавно в питоне появилась новая фича (которую я вам пока не покажу), а я решил сделать новый формат – вопрос-загадку.
Мы все знаем, что Free-Threading работает совсем по-другому, вместо одного глобального GIL, у нас множество критических секций per-object и атомарных операций.
Тут обычный питоновский код на тредах. Создаем 10 тредов и идем по итератору, складываем его объекты в одну общую сумму с локом. В итоге должно получиться значение равное
Перед запуском подумайте сначала сами:
- Что вообще может произойти?
- Как поправить текущую ситуацию в теории?
- Как можно поменять код сейчас без каких-либо новых фичей, чтоб заработало?
- Что было бы идеально увидеть в качестве решения из коробки?
- Где бы вы хотели увидеть такое решение в модулях питона?
Ответ и ссылки будут вечером. В комментах - обсуждаем!
| Поддержать | YouTube | GitHub | Чат |
Недавно в питоне появилась новая фича (которую я вам пока не покажу), а я решил сделать новый формат – вопрос-загадку.
Мы все знаем, что 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 | Чат |
GitHub
sobolevn - Overview
sobolevn has 633 repositories available. Follow their code on GitHub.
❤17👍13🤔8🤡2👎1😢1
Находки в опенсорсе
Free-Threading и итераторы: что могло пойти так? Недавно в питоне появилась новая фича (которую я вам пока не покажу), а я решил сделать новый формат – вопрос-загадку. Мы все знаем, что Free-Threading работает совсем по-другому, вместо одного глобального…
Настало время ответов!
Во-первых
Код из примера упадет с
Почему так? Нельзя дважды запустить один и тот же генератор, даже в одном треде.
Самый простой пример:
Исходник:
Во-вторых
Переиспользовать генераторы в разных тредах - особенно плохая идея. О чем теперь явно написано в документации. Данный паттерн не поддерживается во Free-Threading нативно. Корневая причина, для самых любопытных: https://github.com/python/cpython/issues/120496
PR: https://github.com/python/cpython/pull/148894
Документация: https://docs.python.org/3.15/library/threading.html#iterator-synchronization
Был добавлен способ вызова
Для того, чтобы код из оригинального примера заработал, нужно заменить
Теперь - должно быть понятно, почему такая фича была добавлена.
Было ли интересно поковырять? Было ли сложно? :)
Во-первых
Код из примера упадет с
ValueError: generator already executing.Почему так? Нельзя дважды запустить один и тот же генератор, даже в одном треде.
Самый простой пример:
def g():
i = next(me)
yield i
me = g()
next(me) # ValueError
Исходник:
static PySendResult // Objects/genobject.c
gen_send_ex(PyGenObject *gen, PyObject *arg, PyObject **presult)
{
int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state);
// ...
if (frame_state == FRAME_EXECUTING) {
PyErr_SetString(PyExc_ValueError, "generator already executing");
return PYGEN_ERROR;
}
// ...
}
Во-вторых
Переиспользовать генераторы в разных тредах - особенно плохая идея. О чем теперь явно написано в документации. Данный паттерн не поддерживается во Free-Threading нативно. Корневая причина, для самых любопытных: https://github.com/python/cpython/issues/120496
PR: https://github.com/python/cpython/pull/148894
Документация: https://docs.python.org/3.15/library/threading.html#iterator-synchronization
Был добавлен способ вызова
__next__ под локом (как правильно догадались в комментариях):
class serialize_iterator:
def __init__(self, iterable):
self._iterator = iter(iterable)
self._lock = Lock()
def __iter__(self):
return self
def __next__(self):
with self._lock:
return next(self._iterator)
Для того, чтобы код из оригинального примера заработал, нужно заменить
iterator = producer(limit) на iterator = threading.serialize_iterator(producer(limit)). Есть еще декоратор @synchronized_iterator для определения threadsafe генераторов сразу.Теперь - должно быть понятно, почему такая фича была добавлена.
Было ли интересно поковырять? Было ли сложно? :)
GitHub
Sequence iterator thread-safety · Issue #120496 · python/cpython
Bug report Bug description: Sequence iterators are not thread-safe under the free-threaded build. They'll sometimes be accessed with the same index. Here is a minimal repro: import concurrent.f...
👍61🔥16🤯7❤3🎉1