Как вызвать скрипты на python в 1С по технологии NativeAPI

15.04.24

Разработка - Языки и среды

Будем писать свои скрипты на питоне и запускать их на 1С.

Прежде всего хочется написать тут ДИСКЛЕЙМЕР!!!

Я не умею писать на плюсах, не умею писать на питоне, я просто любознательный. Если у вас кровь бежит из глаз при просмотре моего кода, напишите в комментариях, как надо писать правильно. 

Спасибо!

 

Ну теперь можно и начать. Все, что нам понадобится сегодня:

 

Устанавливаем питон и открываем шаблон компоненты в CLion. Во-первых, нас будет интересовать файл CMakeLists.txt. Нам нужно указать, что в проекте будет использована библиотека питона для С. Для этого вписываем такие строки в файл:

 
 CMakeLists.txt

Отлично, с самым сложным разобрались. Идем дальше.

Есть тут такие файлы, которые называются SapmleAddIn.h и SapmleAddIn.cpp. Мы конечно же захотим, чтобы наша компонента называлась по-другому. Нажимаем SapmleAddIn.h в CLion левой клавишей мыши, выбираем Refactor->Rename и называем по-своему. У меня это будет Component4Python. Готово, все вызовы в коде, а также файл .cpp изменили название. Вернемся в файл CMakeLists.txt и в самом верху поменяем название:

project(Component4Python)
set(TARGET Component4Python)

и в файле addin.def:

LIBRARY "Component4Python"

С подготовкой закончено, идем ваять свою компоненту. В чем сама суть? В коде плюсов мы вызываем интерпритатор Python и даем ему команды, что импортировать, что вызвать.

Последовательность простая:

  • Инициализация
  • Полезная нагрузка
  • Деинициализация

С полезной нагрузкой разберемся позднее. С инициализацией и вот этим сложным словом в последнем пункте, не все так просто. Если почитать доку, можно увидеть, что:

Некоторые расширения могут работать некорректно, если их процедура инициализации вызывается более одного раза; это может произойти, если приложение вызывает Py_Initialize() и Py_FinalizeEx() более одного раза.

То есть вызывать Py_Initialize() и Py_FinalizeEx() мы должны только один раз. Где это удобнее всего сделать? Конечно же при создании и уничтожении компоненты. За эти события отвечает файл exports.cpp:

 
 exports.cpp

Но боюсь можно отхватить по голове за то, что в момент подключения или уничтожении компоненты, платформа может упасть. Причем, платформа особо не будет говорить ничего вменяемого, кроме "Аварийная ошибка" или "Произошла неисправимая ошибка". Так что забудем весь этот ужас и создадим 2 метода нашей компоненты. Пойдем в файл Component4Python.h и объявим о намерении иметь следующие методы:

 
 Component4Python.h

И приступим к реализации наших методов в файле Component4Python.cpp

 
 Component4Python.cpp

Таким образом, мы сможем импортировать библиотеки, установленные на компьютере и вызывать скрипты на python. НО! Ходят слухи, что это плохая практика. Лучше создать виртуальное окружение для питона и хранить необходимые либы в этом окружении. Ну что, давайте так и сделаем. Сделать это можно не только лишь из командной строки (а я обычный 1Сник, я люблю тыкать кномпачки и чтобы все по волшебству работало), но и в IDE CLion. Для этого открываем Settings->Build->Python interpreter, нажимаем Add Interpreter, выбираем директорию, где это окружение будет обитать и нажимаем OK.

В заголовочном файле Component4Python.h объявим еще одну переменную:

 
 Component4Python.h

И перепишем реализацию некоторых методов:

 
 Component4Python.cpp

То есть, теперь мы можем сказать компоненте путь до виртуального окружения. Если же окружение не установлено, компонента найдет python, установленный в системе.

Еще один момент, тут использованы исключения throw. Вообще API 1С говорит что все ошибки надо обрабатывать через метод AddError. Последним параметром этот метод принимает булево, которое говорит, надо прерывать код или нет. Вот у меня не получилось через этот метод прервать код, поэтому решил просто кидать исключение, 1С это прекрасно принимает.

Давайте перейдем уже к вызову питоновских методов. Для примера, я хочу вызывать метод, который возвращает мне QR код из строки. QR будет возвращен в виде картинки, нам надо будет его преобразовать в байты и вернуть в 1С. В 1С мне лень городить обработки, я буду все выполнять в консоли кода "Инструментов разработчика" от Tormozit.

Для простого примера я хочу выполнить вот такой код Python:

 
 Какой-то простой код Python

Передаем строку в библиотеку QR на питоне, сохраняем в поток данных и возвращаем двоичные данные. На 1С код будет выглядеть вот так:

 
 Какой-то код на 1С

Из кода на 1С видно, что мы должны вызвать метод СоздатьПростойQR, которого в компоненте еще нет. Идем писать его, начиная с заголовочного файла:

 
 Component4Python.h

И реализацию:

 
 Component4Python.cpp

Вот и сама начинка Python C API. Для того, чтобы вызвать import qrcode мы делаем вызов PyImport_ImportModule("qrcode"); ну и далее можно по названиям объектов и методов сопоставить код в питоне и код на плюсах. Что важно понимать, в API есть множество методов для вызова объектов/методов питона. В примере показан  PyObject_CallMethodNoArgs если надо вызвать метод объекта без аргументов, PyObject_CallMethodOneArg для вызова метода объекта с одним параметром,  но помимо этих есть еще куча методов. Например, создание объекта с передачей *args мы рассмотрим ниже.

Надо понимать, что каждый вызов метода python может вернуть результат выполнения кода или null, если что-то пошло не так. А значит, после каждого вызова нам надо делать проверку на null, попытаться получить причину возникновения ошибки, распарсить ее и вернуть в 1С (любителям в 1С возвращать результат или неопределено передаю большой привет). 

Мы не можем передать строку из плюсов в питон, для передачи строки ее надо сначала привести в правильный тип. Этим занимается метод PyUnicode_FromString

Функция Py_DECREF нужна для удаления неиспользуемых более объектов, т.н. чистка памяти, которую мы все очень боимся при работе на плюсах.

Кстати, я конечно не исключаю, что в моем коде есть утечки памяти, хоть я очень старался правильно объявлять объекты. Если найдете ошибку, добро пожаловать в комменты.

Пришло время собирать нашу компоненту и использовать ее в 1С.

Настроим сборочку. Идем в Settings->Build->Toolchains и добавляем Visual Studio:

 
 Пример настройки

Потом идем в Settings->Build->CMake и создаем debug и release сборки:

 
 Примеры настройки

В CLion есть кнопочка сверху справа в виде молотка, называется она build и предназначена для сборки проекта.

Нажимаем на нее и ждем когда в логах внизу нам скажут что все ок:

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

 
 дерево

Нас конечно же интересует *.dll файл, нажмем на него правой кнопкой мыши и скопируем полный путь. В коде 1С, который я привел выше, этот путь надо поместить в переменную "Путь". Все, открываем 1С, пытаемся выполнить наш код и получаем ошибку:

То есть, питон явно говорит, что не может импортировать qrcode. Это сообщение обрабатывается в нашей компоненте и отдается в 1С. Правда здорово? 

Опять же, код на 1С говорит, что мы запускаемся в виртуальном окружении, а значит такой библиотеки на питоне не хватает в этом окружении. Мòзги из JetBrains уже продумали, что и как надо делать в их IDE и дали нам возможность устанавливать пакеты питона в виртуальное окружение прямо из IDE.

Убедимся, что сейчас IDE настроена на наше виртуальное окружение, зайдем в Settings->Build->Python Interpreter

 
 Настройки

Путь должен быть точно таким, каким мы передаем в компоненту в коде 1С.

Далее в CLion слева выбираем Python Package:

 
 кнопка

в поиске забиваем qrcode и нажимаем install:

 
 пакеты

Снова запускаем код в 1С и получаем результат:

 
 Результат

Мы использовали готовые библиотеки и смогли имитировать небольшой скрипт на питоне. Но что, если скрипт написан был не какими-то чуваками, а вами? А если там при создании объекта несколько параметров? Давайте рассмотрим такой вариант. Откроем PyCharm и создадим проект:

 
 Новый проект на питоне

Так же мы должны сразу при создании проекта указать наше созданное виртуальное окружение.

Будем писать маленький валидатор json, который будет проверять наш json согласно переданной json schema.

Вот такую структуру проекта мы создадим:

 
 Структура проекта

init файл оставим в покое, он нам тут не особо понадобится. В файле validator будет вот такой код:

 
 validator.py

А в файле setup объявим наш пакет для установки:

 
 setup.py

Все, что нам остается сделать, это в терминале IDE выполнить команду "pip install ." для установки нашего нового пакета (подробнее о сборке пакетов можно узнать на Хабре):

 
 Установка пакета

И теперь будем дальше мучить нашу компоненту. Объявляем необходимые методы:

 
 Component4Python.h

Далее нам надо собрать параметры в массив, в том порядке, в котором параметры объявлены в методе питона и создать класс валидатор. 

 
 Component4Python.cpp

Мы просто будем вызывать валидацию, если будет валиться ошибка, тогда будем перехватывать ее и отправлять в 1С в строковом виде

 
 1С

 Запускаем и видим ошибку "Модуль не найден"

Очень много времени и сил я потратил на эту ошибку. В интернете по ней особо инфы нет ни у нас, ни за бугром. Оказалось все очень просто, мы собираем debug сборку, а rpds - это не скрипт на питоне, это библиотека, собранная под release. Можно упороться и собрать ее под debug, но я думаю это информация для отдельной статьи, а мы можем просто поменять сборку на release, собрать dll и подключить к 1С именно ее.

 
 Меняем сборку

Запускаем:

 
 Результат

Поговорим об отладке.

К сожалению, release сборку отладить мы не можем, а вот debug очень даже. Вернемся к методу формирования qr кода и поставим точку остановы на вызове 

auto qrcode = PyImport_ImportModule("qrcode");

Запустим 1С и в CLion нажмем комбинацию CTRL+ALT+F5

 
 Подключение к отладчику

Выбираем 1С и нажимаем attach.

После этого запускаем формирование qr кода в 1С и попадаем в отладчик в CLion, где мы можем посмотреть какие объекты мы получаем. Либо можно в отладчике выполнить произвольный код и посмотреть вывод.

Итоги.

Конечно, это очень поверхностная статья. К тому же это API очень низкоуровневое, использовать его сложно. Но если вам эта тема интересна, если эта статья "зайдет" сообществу, я с удовольствием опишу другие варианты вызова python скриптов, или же покажу этот API более развернуто.

Спасибо за внимание!

Весь код можно посмотреть на гитхаб

Читайте другие мои статьи:

См. также

Мобильная разработка Языки и среды 1С:Элемент Программист Бесплатно (free)

Flutter может быть использован с 1С:Предприятием для разработки кроссплатформенных мобильных приложений, обеспечивая единый интерфейс и функциональность на устройствах под управлением iOS и Android. Это позволяет создавать приложения с высокой производительностью благодаря использованию собственного движка рендеринга Flutter. Интеграция Flutter с 1С:Предприятием позволяет создавать мобильные приложения любого уровня сложности, интегрировать их в корпоративные информационные системы, а также реализовывать бизнес-логику

19.03.2024    18310    ROk_dev    74    

43

Языки и среды Программист Стажер Платформа 1С v8.3 Бесплатно (free)

Существует множество языков программирования, и каждый имеет свои особенности по работе с типами данных. Слабые, явные, динамические и другие... Но кто же здесь 1С и почему с приходом "строгой" типизации EDT 1С-программистам стоит задуматься над изменением своих привычек.

16.01.2024    7327    SeiOkami    25    

61

Языки и среды Программист Бесплатно (free)

Пример небольшого приложения, с которого можно начать изучать язык программирования Dart.

08.08.2023    4205    acvatoris    6    

15

Языки и среды Программист Платформа 1С v8.3 Россия Бесплатно (free)

Написание статического анализатора для 1С традиционным способом на Си.

30.06.2023    3496    prohorp    15    

12

Языки и среды Программист Абонемент ($m)

Поставили нам задачу - вынести на отдельный сервер функционал получения заказов от клиентов по электронной почте, парсинг полученных XLS в приемлемый вид и трансформация заказов в красивый JSON, понятный нашей учетной системе на 1С. Всю эту красоту желательно запустить в отдельном докер - контейнере, по возможности не тратя лицензии, поэтому отдельно стоящую конфигурацию на БСП отвергаем сразу. Можно было бы собрать всё на Apache Airflow или Apache NiFi, но решили попробовать реализовать всю логику без Open Source, будем делать свой ETL, с Исполнителем, который в версии 3.0 научился взаимодействовать с электронной почтой по IMAP. Начнем с середины - сначала напишем скрипты, а потом соберем их в рабочую конструкцию

1 стартмани

01.06.2023    2313    0    kembrik    2    

7

Языки и среды Программист Платформа 1С v8.3 Бесплатно (free)

При работе с 1С ORM (object relation mapping) все время преследует ощущение постоянного создания монолитного приложения — один раз привязался к какой либо сущности (например, справочник Контрагенты), и весь код заполнен ссылками на эту конкретную реализацию. Можно ли независимо разрабатывать в ORM совместимые между собой справочник «Контрагентов» и использующий его документ «Платежное поручение», но при этом избежать жестких зависимостей? Спасут ли нас микросервисы? Пример на аннотациях Java демонстрирует, как это возможно делать.

13.03.2023    1305    1CUnlimited    0    

3

Файловый обмен (TXT, XML, DBF), FTP Языки и среды Программист Платформа 1С v8.3 Бесплатно (free)

Опыт работы методами языка xPath в 1С.

04.03.2023    5602    DemetrKlim    41    

47
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. SerVer1C 815 15.04.24 11:55 Сейчас в теме
Интересно, но всё захардкодено. Для любой итерации расчета придётся переписывать компоненту. Попробуйте сделать возможность передачи питоновского скрипта в компоненту вместе с передачей параметров в компоненту и получения результата из компоненты, типа как тут с шарпом: 1С# - Расширяем код 1С кодом на C#
skif-m; starik-2005; +2 Ответить
2. YA_418728146 584 15.04.24 13:22 Сейчас в теме
(1) Абсолютно согласен. Я начал изучение этих возможностей с основ, основы и описываю. Но на данном API я бы не стал реализовывать абстрактную компоненту для запуска рандомных скриптов. Для этого есть более продвинутые библиотеки, о которых я постараюсь написать в будущем

С другой стороны, ваш подход из статьи, где я надо передать готовую строку скрипта, очень просто реализовать. Достаточно в код компоненты передать строку, как я это делал для QR кода, только в С++ сделать вызов PyRun_SimpleString(МояСтрокаСоСкриптом)
starik-2005; +1 Ответить
3. gybson 15.04.24 16:36 Сейчас в теме
Мне кажется веб-сервис на питоне будет более разумным решением
JohnyDeath; mysm; +2 Ответить
7. YA_418728146 584 16.04.24 12:49 Сейчас в теме
(3) и сразу напрашивается вопрос. Кто будет админить веб-сервис? Если услуги по администрированию сервиса на питоне будет лежать долгом на плечах отдела 1С, я пожалуй откажусь от такого решения. Если же этим будет заниматься отдел администраторов сервисов и отдел питонистов для разработки свмого сервиса, тогда я с вами соглашусь - это более разумное решение.
8. gybson 16.04.24 16:50 Сейчас в теме
(7) тот же, кто и компоненту. Ее стабильная работа ничем не гарантирована. И всегда еще есть вариант запуска через командную строку.

Т.е. опыт хороший, интересный, но скорее как развлечение.
4. user1129453 15.04.24 21:55 Сейчас в теме
Статья очень интересная и подробная) Жаль, что нельзя поставить больше лайков) Хотелось бы продолжения
kote; YA_418728146; +2 Ответить
5. friskdb 33 16.04.24 08:45 Сейчас в теме
Реализация кажется немного сложной. В чем плюсы этого решения по сравнению веб-сервиса или такого варианта https://infostart.ru/1c/tools/1943747/ ?
6. YA_418728146 584 16.04.24 12:41 Сейчас в теме
(5) com, web-service, 1C NativeAPI - это совсем разные технологии для сравнения. Все зависит от условий ТЗ. Если заказчику удобно подключать скрипты на питоне по com, лучше использовать вашу разработку. Если ТЗ требует выполнения кода в рамках компоненты на плюсах+питоне - тогда можно обратить внимание на мою статью. Если нужен абстрактный сервис (или какой-то микросервис), тогда конечно поднимаете веб-сервис с маленькой базой и работаете с ним.
9. starik-2005 3087 18.04.24 07:20 Сейчас в теме
За статью плюс однозначно.

(7)
Кто будет админить веб-сервис?
Какой классный вопрос! Даже интересно стало, на сколько сложнее админить вебсервис (в большинстве случаев веб-сервер уже есть), чем компоненту?

Во-вервых, веб-сервер - это отдельная служба на отдельной машине (можно даже виртуальной), а компонента - это пользовательская программа, которую нужно поставить на каждый компьютер, который предполагает использование этой внешней компоненты. Т.е. или мы один сервис админим (можно просто сделать его снапшот и в случае чего нетого просто накатить из него всю машину), или мы админим в части питона и виртуального окружения каждый компьютер, на котором пользователь вызывает ВК.

Во-вторых, веб-сервис доступен с любого компьютера, которому мы разрешим, и подключить нового пользователя не составит большого труда. А вот новому пользователю с новым компьютером админ должен будет не забыть все это установить. При том если устанавливать это все с интернетов, то установится какая-то последняя версия, которая далеко не факт, что будет совместима с этой ВК.

В общем, реализация подобных механизмов в виде отдельных сервисов - это хорошая практика, т.к. позволяет выделить единую инфраструктурную единицу в архитектуре ИТ, а не плодит на каждом компьютере пользователей кучу ПО, вызывая проблемы с поддержкой. Да и документацию мало кто любит писать, а еще меньше любят поддерживать ее в актуальном состоянии. В итоге админам только гемор, а толку чуть.

Если бы компонента содержала в себе не вызов кода питона извне, а весь код целиком, то смысл был бы, конечно. Но когда компонента завязана на отдельное ПО, которое с ней никак не связано, то смысла в этом сразу же становится крайне мало. При том у питона есть куча механизмов компиляции, что позволяет без всей этой возни в гиперпространстве виртуальных окружений просто использовать что-то типа КомандаСистемы("МойКрутойСкриптНаПитоне.ЕХЕ").
JohnyDeath; +1 Ответить
10. starik-2005 3087 18.04.24 07:25 Сейчас в теме
(5) Плюсы хотя бы в том, что это нативная компонента, а не СОМ-объект, т.е. это будет работать на православных системах (всяких там линуксах и других линуксах, и еще других линуксах, ...)
11. starik-2005 3087 18.04.24 07:36 Сейчас в теме
Кстати, по поводу лицензии для либы Infactum, то у него прописана "GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007", о ней в интернетах пишут вот что:
GNU Affero General Public License — лицензия для облачных сервисов

Стандартную общественную лицензию Affero обычно называют усиленной версией GPL — в противоположность LGPL. AGPL создавали специально для веб-приложений, чтобы пользователи модифицированной программы в сети имели доступ к её исходному коду. AGPL совместима с GPL.

Первая версия лицензии на основе GPLv2 вышла в 2002 году, а вторая — на основе GPLv3 в 2007-м. Согласно Affero, если пользователи имеют доступ к модифицированной программе, которая исполняется на сервере, то сервер должен предоставлять доступ к исходному коду этой программы.

Основная цель AGPL — предотвратить скрытое использование свободного софта в Сети. Как мы уже сказали ранее, GPL плохо работает с моделью SaaS, ведь пользователь не может посмотреть исходный код облачного сервиса. Усиленная версия GPL позволяет избежать этого. Если разработчик выполнил требования лицензии и предоставил пользователям доступ к исходному коду, то пользователи также могут включить его изменения в свою версию.

Примеры продуктов, где используется AGPLv3:

GNUnet — программный пакет для безопасного P2P-соединения;
Grafana — приложение для аналитики и интерактивной визуализации данных;
Onlyoffice — пакет офисных приложений, включающий онлайн-редактор документов, систему документооборота, CRM, корпоративную социальную сеть и почтовый сервер;
Nextcloud — набор клиент-серверных программ для создания и использования хранилищ данных.
Показать
Почему он заюзал такую лицуху вместо "GNU Lesser General Public License — лицензия для библиотек" - для меня загадка.
12. kote 537 28.05.24 12:54 Сейчас в теме
В своё время на вот такой компоненте (https://infostart.ru/1c/tools/186206/) сделал коннектор с шиной и много побаловался - очень удобно..

Гляньте, вот что-то подобное по юзабельности бы - очень зашло, мне кажется
13. YA_418728146 584 29.05.24 14:15 Сейчас в теме
(12) Да, мне тоже зашло. К сожалению автор не оставил исходного кода, а писать такое с 0, надо или много энтузиазма, или много денег)
Оставьте свое сообщение