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

Переиспользуем Python-функции между проектами — без переписывания и передеплоя

Переиспользуем Python-функции между проектами — без переписывания и передеплоя - 1

У меня есть функция. Крошечная – десяток строк, которые приводят суммы из платёжек к нормальному виду: "1 234,50 ₽" превращают в 1234.5. Ничего гениального. Но я писал её, кажется, раз пять. В парсере банковских выписок, в импортёре заказов, в отчёте для бухгалтерии, в скрипте, который потом выкинул, и ещё где-то, о чём уже забыл.

И каждый раз – чуть по-другому. Где-то забыл про неразрывный пробел. Где-то евро вместо рубля. В одном месте нашёл баг с минусом у отрицательных сумм и починил – а в остальных четырёх копиях он так и остался, потому что кто вообще помнит, где ещё живёт этот код.

Если что-то из этого знакомо – думаю, дальше будет полезно. Это первая из нескольких статей про splime – инструмент, который вырос ровно из этой задачи: переиспользовать код между проектами, не копируя его. Начну с общей картины: что это, зачем и как выглядит в работе.

Что мы вообще делаем с такими функциями

Когда полезная функция нужна во втором проекте, вариантов обычно три, и каждый по-своему раздражает.

Скопировать. Быстро и честно ровно до того момента, пока копий не станет три. Дальше начинается то, с чего я начал: правку в одной копии остальные не видят, и функция потихоньку расползается на пять слегка разных версий с разными багами.

Вынести в общий пакет. Взрослое решение – и правильное, когда функций много. Но ради одной clean_amount вы теперь версионируете пакет, публикуете его, тянете целиком в каждый проект (со всеми его зависимостями) и обновляете везде, где он прижился. Многовато возни ради десятка строк.

Завернуть в микросервис. Тоже вариант – если это сервис. Но clean_amount не сервис. Писать HTTP-обвязку, деплой и следить за аптаймом ради нормализации строки – это из пушки по воробьям.

А по сути нужно одно: опубликовать функцию один раз и звать её по имени откуда угодно. Не таскать код, не тянуть зависимости, не поднимать сервис. Позвал по имени – получил результат. Вот из этого желания и вырос splime.

splime за шесть строк

Ставим пакет (нужен Python 3.13+) и поднимаем локальный демон – это фоновый процесс, который будет хранить ваши функции и запускать их в любом проекте и окружении:

pip install splime
spl-daemon serve        # поднимет локальный демон на http://127.0.0.1:8765

Теперь та самая функция. Публикуем её как объект-ноду – версионированную, переносимую единицу – и зовём по имени:

from spl import SPLClient

def clean_amount(raw: str) -> float:
    # "1 234,50 ₽"  ->  1234.5
    cleaned = raw.replace(" ", "").replace("xa0", "").replace("₽", "")  # xa0 - неразрывный пробел
    return float(cleaned.replace(",", "."))

client = SPLClient()                       # локально; на сервер ничего не уходит
client.publish(clean_amount, name="clean_amount")

result = client.call("clean_amount", kwargs={"raw": "1 234,50 ₽"})
print(result.mode)     # "local"
print(result.output)   # 1234.5
Первый запуск: поднимаем демон, публикуем clean_amount и зовём по имени - в ответ 1234.5.

Первый запуск: поднимаем демон, публикуем clean_amount и зовём по имени – в ответ 1234.5.

Всё. Это весь цикл: определили функцию, publish – и она стала объектом локальной библиотеки (нодой); call по имени – и вернулось значение. Причём из любого другого скрипта на этой машине достаточно client.call [1]("clean_amount", ...) – саму функцию второй раз писать не нужно, она уже опубликована.

Вернулось, кстати, не только число. У результата есть result.output (само значение), result.mode (где выполнялось) и result.artifacts – файлы, которые нода сложила по ходу дела. Плюс у запуска остаётся история: что вызывали, с какими аргументами, чем завершилось даже в случае ошибки [2] или падения.

Что здесь только что произошло

Три вещи, которые стоит удержать в голове, которые демонстрируют явно почти всю идею.

Нода – это ваша функция, упакованная в версионированную переносимую форму. Не pickle, не «слепок процесса», а разобранное описание функции, которое можно хранить, переносить и воспроизводить. При этом обо всех зависимостях и окружении уже позаботились фреймворк и локальный сервис splime.

Демон – локальный сервис. Он хранит опубликованные объекты, собирает под них окружение и запускает исполнение, в том числе в Docker-контейнере. Живёт у вас на локальной машине.

Local-first. Обратите внимание [3]: SPLClient() без всяких токенов работает полностью локально и никуда не ходит по сети. Никакого обязательного облака, аккаунта и «войдите, чтобы продолжить». Сервер подключается позже и только если он вам нужен.

Схема: SPLClient публикует и зовёт ноды; локальный демон хранит их в библиотеке, собирает окружение и исполняет.

Схема: SPLClient публикует и зовёт ноды; локальный демон хранит их в библиотеке, собирает окружение и исполняет.

«А если данные не у меня на машине?»

Вот тут – самое интересное, и ровно то, ради чего всё затевалось. Допустим, у вас опубликована нода потяжелее – score_batch, которой нужны данные и GPU с другой машины. Тот же самый вызов становится удалённым, стоит указать, где выполнять:

result = client.call(
    "score_batch",
    kwargs={"date": "2026-06-08"},
    target_machine="gpu-box",     # выполнить там, где живут данные/GPU/доступы
)
print(result.mode)     # "server"

Функция может ходить в базу, которой нет на вашем компьютере. Ей может быть нужен GPU или доступы, которые есть только на одной конкретной удаленной машине. Код при этом не переезжает к данным вручную и не дублируется – вы зовёте ноду по имени, а выполняется она там, где надо. Как это устроено внутри – тема следующих статей; пока достаточно того, что вызов один и тот же, меняется только «где». (Для удалённого запуска нужно просто подключить очередной локальный демон целевой машины к серверу splime – https://splime.io/app/ [4] – про настройку расскажу дальше в серии; локально же всё работает сразу после pip install.)

Console на splime.io/app: подключённые машины-демоны, библиотеки и история запусков.

Console на splime.io/app: подключённые машины-демоны, библиотеки и история запусков.

Сразу про рамки: где splime, а где – другие инструменты

Чтобы не было завышенных ожиданий – и чтобы сэкономить нам время в комментариях.

Для расписаний и сложных DAG’ов есть прекрасные оркестраторы: Airflow, Prefect, Temporal. Сегодня splime не про это – он не гоняет пайплайны по расписанию, а даёт переиспользуемые ноды, которые такой оркестратор может вызывать. Одно неплохо ложится на другое.

Для массивной параллельности – размазать одну задачу на сотни ядер – есть Ray и родственные ему штуки. splime не соревнуется с ними за пропускную способность: его забота – версии, переносимость и вызов той или иной функции “просто по имени”.

Публичного маркетплейса кода splime сегодня тоже не строит: ноды приватные, вы публикуете их для себя и своей команды, с явным владением и доступами, а не выкладываете в общую витрину.

И это не просто “запусти любой код где угодно”: splime исполняет функции, которым вы осознанно доверяете, на машинах, которые вы контролируете, – граница доверия проходит там же, где всегда: на том, что вы сами решили опубликовать.

Если одной строкой: splime превращает проверенные Python-функции в версионированные переносимые ноды, которые можно переиспользовать между проектами и запускать локально или удалённо.

Стадия готовности splime

splime – молодой проект. Это alpha: публичный API ещё может меняться между релизами, и мы не притворяемся, что за плечами годы продакшена и толпы пользователей. Открыто главное – сам фреймворк и локальный демон, то есть весь локальный движок: он ставится из PyPI, а в код можно залезть на GitHub и посмотреть, как всё устроено, а не верить мне на слово. Сервер и веб-панель (Console на splime.io/app), через которые демоны собираются в команду и появляется удалёнка, – управляемая часть, её мы держим у себя. Если идея выглядит для вас интересно или, наоборот, кажется, что мы делаем ерунду, – сейчас как раз тот момент, когда обратная связь влияет на продукт.

Попробовать: pip install splime (Python 3.13+). Код, issue и звезда, если зашло: github.com/yastrebovks/splime [5]. Документация и честный статус проекта: splime.io [6].

Что дальше в серии

Это была общая картина. Дальше мы планируем копнуть вглубь, рассказа о тех или иных инженерных решениях, которые мы приняли в ходе реализации, а также причины такого выбора; благо локальный движок открыт, и по ходу можно сверяться с кодом:

  1. Локальный уровень. Как устроен демон, где и в каком виде живут ноды и как две ноды передают данные друг другу: адаптеры, теги артефактов и почему ребро между нодами это файл, а не pickle через pipe.

  2. Окружения. Как демон собирает venv через uv и как выбрать рантайм отдельно для каждой ноды: native, venv-subprocess или docker.

  3. Запуски как данные. Пайплайн упал на третьей ноде – смотрим манифест запуска, меняем адаптер и продолжаем с места падения, не пересчитывая то, что уже посчитано.

  4. Куда мы ведём проект. Command-ноды, декларации, ноды прямо из GitHub-репозитория и первый не-Python язык и почему мы принимаем именно такие решения (включая подходы, которые попробовали и осознанно выкинули).

splime молодой, и то, куда он пойдёт дальше, во многом зависит от таких обсуждений. Если что-то показалось спорным, неудобным или, наоборот, попало в точку – напишите в комментариях или заведите issue на GitHub. Из этих разговоров вырастут и следующие статьи серии, и приоритеты в роадмапе. Я рядом и отвечу.

Автор: YastrebovKS

Источник [7]


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

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

URLs in this post:

[1] client.call: http://client.call

[2] ошибки: http://www.braintools.ru/article/4192

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

[4] https://splime.io/app/: https://splime.io/app/

[5] github.com/yastrebovks/splime: https://github.com/yastrebovks/splime

[6] splime.io: http://splime.io

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

www.BrainTools.ru

Rambler's Top100