- BrainTools - https://www.braintools.ru -

Находки в опенсорсе: мир питона за август 2026

Всем привет, вот и заканчивается лето :(

Мой прошлый дайджест [1] неожиданным образом зашел, а значит – людям такое интересно. В августе было много новых интересных PEP’ов (например, PEP 805: Safe Parallel Python), которые я поревьюил, было много новых релизов, было много новых опенсорс проектов чуваков из нашего сообщества, пару интересных подкастов и множество другого интересного!

С меня как всегда статья без нейрослопа и ИИ, с вас как всегда интересные комментарии и лайки! Но немного нейрослопа я все же добавил в некоторые свои проекты. Но осторожно и с пользой для всех.

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


CPython

После долгих споров и обсуждений про наш PEP-841 [2] и синтаксис f{1: 2} для frozendict, я решил сделать небольшую паузу от питона. В опенсорсе важно уметь переключать свое внимение, чтобы не ловить усталось и выгорание.

Потому, единственное, чем я занимался в CPython: ревью и обсуждение PEPов.

PEP 805

Я хочу показать самый интересный из них: PEP 805: Safe Parallel Python [3]. Марк Шенон предлагает сделать ОЧЕНЬ много всего для того, чтобы Free-Threading в CPython наконец начал нормально работать. Он буквально пишет: если мы оставим все как есть сейчас, то Free-Threading будет очень сложно использовать.

В чем суть предложения?

  1. Главная идея PEP’а, что VM питона должна контролировать доступ к объектам из разных потоков. И контролировать корректность такого доступа. Чтобы такого добиться, предлагается добавить довольно много новых частей Python

  2. Как? Самое главное – добавить каждому объекту __shareable__ атрибут: Immutable – которые нельзя менять и можно шарить в рамках ThreadGroup, Local – существующие только в рамках одной ThreadGroup (можно смело мутировать), Protected – можно мутировать с мьютексом, Synchronized – специальный маркер для билтинов (им ненужна внешняя синхронизация, они умеют сами)

  3. Добавить threading.ThreadGroup объекты, по сути – группа тредов, которые вместе работают над какими-то общими данными. Что позволит упростить синхронизацию общих объектов для них

  4. Добавить функции для управления стейтом объектов: freeze(), syncronize()

Пример:

class State:
       def __init__(self, value):
           self.value = value

class Counter:
    def __init__(self):
        self.mutex = Lock() 
        with self.mutex:
            self.number = self.mutex.protect(State(0))
        self.__freeze__()

    def value(self):
        with self.mutex:
            return self.number.value

    def increment(self):
        with self.mutex:
            self.number.value += 1

Как оно работает? Да ктож его знает 🌚️️

И хоть я оставил всего два мелких замечания, было очень интересно почитать. Скажу честно, что сесть и проанализировать каждый нюанс предложения у меня не было ни времени, ни сил. Но у нас в сообществе есть замечательный @zzzzzzerg [4], который уделил ПЕПу куда больше внимания [5]. Очень жду его развернутого мнения в комментариях под данной статьей.

Обсуждение: Что вы думаете про PEP-805? Сложно? Или наоборот?

Самый короткий crash в питоне

Попробуйте запустить такое в Python 3.14.0:

__conditional_annotations__ = 0
a: 1

Все упадет [6]. Не с исключением, а с core-dumped. Почему?

Когда в CPython добавляли поддержку ленивых аннотаций в PEP-649 [7], то нужно было как-то поддержать такой код:

>>> import dis
>>> dis.dis('''
... if int():
...     x: int = 0
... else:
...     x: str = ''
... ''')
  --           MAKE_CELL                0 (__conditional_annotations__)

   0           RESUME                   0

   2           LOAD_CONST               1 (<code object __annotate__ at 0x10cfe2710, file "<dis>", line 2>)
               MAKE_FUNCTION
               STORE_NAME               3 (__annotate__)
               BUILD_SET                0
               STORE_NAME               0 (__conditional_annotations__)
               LOAD_NAME                1 (int)
               PUSH_NULL
               CALL                     0
               TO_BOOL
               POP_JUMP_IF_FALSE        9 (to L1)
               NOT_TAKEN

   3           LOAD_SMALL_INT           0
               STORE_NAME               2 (x)
               LOAD_NAME                0 (__conditional_annotations__)
               LOAD_SMALL_INT           0
               SET_ADD                  1
               POP_TOP
               LOAD_COMMON_CONSTANT     7 (None)
               RETURN_VALUE

   5   L1:     LOAD_COMMON_CONSTANT     8 ('')
               STORE_NAME               2 (x)
               LOAD_NAME                0 (__conditional_annotations__)
               LOAD_SMALL_INT           1
               SET_ADD                  1
               POP_TOP
               LOAD_COMMON_CONSTANT     7 (None)
               RETURN_VALUE

Disassembly of <code object __annotate__ at 0x10cfe2710, file "<dis>", line 2>:
  2           RESUME                   0
              LOAD_FAST_BORROW         0 (format)
              LOAD_SMALL_INT           2
              COMPARE_OP             132 (>)
              POP_JUMP_IF_FALSE        3 (to L1)
              NOT_TAKEN
              LOAD_COMMON_CONSTANT     1 (NotImplementedError)
              RAISE_VARARGS            1
      L1:     BUILD_MAP                0

  3           LOAD_SMALL_INT           0
              LOAD_GLOBAL              0 (__conditional_annotations__)
              CONTAINS_OP              0 (in)
              POP_JUMP_IF_FALSE       10 (to L2)
              NOT_TAKEN
              LOAD_GLOBAL              2 (int)
              COPY                     2
              LOAD_CONST               1 ('x')

  2           STORE_SUBSCR

  5   L2:     LOAD_SMALL_INT           1
              LOAD_GLOBAL              0 (__conditional_annotations__)
              CONTAINS_OP              0 (in)
              POP_JUMP_IF_FALSE       10 (to L3)
              NOT_TAKEN
              LOAD_GLOBAL              4 (str)
              COPY                     2
              LOAD_CONST               1 ('x')

  2           STORE_SUBSCR
      L3:     RETURN_VALUE

Вот такая сложная машинерия для простого резолвинга аннотаций получается. Что тут происходит? Здесь мы определяем, как будет создаваться байткод для резолва аннотации:

  3           LOAD_SMALL_INT           0
              LOAD_GLOBAL              0 (__conditional_annotations__)
              CONTAINS_OP              0 (in)
              POP_JUMP_IF_FALSE       10 (to L2)
              NOT_TAKEN
              LOAD_GLOBAL              2 (int)
              COPY                     2
              LOAD_CONST               1 ('x')

{'x': int} если if int() будет True,


  5   L2:     LOAD_SMALL_INT           1
              LOAD_GLOBAL              0 (__conditional_annotations__)
              CONTAINS_OP              0 (in)
              POP_JUMP_IF_FALSE       10 (to L3)
              NOT_TAKEN
              LOAD_GLOBAL              4 (str)
              COPY                     2
              LOAD_CONST               1 ('x')

и {'x': str} если False. Проверим на чуть другом примере:

>>> if int():
...     x: int = 0
... else:
...     x: str = ''
...     
>>> __annotate__(1)
{'x': <class 'str'>}

и

>>> if int() == 0:
...     x: int = 0
... else:
...     x: str = ''
...     
>>> __annotate__(1)
{'x': <class 'int'>}

Я проревьюил фикс [8], очень забавно получилось!

Обсуждение: Знали ли вы про __conditional_annotations__? Не кажется ли вам, что такая фича – уже прям сильно сложно?

А еще поговорили про CPython и его будущее в подкасте у Монтаны:

wemake-python-styleguide

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

Лучший тул для качества кода на питоне: для вас и ваших агентов!

Выпустил wemake-python-styleguide@1.8.0 [11]!

Что в новом релизе?

  1. Поддержка Python3.15 и запрещены lazy import конструкции [12]

  2. Улучшена поддержка t строк, теперь мы считаем их сложность (как в f строках). Например, строка t'Hello, {user.profile.get_name()}' теперь будет запрещена, а t'Hello, {user}' – нет

  3. Добавлена поддержка ИИ: мы сделали скилл /wps [13] для написания кода агентами так, чтобы агент сразу писал код с контекстом будущих проверок и не тратил лишние токены на исправления

  4. Добавили локальный MCP [14], теперь агент может сам его стартануть, может его спрашивать про коды ошибок, как их исправлять. Меньше токенов жжется!

Установка  в Claude

Пример установки плагина в Claude

Обсуждение: Пробовали ли вы использовать wemake-python-styleguide с агентами? Довольны ли вы результатом?

Немного стыдного нейрослопа

Как и обещал в начале статьи.

Пока я делал MCP и прочее, мне конечно требовалось поставить агент локально. А как его можно безопасно локально запустить? А никак! Пришлось костылить [15] запуск агента в докере:

claude () {
  # How it works:
  # 1. First run: the native installer drops the binary into ~/.local
  #    (volume `claude-bin`), then you go through the normal login /
  #    onboarding flow in the CLI.
  # 2. Everything Claude Code writes — auth, settings, .claude.json,
  #    session history, memory — lands in `claude-settings`, because
  #    CLAUDE_CONFIG_DIR redirects all of it there.
  # 3. Later runs: binary and config are already in the volumes, so no
  #    reinstall and no re-auth. `claude update` inside the container
  #    also persists, since it writes to ~/.local/share/claude.
  # 4. The project is mounted at its host path (not /workspace), so
  #    each repo keeps its own per-project sessions and memory.

  local project="$(readlink -f '.')"
  local image='buildpack-deps:bookworm-scm'  # has curl, git, bash

  docker run -it --rm 
    -e CLAUDE_CONFIG_DIR=/claude-config 
    -e PATH="/root/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" 
    -v claude-settings:/claude-config 
    -v claude-bin:/root/.local 
    -v "$project":"$project" 
    -w "$project" 
    "$image" 
    bash -c '
      if [ ! -x "$HOME/.local/bin/claude" ]; then
        curl -fsSL https://claude.ai/install.sh | bash
      fi
      exec claude "$@"
    ' _ "$@"
}

Можно посмотреть все мои дотфайлы: https://github.com/sobolevn/dotfiles [16] Там много полезного.

Обсуждение: А как вы запускаете агентов локально? Что еще можно обезопасить?

Кстати про нейрослоп. Провели стрим с @Propan671 [17], пытались решить задачки из django-modern-rest полностью при помощи агентов. Только вайбкодом. И очень сложные. И security-related.

Получилось ли? Посмотрите стрим или прочитайте итоговый пост [18]!

django-modern-rest

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

Выпустили релиз django-modern-rest@0.14.0 [21]!

Что добавили?

  • Официальную поддержку нового тайпчекера ty

  • Добавили полезностей для Router, теперь можно включать Router в urlpatterns просто как router.to_urlpatterns(), сильно меньше кода

  • Добавили поддержку @validate для создания переиспользуемых контроллеров [22]

Сейчас работаем над релизом 0.15.0, там будет очень много изменений [23]. А еще завтра начнется событие в нашем сообществе: #opensource_september [24], следить за ним можно будет у меня в тг [25].

А еще я записал подкаст про django-modern-rest с уважаемым @PyLounge [26]

Обсуждение: Появилось желание попробовать после подкаста?

django-stubs

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

Выпустили django-stubs@6.1.0 [29]! Релиз с поддержкой django@6.1.

А еще мы теперь поддерживаем pyrefly и ty в качестве официальных тайпчекеров. Конечно, силы плагинов mypy там нет, но базовые вещи – работают. Мы теперь гоняем тесты и проверяем.

Обсуждение: а каким тайпчекером пользуетесь вы?

Проекты сообщества

  • https://github.com/dshein-alt/rusty [30]Result как в Rust, но только для питона

  • https://github.com/dshein-alt/serde [31] – Библиотека для сериализации моделей на базе NamedTuple

  • https://github.com/dshein-alt/ropds [32] – Быстрый и лёгкий сервер персональной электронной библиотеки с каталогом OPDS 1.2 / 2.0 и веб-интерфейсом.

  • https://github.com/th0r3nt/JAWL [33] (Just A While Loop) – ИИ-фреймворк для автономных ИИ-агентов через ReAct-циклы.

  • https://github.com/fomenko-ai/pytest-hardware-template [34] – шаблон автоматизации тестирования оборудования на основе pytest. Основная идея – сделать как можно более общий универсальный шаблон и развивать его как библиотеку, адаптируя будущие обновления шаблона во внутренние проекты компании с помощью AI скиллов.

  • https://github.com/brilliance-admin/backend-python [35] – админ панели на vue под sqlalchemy/django/любой источник данных с backend

  • https://github.com/goduni/unissh [36] – современный, опенсорсный SSH клиент с selfhosted zero-knowledge сервером для синхронизации данных

  • https://github.com/kap-sh/capo [37] – нормальный Python SDK для Amazon, поверх zapros [38], вместо ужасного boto. Про него на канале вышел пост [39]

  • https://github.com/pomponchik/incontext [40] – плагин для Hermes agent, который делает переменную max_tokens динамической, зависящей от остатка эффективного контекстного окна (т.е. с учетом запаса на сжатие)

  • https://github.com/rtugov/agentcontext [41] – В общем история такая – неделю назад угорел по ИИшкам и локальным инференсам. В итоге понял что нет тулинга чтобы посмотреть контекст того же codex в риалтайме. В итоге я сделал свой proxy который встает между codex, claude, opencode, pi, etc. и просто показывает контекст запуска по шагам

  • https://github.com/mutating/skelet [42] – Работа с несколькими источниками конфига, в разных форматах (в т.ч. перечисленные переменные окружения, YAML-файлы и другие), типизация (как на рантайме, так и в статик тайп чек тайме), и работа с секретами

  • https://github.com/pomponchik/autofission [43] – По сути управляет одной настройкой – MaxScale в кластере Куба при использовании lambda функций. Теперь она не хардкодится, а определяется по реальной загрузке кластера, которая информация о которой регулярно обновляется

  • https://github.com/OlegYurchik/metaorm [44] – Асинхронный слой работы с БД поверх SQLModel

  • https://github.com/Khabib73/open-source-playbook [45] – Гайд для корректно, вежливого и продуктивного общения в опенсорсе. Многим бы пригодилось!

Хотите оказаться в списке? Пишите про свой проект с тегом #opensource к нам в чат [46].

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

Выводы

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

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

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

До встречи в сентябре!

Автор: sobolevn

Источник [57]


Сайт-источник BrainTools: https://www.braintools.ru

Путь до страницы источника: https://www.braintools.ru/article/34986

URLs in this post:

[1] Мой прошлый дайджест: https://habr.com/ru/articles/1065404

[2] PEP-841: https://peps.python.org/pep-0841/

[3] PEP 805: Safe Parallel Python: https://github.com/python/peps/pull/5101

[4] @zzzzzzerg: https://www.braintools.ru/users/zzzzzzerg

[5] внимания: http://www.braintools.ru/article/7595

[6] упадет: https://github.com/python/cpython/issues/154902

[7] PEP-649: https://peps.python.org/pep-0649/

[8] проревьюил фикс: https://github.com/python/cpython/pull/155026

[9] репозиторий: https://github.com/wemake-services/wemake-python-styleguide

[10] зеркало: https://gitverse.ru/sobolevn/wemake-python-styleguide

[11] wemake-python-styleguide@1.8.0: https://github.com/wemake-services/wemake-python-styleguide/releases/tag/1.8.0

[12] конструкции: https://github.com/wemake-services/wemake-python-styleguide/issues/3639

[13] скилл /wps: https://github.com/wemake-services/wemake-python-styleguide/blob/master/.agents/skills/wps/SKILL.md

[14] локальный MCP: https://github.com/wemake-services/wemake-python-styleguide/blob/master/wemake_python_styleguide/mcp.py

[15] костылить: https://github.com/sobolevn/dotfiles/blob/master/shell/.functions

[16] https://github.com/sobolevn/dotfiles: https://github.com/sobolevn/dotfiles

[17] @Propan671: https://www.braintools.ru/users/Propan671

[18] итоговый пост: https://t.me/opensource_findings/980

[19] репозиторий: https://github.com/wemake-services/django-modern-rest

[20] зеркало: https://gitverse.ru/sobolevn/django-modern-rest

[21] django-modern-rest@0.14.0: https://github.com/wemake-services/django-modern-rest/releases/tag/0.14.0

[22] переиспользуемых контроллеров: https://django-modern-rest.readthedocs.io/en/latest/pages/reusable-code.html

[23] очень много изменений: https://github.com/wemake-services/django-modern-rest/blob/master/CHANGELOG.md#0150-wip

[24] #opensource_september: https://github.com/wemake-services/django-modern-rest/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22opensource%20september%22

[25] у меня в тг: https://t.me/opensource_findings

[26] @PyLounge: https://www.braintools.ru/users/PyLounge

[27] репозиторий: https://github.com/typeddjango/django-stubs

[28] зеркало: https://gitverse.ru/sobolevn/django-stubs

[29] django-stubs@6.1.0: https://github.com/typeddjango/django-stubs/releases/tag/6.1.0

[30] https://github.com/dshein-alt/rusty: https://github.com/dshein-alt/rusty

[31] https://github.com/dshein-alt/serde: https://github.com/dshein-alt/serde

[32] https://github.com/dshein-alt/ropds: https://github.com/dshein-alt/ropds

[33] https://github.com/th0r3nt/JAWL: https://github.com/th0r3nt/JAWL

[34] https://github.com/fomenko-ai/pytest-hardware-template: https://github.com/fomenko-ai/pytest-hardware-template

[35] https://github.com/brilliance-admin/backend-python: https://github.com/brilliance-admin/backend-python

[36] https://github.com/goduni/unissh: https://github.com/goduni/unissh

[37] https://github.com/kap-sh/capo: https://github.com/kap-sh/capo

[38] zapros: https://github.com/kap-sh/zapros

[39] вышел пост: https://t.me/opensource_findings/983

[40] https://github.com/pomponchik/incontext: https://github.com/pomponchik/incontext

[41] https://github.com/rtugov/agentcontext: https://github.com/rtugov/agentcontext

[42] https://github.com/mutating/skelet: https://github.com/mutating/skelet

[43] https://github.com/pomponchik/autofission: https://github.com/pomponchik/autofission

[44] https://github.com/OlegYurchik/metaorm: https://github.com/OlegYurchik/metaorm

[45] https://github.com/Khabib73/open-source-playbook: https://github.com/Khabib73/open-source-playbook

[46] в чат: https://t.me/opensource_findings_chat

[47] Сделали папку: https://t.me/opensource_findings/982

[48] Горячо обсуждали у меня в канале: https://t.me/opensource_findings/981

[49] тут хроника истории: https://t.me/opensource_findings/984

[50] Починил поддержку старых версий: https://github.com/pytest-dev/pytest-django/pull/1304

[51] returns@0.29.0: https://github.com/dry-python/returns/releases/tag/0.29.0

[52] Правил баги: https://github.com/litestar-org/litestar/pull/4971

[53] https://boosty.to/sobolevn: https://boosty.to/sobolevn

[54] https://github.com/sponsors/wemake-services: https://github.com/sponsors/wemake-services

[55] главный канал: https://telegram.me/opensource_findings

[56] поучаствовать: https://telegram.me/opensource_findings_python

[57] Источник: https://habr.com/ru/articles/1076694/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1076694

www.BrainTools.ru

Rambler's Top100