Рубрики
Без рубрики

Написание услуг D-Bus в Python и скрученным

Быстрый учебник на D-Bus и как позвонить и написать услуги D-Bus с помощью Python и скрученным. Помечено Python, DBus, Twisted, RPC.

Я упомянул в предыдущем сообщении, что во время написания Korbenware, My My Linux Desktop Management Management Software, которое я закончил написание сервисной структуры D-Bus. Сегодня я хотел написать о том, что именно это означает и как вы можете взаимодействовать с ним с помощью Python, Twisted и TxDbus.

Что такое D-автобус?

D-Bus – это система, которая позволяет проводить локально-ориентированные на рабочие процессы для рабочего стола для взаимодействия друг с другом и вносить удаленные процедуры вызовы между ними. Это часть стека Freedesktop, SystemD интегрируется с ним, и в значительной степени каждый рабочий стол Linux его установил. D-Bus позволяет одной программе запустить действия или запросы данных из другой программы.

Одним из употреблений для этого является системные уведомления. Например, если вы перейдете на вашу машину Linux (у которого есть Notify-отправьте установить) и запустить:

$ notify-send "hello world!"

Ваша машина должна показать уведомление на рабочем столе:

Способ того, что это на самом деле работает, так это: My Server Server Server, которая бывает Данст , подключается к серверу D-Bus, работающего на моем машине и обнажает интерфейс уведомлений, который соответствует стандарту, которое использует настольные ложки Linux для уведомлений. Затем, когда я хочу сделать уведомление, Уведомление-Отправить Переходит к D-Bus ищет этих ожидаемых уведомлений и вызывает удаленный метод, поставляющий мое скромное сообщение.

Это довольно круто. Поскольку D-Bus существует, любые настольные услуги, имеющие использование для действий, вызванные внешними программами, могут использовать эту общую систему для раскрытия их функциональности. Поскольку интерфейс уведомлений стандартизирован, я могу подключить различные реализации одной и той же идеи в этом месте. В моем случае я использую Dunst, но Gnome и KDE все имеют свои собственные реализации уведомлений, которые глубоко интегрированы со своими настольными компьютерами.

Стек D-Bus имеет кучу функций Slick помимо основных RPC. Он может выставлять свойства, рекламирующие отраженные интерфейсы и, с системой интеграцией, даже позволяют услугам быть проснулся лениво При первом использовании.

Изготовление RPC звонит через D-Bus

Любая среда программирования может подключаться и взаимодействовать с D-Bus. Мой проект написан в Python и скручен, поэтому я использую библиотеку под названием TXDBUS Отказ

Чтобы отправить уведомление с TXDBUS, сначала мы настроили скрученный реактор и создайте подключение к D-шине:

from twisted.internet import reactor

from txdbus import client

conn = await client.connect(reactor)

Затем мы создаем вызов D-Bus, чтобы получить интерфейс RPC:

remote_obj = await conn.getRemoteObject(
    'org.freedesktop.Notifications',
    '/org/freedesktop/Notifications'
)

Услуги на D-Bus Expose удаленные объекты, которые находятся на шине на пути объекта. Каждое обслуживание имеет свое собственное название шины, и каждое из нескольких объектов, выставленных сервисом, имеет свой собственный путь объекта. В этом случае сервер уведомлений использует «org.freedesktop. Автобус уведомлений и обнажает объект уведомлений на «/ORG/Freedesktop/Уведомления».

D-BUS отправит обратно полезную нагрузку, которая описывает интерфейс для этого объекта, что означает, что TXDBUS может построить рабочий объект RPC, отражая его без какой-либо конфигурации на конце клиента.

Как только у нас есть объект, на самом деле создание уведомления выглядит так:

await remote_obj.callRemote(
    'Notify',  # The name of the method
    'my-test-app',  # The name of my app
    0,  # An optional ID of a previous notification to replace
    '', # An optional path to an icon
    'This is a test message.', # A summary
    'Hello World!', # A message
    [],  # Actions that the user can take
    dict(),  # Hints - extra parameters
    5000  # How long the notification sticks around
)  # Returns the ID of the notification

Объект уведомлений предоставляет метод, называемый «уведомление», который принимает большой список аргументов. Аргументы D-BUS не имеют истинной концепции нулевого значения, поэтому любой данный дополнительный аргумент принимает аргумент «FASSEY». Например, уведомление может отображать значок, но если у вас нет значка, чтобы поделиться, вы отправляете пустую строку вместо нулевого значения.

Эта API немного неуклюже, но для таких клиентов, мы можем обернуть наши объекты RPC в простом Фасад Отказ Вот тот, который я написал для Korbenware:

import attr


@attr.s
class Notifier:
    connection = attr.ib()
    remote_obj = attr.ib()

    async def create(connection):
        remote_obj = await connection.getRemoteObject(
            'org.freedesktop.Notifications',
            '/org/freedesktop/Notifications'
        )

        return Notifier(connection, remote_obj)

    async def notify(
        self,
        appname='',
        replaces=0,
        icon='',
        summary='',
        message='',
        actions=None,
        hints=None,
        timeout=1000
    ):
        actions = actions or []
        hints = hints or dict()

        return await self.remote_obj.callRemote(
            'Notify',
            appname,
            replaces,
            icon,
            summary,
            message,
            actions,
            hints,
            timeout
        )

Учитывая этот фасад, создание уведомлений от Korbenware выглядит так:

from korbenware.notifications import Notifier

notifier = await Notifier.create(conn)

await notifier.notify(
    appname='my-test-app',
    message='Hello World!',
    summary='This is a test message.'
)

Это вообще неплохо – я довольно доволен этим.

Раскрытие услуг над D-Bus

Вызов объектов клиента круто и все, но я хочу иметь возможность контролировать менеджер сеанса Korbenware через D-Bus. Другими словами, я хочу разоблачить услугу самостоятельно – так что это выглядит?

Для этого мне понадобится еще несколько импорта:

from twisted.internet import defer
from txdbus import objects
from txdbus.interface import DBusInterface, Method

Теперь я могу определить класс объекта D-Bus, который имеет интерфейс D-Bus и связанные с ними методы:

class MyObj(objects.DBusObject):
    iface = DBusInterface(
        'org.example.MyIFace',
        Method('Ping', arguments='s', returns='s')
    )

    dbusInterfaces = [iface]

    def __init__(self, objectPath):
        super().__init__(objectPath)

    def dbus_Ping(self, arg):
        print(f'Received: {arg}')
        return f'You sent: {arg}'

conn.exportObject(MyObj('/MyObjPath'))

await conn.requestBusName('org.example')

В этом фрагменте я определяю класс, который наследует от txdbus.Objects. DbusObject у него есть собственность на нем называется dbusinterfaces список, содержащий экземпляр txdbus.interfaces. Dbusinterface. . Этот интерфейс определяет свой путь DBus и любые методы, которые он имеет – в этом случае метод, называемый «Ping», который занимает и возвращает строку. Объект также определяет логику метода с помощью метода Python, называемой dbus_ping. . Наконец, я экспортирую объект в D-Bus с объектом, созданным объектом по объекту «/myobjPath» и запрашивать название автобуса «org.example».

Теперь мой сервис онлайн, и я могу назвать это, как я сделал объект уведомлений:

myObj = await conn.getRemoteObject(
    'org.example',
    '/MyObjPath'
)

await myObj.callRemote('Ping', 'Hello world!')

Это будет печатать “Получено: Hello World!” На стороне сервера соединения, а возвращение “Вы отправили: Hello World!” на клиенте. Я управляю этими фрагментами в ноутбуке Jupyter, поэтому мое клиентское соединение также является моим сервером.

Типы D-шины и подписи

У вас, вероятно, есть вопросы о том, как аргументы и возвращаемое значение указываются с «S» и «S», соответственно. D-BUS имеет мини-язык для описания отправленных полезных нагрузок и полученных с использованием собственного формата двоичного сериализации. Этот формат имеет ряд типов данных, и каждый тип данных имеет свой собственный синтаксис для описания того, как выглядит так. Любая коллекция их называется «подписью». Вот некоторые примеры:

  • ‘S’ – строка
  • ‘b’ – логический
  • ‘i’ – 32-битное целое число
  • ‘u’ – unsigned 32 бит целое число
  • ‘V’ – тип варианта – D-Bus отправит тип его значения наряду с значелем внутри полезной нагрузки
  • ‘(Si)’ – структура, содержащая строку и целое число
  • ‘ai’ – массив целых чисел
  • ‘{sv}’ – дикт со строчными ключами и вариантными значениями

Полная документация можно найти онлайн Отказ

Они могут быть собраны, чтобы сформировать полную полезную нагрузку. Вот что подпись аргументов на вызов уведомления выглядит:

susssasa{sv}i

Они соответствуют аргументам, которые мы видели ранее: строка (имя приложения), целое число приложений (идентификатор уведомления), еще три строки (значок, сводка и сообщение соответственно), массив строк (наши действия), дикт со строчными клавишами и вариантными значениями (дополнительные подсказки) и целое число (время ожидания уведомлений).

Что еще может это сделать?

Это царапает поверхность возможностей D-Bus – она также поддерживает удаленные свойства, Pubsub через механизм, называемый сигналами, распространение ошибок и многое другое. TXDBUS имеет Полный учебник онлайн Это проходит некоторые из этих расширенных функций.

Что дальше?

Этот пост охватывает основы D-Bus, то, что она делает и как с этим взаимодействовать. Однако, в то время как фасад хорошо работал для отраженных клиентов, я нашел написание обертков для услуг немного неуклюже. Вместо того, чтобы урегулировать, я решил побриться яка и написать пользовательские рамки вокруг него, используя Attrs и barshmallow. Я думаю, что работа, которую я сделал здесь, очень круто и я взволнован, чтобы поделиться этим с людьми – Но в будущем в блоге пост. Будьте на связи!

Оригинал: “https://dev.to/jfhbrook/writing-d-bus-services-in-python-and-twisted-1l60”