Привет! Меня зовут Никита Соболев. Я занимаюсь опенсорс разработкой полный рабочий день. Тут я рассказываю про #python, #c, интересные проекты, коммиты, доклады, и тд. Поддержать: https://boosty.to/sobolevn Для связи: @sobolev_nikita
PEP 810: Explicit lazy imports
На обсуждение вышел новый PEP, который предлагает добавить в Python 3.15 новый вид импортов.
https://peps.python.org/pep-0810/
lazy import json
lazy from json import dumps
import sys
lazy import json
print('json' in sys.modules) # Модуля еще нет
# Загрузка начинается вот тут:
result = json.dumps({"hello": "world"})
print('json' in sys.modules) # Теперь он загружен
if TYPE_CHECKING: import some_module
, было множество предложений по добавлению import type
конструкции, пример: https://discuss.python.org/t/type-only-imports/96755LazyLoader
и страшных вещей вроде https://scientific-python.org/specs/spec-0001__lazy_import__
(аналог __import__
для импорта )__lazy_modules__
, где будут храниться ленивые импорты текущего модуляtypes.LazyImportType
как тип нового "ленивого" модуля (аналог `types.ModuleType`)sys.lazy_modules
для списка ленивых модулей глобальноsys.set_lazy_imports_filter
для глобальных настроек импортов, что? 🤯IMPORT_NAME
получит флаг, является ли импорт ленивымimport *
не будет доступен в lazy
режиме.
# SyntaxError: lazy from ... import * is not allowed
lazy from json import *
django-modern-rest
. Простую и удобную библиотеку для REST API в Django. Пока на стадии pre-alpha, но уже скоро будет первый релиз. Можно уже ставить ⭐, чтобы потом всем говорить, что первыми узнали про новую модную штуку!Записи с нашей бесплатной конфы в Нижнем подъехали!
Во-первых, хочу еще раз повторить слова благодарности всем: организаторам и волонтерам, спикерам, участникам. Отдельно хочу отметить тех, кто поддержал конфу анонсами. Приехало и пришло очень много людей: 1100+ регистраций, почти 600 человек на площадке. Вы все крутые!
Во-вторых, в программе случились две замены. Ждем ребят, кто не смог приехать, на будущих мероприятиях.
Постарался найти баланс между хардкорнейшими докладами, спикерами-новичками (они обязательно должны быть на любой конференции, по моему мнению, я когда-то был таким спикером-новичком) и легкими погружениями в технологии.
Конкуренция за звание "лучшего" доклада – высочайшая. Выбирать будем не по каким-то там голосованиям, которые ни на что не влияют, а по просмотрам на ютюбе. Поддержите спикеров и меня просмотрами :)
Выкладываю записи сразу, а не через полгода.
Доклады в порядке выступлений:
- Чего вы не знали о строках в Python? Василий Рябов
- ИИ-агенты в каждый дом, Алексей Порядин @sw1logs
- Генератор байткода и байткод генератора, Михаил Ефимов
- Внутреннее устройство сборки мусора в CPython 3.14+, Сергей Мирянов
- Проектирование — это когда чувствуешь, а не какие-то там циферки, Николай Хитров @nkhitrov_blog
- Дотянуться до кремния. HighLoad Python: SIMD, GPU – Пётр Андреев @py_up
- Continuous profiling, Давид Джалаев @dzhalaevd_blog
Доклад от Ильи Ильиных был в другом зале, он записывал его сам - ждите доклад про Vim у него на канале @kydavoiti
Но, на конфе были и минусы :)
Следующий раз постараемся поправить шум от стендов (мешали в большом зале), записать все доклады (сейчас только один зал), пригласить еще больше гостей.
Наша традиционная вечеринка собрала полный бар людей, было очень весело. Отлично посидели, погуляли, поболтали. Лучшее сообщество!
При поддержке:
- https://it52.info
- https://itgorky.ru
Обсуждение: Кто был? Как вам конфа? Как вам Нижний Новгород?
Если понравилось: напишите приятное организаторам и спикерам. Если не понравилось – пишите вашу критику. Будем делать лучше.
| Поддержать | sobolevn">YouTube | GitHub | Чат |
Находки в опенсорсе: EasyP – тулбокс для ProtoBuf файлов
https://www.youtube.com/watch?v=XI-dNpM77iMeasyp
– пакетный менеджер, билд-система и линтер для .proto
файлов.
Хоть easyp
и написан на #go 😱, одна из его фишек в том – что вы можете использовать любые плагины для генерации финального кода: он может быть хоть на #python, хоть на #rust.
Если много используете ProtoBuf – обязательно для ознакомления!
Как оно работает?
# Секция для правил линтера:
lint:
use:
- DEFAULT
# Секция с зависимостями:
deps:
- github.com/googleapis/googleapis
- github.com/grpc-ecosystem/grpc-gateway@v2.20.0
# Секция для правил сборки и генерации итоговых файлов:
generate:
plugins:
- name: go
out: .
opts:
paths: source_relative
- name: go-grpc
out: .
opts:
paths: source_relative
require_unimplemented_servers: false
Makefile
с кучей скриптов для сборки.minimal vscode: убираем вкладки
https://www.youtube.com/watch?v=reT_wnDSaX4
Вкладки любят делать вид, что они очень полезны. Однако, такое впечатление обманчиво. Навигация по ним будет съедать у вас кучу времени. Взамен – есть способы лучше.
Что будет в видео?
- Как убрать вкладки?
- Как сделать заголовок вкладки читаемым?
- Что использовать вместо вкладок?
- Какие способы навигации удобнее вкладок?
Пельмени
Пельмени? Пельмени!
А еще в середине видео я решил попиарить свои любимые пельмени из родного Новосибирска. А мы там знаем толк в пельменях. Если вы не сидите в нашем чате, то вы пропустили все мемы про пельмени, и откуда вообще такая тема пошла :)
Сделал для вас набор рецептов для их приготовления от самого простого уровня до довольно замороченного: github.com/sobolevn/the-best-python-course/tree/main/minimal_vscode/dumplings
Рецепты написаны на языке Cooklang
: https://cooklang.org
Специальный язык для разметки рецептов + среда для рендеринга рецептов в терминале и в виде сайтика.
Очень прикольная штука, выглядит вот так:
Положите пельмени в воду, варите пока не всплывут все пельмени и еще ~{7%min} после.
Главное – не переварить, блин!
При подаче смешайте @кетчуп и @майонез в качестве соуса.
Большая бесплатная Python конференция в Нижнем Новгороде!
У меня хорошая новость. Делаем бесплатную конфу по питону, пригласили топовых специалистов: core-разработчиков, контрибьюторов, организаторов разных комьюнити движух, специалистов из индустрии. И все – участники нашего чата. Как же так получилось 🌚
Вот такой список докладов:
- Подопригора Кирилл, CPython core dev, Evrone: Кто такой CFG в CPython, и какую роль он выполняет?
- Хитров Николай, организатор @peer_2beer, Точка Банк: Проектирование — это когда чувствуешь, а не какие-то там циферки
- Ильиных Илья, организатор @spbpythonnews, блоггер @kydavoiti: Vim — это метаредактирование
- Джалаев Давид, Газпром-нефть ЦР: Continuous profiling
- Порядин Алесей, участник @pytho_nn: AI-агенты в каждый дом
- Мирянов Сергей, CPython contributor, РН-Технологии: Внутреннее устройство сборки мусора в CPython 3.14+
- Неретин Степан, CPython contributor, Postgres Professional: Своя файловая система за 5 минут на Python
- Ефимов Михаил, CPython contributor, X5 Tech: Генератор байткода и байткод генератора
Ну мощь же 💪
Да, конференции можно делать и не за 50 тыщ за билет.
А еще будет много доброго общения, обсуждения кишочков питона, настолки и тусовка до утра :)
Идеальный день.
Бронируйте дату! Если давно хотели побывать в Нижнем – вот идеальный повод. Буду рад всех видеть лично!
📍 Когда: 20 сентября 2025 года
📍 Где: ул. Нижне-Волжская набережная, 11, «Академия Маяк» им. А.Д. Сахарова
Регистрация обязательна: https://dev.itgorky.ru
| Поддержать | sobolevn">YouTube | GitHub | Чат |
minimal vscode: убираем Activity и Side Bars
https://www.youtube.com/watch?v=wxbifNb1Q1o
Одни из главных потребителей места на мониторе: Activity Bar и Side Bar.
Их настолько просто убрать, чтобы оставалось больше места для кода, что я не вижу причин НЕ делать так.
- В несколько хоткеев сделаем столько же (и даже больше, учитывая новый отдельный Outline View)
- А еще отцентрируем Command Palette, чтобы было удобнее
- Покажу, почему Side Bar лучше иметь справа, а не слева от кода
- Прячем Side Bar, когда он нам не нужен (почти всегда)
- Покажу своих котов 🐈
Короткое, но максимально содержательное видео.
А что дальше по контенту?
На неделе будет большой пост из цикла "отвечаем на любимые вопросы с собесов": поговорим про то, как создаются объекты. PyObject_New
, PyObject_NewVar
, аллокаторы, изменяемость и неизменяемость. Поговорим: чем мутабельность int
отличается от мутабельности tuple
и list
. Спрашивали ли вас такое? Считаете ли вы такой вопрос вообще полезным?
А еще скоро будут видео с прошлого митапа PythoNN! Три очень крутых доклада.
Следом будет продолжение "Лучшего курса по Питону" – устройство memoryview
, полезнейший тип данных в питоне, который незаслуженно часто забывают.
Потом вернемся к чистке vscode от ненужного.
Если нравится такой план, не забудь поддержать :)
Обсуждение: пользуетесь ли вы Activity / Side Bar? Какие у вас самые популярные варианты использования данных инструментов?
| Поддержать | sobolevn">YouTube | GitHub | Чат |
Находки на рыбалке: FastStream
https://www.youtube.com/watch?v=Qf4r8xV-YNk
Сгоняли с @fastnewsdev на рыбалку: поговорили про FastStream, пожарили мясо, обсудили проблемы разработчиков при работе с очередями / брокерами сообщений.
В своем первом посте мы уже знакомились с базовыми фичами FastStream'а, но для тех, кто пропустил, напоминаю: FastStream – аналог FastAPI, но для работы с событиями в брокерах / очередях.
Пример:
from faststream import FastStream
from faststream.kafka import KafkaBroker
broker = KafkaBroker("localhost:9092")
app = FastStream(broker)
@broker.publisher("another-topic")
@broker.subscriber("first-topic")
async def handle(user: str) -> str:
return f"Hi, {user}!"
first-topic
, парсит из них поле user
типа str
, выполняет логику обработки, отправляет новое сообщение в another-topic
. Просто? Удобно?PEP-797: Иммортализация произвольных объектов
- Ссылка на черновик PEP: https://github.com/python/peps/pull/4497
- Оригинальный PEP-683 с Immortal объектами: https://peps.python.org/pep-0683
Продолжаем про будущее субинтерпретаторов! Да, что-то меня увлекла данная тема :)
Как делиться объектами без копирования данных в субинтерпретаторах?
Чтобы легко делиться объектами между субинтерпретаторами, необходимо, чтобы они были полностью иммутабельными. Полностью. Не с точки зрения питона, а с точки зрения C.
Даже если вы будете делиться объектами, которые внешне нельзя изменить, то все еще будет один очень важный нюанс.
Напомню, что объект питона с точки зрения C выглядит так (упрощенно):
struct PyObject {
Py_ssize_t ob_refcnt;
PyTypeObject *ob_type;
};
ob_refcnt
все время меняется. Буквально в любом C коде все время вызовы Py_DECREF
и Py_INCREF
. Без изменения счетчика ссылок невозможно выполнить никакой код. И буквально все части питона содержат вызовы, которые уменьшают или увеличивают счетчик ссылок во время работы программы. ob_refcnt
. А чтобы его не менять, нам нужно, чтобы объект был бессмертным: жил на протяжении всего времени жизни программы и потом был правильно собран.static
в C и 'static
в Rust.
#define _Py_IMMORTAL_REFCNT_LOCAL UINT32_MAX
Immortal
, когда у него просто очень большой refcnt
. Что легко проверить:
>>> import sys
>>> sys.getrefcount(None)
3221225472
>>> x = None # он не будет меняться при создании новых имен:
>>> sys.getrefcount(None)
3221225472
static inline Py_ALWAYS_INLINE void Py_INCREF(PyObject *op)
{
if (_Py_IsImmortal(op)) {
_Py_INCREF_IMMORTAL_STAT_INC();
return;
}
op->ob_refcnt++;
}
static inline Py_ALWAYS_INLINE void Py_DECREF(PyObject *op)
{
if (_Py_IsImmortal(op)) {
return;
}
if (--op->ob_refcnt == 0) {
_Py_Dealloc(op);
}
}
import sys
admin = User('admin')
sys._immortalize(admin)
assert sys._is_immortal(admin)
admin
будет жить до конца программы. И его можно будет использовать в разных субинтерпретаторах без копирования:
>>> from concurrent import interpreters
>>> interp = interpreters.create()
>>> interp.prepare_main(admin=admin)
>>> interp.exec('print(admin)')
'<User: admin>'
PEP-734: subinterpreters и _interpqueues
Еще одной важной часть субинтерпретаторов являются очереди. Прочитать про них можно тут:
- https://peps.python.org/pep-0734/#queue-objects
- https://github.com/python/cpython/blob/main/Lib/concurrent/interpreters/_queues.py
- https://github.com/python/cpython/blob/main/Modules/_interpqueuesmodule.c
Зачем нужны очереди? Чтобы передавать объекты между интерпретаторами:
>>> from concurrent import interpreters
>>> interp = interpreters.create()
>>> queue = interpreters.create_queue()
>>> complex_object = [1, 'a', {'key': 'val'}, (2, 3), None, object()]
>>> complex_object # notice original id
[1, 'a', {'key': 'val'}, (2, 3), None, <object object at 0x101169ff0>]
>>> queue.put_nowait(complex_object)
>>> interp.prepare_main(queue=queue) # passing `queue` object to other interpreter
>>> interp.exec('print(queue.get_nowait())') # notice id change
[1, 'a', {'key': 'val'}, (2, 3), None, <object object at 0x101b054e0>]
>>> id(12)
4308538240
>>> interp.exec('print(id(12))')
4308538240
memoryview
(как и другие PyBuffer
объекты) имеет особые гарантии. Мы передаем память прям физически. Без копирования. Потому что np.array
и подобное - тоже буферы. Если бы мы копировали память буферов, то такие вычисления не отличались бы от multiprocessing
, который часть по памяти имеет проблемы. Здесь – все по-другому:
>>> b = bytearray(b'123')
>>> m = memoryview(b)
>>> queue.put_nowait(m)
>>> interp.exec('(m := queue.get_nowait()); print(m); m[:] = b"456"') # changing memory directly
<memory at 0x103274940>
>>> b # was changed in another interpreter!
bytearray(b'456')
pickle
(пока что). В планах – ускорить и упростить данный процесс:
# ex.py
>>> class Custom:
... def __init__(self, arg: int) -> None:
... self.arg = arg
... def __getstate__(self):
... print('__getstate__')
... return {'arg': self.arg}
... def __setstate__(self, state):
... print('__setstate__', state)
... self.arg = state['arg']
# main.py
>>> from ex import Custom
>>> c = Custom(1)
>>> c
<ex.Custom object at 0x101a078c0>
>>> queue.put_nowait(c)
__getstate__
>>> interp.exec('print(queue.get_nowait())') # different object
__setstate__ {'arg': 1}
<ex.Custom object at 0x103222f20>
PEP-734: Subinterpreters in stdlib
- PEP: https://peps.python.org/pep-0734
- Обсуждение: https://discuss.python.org/t/pep-734-multiple-interpreters-in-the-stdlib/41147
- Документация: https://docs.python.org/3.14/library/concurrent.interpreters.html
Что оно такое?
Несколько полноценных интерпретаторов работающих рядом. Какие плюсы?
- Один процесс
- Один тред, но руками можно создавать еще
- Простые данные можно шарить без необходимости pickle, сложные нужно пиклить
- По GILу на интерпретатор, все еще можно получить плюшки настоящей многозадачности по сети
- Работает с asyncio
Минусы:
- C
код нужно было значительно переработать, не все C расширения поддерживаются (пока)
Получается хорошая универсальность для разных задач.
Немного истории
Есть несколько важных нетехнических аспектов про процесс создания данной фичи:
- PEP-734 и Free-Threading делают очень похожие вещи – позволяют реализовывать настоящую многозадачность, но разными способами
- Изначально субинтерпретаторы появились в 3.10 в виде только C-шного АПИ
- Есть отдельный PyPI пакет с данным кодом
- Пайтон часть в виде PEP-734 был добавлена в 3.14 уже после feature freeze
- Изначально планировалось добавить его как модуль interpreters, однако в последний момент он стал concurrent.interpreters, вот тут доступно большое обсуждение
Как работает?
Внутри довольно много разных C-шных модулей:
- Основа: https://github.com/python/cpython/blob/main/Python/crossinterp.c
- Дефиниция модуля: https://github.com/python/cpython/blob/main/Modules/_interpretersmodule.c
- Очередь для обмена сообщениями между интерпретаторами: https://github.com/python/cpython/blob/main/Modules/_interpqueuesmodule.c
- Набор примитивов: https://github.com/python/cpython/blob/main/Modules/_interpchannelsmodule.c
Но, для пользователей - важен только питоновский АПИ, что прекрасно. Он получился простым и понятным:
interp = interpreters.create()
try:
interp.exec('print("Hello from PEP-554")')
finally:
interp.close()
def worker_cpu(arg: tuple[int, int]):
start, end = arg
fact = 1
for i in range(start, end + 1):
fact *= i
from concurrent.futures import InterpreterPoolExecutor
def bench_subinterpreters():
with InterpreterPoolExecutor(CPUS) as executor:
list(executor.map(worker, WORKLOADS))
Regular: Mean +- std dev: 163 ms +- 1 ms
Threading with GIL: Mean +- std dev: 168 ms +- 2 ms
Threading NoGIL: Mean +- std dev: 48.7 ms +- 0.6 ms
Multiprocessing: Mean +- std dev: 73.4 ms +- 1.5 ms
Subinterpreters: Mean +- std dev: 44.8 ms +- 0.5 ms
def worker_io(arg: tuple[int, int]):
start, end = arg
with httpx.Client() as client:
for i in range(start, end + 1):
client.get(f'http://jsonplaceholder.typicode.com/posts/{i}')
concurrent.interpreters
показывают хорошее время:
Regular: Mean +- std dev: 1.45 sec +- 0.03 sec
Threading with GIL: Mean +- std dev: 384 ms +- 17 ms (~1/4 от 1.45s)
Threading NoGIL: Mean +- std dev: 373 ms +- 20 ms
Multiprocessing: Mean +- std dev: 687 ms +- 32 ms
Subinterpreters: Mean +- std dev: 547 ms +- 13 ms
asyncio
так параллелить, хотя я пока и не пробовал.__code__.co_exceptiontable: начало
Чтобы познакомиться с таблицами обработки исключений, нам потребуется нырнуть глубоко.
Базовая идея: таблица исключений показывает, какие строки байткода покрыты обработчиками ошибок, а какие – нет. По сути – таблица неявных переходов между логическими лейблами. За счет данной технологии реализованы питоновские "zero-cost exceptions".
Возьмем для примера вот такую простую функцию:
def other(x, y):
res = None
try:
res = x / y
except ZeroDivisionError:
res = 0
finally:
print(res)
ExceptionTable:
L1 to L2 -> L3 [0]
L3 to L4 -> L6 [1] lasti
L4 to L5 -> L7 [0]
L5 to L6 -> L6 [1] lasti
L6 to L7 -> L7 [0]
L7 to L8 -> L8 [1] lasti
try
(метки c L1
по L2
невключительно) прыгаем на L3
.finally
после успешного случая:
3 L1: LOAD_FAST_BORROW_LOAD_FAST_BORROW 1 (x, y)
BINARY_OP 11 (/)
STORE_FAST 2 (res)
7 L2: LOAD_GLOBAL 3 (print + NULL)
LOAD_FAST_BORROW 2 (res)
CALL 1
POP_TOP
LOAD_CONST 1 (None)
RETURN_VALUE
finally
разложился в набор байткода прямо после тела try
. Нам не нужно никаких дополнительных манипуляций, чтобы управлять указателем на следующую инструкцию. Так происходит благодаря псевдо-инструкции SETUP_FINALLY. Так было не всегда, раньше тут был JUMP_FORWARD
, а finally
был общий для всех.finally
после обработанного ZeroDivisionError
исключения:
-- L3: PUSH_EXC_INFO
4 LOAD_GLOBAL 0 (ZeroDivisionError)
CHECK_EXC_MATCH
POP_JUMP_IF_FALSE 6 (to L5)
NOT_TAKEN
POP_TOP
5 LOAD_SMALL_INT 0
STORE_FAST 2 (res)
L4: POP_EXCEPT
JUMP_BACKWARD_NO_INTERRUPT 28 (to L2)
ZeroDivisionError
, то обрабатываем, в конце обработки ошибки прыгаем к L2
. Если ошибка другая, то прыгаем в L5
(будет ниже). Из интересного, POP_EXCEPT убирает текущее исключение из tstate->exc_info
, так исключение считается обработанным.
4 L5: RERAISE 0
-- L6: COPY 3
POP_EXCEPT
RERAISE 1
L7: PUSH_EXC_INFO
7 LOAD_GLOBAL 3 (print + NULL)
LOAD_FAST_CHECK 2 (res)
CALL 1
POP_TOP
RERAISE 0
-- L8: COPY 3
POP_EXCEPT
RERAISE 1
except
и finally
блоках. Для такого у нас есть:L3 to L4 -> L6 [1] lasti
для обработки ошибок в except
L7 to L8 -> L8 [1] lasti
для обработки ошибок в finally
other(1, 2)
: L1
-> L2
(finally)other(1, 0)
: L1
-> L3
-> L4
-> L2
(finally)other(1, 'a')
: L1
-> L3
(TypeError) -> L5
-> L7
(finally) -> L8
finally
в обработке сложных ошибок?unraisable exceptions в питоне
Мы все с вами привыкли, что в питоне можно "зарайзить" исключение в любой момент: raise Exception
Но, что если в какой-то момент времени мы не можем вызывать исключение?
Простейший пример: что произойдет при запуске такого скрипта?
# ex.py
class BrokenDel:
def __del__(self):
raise ValueError('del is broken')
obj = BrokenDel()
del obj
print('done!') # будет ли выведено?
del
вызовет ValueError
и программа завершится
» ./python.exe ex.py
Exception ignored while calling deallocator <function BrokenDel.__del__ at 0x10303c1d0>:
Traceback (most recent call last):
File "/Users/sobolev/Desktop/cpython/ex.py", line 3, in __del__
raise ValueError('del is broken')
ValueError: del is broken
done!
dealloc
для list
?
static void
list_dealloc(PyListObject *op)
{
Py_ssize_t i;
PyObject_GC_UnTrack(op); // убираем объект из отслеживания gc
if (op->ob_item != NULL) {
i = Py_SIZE(op);
while (--i >= 0) {
Py_XDECREF(op->ob_item[i]); // уменьшаем счетчик ссылок каждого объекта в списке
}
op->ob_item = NULL;
}
PyObject_GC_Del(op);
}
PyErr_SetString(PyExc_ValueError, "some text")
NULL
как PyObject *
из соответствующих АПИ, показывая, что у нас ошибка. Если вернуть NULL
нельзя, то мы не можем поставить ошибку. А тут у нас void
и вернуть вообще ничего нельзя. Потому приходится использовать вот такой подход с unraisable exception
import atexit
def foo():
raise Exception('foo')
atexit.register(foo)
gc
В Python3.14 добавили подсветку синтаксиса в новом PyREPL
- PR: https://github.com/python/cpython/pull/133247
Выглядит прикольно. А еще можно делать свои темы, вот пример дефолтной:
theme = {
"PROMPT": colors.BOLD_MAGENTA,
"KEYWORD": colors.BOLD_BLUE,
"BUILTIN": colors.CYAN,
"COMMENT": colors.RED,
"STRING": colors.GREEN,
"NUMBER": colors.YELLOW,
"OP": colors.RESET,
"DEFINITION": colors.BOLD,
"SOFT_KEYWORD": colors.BOLD_BLUE,
"RESET": colors.RESET,
}
your_theme
export PYTHONSTARTUP='import _colorize; from your_theme import theme; _colorize.set_theme(theme)'
PYTHONSTARTUP
: https://docs.python.org/3/using/cmdline.html#envvar-PYTHONSTARTUPПервый чемпионат по опенсорсной настолке про IT: Ship IT!
Как вы можете знать, я сделал опенсорсую настолку про IT: github.com/sobolevn/ship-it-boardgame
Её можно бесплатно распечатать и поиграть. Она так же есть бесплатно в стиме для Table Top Simulator.
Игра от 2 до 5 игроков, от 30 до 45 минут, для игроков 16+
Шуточная игра для тех, кто любит IT. Первыми задеплойте все компоненты вашей архитектуры в прод и победите конкурентов! И прямо как в настоящей жизни нужно: душить и отменять своих коллег, следить за своей инфраструктурой и не деплоить в пятницу, применять sudo
, выгорать и воровать, находить ошибки и уязвимости в чужих приложения. Игра с нешуточными последствиями!
PiterPy
Но, у меня есть новости куда круче. На ближайшем PiterPy – мы устроим первый официальный чемпионат по Ship IT!
Формат: 10 минут на разминочную игру. Далее 3 полных круга, победители проходят дальше.
Ведущий: я!
Призы: за выход в финал – настольная игра. За победу – бесплатный билет на любую конференцию jug.ru по вашему выбору. 🏆
Правила: https://github.com/sobolevn/ship-it-boardgame/blob/master/ru/rules.md
Играем в душный режим с фрилансом!
Версия игры: 0.0.23
Обязательно перед игрой прочитайте правила игры, если еще не играли :) Можно предварительно потренироваться в Steam'е с друзьями или нашей группе.
Дата и время: 16 мая, на вечеринке PiterPy + IML. Начало в 19:30.
Место: Зал 4.
✅ Регистрация на игру обязательна: https://docs.google.com/forms/d/e/1FAIpQLSctYdyrcSPKFY79o4A5-1-FZxIUnoIcjHuELFTDlkoRMA4Q_w/viewform
Жду всех настольщиков на настоящую зарубу!
Если еще не купили билет, то вот промокод на 25% скидки на персональный билет: OPENSOURCEFINDINGS
А для студентов и преподавателей ВУЗов там вообще есть отдельные билеты.
Реклама. ООО «Джуг Ру Груп». ИНН 7801341446 erid 2RanymVajKX
Perforator — система непрерывного профилирования для разных языков
https://github.com/yandex/perforator
Главные фичи:
> Efficient and high-quality collection of kernel + userspace stacks via eBPF
> Scalable storage for storing profiles and binaries
> Support of unwinding without frame pointers and debug symbols on host
> Convenient query language and UI to inspect CPU usage of applications via flamegraphs
> Support for C++, C, Go, and Rust, with experimental support for Java and Python
> Generation of sPGO profiles for building applications with Profile Guided Optimization (PGO) via AutoFDO
Но самое главное – у Perforator есть режим Continuous Profiling, где на сервак ставится агент, который передает информацию о производительности всех сервисов. На что тратит всего около 1% CPU.
Очень полезный и полный пост с анонсом на хабре.
Важное ограничение: пока работает только на x86_64 Linux, ARM поддержка планируется в ближайшем будущем.
Профилируем код на Python
Нас конечно же больше всего интересует, как данная штука умеет профилировать код на питоне.
Пока что работают только версии после 3.12, потому что нативная поддержка perf
появилась именно там: https://docs.python.org/3/howto/perf_profiling.html
Смотрим доку, как профилировать питон: https://perforator.tech/docs/en/tutorials/python-profiling
Сначала собираем при помощи docker
в пару строк: https://perforator.tech/docs/en/guides/build#container
Прямо в примере в доке есть код, который будет работать неоптимально. Запустим его:
» python server.py
My pid is 53000
Serving on port 9007
sudo perforator record --pid $YOUR_PID --duration 1m --serve ":9006"
http://localhost:9006
вас будет ждать flamegraph работы скрипта.
import requests
import random
while True:
user_id = random.randint(1, 1000000)
requests.get(f"http://localhost:9007/search_user?user_id={user_id}")
Минусы Litestar
Внезапно вспомнил, что я меинтейнер фреймворка Litestar.
Для тех, кто пропустил:
> Litestar не является микро-фреймворком. В отличие от таких фреймворков, как FastAPI, Starlette или Flask,
> Litestar изначально включает множество функций, необходимых для типичного современного веб-приложения,
> таких как интеграция с ORM, клиентские и серверные сессии, кэширование, поддержка OpenTelemetry и многое другое.
from dataclasses import dataclass
from litestar import Litestar, post
@dataclass
class User:
name: str
@post(path="/")
async def index(data: User) -> User:
return data
app = Litestar(route_handlers=[index])
Pydantic
, msgspec
и даже базовые dataclasses
в качестве моделейadvanced_sqlalchemy
, очень удобно для крудов)v3
(или которые все еще будут с нами)
class UserController(Controller):
path = "/user"
dependencies = {"user": Provide(retrieve_db_user)}
@patch(path="/{user_id:uuid}") # <- тут тоже можно указывать `dependencies`
async def get_user(self, user: User) -> User: ...
user
, который мы указываем в dependencies
и в параметрах может не совпасть. Есть некоторая валидация для такого в рантайме, но все равно очень рисково. v3
, ваши голоса – могут помочь :) app.state
, что очень криво. Данная проблема признана ошибкой дизайна и будет исправлено в v3
, ура!pydantic.Json
не обработан, имеет кривую схему. Сейчас мы обсуждаем стоит ли откатиться до дефолтной схемы. Стоит ли?PydanticPlugin
игнорирует ConfigDict
, который объявлен в самой моделе, в v3
будет пофикшено. Сейчас параметры вроде by_alias
и round_trip
надо передавать в плагин. Что свело меня с ума, искал ошибку несколько часовv3
: https://github.com/litestar-org/litestar/issues/4009Набор новых интересных PEPов и обсуждений для 3.15+
Сегодня будет пост для тех, кто любит заглядывать в будущее.
Поговорим про то, что только-только начинают делать в питончике.
JIT и FT
https://github.com/python/cpython/issues/139038
Описывает планы на 3.15 и 3.16 по развитию JIT.
Самое важное планируют:
- Сделать JIT на 5-10% быстрее для всех случаев, до 50% скорости для краевых случаев
- Трассирующий JIT: https://github.com/python/cpython/issues/139109
- Поддежка Free-Threading режима: https://github.com/python/cpython/issues/133171
- К 3.16 планируют найти еще 2х меинтейнеров, потому что сейчас очень мало людей работают над джитом. Если всегда хотели - присоединяйтесь!
https://discuss.python.org/t/pre-pep-safe-parallel-python/103636
Обсуждение по будущему FT и subinterpreters как практических концепций.
Марк Шеннон предлагает сделать из Python Erlang 🌚
Каждому объекту предлагают добавить:
- Метод __freeze__()
в большинство классов, чтобы делать объект иммутабельным (чтобы можно его было шарить между потоками и интерпретаторами)
- Метод __protect__(self: Self, obj: T) -> T
, чтобы помечать объекты как "защищенные". Их можно менять, но с мьютексом
- И сам __mutex__
, чтобы работать с защищенными объектами
https://discuss.python.org/t/add-virtual-threads-to-python/91403
Еще одно предложение от Марка – virtual threads в Python!
Кажется, что не только мне не нравится asyncio
.
Обсуждение на очень раннем шаге, можно принять участие :)
PEPs
https://peps.python.org/pep-0798 позволит нам писать вот такой код:
list_of_lists = [[1, 2], [3, 4]]
flat_list = [*l for l in list_of_lists]
profiling
в CPythonprofile
теперь deprecatedprofiling.sampling
cProfile
теперь будет доступен еще и как profile.tracing
TypedDict
:
class Movie(TypedDict, extra_items=str):
name: str
rating: float
year: int
str
. Что удобно, если у нас гигантский json с кучей похожих полей, например.
class Movie(TypedDict, closed=True):
name: str
rating: float
year: int
intmath
math
. Если интересуетесь математикой в питоне – ПЕП для вас.`__typing_subst__` – магический метод в Python, про который вы не слышали
В питоне сложилась довольно сложная ситуация с типизацией. В отличии от многих других языков, у нас нет специального "мини-языка типов". Что сильно упрощает работу с типизацией для авторов языка. Ведь им не нужно поддерживать поведение объектов типизации в рантайме.
А у нас есть только старый добрый питон и его объекты. Да, в питоне – действительно всё объект. Даже типизация. Оттого, любой код, который вы пишите в аннотациях – должен корректно работать в рантайме. Ведь у нас есть потребители такого, например: pydantic
.
Скажем, у нас есть TypeAliasType объект с одной типовой переменной. Мы хотим её явно указать:
type StrDict[_V] = dict[str, _V]
numbers: StrDict[int] = {'one': 1}
StrDict[int]
вызовет __getitem__
вот тут. Кстати, там Cшный код, конечно же. И нам просто вернется нужный GenericAlias объект с нужными __args__
:
>>> type StrDict[_V] = dict[str, _V]
>>> StrDict[int], type(StrDict[int]), StrDict[int].__args__
(StrDict[int], <class 'types.GenericAlias'>, (<class 'int'>,))
_V
на int
работает! Пока что 🌚TypeVar
, TypeVarTuple
, ParamSpec
. У каждого из них есть свои правила, как можно их заменять (в некоторых контекстах):TypeVar
можно заменять на одно другое типовое значениеTypeVarTuple
можно заменять на любое количество типовых значенийParamSpec
можно заменять на Concatenate
, ...
и список типов
>>> from collections.abc import Callable
>>> from typing import ParamSpec, TypeVar
>>> P = ParamSpec('P')
>>> R = TypeVar('R')
>>> Callable[P, R][0, int]
Traceback (most recent call last):
TypeError: Expected a list of types, an ellipsis, ParamSpec, or Concatenate. Got <class 'int'>
P
на 0
, что не является корректной заменой.__typing_subst__
.ParamSpec.__typing_subst__
вызывает typing._paramspec_subst
. Почему так? В 3.12 все переписали на C в спешке. Но некоторые части были слишком сложны, их оставили на питоне.
>>> P.__typing_subst__([])
()
>>> P.__typing_subst__([int, str])
(<class 'int'>, <class 'str'>)
>>> P.__typing_subst__(...)
Ellipsis
>>> P.__typing_subst__(0)
Traceback (most recent call last):
TypeError: Expected a list of types, an ellipsis, ParamSpec, or Concatenate. Got 0
__typing_prepare_subst__
. Он нужен, чтобы собрать аргументы для замены. Потому что у нас, например, могут быть неявные аргументы с default
. Проверим на TypeVarTuple
:
>>> class Custom[T1, *Ts=(int, int)]: ...
...
>>> Custom[str]
__main__.Custom[str, [int, int]]
>>> Custom[str, str]
__main__.Custom[str, str]
>>> Custom.__type_params__[1].__typing_prepare_subst__(*Custom.__type_params__[1], ())
([(<class 'int'>, <class 'int'>)],)
>>> Custom.__type_params__[1].__typing_prepare_subst__(*Custom.__type_params__[1], (str,))
((<class 'str'>,),)
[int, int]
нам как раз добавили в __args__
через __typing_prepare_subst__
вот тут.Generic
с параметрами. Потому с 3.14 все аннотации будут ленивыми по-умолчанию. А __annotate__
будет выполняться только тогда, когда аннотации будут запрашивать реально для рантайма.Три типа объектов в Питоне
В питоне часто любят обсуждать "мутабельные" и "иммутабельные" объекты, но крайне редко объясняют, в чем же на самом деле разница. Сегодня мы посмотрим на такое со стороны C.
PyObject
Все мы знаем, что в питоне все объект или PyObject *
, который упрощенно выглядит так (в FT сборке он посложнее):
struct _object {
Py_ssize_t ob_refcnt;
PyTypeObject *ob_type;
}
>>> class A:
... __slots__ = ()
>>> class B:
... __slots__ = ()
>>> a = A()
>>> type(a)
<class '__main__.A'>
>>> a.__class__ = B
>>> type(a)
<class '__main__.B'>
None
: ob_refcnt
не меняется (immortal), тип менять нельзя, ведь Py_TPFLAGS_IMMUTABLETYPE
установлен (static type), размер неизменный 0
для всех потенциальных значенийint
: ob_refcnt
может меняться для больших чисел (маленькие инты - immortal), тип менять нельзя, размер нельзя менять, но он будет разный для разных чисел:
>>> sys.getsizeof(1)
28
>>> sys.getsizeof(10000000000000)
32
list
: ob_refcnt
всегда меняется, тип менять нельзя, размер меняетсяPyObject
ничего такого нет.
struct PyVarObject {
PyObject ob_base;
Py_ssize_t ob_size; /* Number of items in variable part */
};
#define PyObject_HEAD PyObject ob_base;
#define PyObject_VAR_HEAD PyVarObject ob_base;
ob_size
, аллоцируем новую память (если нужно) под новые объекты внутри.
typedef struct {
PyObject_VAR_HEAD
/* Vector of pointers to list elements. list[0] is ob_item[0], etc. */
PyObject **ob_item;
/* ob_item contains space for 'allocated' elements. The number
* currently in use is ob_size.
*/
Py_ssize_t allocated;
} PyListObject;
list
(и многие другие) не просто PyObject
, они:PyListObject
PyVarObject
PyObject
None
не имеет внутреннего состояния вообще (не использует ничего)int
может иметь разный размер, но не может изменяться, потому использует PyObject_HEAD
(раньше был PyObject_VAR_HEAD
, там сложная история):
typedef struct _PyLongValue {
uintptr_t lv_tag; /* Number of digits, sign and flags */
digit ob_digit[1];
} _PyLongValue;
struct _longobject {
PyObject_HEAD
_PyLongValue long_value;
};
list
может иметь разный размер и может изменятся, потому использует PyObject_VAR_HEAD
, как я показывал вышеlen()
для list
? Используем ИИ для ревью кода
Сейчас довольно много говорят про написание кода при помощи ИИ, но меньше говорят про другой тип агентов (который лично мне нравится больше): ревью и анализ кода.
Ребята из GitVerse не так давно выпустили данную фичу, она доступна для всех. Я воспользовался возможностью и потестил.
Документация: https://gitverse.ru/docs/ai-agents/gigacode-agent
Методология проверки
Полный репозиторий: https://gitverse.ru/sobolevn/ai-code-review
Я придумал много разных типов ошибок и написал специально 🌚 плохой код на питоне. Вот он: https://gitverse.ru/sobolevn/ai-code-review/content/master/ai_code_review/example.py
- Небезопасный Python код: subprocess.run([user_input, ...])
- Плохое имя переменной: moy_lubimiy_film = '{"album_id": 1, "next_song": 2}'
- Сложный Python код в функции complex_code
- Затирание встроенного в Python имени: input
- Использование Any
без явной причины: input: Any
- Неправильная аннотация типа данных: x: int = 'a'
- Логическая ошибка: if subvalue in seen
- Использование несуществующей переменной: missing_var
- Плохо отформатированный Python код: my_unformatted_list
- Некорректный порядок импортов: from typing import Any
- Небезопасная и устаревшая зависимость django@1.9
в pyproject.toml
Далее, я проверил, можно ли вообще статически найти ошибки, которые я заложил?
Я сделал GitVerse Actions файл, который запускает mypy
, wemake-python-styleguide
, ruff
, safety
.
И удостоверился, что они могут найти все ошибки, кроме одной проблемы из двух самых сложных во всем программировании:
- Плохое имя переменной: moy_lubimiy_film = '{"album_id": 1, "next_song": 2}'
Данная ошибка будет baseline для нашего ИИ.
Ревью при помощи ИИ
Чтобы ревью случилось, нужно просто добавить giga-code-agent
в качестве ревьюера.
Что он делает?
1. Делает описание PR, если его нет. Там кратко перечисляет тип PR, ключевые изменения, важные технические детали. Удобно! Часто такие PRы просто раньше были пустые
2. Делает ревью. Описывает найденные проблемы. Учитывает тесты, которые отвечают за изменения. Действительно находит многие проблемы. От типизации до оформления. В текстах README.md
нашел несколько ошибок. Но! Очень важно: находит не все ошибки
3. Делает советы по изменениям. Пока они оставляют желать лучшего. Но изменения по форматированию - я бы принял, остальные - нет
4. Можно уточнять что-то при помощи команды /ask
Итог: найдено 6 (или 3 в зависимости от способа запуска) ошибок. Что уважаемо, учитывая, что было 0 конфигурации, 0 интеграций. Надеюсь, дальше будет еще лучше :)
Наша baseline ошибка, кстати, была найдена, когда я явно задал контекст.
Вывод: используйте ИИ ревью вместе с другими инструментами статического анализа.
Полный отчет со всеми подробностями по ссылке.
Обсуждение: доверили бы вы ревьюить свой код ИИ?
minimal vscode: открываем окна
https://www.youtube.com/watch?v=frZkPK_1Ui4
Нет, не от духоты, ее в видео как раз не будет 🌚️️️️
Видео короткое, динамичное, практичное.
Перед тем как учиться пользоваться vscode, необходимо:
1. Её поставить
2. Научиться её открывать
3. Располагать её на рабочем пространстве
В видео поговорили про:
- Brewfile и синхронизацию программ / плагинов
- Hotkey managers на примере https://github.com/koekeishiya/skhd
- Тайловые менеджеры окон: https://github.com/rxhanson/Rectangle
- Красивости вроде https://topnotch.app и https://hazeover.com
Все материалы для всех операционных систем тут: https://github.com/sobolevn/the-best-python-course/blob/main/minimal_vscode/links/1-open-vscode.md
Большое спасибо за такой отклик и поддержку 🧡, видео про отключение лишних панелей навигации уже в работе. Скоро будет!
Обсуждение: какие тайловые менеджеры используете вы?
| Поддержать | sobolevn">YouTube | GitHub | Чат |
Делаем бесплатный курс по vscode?
Довольно часто последнее время наблюдаю, как программируют другие люди. На собесах в своем окружении, в паре со мной, на ютюбе и тд. И вот что я замечаю. Очень много людей страдает от базовых вещей, которые можно сделать простыми и удобными. Я хочу помочь.
Тем более видосы с нарезкой моего подкаста на данную тему с @t0digital собрали много обсуждений и даже возмущений. А значит – тема горячая :)
Будем делать из второй картинки третью.
О чем поговорим?
- Почему DX важен?
- Почему vscode, а не vim / pycharm / emacs / тд. И как применить такие же подходы к другим средам
- О минимализме. Для успешной работы вам нужно меньше инструментов, а не больше
- О том, как сделать минимальное количество полезных горячих клавиш, которыми вы реально будете пользоваться
- Как навигироваться по коду, файлам, важным местам в проекте
- Какие принципы позволят вам сделать свой уникальный рабочий сетап, который удобен вам
- Как можно делать свои крутые инструменты, как пример для работы со сложными кейсами в git: https://github.com/sobolevn/fzf-simple-git
- Как писать свои темы, плагины. И когда их не писать
Будет крайне полезно, чтобы писать код быстрее и проще.
Мои конфиги за ~10 лет работы всегда можно посмотреть тут: https://github.com/sobolevn/dotfiles
Собираем донат goal на +16 человек – и начинаем! Все будет бесплатно и на ютюбе. Подписка на https://boosty.to/sobolevn стартует со 100 рублей.
Холивар про IDE объявляется открытым в комментах 🌚
Лучший курс по Python 15: Subinterpreters
Продолжаем говорить про субинтерпретаторы.
Пригласил их автора – Eric Snow – чтобы поговорить про историю, актуальное состояние проекта и его будущее. Особо отмечу темы про будущие оптимизации. Как ускорить старт интерпретатора? Как сделать возможным шеринг большего количества данных?
Все здесь! Есть русские и английские субтитры.
А еще я добавил много примеров кода и ссылки на исходники / пепы / статьи.
Жмите на паузу и читайте :)
https://www.youtube.com/watch?v=VBiaNNpLzWA
Внутри:
00:00 Вступление
00:11 Представление гостя
01:20 Введение в subinterpreters и PEP-554
04:21 Как subinterpreters выглядят с точки зрения OS?
05:50 Зачем добавили субинтерпретаторы в Python1.5?
08:01 Сколько субинтерпретаторов можно запустить в один момент?
09:19 История subinterpreters
17:25 Изоляция модулей и PEP-687
25:22 Immortal objects и PEP-683
29:15 Static Types
32:50 Проблема с модулем SSL
34:44 Связь subinterpreters и free-threading
42:45 Erlang и Actor Model
43:50 CSP, Channels
45:23 _interpqueues
46:11 (не)Копирование данных при отправке данных в очередь
48:53 Можно ли безопасно делить все буфферы? memoryview
49:53 subinterpreters vs multiprocessing
53:09 subinterpreters and asyncio
56:07 PEP-734
56:37 Сборщик мусора, GC
58:13 Как сделать еще быстрее и лучше в будушем?
01:03:34 Какие библиотеки стоит сделать сообществу?
01:08:14 Завершение
Обсуждение: кого бы вы хотели видеть в качестве будущего гостя?
| Поддержать | sobolevn">YouTube | GitHub | Чат |
Анонс FishITStream
(да, аллюзия на FastStream)
Мы с пацанами решили, что если и делать стримы по программированию, то сразу стримы с рыбалки. Потому что нет ничего лучше, чем говорить про программирование с рюмкой чая да на природе, глядя на озерцо.
Кто будет на связи:
– Коля Хитров — Python-блогер и серийный спикер
– Никита Пастухов — автор FastStream, галерный гребец, филантроп
– Роман Пожарнов — автор asgi-monitor, спортивный рыбак, говорит на рыбьем языке
– ну и я, конечно
О чем поговорим:
– Развитие языка Python: как развивается язык, и почему Python всё ещё лучше Go!
– Как сделать стрим из дикой природы
– Конференции и нетворкинг: зачем идти слушать и выступать, как найти тему для доклада
– Какую прикормку лучше всего брать на карася
– И, конечно же, мы не оставим без внимания OpenSource: обсудим развитие продуктов, успешные проекты, мотивацию людей и секреты правильного использования OSS
– А еще всякое про карьеру и прочее, в чем я не разбираюсь 🌚
Время: 29 июня, 12:00 МСК
Место: https://www.youtube.com/watch?v=j-XAjIlCRGg
Кстати, стрим будет на канале Коли, туда можно смело подписываться!
Надеюсь, что все получится технически.
Находки в опенсорсе: pyrefly
https://youtube.com/watch?v=7TdxFGB6LKY
Еще одно видео про еще один новый тайпчекер для питона на расте!
Много их нынче стало.
В видео:
- Обсуждаем первую версию: pyre-check
, обсудили taint analysis
- Сравниваем pyrefly
с ty
и mypy
- Смотрим на внутреннее устройство
- Применяем на реальном проекте
Ключевые ссылки из выпуска:
– Доклад о pyrefly на PyCon: https://youtu.be/ZTSZ1OCUaeQ?si=s_DPOOzsdeTk5Uqo
– pyrefly vs ty: https://blog.edward-li.com/tech/comparing-pyrefly-vs-ty (сильно советую!)
Вывод: пока очень сырой, много багов, но быстрый. Ключевой вывод: отлично, что есть конкуренция
В Python3.14 добавили новую библиотеку для сжатия: Zstandard
- PEP: https://peps.python.org/pep-0784
- Документация: https://docs.python.org/3.14/library/compression.zstd.html
- Реализация: https://github.com/python/cpython/pull/133027
Существует такой новый алгоритм для сжатия: Zstandard c хорошим процентом сжатия и быстрым алгоритмом сжатия / разжатия. Его добавили в 3.14 как нативный модуль. И как раз заодно решили прибрать все другие алгоритмы в общий модуль compression.*
Теперь:
- compression.lzma
отвечает за lzma
- compression.zstd
за Zstandard
- compression.gzip
за gzip
и так далее
Пока данные новые модули просто делают re-export всех объектов из оригинальных модулей. Однако, в какой-то момент старые имена могут быть задеприкейчены.
> Any deprecation or removal of the existing modules is left to a future decision but will occur no sooner than 5 years from the acceptance of this PEP.
Как работает?
Сам алгоритм сжатия хорошо описан в соответствующем RFC. Его лучше почитать отдельно. Тем, кому такое интересно.
А мы поговорим про питоновскую часть.
Теперь питон зависит от новой опциональной библиотеки zstd.h
, что будет, если ее нет? И тут мы должны познакомиться с системой конфигурации и сборки питона.
Мы используем AutoConf или .ac
. Данный зверь – свой большой мир, который хочется как раз показать на примере. Спорим, вы не сможете с первого раза прочитать данную конструкцию?
dnl zstd 1.4.5 stabilised ZDICT_finalizeDictionary
PKG_CHECK_MODULES([LIBZSTD], [libzstd >= 1.4.5], [have_libzstd=yes], [
WITH_SAVE_ENV([
CPPFLAGS="$CPPFLAGS $LIBZSTD_CFLAGS"
CFLAGS="$CFLAGS $LIBZSTD_CFLAGS"
LIBS="$LIBS $LIBZSTD_LIBS"
AC_SEARCH_LIBS([ZDICT_finalizeDictionary], [zstd], [
AC_MSG_CHECKING([ZSTD_VERSION_NUMBER >= 1.4.5])
AC_COMPILE_IFELSE([
AC_LANG_PROGRAM([@%:@include "zstd.h"], [
#if ZSTD_VERSION_NUMBER < 10405
# error "zstd version is too old"
#endif
])
], [
AC_MSG_RESULT([yes])
AC_CHECK_HEADERS([zstd.h zdict.h], [have_libzstd=yes], [have_libzstd=no])
], [
AC_MSG_RESULT([no])
have_libzstd=no
])
], [have_libzstd=no])
AS_VAR_IF([have_libzstd], [yes], [
LIBZSTD_CFLAGS=${LIBZSTD_CFLAGS-""}
LIBZSTD_LIBS=${LIBZSTD_LIBS-"-lzstd"}
])
])
])
AC_CHECK_HEADERS([zstd.h zdict.h], [have_libzstd=yes], [have_libzstd=no])
, она проверяет наличие нужных хедеров / зависимостей для компиляции. Данный код создает несколько проверок при генерации файла ./configure
, которые позволяют проверить, есть ли такая библиотека на машине сборки../configure
потом собирается правильный Makefile
для компиляции самого питона
@MODULE__ZSTD_TRUE@_zstd _zstd/_zstdmodule.c _zstd/zstddict.c _zstd/compressor.c _zstd/decompressor.c
Makefile
, если библиотека есть:
MODULE__ZSTD_STATE=yes
MODULE__ZSTD_CFLAGS=-I/opt/homebrew/opt/zstd/include
MODULE__ZSTD_LDFLAGS=-L/opt/homebrew/opt/zstd/lib -lzstd
Modules/_zstd/_zstdmodule.o: $(srcdir)/Modules/_zstd/_zstdmodule.c $(MODULE__ZSTD_DEPS) $(MODULE_DEPS_SHARED) $(PYTHON_HEADERS); $(CC) $(MODULE__ZSTD_CFLAGS) $(PY_STDMODULE_CFLAGS) $(CCSHARED) -c $(srcdir)/Modules/_zstd/_zstdmodule.c -o Modules/_zstd/_zstdmodule.o
# ...
Makefile
просто не будет данной цели для сборки.Находки в опенсорсе: ty (red-knot)
https://www.youtube.com/watch?v=5PCP4ICoirg
Вышло видео про новый тайпчекер и lsp: ty (старое название red-knot
)
от авторов ruff
и uv
.
Пока по первым впечатлениям – бомба! Не смотря на версию 0.0.0a8
🌚
Из плюсов:
- Быстрый
- На расте
- Куча новых фичей для типов
- Полная спецификация
- Интеграция с ruff
и IDEшками
Из минусов:
- Пока есть баги (но их поправят, конечно же)
- Нет плагинов (и скорее всего никогда не будет)
- Софт от молодой и маленькой компании
- Как сделать поддержку ty
и mypy
вместе? Если использовались ty_extensions
🤷♂️
Обсуждение: а как вам проект? Успели попробовать?
| Поддержать | sobolevn">YouTube | GitHub | Чат |
PEP 758: except и except* без ()
- PEP: https://peps.python.org/pep-0758
- PR: https://github.com/python/cpython/pull/131833
В 3.14 мы теперь можем не указывать скобки в except
и except*
, когда мы ловим несколько типов исключений и не используем as
, пример:
>>> try:
... res = int(data['value'])
... except ValueError, KeyError:
... res = 0
>>> try:
... res = int(data['value'])
... except (ValueError, KeyError):
... res = 0
as
мы все еще должны использовать скобки:
>>> try:
... res = int(data['value'])
... except ValueError, KeyError as exc:
... res = 0
...
File "<python-input-3>", line 3
except ValueError, KeyError as exc:
^^^^^^^^^^^^^^^^^^^^^^^^^^^
SyntaxError: multiple exception types must be parenthesized when using 'as'
>>> try:
... res = int(data['value'])
... except (ValueError, KeyError) as exc:
... res = 0
except_block[excepthandler_ty]:
| invalid_except_stmt_indent
| 'except' e=expression t=['as' z=NAME { z }] ':' b=block {
_PyAST_ExceptHandler(e, (t) ? ((expr_ty) t)->v.Name.id : NULL, b, EXTRA) }
| 'except' ':' b=block { _PyAST_ExceptHandler(NULL, NULL, b, EXTRA) }
except_block[excepthandler_ty]:
| invalid_except_stmt_indent
| 'except' e=expressions ':' b=block {
_PyAST_ExceptHandler(e, NULL, b, EXTRA) }
| 'except' e=expression 'as' t=NAME ':' b=block {
_PyAST_ExceptHandler(e, ((expr_ty) t)->v.Name.id, b, EXTRA) }
| 'except' ':' b=block { _PyAST_ExceptHandler(NULL, NULL, b, EXTRA) }
except_block
большое правило, которое умеет парсить except
в его разных видах. excepthandler_ty
– его Cшный тип, такое упрощение для кодогенерации конечного parser.c
|
указаны варианты, какие могут быть грамматически корректные except
в Pythoninvalid_
– работают только на второй прогон парсера, чтобы показывать более качественные ошибки пользователямexcept
, потом любые выражения, но без as
except
, потом любое одно выражение, далее as
4.
, а as
был опциональный (внутри []
скобок в описании грамматики)PEP 750: t-строки в 3.14
В питон добавили еще один способ форматировать строки. Теперь – со специальным АПИ для внешних интеграций.
- PEP: https://peps.python.org/pep-0750
- Реализация: https://github.com/python/cpython/pull/132662
Основная причина: использовать f
строки удобно, но нет никакого АПИ для перехвата момента "вставки" или интерполяции значений. Например, при форматировании html или sql – требуется специальным образом делать escape для значений. И раньше код вида f"<div>{template}</div>"
представлял собой дыру в безопасности и потенциальное место для XSS.
string.templatelib.Template
Новый префикс t
не будет создавать объект str
, он будет создавать объект класса string.templatelib.Template
:
>>> user = 'sobolevn'
>>> template = t"Hi, {user}"
>>> template
Template(strings=('Hi, ', ''), interpolations=(Interpolation('sobolevn', 'user', None, ''),))
>>> from string.templatelib import Template
>>> isinstance(template, Template)
True
template
– у нас не произошло форматирование сразу. Мы создали объект, у которого есть свойства strings
и interpolations
, из которых можно собрать финальную отформатированную строку.
>>> domain = 'example.com'
>>> query = 'python string formatting is too complex'
>>> template = t'https://{domain}?q={query}'
query
, то мы будем использовать quote_plus
для его форматирования. Остальные значения – будем вставлять как есть:
>>> from string.templatelib import Template, Interpolation
>>> from urllib.parse import quote_plus
>>> def format_url(template: Template) -> str:
... parts = []
... for part in template:
... match part:
... case str() as s: # regular string
... parts.append(s)
... case Interpolation(value, expression='query'):
... parts.append(quote_plus(value))
... case Interpolation(value):
... parts.append(value)
... return ''.join(parts)
>>> format_url(template)
'https://example.com?q=python+string+formatting+is+too+complex'
Template
был отформатирован. Нами. Ручками.=
как обычно в f
строках: t'{user=}'
!r
, !s
, .2f
, тдt
строки можно конкатенировать: t'Hello' + t' , world!'
и t'Hello, ' + 'world'
rt"Hi \n!"
_PyTemplate
Template
и Interpolation
написанные на CBUILD_INTERPOLATION
и BUILD_TEMPLATE
>>> import dis
>>> user = 'sobolevn'
>>> dis.dis('t"Hi, {user}"')
0 RESUME 0
1 LOAD_CONST 2 (('Hi, ', ''))
LOAD_NAME 0 (user)
LOAD_CONST 1 ('user')
BUILD_INTERPOLATION 2
BUILD_TUPLE 1
BUILD_TEMPLATE
RETURN_VALUE
Сложности запуска Docker в CI
Когда я писал прошлый пост про работу CI в GitVerse, я получил несколько вопросов относительно: а как работает Docker-in-Docker (DinD) в таком CI? Я спросил ребят, как они планируют реализовать данную фичу в ближайшем будущем. Ответ получился очень интересным.
Со стороны задача "запустить DinD в публичном CI" не выглядит как-то архи-сложно. Однако, на деле как всегда есть нюансы.
Какие вообще есть варианты запуска DinD?
1. Можно взять docker:dind и прокинуть ему docker.sock
, а затем получить побег из курятника, и наблюдать, как пользователи получают полный доступ к машине, где гоняются другие сборки других проектов (с секретами, конечно же). Так делать совершенно точно нельзя!
Вот пример, насколько просто сбежать из такого контейнера (в самом простом случае):
# Запускаем контейнер
» docker run --name=first -v /var/run/docker.sock:/var/run/docker.sock -it docker:dind sh
# Внутри docker:
/ # ls -alh /var/run/docker.sock
srwxr-xr-x root /var/run/docker.sock
/ # hostname
700809c044d6 # <- наш текущий хост, контейнер `first`
/ # docker container ls
CONTAINER ID NAMES
e7d7857b965a other
700809c044d6 first
/ # docker exec -it other sh
/ # hostname
e7d7857b965a # <- мы получили доступ к соседнему контейнеру на хосте :(
docker.sock
и использует root
внутри контейнера. Даже если вам нужно выставить docker.sock
, то есть варианты лучше docker:dind
и запустить его с --privileged
, прокинуть ему DOCKER_TLS_CERTDIR
, запустить второй контейнер "клиент" без --privileged
, но с нужными сертификатами, и выполнять все на нем. Такой способ уже безопаснее, но все равно есть много вариантов побега и privilege escalation
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: wemake-services/wemake-python-styleguide@1.1
wemake-services/wemake-python-styleguide
и выполняет код action внутри dockerИНН:7736632467
Erid:2W5zFJeNAVn
Сайт: https://gitverse.ru/home