Как подключать pytest-фикстуры без лишних аргументов теста
Иногда фикстура нужна только ради побочного эффекта: например, перед тестом она подменяет
Декоратор
В короткой статье с примером показано, как убрать серый неиспользуемый параметр и сохранить зависимость на виду. Явность победила, сигнатура тоже.
Иногда фикстура нужна только ради побочного эффекта: например, перед тестом она подменяет
os.environ. Если добавить её в параметры функции, pytest всё выполнит, но IDE подсветит неиспользуемый аргумент. Автор избегает autouse=True: по его оценке, так о тестах труднее рассуждать.Декоратор
@pytest.mark.usefixtures с именем mock_env назначает фикстуру конкретному тесту. Pytest запускает mock_env, а в сигнатуре остаются только объекты, к которым код обращается напрямую. Декоратор принимает несколько имён фикстур, но помечать им другую фикстуру нельзя.В короткой статье с примером показано, как убрать серый неиспользуемый параметр и сохранить зависимость на виду. Явность победила, сигнатура тоже.
Где заканчивается ускорение NumPy и что выбрать дальше
Векторизация выполняет цикл низкоуровневым кодом. В тесте прибавление 17 к 100 млн элементов заняло 6,13 секунды для списка CPython и 0,07 секунды для массива NumPy.
Предел виден, когда нужной операции нет в NumPy или вычисление создаёт промежуточный массив.
В разборе Python⇒Speed решения разделены по задаче. PyPy ускоряет циклы по обычным объектам, но плохо сочетается с NumPy. Numba компилирует функции для NumPy. Cython, Rust, C и C++ позволяют собрать расширение заранее.
«Явное лучше неявного» работает и для оптимизации: выбирайте инструмент по форме узкого места.
Векторизация выполняет цикл низкоуровневым кодом. В тесте прибавление 17 к 100 млн элементов заняло 6,13 секунды для списка CPython и 0,07 секунды для массива NumPy.
Предел виден, когда нужной операции нет в NumPy или вычисление создаёт промежуточный массив.
np.abs(arr).mean() для массива на 1 ГБ выделяет ещё 1 ГБ под абсолютные значения. Цикл Python убирает копию, но возвращает расходы интерпретатора.В разборе Python⇒Speed решения разделены по задаче. PyPy ускоряет циклы по обычным объектам, но плохо сочетается с NumPy. Numba компилирует функции для NumPy. Cython, Rust, C и C++ позволяют собрать расширение заранее.
«Явное лучше неявного» работает и для оптимизации: выбирайте инструмент по форме узкого места.
Как кэшировать методы чужого Python-клиента
Если библиотечный клиент нельзя менять, его методы можно переопределить с functools.lru_cache. Но кеш тогда включает self в ключ и удерживает экземпляр до вытеснения записи или очистки.
Питоничнее обернуть уже связанный метод после создания объекта: кеш будет отдельным для каждого экземпляра, поэтому состояния клиентов не смешаются. Для множества методов автор перехватывает обращения через __getattribute__, кеширует метод при первом вызове и сохраняет обёртку в __dict__. Если объект создаёт фреймворк, эту механику можно вынести в прокси.
В разборе Caching a lot of methods in Python показано, как вернуть прокси автодополнение через __dir__ и проверки isinstance() через wrapt.ObjectProxy. Для пары методов решение проще: заменить их кешированными прямо у готового объекта.
Если библиотечный клиент нельзя менять, его методы можно переопределить с functools.lru_cache. Но кеш тогда включает self в ключ и удерживает экземпляр до вытеснения записи или очистки.
Питоничнее обернуть уже связанный метод после создания объекта: кеш будет отдельным для каждого экземпляра, поэтому состояния клиентов не смешаются. Для множества методов автор перехватывает обращения через __getattribute__, кеширует метод при первом вызове и сохраняет обёртку в __dict__. Если объект создаёт фреймворк, эту механику можно вынести в прокси.
В разборе Caching a lot of methods in Python показано, как вернуть прокси автодополнение через __dir__ и проверки isinstance() через wrapt.ObjectProxy. Для пары методов решение проще: заменить их кешированными прямо у готового объекта.
death and gravity
Caching a lot of methods in Python
... in which you'll learn how to cache a lot of methods in Python, even for objects created by someone else. Did you know that, contrary to popular belief, Python programmers can have a little monkey patching as a treat?
Как перевести Python-сервер MCP с FastMCP на SDK 2.x
Свежая установка зависимостей сломала сервер автора: в requirements.txt пакет
Миграция начинается с замены импорта и имени класса. Декораторы
В пошаговом разборе на DEV Community есть проверки синхронных обработчиков, переменных окружения и позиционных аргументов конструктора. Так пакет
Свежая установка зависимостей сломала сервер автора: в requirements.txt пакет
mcp был без ограничения версии, поэтому pip подтянул ветку 2.x, где FastMCP переименовали в MCPServer. Код репозитория не менялся, а прежний импорт перестал работать. Explicit is better than implicit, особенно в файле зависимостей.Миграция начинается с замены импорта и имени класса. Декораторы
@mcp.tool(), @mcp.resource() и тела инструментов в этом проекте остались прежними. Затем стоит закрепить mcp>=2,<3 и отдельно оставить httpx: SDK 2.x больше не устанавливает его как зависимость.В пошаговом разборе на DEV Community есть проверки синхронных обработчиков, переменных окружения и позиционных аргументов конструктора. Так пакет
mcp обновляется внутри репозитория и не откатывается для остальных проектов в системном Python.Почему миллион чисел в Python занимает 35 МБ
Список из миллиона целых, которые помещаются в 64 бита, занимает в CPython около 35 МБ вместо ожидаемых 8 МБ. Из них 8 МБ уходят на указатели списка, ещё 28 МБ на числа: небольшой объект
В CPython каждое число является объектом. Кроме значения, у него есть счётчик ссылок и указатель на тип. На 64-битной системе эти служебные поля добавляют минимум 16 байт к каждому объекту. Явное лучше неявного, особенно в профиле памяти.
Массив NumPy с типом
Список из миллиона целых, которые помещаются в 64 бита, занимает в CPython около 35 МБ вместо ожидаемых 8 МБ. Из них 8 МБ уходят на указатели списка, ещё 28 МБ на числа: небольшой объект
int весит 28 байт.В CPython каждое число является объектом. Кроме значения, у него есть счётчик ссылок и указатель на тип. На 64-битной системе эти служебные поля добавляют минимум 16 байт к каждому объекту. Явное лучше неявного, особенно в профиле памяти.
Массив NumPy с типом
uint64 хранит значения вместо ссылок на отдельные объекты Python. Поэтому миллион элементов занимает 8 МБ плюс память на импорт библиотеки. В разборе структуры объектов CPython есть замеры и код. Для больших числовых наборов проверьте NumPy до того, как 8 ГБ превратятся в 35 ГБ.❤1
Что ускоряет django-msgspec в Django и где он расходится с json
django-msgspec заменяет компоненты Django и Django REST Framework аналогами на базе msgspec. Его написанные на C кодировщик и декодировщик JSON работают в несколько раз быстрее стандартной библиотеки. В пакете есть JsonResponse, тестовый клиент и json_script, а сериализаторы сессий и парсеры Django REST Framework подключаются через настройки.
Совместимость близка к json, но не полна. Числовые ключи словаря превращаются в строки, большие целые сохраняются. При этом бесконечность кодируется как null, а ключи None и bool вызывают ошибку. Explicit is better than implicit: перед заменой стоит прогнать тесты на реальных данных.
В разборе Адама Джонсона есть примеры настройки для ответов, сессий, dumpdata и loaddata, а также Django REST Framework.
django-msgspec заменяет компоненты Django и Django REST Framework аналогами на базе msgspec. Его написанные на C кодировщик и декодировщик JSON работают в несколько раз быстрее стандартной библиотеки. В пакете есть JsonResponse, тестовый клиент и json_script, а сериализаторы сессий и парсеры Django REST Framework подключаются через настройки.
Совместимость близка к json, но не полна. Числовые ключи словаря превращаются в строки, большие целые сохраняются. При этом бесконечность кодируется как null, а ключи None и bool вызывают ошибку. Explicit is better than implicit: перед заменой стоит прогнать тесты на реальных данных.
В разборе Адама Джонсона есть примеры настройки для ответов, сессий, dumpdata и loaddata, а также Django REST Framework.
Как collections.deque хранит элементы блоками
У
При
В разборе на mathspp.com эта механика собрана в упрощённой Python-реализации для Python 3.15. По коду можно проследить
У
deque два конца, поэтому легко представить узел на каждый объект. В CPython звено списка хранит блок до 64 элементов. Соседние блоки связаны в обе стороны, а индексы указывают на первый и последний элементы.При
append и pop внутри блока меняются индекс и ячейка. Новый блок нужен, лишь когда крайний заполнен; опустевший крайний блок отсоединяется. Поэтому интерпретатор реже выделяет и освобождает память, чем при отдельном узле на элемент, и deque работает быстрее.В разборе на mathspp.com эта механика собрана в упрощённой Python-реализации для Python 3.15. По коду можно проследить
append и pop; левый край устроен симметрично.❤1👍1
Как упаковать Go-бинарник в wheel и подключить к Python
PyPI может раздавать не только Python-код. go-to-wheel собирает Go-программу в отдельные wheel-пакеты для Windows, macOS и Linux, а pip или uv выбирают сборку под ОС и архитектуру. Внутри пакета лежит бинарник; Python-точка входа находит его и запускает с переданными аргументами.
Simon Willison показывает схему на sqlite-scanner: утилиту можно вызвать через
В статье Simon Willison’s Weblog разобраны структура wheel, команда сборки и пример плагина Datasette.
PyPI может раздавать не только Python-код. go-to-wheel собирает Go-программу в отдельные wheel-пакеты для Windows, macOS и Linux, а pip или uv выбирают сборку под ОС и архитектуру. Внутри пакета лежит бинарник; Python-точка входа находит его и запускает с переданными аргументами.
Simon Willison показывает схему на sqlite-scanner: утилиту можно вызвать через
uvx sqlite-scanner, а другой Python-пакет может объявить её зависимостью и запускать через subprocess. Пользователю не нужны Go и ручная сборка. PyPI немного выходит за должностную инструкцию, зато граница остаётся явной: Go отвечает за бинарник, Python — за установку и запуск.В статье Simon Willison’s Weblog разобраны структура wheel, команда сборки и пример плагина Datasette.
❤1🔥1
Как писать Python-скрипты, которые удобно запускать в пайплайнах
В разборе Bite code! собраны приёмы, которые оставляют однофайловый скрипт простым, но делают его предсказуемым для пользователя.
Зависимости указываются в комментарии в начале файла:
Результат операции отправляйте в
В разборе Bite code! собраны приёмы, которые оставляют однофайловый скрипт простым, но делают его предсказуемым для пользователя.
Зависимости указываются в комментарии в начале файла:
uv run создаёт временное виртуальное окружение, ставит пакеты и запускает скрипт. Секрет сначала ищется в переменной окружения с префиксом имени скрипта, затем в хранилище ключей ОС, а при отсутствии запрашивается через getpass и сохраняется после проверки.Результат операции отправляйте в
stdout, ошибки и журнал в stderr: тогда конвейер получит только данные. Для пользователя оставляйте print(), диагностику включайте через logging по запросу, а переменные окружения перечисляйте в --help через argparse.👍2❤1
Как точечно сравнивать скорость функций Python с tprof
Профилировщик помогает найти медленную функцию. Но после каждой правки снова измерять всю программу неудобно: отчёт приходится фильтровать, а профилировщик добавляет накладные расходы. tprof для Python 3.12+ следит только за выбранными функциями.
В режиме сравнения одна функция становится базовой, а столбец delta показывает разницу. В примере
В разборе Адам Джонсон показывает команды и Python API. tprof работает через
Профилировщик помогает найти медленную функцию. Но после каждой правки снова измерять всю программу неудобно: отчёт приходится фильтровать, а профилировщик добавляет накладные расходы. tprof для Python 3.12+ следит только за выбранными функциями.
В режиме сравнения одна функция становится базовой, а столбец delta показывает разницу. В примере
sum(range(...)) выполнялся примерно на 62% быстрее цикла с накоплением. Запустить замер можно из командной строки, через контекстный менеджер или декоратор.В разборе Адам Джонсон показывает команды и Python API. tprof работает через
sys.monitoring: регистрирует обработчики только для целевых функций, а время измеряет в C. Остальная программа не получает накладных расходов профилировщика.👀1
Как точно типизировать *args и **kwargs в Python
Запись
Для разнородных аргументов нужен Unpack:
В разборе аннотаций показаны и необязательные ключи: все сразу через total=False либо отдельные через NotRequired. До Python 3.12 эти типы берутся из typing_extensions. Подход пригодится обёрткам, которые передают аргументы функции с той же сигнатурой.
Запись
*args: tuple[int, str] выглядит как тип всего кортежа, но проверщик читает её иначе: каждый позиционный аргумент должен быть таким кортежем. У **kwargs: dict[...] тип также относится к каждому именованному аргументу. Типизатор здесь не телепат.Для разнородных аргументов нужен Unpack:
Unpack[tuple[int, str]] задаёт типы и порядок позиционных аргументов. Именованные описываются через TypedDict и Unpack[Kw], поэтому проверщик знает тип каждого ключа.В разборе аннотаций показаны и необязательные ключи: все сразу через total=False либо отдельные через NotRequired. До Python 3.12 эти типы берутся из typing_extensions. Подход пригодится обёрткам, которые передают аргументы функции с той же сигнатурой.
👍2
Почему в Python стоит подменять свой интерфейс, а не httpx
Если бизнес-тест напрямую подменяет httpx.Client и httpx.Response, даже проверка пустого списка требует трёх вложенных моков. Тест повторяет чужой API, а его смысл тонет в настройке объектов.
Автор предлагает поставить между бизнес-логикой и httpx тонкий DockerRegistryClient с методами get_repos() и get_repo_tags(). Бизнес-код работает с понятными операциями, тест подменяет один собственный объект, а смена HTTP-библиотеки не затрагивает эти проверки. Вложенность отступает перед явным интерфейсом: вполне по-питоновски.
В статье «Don’t Mock What You Don’t Own» in 5 Minutes есть код до и после рефакторинга и границы эвристики. Обёртка не нужна, если сторонний API уже удобен; для редких сетевых ошибок прямой мок тоже может оказаться проще.
Если бизнес-тест напрямую подменяет httpx.Client и httpx.Response, даже проверка пустого списка требует трёх вложенных моков. Тест повторяет чужой API, а его смысл тонет в настройке объектов.
Автор предлагает поставить между бизнес-логикой и httpx тонкий DockerRegistryClient с методами get_repos() и get_repo_tags(). Бизнес-код работает с понятными операциями, тест подменяет один собственный объект, а смена HTTP-библиотеки не затрагивает эти проверки. Вложенность отступает перед явным интерфейсом: вполне по-питоновски.
В статье «Don’t Mock What You Don’t Own» in 5 Minutes есть код до и после рефакторинга и границы эвристики. Обёртка не нужна, если сторонний API уже удобен; для редких сетевых ошибок прямой мок тоже может оказаться проще.
Как применять структурное сопоставление с образцом к деревьям в Python
Структурное сопоставление особенно выразительно на рекурсивных структурах. Автор моделирует булевы выражения датаклассами
Вычислитель захватывает поля прямо в образце. Для переменной он берёт значение из словаря, для
Тот же приём автор переносит на форматированный вывод дерева. Получается компактный разбор того, как рекурсивно применять match к древовидным данным, не превращая обход в цепочку проверок типов.
Структурное сопоставление особенно выразительно на рекурсивных структурах. Автор моделирует булевы выражения датаклассами
Var, And, Or и Not, а затем разбирает каждый тип веткой case.Вычислитель захватывает поля прямо в образце. Для переменной он берёт значение из словаря, для
And и Or рекурсивно обходит дочерние выражения через all() и any(), для Not инвертирует результат. Общая ветка выбрасывает ошибку, если появился неизвестный подкласс: явное лучше неявного.Тот же приём автор переносит на форматированный вывод дерева. Получается компактный разбор того, как рекурсивно применять match к древовидным данным, не превращая обход в цепочку проверок типов.
❤1
Pyodide разрешил публиковать WASM-колёса прямо в PyPI
В Pyodide 314.0 появилась прямая публикация пакетов для WebAssembly в PyPI. Раньше команда сама собирала и размещала пакеты, а теперь сопровождающие могут выпускать колёса для Pyodide так же, как для Linux, macOS и Windows.
На момент публикации в Simon Willison’s Weblog новые теги pyemscripten использовали 28 пакетов.
В Pyodide 314.0 появилась прямая публикация пакетов для WebAssembly в PyPI. Раньше команда сама собирала и размещала пакеты, а теперь сопровождающие могут выпускать колёса для Pyodide так же, как для Linux, macOS и Windows.
На момент публикации в Simon Willison’s Weblog новые теги pyemscripten использовали 28 пакетов.
Что меняет re.prefixmatch() в Python 3.15
В Python 3.15 появится
re.match() проверяет совпадение только в начале строки. Поэтому шаблон из шести цифр принимает и 345071-in-abbey-wood: подходящий префикс найден, а остаток не мешает успеху. Для проверки всей строки по-прежнему нужен fullmatch().В Python 3.15 появится
prefixmatch(), точный синоним match(). Поведение не изменится, зато имя прямо сообщит, что проверяется лишь начало строки. Явное лучше неявного, даже когда речь всего лишь о названии метода.match() получит статус мягко устаревшего: предупреждений и планов удаления нет, существующий код продолжит работать. В новом коде под Python 3.15+ стоит выбирать prefixmatch() для проверки префикса, а fullmatch() для валидации значения целиком. Примеры и объяснение различий собраны в разборе Адама Джонсона.Как
Для цикла
В примере из статьи арифметическая последовательность вычисляет элемент по формуле прямо в
Проверять поддержку целочисленных индексов заранее Python не станет. Поэтому обёртка над словарём выдаёт значения для ключей 0 и 1, а на ключе 2 падает с
__getitem__ превращает объект Python в итерируемыйДля цикла
for объекту не всегда нужен __iter__. Если класс принимает целые индексы от 0, Python последовательно запрашивает элементы с индексами 0, 1, 2 и так далее. Получив IndexError, интерпретатор считает последовательность законченной.В примере из статьи арифметическая последовательность вычисляет элемент по формуле прямо в
__getitem__: seq[3] возвращает 14, а тот же объект без дополнительного метода работает в цикле.Проверять поддержку целочисленных индексов заранее Python не станет. Поэтому обёртка над словарём выдаёт значения для ключей 0 и 1, а на ключе 2 падает с
KeyError: эта ошибка не означает конец итерации. Механику и код можно посмотреть в статье Indexable iterables.Как запустить урезанный Python в браузерной песочнице
Monty от Pydantic исполняет небольшой Python-подобный язык внутри приложения. Интерпретатор написан на Rust для кода, который генерируют языковые модели: файловая система, переменные окружения и сеть недоступны, а функции хоста вызываются только с разрешения разработчика.
Саймон Уиллисон собрал Monty для WebAssembly как модуль JavaScript и Python-пакет для Pyodide: получается песочница внутри песочницы.
Поддерживается часть синтаксиса, пока без классов. В разборе в Simon Willison’s Weblog есть команды сборки, устройство вариантов и браузерные демо.
Monty от Pydantic исполняет небольшой Python-подобный язык внутри приложения. Интерпретатор написан на Rust для кода, который генерируют языковые модели: файловая система, переменные окружения и сеть недоступны, а функции хоста вызываются только с разрешения разработчика.
Саймон Уиллисон собрал Monty для WebAssembly как модуль JavaScript и Python-пакет для Pyodide: получается песочница внутри песочницы.
Поддерживается часть синтаксиса, пока без классов. В разборе в Simon Willison’s Weblog есть команды сборки, устройство вариантов и браузерные демо.
Как исправить вызов NamedTuple в Python 3.15
В Python 3.15 больше не поддерживается создание
Исправить код можно двумя способами: объявить класс, наследующий
Ruff 0.16 автоматически переводит обе формы в классовую по правилу
В Python 3.15 больше не поддерживается создание
NamedTuple через именованные аргументы полей. В версиях 3.13 и 3.14 такой код выдаёт предупреждение об устаревании. Этот синтаксис никогда не был документирован и работал лишь потому, что старая реализация принимала произвольные именованные аргументы. «Явное лучше неявного» здесь стало требованием интерпретатора.Исправить код можно двумя способами: объявить класс, наследующий
NamedTuple, или передать в функциональную форму список пар «имя, тип». Классовый вариант читается проще и оставляет место для методов и строк документации.Ruff 0.16 автоматически переводит обе формы в классовую по правилу
UP014: достаточно запустить ruff check --fix. Для старых версий правило нужно включить отдельно. Примеры обоих исправлений есть в разборе Адама Джонсона.Как читать многострочный ввод в Python REPL несколько раз
Вызов
Повторный вызов заканчивается ошибкой OSError: после первого чтения Python закрывает файловый дескриптор 0. Передайте
Явный параметр здесь действительно лучше неявно закрытого ввода. Приём пригодится, когда в интерактивной сессии нужно последовательно прочитать несколько блоков текста. Два таких чтения показаны в разборе TIL #139.
Вызов
open(0).read() читает стандартный ввод до конца потока и сохраняет строки вместе с переносами. Для завершения ввода нажмите Ctrl+D в Unix или Ctrl+Z в Windows.Повторный вызов заканчивается ошибкой OSError: после первого чтения Python закрывает файловый дескриптор 0. Передайте
closefd=False, и стандартный ввод останется доступен: open(0, closefd=False).read() можно вызывать снова.Явный параметр здесь действительно лучше неявно закрытого ввода. Приём пригодится, когда в интерактивной сессии нужно последовательно прочитать несколько блоков текста. Два таких чтения показаны в разборе TIL #139.
Как emojet ускоряет работу с эмодзи в Python
Библиотека emojet преобразует названия в эмодзи и обратно, поддерживает поиск и закрывает основное API пакета emoji. Имена доступны на 14 языках, а также в вариантах, принятых в GitHub и Slack.
В тестах автора на Python 3.14 для macOS и ARM импорт оказался в 23,7 раза быстрее, преобразование эмодзи в названия в 69,9 раза, обратная операция в 3,5 раза. Импорт с первым вызовом использовал около 17,4 МБ памяти вместо 29,2 МБ у emoji.
Раньше emoji при импорте загружал и разбирал JSON-файл размером 520 КБ. emojet написан на Rust и хранит данные в скомпилированных статических таблицах: разбирать файл при запуске не нужно. Для поиска имён используется хеш-таблица без коллизий, а для распознавания последовательностей эмодзи — дерево символов. В статье разобраны устройство библиотеки и воспроизводимый тест производительности.
Библиотека emojet преобразует названия в эмодзи и обратно, поддерживает поиск и закрывает основное API пакета emoji. Имена доступны на 14 языках, а также в вариантах, принятых в GitHub и Slack.
В тестах автора на Python 3.14 для macOS и ARM импорт оказался в 23,7 раза быстрее, преобразование эмодзи в названия в 69,9 раза, обратная операция в 3,5 раза. Импорт с первым вызовом использовал около 17,4 МБ памяти вместо 29,2 МБ у emoji.
Раньше emoji при импорте загружал и разбирал JSON-файл размером 520 КБ. emojet написан на Rust и хранит данные в скомпилированных статических таблицах: разбирать файл при запуске не нужно. Для поиска имён используется хеш-таблица без коллизий, а для распознавания последовательностей эмодзи — дерево символов. В статье разобраны устройство библиотеки и воспроизводимый тест производительности.