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

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

رفتن به کانال در Telegram

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

نمایش بیشتر

📈 تحلیل کانال تلگرام Находки в опенсорсе

کانال Находки в опенсорсе (@opensource_findings) در بخش زبانی انگلیسی بازیگری فعال است. در حال حاضر جامعه شامل 11 456 مشترک است و جایگاه 10 892 را در دسته فناوری و برنامه‌ها و رتبه 57 444 را در منطقه روسيا دارد.

📊 شاخص‌های مخاطب و پویایی

از زمان ایجاد در невідомо، پروژه رشد سریعی داشته و 11 456 مشترک جذب کرده است.

بر اساس آخرین داده‌ها در تاریخ 05 ژوئن, 2026، کانال فعالیت پایداری دارد. در ۳۰ روز گذشته تغییر اعضا برابر 109 و در ۲۴ ساعت گذشته برابر -2 بوده و همچنان دسترسی گسترده‌ای حفظ شده است.

  • وضعیت تأیید: تأیید نشده
  • نرخ تعامل (ER): میانگین تعامل مخاطب 62.64% است و در ۲۴ ساعت نخست پس از انتشار، محتوا معمولاً 29.94% واکنش نسبت به کل مشترکان کسب می‌کند.
  • دسترسی پست‌ها: هر پست به طور میانگین 7 177 بازدید دریافت می‌کند. در اولین روز معمولاً 3 431 بازدید جمع‌آوری می‌شود.
  • واکنش‌ها و تعامل: مخاطبان به‌طور فعال حمایت می‌کنند؛ میانگین واکنش به هر پست 80 است.
  • علایق موضوعی: محتوا بر موضوعات کلیدی مانند pyobject, питон, slots, gil, github تمرکز دارد.

📝 توضیح و سیاست محتوایی

نویسنده این فضا را محل بیان دیدگاه‌های شخصی توصیف می‌کند:
Привет! Меня зовут Никита Соболев. Я занимаюсь опенсорс разработкой полный рабочий день. Тут я рассказываю про #python, #c, опенсорс и тд. Поддержать: https://boosty.to/sobolevn РКН: https://vk.cc/cOzn36 Связь: @sobolev_nikita

به لطف به‌روزرسانی‌های پرتکرار (آخرین داده در تاریخ 06 ژوئن, 2026)، کانال همواره به‌روز و دارای دسترسی بالاست. تحلیل‌ها نشان می‌دهد مخاطبان به‌طور فعال با محتوا تعامل دارند و آن را به نقطه اثرگذاری مهم در دسته فناوری و برنامه‌ها تبدیل کرده‌اند.

11 456
مشترکین
-224 ساعت
+17 روز
+10930 روز

در حال بارگیری داده...

جذب مشترکین
ژوئن '26
ژوئن '26
+18
در 6 کانال‌ها
مه '26
+192
در 8 کانال‌ها
Get PRO
آوریل '26
+103
در 5 کانال‌ها
Get PRO
مارس '26
+266
در 10 کانال‌ها
Get PRO
فوریه '26
+151
در 1 کانال‌ها
Get PRO
ژانویه '26
+102
در 3 کانال‌ها
Get PRO
دسامبر '25
+157
در 12 کانال‌ها
Get PRO
نوامبر '25
+192
در 3 کانال‌ها
Get PRO
اکتبر '25
+236
در 7 کانال‌ها
Get PRO
سپتامبر '25
+214
در 8 کانال‌ها
Get PRO
اوت '25
+323
در 9 کانال‌ها
Get PRO
ژوئیه '25
+730
در 30 کانال‌ها
Get PRO
ژوئن '25
+302
در 4 کانال‌ها
Get PRO
مه '25
+375
در 3 کانال‌ها
Get PRO
آوریل '25
+343
در 10 کانال‌ها
Get PRO
مارس '25
+1 558
در 9 کانال‌ها
Get PRO
فوریه '250
در 9 کانال‌ها
Get PRO
ژانویه '250
در 8 کانال‌ها
Get PRO
دسامبر '24
+103
در 5 کانال‌ها
Get PRO
نوامبر '24
+1 802
در 3 کانال‌ها
Get PRO
اکتبر '24
+374
در 6 کانال‌ها
Get PRO
سپتامبر '24
+355
در 0 کانال‌ها
Get PRO
اوت '24
+64
در 4 کانال‌ها
Get PRO
ژوئیه '24
+11
در 0 کانال‌ها
Get PRO
ژوئن '24
+11
در 0 کانال‌ها
Get PRO
مه '24
+5
در 1 کانال‌ها
Get PRO
آوریل '240
در 0 کانال‌ها
Get PRO
مارس '240
در 0 کانال‌ها
Get PRO
فوریه '240
در 0 کانال‌ها
Get PRO
ژانویه '240
در 0 کانال‌ها
Get PRO
دسامبر '230
در 0 کانال‌ها
Get PRO
نوامبر '23
+40
در 1 کانال‌ها
Get PRO
اکتبر '23
+63
در 0 کانال‌ها
Get PRO
سپتامبر '23
+58
در 0 کانال‌ها
Get PRO
اوت '23
+40
در 0 کانال‌ها
Get PRO
ژوئیه '23
+33
در 0 کانال‌ها
Get PRO
ژوئن '23
+27
در 0 کانال‌ها
Get PRO
مه '23
+34
در 0 کانال‌ها
Get PRO
آوریل '23
+45
در 0 کانال‌ها
Get PRO
مارس '23
+55
در 0 کانال‌ها
Get PRO
فوریه '23
+41
در 0 کانال‌ها
Get PRO
ژانویه '23
+36
در 0 کانال‌ها
Get PRO
دسامبر '22
+24
در 0 کانال‌ها
Get PRO
نوامبر '22
+135
در 0 کانال‌ها
Get PRO
اکتبر '22
+91
در 0 کانال‌ها
Get PRO
سپتامبر '220
در 0 کانال‌ها
Get PRO
اوت '22
+28
در 0 کانال‌ها
Get PRO
ژوئیه '22
+58
در 0 کانال‌ها
Get PRO
ژوئن '22
+38
در 0 کانال‌ها
Get PRO
مه '22
+63
در 0 کانال‌ها
Get PRO
آوریل '22
+69
در 0 کانال‌ها
Get PRO
مارس '22
+27
در 0 کانال‌ها
Get PRO
فوریه '22
+35
در 0 کانال‌ها
Get PRO
ژانویه '22
+45
در 0 کانال‌ها
Get PRO
دسامبر '21
+60
در 0 کانال‌ها
Get PRO
نوامبر '21
+66
در 0 کانال‌ها
Get PRO
اکتبر '21
+107
در 0 کانال‌ها
Get PRO
سپتامبر '21
+195
در 0 کانال‌ها
Get PRO
اوت '21
+111
در 0 کانال‌ها
Get PRO
ژوئیه '21
+70
در 0 کانال‌ها
Get PRO
ژوئن '21
+43
در 0 کانال‌ها
Get PRO
مه '21
+38
در 0 کانال‌ها
Get PRO
آوریل '21
+66
در 0 کانال‌ها
Get PRO
مارس '21
+86
در 0 کانال‌ها
Get PRO
فوریه '21
+85
در 0 کانال‌ها
Get PRO
ژانویه '21
+90
در 0 کانال‌ها
Get PRO
دسامبر '20
+4 505
در 0 کانال‌ها
تاریخ
رشد مشترکین
اشارات
کانال‌ها
06 ژوئن+1
05 ژوئن+2
04 ژوئن+6
03 ژوئن0
02 ژوئن+6
01 ژوئن+3
پست‌های کانال
Значимые и незначимые пробелы в Python Во время стрима я решил, что сейчас у меня будет приключение на 15 минут, что я быстренько запилю новую синтаксическую ошибку. В чем суть? Довольно легко опечататься и написать вместо корректного lazy from os import path неправильную форму from os lazy import path. На что человек просто получит голый SyntaxError без подсказок и советов. Оно работает, но DX не самый лучший для новой фичи. Особенно, учитывая тот факт, что from os lazy import path выглядит консистентно с lazy import os. И первая часть задачи у меня получилась прямо на стриме. Теперь from os lazy import path выдает красивую ошибку:

>>> from os lazy import path
  File "<python-input-0>", line 1
    from os lazy import path
            ^^^^
SyntaxError: use 'lazy from ... ' instead of 'from ... lazy import'
А вот часть с from . lazy import name у меня сразу не вышла. На стриме оч сложно программировать. Я, честно сказать, сначала растерялся. А потом понял: в питоне есть значимые пробелы: например для идентации кода. Они превращаются в токен INDENT. А есть незначимые: a+b и a + b - одинаковый код. Что на самом деле ведет к чудовищам вида:

>>> 1. .real
1.0

>>> 1if True else 0
<python-input-2>:1: SyntaxWarning: invalid decimal literal
1

>>> [1.0for _ in range(1)]
<python-input-3>:1: SyntaxWarning: invalid decimal literal
[1.0]
И как вы уже могли догадаться: from . lazy import x и from .lazy import x - ОДИН И ТОТ ЖЕ КОД. Более того, он абсолютно корректно работает. И жадно импортирует имя x из модуля lazy. Что собственно и стало причиной, почему в PEP сделали lazy from, а не from ... lazy import. Теперь я поправил свой PR, чтобы выкидывать еще один SyntaxWarning:

>>> from . lazy import x
<python-input-0>:1: SyntaxWarning: 'from . lazy import' is the same as 'from .lazy import'; did you mean 'lazy from . import'?
Кстати, тут можно сравнить мой код со слопусом. К вопросу о "качестве" ИИ-поделок. Теперь оба случая ошибочного импорта обрабатываются корректно. Про канал / стримы После стрима случилось главное: мы почти 3 часа обсуждали, что хотим делать и какую ценность нести людям. Мы поняли, что главная ценность, которую мы можем и хотим давать: помогать людям бороться со страхами. На каждом углу нас пытаются запсиопить тейками вроде "ИИ заменило всех программистов", "IT В С Е", "всех сократили", "работы нет" и прочее. Кажется, что с таким нужно бороться рациональностью, взвешенной позицией, фактами и техническими контентом. То, что мы делаем и любим. Не хочется хайпить на страхах людей, хочется помогать людям быть счастливыми и уверенными. По мере сил, конечно. Обсуждение: а какой контент хотелось бы увидеть вам? :) | Поддержать | YouTube | GitHub | Чат |

2
Начали! https://www.youtube.com/watch?v=W9Hd5dfxjIU
3 898
3
Анонс стрима: "работаем над lazy import'ами в CPython и плачем под аниме" (на превью - я на стриме) Мы с @nkhitrov_blog, @fas
Анонс стрима: "работаем над 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 Приходите задавать свои ответы и хорошо проводить время!
8 667
4
PEP 810: Explicit lazy imports 2 Как я уже писал, в Python 3.15 нас ждут lazy imports. В первом посте я описал основные фичи. Прочитайте его перед продолжением. Во втором посте настало время посмотреть на плохие части. Детали PEP, которые мы не осветили прошлый раз Во-первых, из очевидного: lazy import может быть использован только на уровне модуля, в других местах - он будет вызывать ошибку синтаксиса. Но, что забавно, lazy import не может быть использован внутри даже try блоков. Во-вторых, я не уточнил, как будет работать __lazy_modules__, а там дичь. Мы можем указывать __lazy_modules__ = ['os', 'typing'] в любой версии питона. Очевидно, что работать как ленивые они будут только в 3.15+, в остальных - будет просто неиспользуемый атрибут. Но штука в том, что в разных версиях питона библиотеки будут работать по-разному. Но! Он будет ленивым, только если он может быть ленивым. То есть, если он находится внутри класса, функции, try, тд - он не станет ленивым. Удачи в дебаге, короче. Ну и самое забавное, мы можем управлять глобальным стейтом всех импортов через -X lazy_imports=none|normal|all и так же через переменную окружения PYTHON_LAZY_IMPORTS. Что оно значит? Отключаем все lazy импорты | все работает так, как написано | все импорты ленивые. Мы можем управлять тем, как работают импорты через переменную окружения!! Перечитайте, если вы тоже не поняли. Я вот не сразу понял. Внедрение в питон В stdlib питона уже активно используют lazy импорты. Однако, внутри уже появились циклические импорты. Потому что теперь так можно сделать случайно. И специально. Да, ленивые импорты могут помогать избегать циклических импортов. В некоторых режимах работы. Теперь stdlib больше не работает в режиме -X lazy_imports=none. О чем развернулась жаркая дискуссия, прочитать которую я всем советую: https://github.com/python/cpython/issues/149321 Но и режим -X lazy_imports=all все сломал. Теперь с ним некоторые библиотеки начали работать по-другому. Например: $ PYTHON_LAZY_IMPORTS=normal ./python -c "import shutil; print(shutil._BZ2_SUPPORTED)" False $ PYTHON_LAZY_IMPORTS=all ./python -c "import shutil; print(shutil._BZ2_SUPPORTED)" True Так как импорт становится ленивым, он больше не проверяет есть ли на самом деле библиотека _bz2 у вас. А просто всегда возвращает True. Что делать - пока никто не знает: https://github.com/python/cpython/issues/150167 Вот в такой ситуации мы все оказались. Зато теперь некоторые скрипты будут запускаться быстрее, потому что импорты в некоторых местах стали ленивыми. Иногда, не точно. Почему нельзя было использовать импорт внутри функции? Я не знаю. Мое отношение к данной фиче можно только охарактеризовать словами вечного инструмента wemake-python-styleguide: https://github.com/wemake-services/wemake-python-styleguide/issues/3639 Обсуждение: я даже не знаю, честно. Давайте просто обнимемся в комментах. | Поддержать | YouTube | GitHub | Чат |
8 050
5
Настало время ответов! Во-первых Код из примера упадет с 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 генераторов сразу. Теперь - должно быть понятно, почему такая фича была добавлена. Было ли интересно поковырять? Было ли сложно? :)
7 431
6
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 | Чат |
6 190
7
ИИ переписал 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 | Чат |
15 073
8
Находки в опенсорсе: хлеб > The sourdough framework is an open-source book dedicated to helping you to make the best possible
Находки в опенсорсе: хлеб > 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 Наконец-то нормальные проекты! Люблю домашний хлеб. Делимся рецептами вкусной еды / хлеба в комментах!
9 944
9
Вышел mypy 2.0 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 | Чат |
9 445
10
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 добавляют новый 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 | Чат |
8 271
11
Нерегулярная рубрика "посмотрите, что творится!". Как вы знаете, рынок найма 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 клиент считаете лучшим на данный момент?
0
12
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+ такое будет делаться нативно. Особенно полезно оно будет в 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 Перф Никаких значимых изменений замечено не было. Оффтоп Если вы думаете, какой пакет уже попал в awesome-python в секцию Web APIs для Django наравне с django-rest-framework, то да, можно поздравить :) И в awesome-django тоже 😊 Обсуждение: Какие у вас лучшие практики логгирования исключений? Как-то работаете с группами по-особому? Видите ли вы применение у себя в проекте? | Поддержать | YouTube | GitHub | Чат |
0
13
cibuildwheel: делаем колеса в промышленных масштабах Ссылка: 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 | Чат |
0
14
Нас всех заменят! Сегодня я открыл для себя вайбкодинг. Да, модели действительно пишут код лучше людей. За несколько часов я смог сделать больше, чем за месяц до. Конечно, потребовались некоторые изменения рабочего процесса. Из самого важного: • Описывать контекст по частям • Делать строгие AGENT.md • Использовать последние SOTA модели • Пользоваться скилами готовыми под нужные технологии Еще я заметил, что некоторые языки подходят лучше, чем другие. Пока остановился на Go. Язык очень приятно выглядит. Он простой, но выразительный. Из-за его продвинутой статической типизации и универсальности писать на нем большие проекты будет очень удобно. Как быстро меняется мир! Обсуждение: В комментах посоветуйте своё любимое аниме? Смотреть нечего! Хорошего праздника! #ironid: c435ff72 | Поддержать | YouTube | GitHub | Чат |
0
15
tracecov: считаем покрытие АПИ через спецификацию OpenAPI Вышла новая версия 0.4.0 https://github.com/wemake-services/django-
tracecov: считаем покрытие АПИ через спецификацию OpenAPI Вышла новая версия 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 | Чат |
0
16
И сразу бонусом хочу напомнить, что такое обычное выражение 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. Два поста в один день, когда такое было?!
0
17
PEP 828: async yield from и состояние асинхронных генераторов в питоне PEP: https://peps.python.org/pep-0828 Обсуждение: http
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 В питон хотят добавить 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 | Чат |
0
18
Часть 1: https://t.me/opensource_findings/950 Что будет дальше? – Доработка доки. Я хочу, чтобы люди заново открывали для себя Джангу (лучший фреймворк для веба на питоне, имхо). Изучали лучшие практики, думали про архитектуру. Сейчас дока в хорошем состоянии, но нет предела совершенству – Мы еще даже не пробовали значительно ускорить проект. В рамках идей: переписывания кусков на Rust, Cython, компиляция кода mypyc – Поддержка WebSocket без django-channels (фу, Артём, без негатива) – Поддержка других форматов стриминга кроме SSE, например JsonL – Поддержка cattrs и adaptix – Скилы для LLM для автоматизации перехода с django-rest-framework и django-ninja – Поддержка ty Благодарности Данный проект не стал бы возможен без: – Александра и Алексея – соавторов проекта, они затащили гигантский объем работы – Виктора, кто сделал нам офигительную интерактивную доку! – А так же 51 других контрибьюторов, кто внес неоценимый вклад в проект Большое спасибо всем за помощь, обратную связи и поддержку, без вас – ничего бы не вышло. Лучшее сообщество! 🫶 Ну а я – делаю небольшой перерыв, отдыхаю и работаю дальше! Обсуждение: какие фичи вы бы хотели увидеть в дальнейших релизах? P.S. Если у вас есть подкаст / канал / тд, и вы хотите поговорить со мной про веб фреймворки на питоне – пишите в личку! Сделаем интересное :) | Поддержать | YouTube | GitHub | Чат |
0
19
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+ ⭐ на Гитхабе (сходите поставьте, кто еще не). Вижу, что люди ждут, вижу интерес. Спасибо! 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 для контекста – Но никакого нейрослопа внутри!
0
20
PEP-827: Самое интересное, что случалось с типами в питоне! Текст: 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(HeroBase, 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 | Чат |
0