Python SDKBETA
Модуль разработки кастомных блоков на языке Python — это инструмент для гибкой настройки и расширения функционала модуля автоматизации с помощью пользовательских блоков и подключений. Он позволяет разработчикам создавать собственные решения, адаптированные под конкретные бизнес-задачи, обрабатывать данные и расширять возможности системы.
С помощью Python SDK вы можете:
- Создавать пользовательские блоки. Эти блоки обрабатывают входные данные и возвращают результаты
- Реализовывать подключения к внешним API, хранилищам данных или другим системам
Основные термины:
- Группа объединяет несколько блоков и подключений в интерфейсе системы
- Блок — основная вычислительная единица, получающая входные данные и возвращающая результат
- Подключение используется для ввода данных из внешних источников. Оно содержит описание полей, которые будут доступны блоку, и метаинформацию, отображаемую в системе.
Структура проекта
src
— корневая директория исходного кода проектаmain
— основная директория с исходникамиsdk
— базовые компоненты SDK; директория содержит абстрактные классы, утилиты и классы для обработки данных. Все классы и модули здесь отвечают за базовую логику, которую используют пакеты__init__.py
— маркер модуляabstract_group.py
— абстрактная группаabstract_block.py
— абстрактный блокabstract_connection.py
— абстрактное подключениеargument_processor.py
— класс для разбора и валидации аргументов запуска Python-скриптовblock_params.py
— класс для централизованного хранения параметров блоковdefault_info.py
— шапка стандартного ответа на запрос--get-info
input_processor.py
— класс, запускающий блок и обрабатывающий поток входных данныхmessage_wrapper.py
— набор утилит для формирования сообщений в необходимом формате для обмена с системойutils.py
— набор базовых вспомогательных функций (валидация, преобразование данных и пр.)
packages
— папка для пользовательских пакетов, в каждом из которых реализованы свои группы, блоки и подключения; название подпапкизадается пользователем и служит пространством имен <user_package_name>
— основная директория конкретного пользовательского пакетаblocks
— папка с реализациями блоков (файлы с кодом блоков)__init__.py
— маркер модуля
connections
— папка с реализациями подключений к внешним системам__init__.py
— маркер модуля
group.py
— реализация класса группы, наследуемого от abstract_group.py; здесь описывается мета-информация о группе: UUID, название, иконка и т. п.python_group_<group_hash>.png
— иконка группы; путь к этому файлу относительно папки пакета нужно вернуть в реализации метода группыget_icon()
, тогда иконка появится на группе
Пользовательские блоки должны храниться в папке blocks
, подключения — в папке connections
.
Абстрактная группа (abstract_group.py)
Абстрактная группа определяет интерфейс, который должна реализовать любая пользовательская группа: уникальный UUID, название, категория и путь к иконке. Это позволяет системе правильно отобразить группу и связать с ней нужные блоки.
С примерами использования групп можно ознакомиться на странице: Примеры пользовательских реализаций в Python SDK
Код абстрактной группы
- Особенности при реализации группы:
get_uuid()
— возвращает уникальный идентификатор группыget_icon()
— возвращает путь иконки, ассоциированной с группой относительно папки скриптаget_name()
— возвращает варианты локализации имени группы в виде словаряget_info()
— собирает и возвращает всю основную информацию о группе в виде словаря
from abc import abstractmethod
class AbstractGroup():
def __init__(self):
pass
@abstractmethod
def get_uuid(self)->str:
return ""
@abstractmethod
def get_name(self)->dict:
return {}
@abstractmethod
def get_icon(self)->str:
return ""
def get_info(self):
return {
"uuid":self.get_uuid(),
"name":self.get_name(),
"icon":self.get_icon()
}
Абстрактный блок (abstract_block.py)
Абстрактный блок определяет общую структуру блока: формат входа/выхода, способ обработки данных и поведение блока (например, режим агрегации). Пользователь должен создать наследника этого класса и реализовать логику в методе process_data
.
С примерами использования блоков можно ознакомиться на странице: Примеры пользовательских реализаций в Python SDK
Код абстрактного блока
- Особенности при разработке блока:
get_info()
— собирает и возвращает всю основную информацию о блоке в виде словаряprocess_data()
— возвращает список с соответствующей глубиной вложенности- Итеративный режим (глубина вложенности=3):
- 1 уровень - каждый элемент соответствует одной входящей записи
- 2 уровень - каждый элемент соответствует одной исходящей записи. Набор элементов представляет собой ответ на входящую запись
- 3 уровень - каждый элемент соответствует значению поля исходящей записи. Порядок и типы полей соответствуют описанию в
output_variables
- Аггрегационный режим (глубина вложенности=2, так как нет связи с входящими записями):
- 1 уровень - каждый элемент соответствует одной результирующей исходящей записи
- 2 уровень - каждый элемент соответствует значению поля исходящей записи. Порядок и типы полей соответствуют описанию в
output_variables
- Итеративный режим (глубина вложенности=3):
set_block_output()
— возвращает текущий формат выходных данных из словаряblock_output_dict
set_block_aggr_mode()
— меняет поведение блока: может быть итеративнымFalse
и агрегационнымTrue)
from abc import abstractmethod
class AbstractBlock():
def __init__(self, block_input=None, block_output=None, info_output=None):
self.block_input = block_input # Формат входных полей блока
self.block_output_dict = block_output # Список конфигураций выходных полей блока
self.block_output = None # Формат выходных полей блока. Выбирается из block_output_dict
self.info_output = info_output # Информация о блоке при вызове --get-info
self.aggr_mode = False # Режим работы блока (итеративный/агрегационный)
def get_info(self):
return self.info_output
@abstractmethod
def process_data(self, **data):
"""
Абстрактный метод, вызываемый для обработки входных данных. Выходные данные должны соответствовать формату атрибута block_output
"""
return NotImplementedError
def _set_block_output(self, mode:str):
"""
Приватный метод изменения выходной структуры
"""
assert isinstance(mode, str), f'Invalid block "mode" param type: {type(mode)}'
self.block_output = self.block_output_dict[mode]
@abstractmethod
def set_block_output(self, data_element = None):
"""
Изменяет структуру выходных полей (атрибут block_output) в соответствии с атрибутом-словарем block_output_dict
В качестве условия может использоваться data_element (случайная строка входных данных)
"""
self._set_block_output('default')
def _set_block_aggr_mode(self, aggr_mode:bool):
"""
Приватный метод смены поведения блока
"""
assert isinstance(aggr_mode,bool ), f'Invalid "aggr_mode" param type: {type(aggr_mode)}'
self.aggr_mode = aggr_mode
@abstractmethod
def set_block_aggr_mode(self, data_element = None):
"""
Меняет поведение блока. Может быть итеративным (False) и агрегационным (True)
В качестве условия может использоваться data_element (случайная строка входных данных)
"""
self._set_block_aggr_mode(False)
Абстрактное подключение (abstract_connection.py)
Абстрактный класс задает обязательную структуру подключения: UUID, имя, описание, набор опций и список полей. Пользовательские подключения наследуются от него и реализуют конкретные поля, необходимые для работы с API, файлами и прочими источниками.
С примерами использования подключений можно ознакомиться на странице: Примеры пользовательских реализаций в Python SDK
Код абстрактного подключения
- Особенности при разработке подключения:
get_info()
— собирает и возвращает всю основную информацию о группе в виде словаря
from abc import abstractmethod
class AbstractConnection():
def __init__(self,
uuid:str = None,
name:dict = None,
description:str = None,
optionals:dict = None,
fields:str = None,
connection_input:dict = None
):
self.uuid = uuid
self.name = name
self.description = description
self.optionals = optionals
self.fields = fields
self.connection_input = connection_input
def get_info(self) -> dict:
"""
Возвращает информацию о подключении при вызове --get-info
"""
info_output = {
"uuid":self.uuid,
"name":self.name,
"description":self.description,
"optionals":self.optionals,
"fields":self.fields
}
return info_output
Была ли статья полезна?