Skip to content

Commit

Permalink
More dbus/glib separation and testing improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
caronc committed Dec 27, 2023
1 parent 57ceb32 commit 31e2a73
Show file tree
Hide file tree
Showing 6 changed files with 759 additions and 236 deletions.
2 changes: 1 addition & 1 deletion Dockerfile.py310
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ FROM python:3.10-buster
RUN apt-get update && \
apt-get install -y --no-install-recommends libdbus-1-dev libgirepository1.0-dev build-essential musl-dev bash dbus && \
rm -rf /var/lib/apt/lists/*
RUN pip install --no-cache-dir dbus-python "PyGObject==3.44.2"
RUN pip install --no-cache-dir "PyGObject==3.44.2"

# Apprise Setup
VOLUME ["/apprise"]
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile.py311
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ FROM python:3.11-buster
RUN apt-get update && \
apt-get install -y --no-install-recommends libdbus-1-dev libgirepository1.0-dev build-essential musl-dev bash dbus && \
rm -rf /var/lib/apt/lists/*
RUN pip install --no-cache-dir dbus-python "PyGObject==3.44.2"
RUN pip install --no-cache-dir "PyGObject==3.44.2"

# Apprise Setup
VOLUME ["/apprise"]
Expand Down
145 changes: 102 additions & 43 deletions apprise/plugins/NotifyDBus.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,28 +27,61 @@
# POSSIBILITY OF SUCH DAMAGE.

import sys

from ..AppriseLocale import gettext_lazy as _
from ..common import NotifyImageSize, NotifyType
from ..utils import parse_bool
from .NotifyBase import NotifyBase
from ..common import NotifyImageSize
from ..common import NotifyType
from ..utils import parse_bool
from ..AppriseLocale import gettext_lazy as _

# Default our global support flag
NOTIFY_DBUS_SUPPORT_ENABLED = False

# Image support is dependant on the GdkPixbuf library being available
NOTIFY_DBUS_IMAGE_SUPPORT = False

# Initialize our mainloops
LOOP_GLIB = None
LOOP_QT = None


try:
# dbus essentials
import gi
gi.require_version("Gio", "2.0")
gi.require_version("GLib", "2.0")
from gi.repository import Gio, GLib
# D-Bus Message Bus Daemon 1.12.XX Essentials
from dbus import SessionBus
from dbus import Interface
from dbus import Byte
from dbus import ByteArray
from dbus import DBusException

#
# now we try to determine which mainloop(s) we can access
#

# glib/dbus
try:
from dbus.mainloop.glib import DBusGMainLoop
LOOP_GLIB = DBusGMainLoop()

except ImportError: # pragma: no cover
# No problem
pass

# We're good
NOTIFY_DBUS_SUPPORT_ENABLED = True
# qt
try:
from dbus.mainloop.qt import DBusQtMainLoop
LOOP_QT = DBusQtMainLoop(set_as_default=True)

except ImportError:
try:
from dbus.mainloop.pyqt5 import DBusQtMainLoop
LOOP_QT = DBusQtMainLoop(set_as_default=True)

except ImportError:
# No problem
pass

# We're good as long as at least one
NOTIFY_DBUS_SUPPORT_ENABLED = (
LOOP_GLIB is not None or LOOP_QT is not None)

# ImportError: When using gi.repository you must not import static modules
# like "gobject". Please change all occurrences of "import gobject" to
Expand All @@ -59,6 +92,7 @@

try:
# The following is required for Image/Icon loading only
import gi
gi.require_version('GdkPixbuf', '2.0')
from gi.repository import GdkPixbuf
NOTIFY_DBUS_IMAGE_SUPPORT = True
Expand All @@ -76,6 +110,15 @@
# library available to us (or maybe one we don't support)?
pass

# Define our supported protocols and the loop to assign them.
# The key to value pairs are the actual supported schema's matched
# up with the Main Loop they should reference when accessed.
MAINLOOP_MAP = {
'qt': LOOP_QT,
'kde': LOOP_QT,
'dbus': LOOP_QT if LOOP_QT else LOOP_GLIB,
}


# Urgencies
class DBusUrgency:
Expand Down Expand Up @@ -116,11 +159,11 @@ class NotifyDBus(NotifyBase):
"""

# Set our global enabled flag
enabled = NOTIFY_DBUS_SUPPORT_ENABLED
enabled = bool(NOTIFY_DBUS_SUPPORT_ENABLED)

requirements = {
# Define our required packaging in order to work
'details': _('libdbus-1.so.x or libdbus-2.so.x must be installed.')
'details': _('libdbus-1.so.x, dbus-python and/or pyqt must be installed.')
}

# The default descriptive name associated with the Notification
Expand All @@ -130,7 +173,12 @@ class NotifyDBus(NotifyBase):
service_url = 'http://www.freedesktop.org/Software/dbus/'

# The default protocols
protocol = ('glib', 'dbus')
# Python 3 keys() does not return a list object, it is its own dict_keys()
# object if we were to reference, we wouldn't be backwards compatible with
# Python v2. So converting the result set back into a list makes us
# compatible
# TODO: Review after dropping support for Python 2.
protocol = list(MAINLOOP_MAP.keys())

# A URL that takes you to the setup/help of the specific protocol
setup_url = 'https://github.com/caronc/apprise/wiki/Notify_dbus'
Expand Down Expand Up @@ -202,6 +250,15 @@ def __init__(self, urgency=None, x_axis=None, y_axis=None,
# Track our notifications
self.registry = {}

# Store our schema; default to dbus
self.schema = kwargs.get('schema', 'dbus')

if self.schema not in MAINLOOP_MAP:
msg = 'The schema specified ({}) is not supported.' \
.format(self.schema)
self.logger.warning(msg)
raise TypeError(msg)

# The urgency of the message
self.urgency = int(
NotifyDBus.template_args['urgency']['default']
Expand Down Expand Up @@ -234,19 +291,11 @@ def send(self, body, title='', notify_type=NotifyType.INFO, **kwargs):
"""
Perform DBus Notification
"""
# Acquire our dbus interface
# Acquire our session
try:
dbus_iface = Gio.DBusProxy.new_for_bus_sync(
Gio.BusType.SESSION,
Gio.DBusProxyFlags.NONE,
None,
self.dbus_interface,
self.dbus_setting_location,
self.dbus_interface,
None,
)
session = SessionBus(mainloop=MAINLOOP_MAP[self.schema])

except GLib.Error as e:
except DBusException as e:
# Handle exception
self.logger.warning('Failed to send DBus notification.')
self.logger.debug(f'DBus Exception: {e}')
Expand All @@ -258,37 +307,46 @@ def send(self, body, title='', notify_type=NotifyType.INFO, **kwargs):
title = body
body = ''

# acquire our dbus object
dbus_obj = session.get_object(
self.dbus_interface,
self.dbus_setting_location,
)

# Acquire our dbus interface
dbus_iface = Interface(
dbus_obj,
dbus_interface=self.dbus_interface,
)

# image path
icon_path = None if not self.include_image \
else self.image_path(notify_type, extension='.ico')

# Our meta payload
meta_payload = {
"urgency": GLib.Variant("y", self.urgency),
"urgency": Byte(self.urgency)
}

if not (self.x_axis is None and self.y_axis is None):
# Set x/y access if these were set
meta_payload['x'] = GLib.Variant("i", self.x_axis)
meta_payload['y'] = GLib.Variant("i", self.y_axis)
meta_payload['x'] = self.x_axis
meta_payload['y'] = self.y_axis

if NOTIFY_DBUS_IMAGE_SUPPORT and icon_path:
try:
# Use Pixbuf to create the proper image type
image = GdkPixbuf.Pixbuf.new_from_file(icon_path)

# Associate our image to our notification
meta_payload['icon_data'] = GLib.Variant(
"(iiibiiay)",
(
image.get_width(),
image.get_height(),
image.get_rowstride(),
image.get_has_alpha(),
image.get_bits_per_sample(),
image.get_n_channels(),
image.get_pixels(),
),
meta_payload['icon_data'] = (
image.get_width(),
image.get_height(),
image.get_rowstride(),
image.get_has_alpha(),
image.get_bits_per_sample(),
image.get_n_channels(),
ByteArray(image.get_pixels())
)

except Exception as e:
Expand All @@ -301,7 +359,6 @@ def send(self, body, title='', notify_type=NotifyType.INFO, **kwargs):
self.throttle()

dbus_iface.Notify(
"(susssasa{sv}i)",
# Application Identifier
self.app_id,
# Message ID (0 = New Message)
Expand Down Expand Up @@ -354,13 +411,15 @@ def url(self, privacy=False, *args, **kwargs):
if self.y_axis:
params['y'] = str(self.y_axis)

schema = self.protocol[0]
return f'{schema}://_/?{NotifyDBus.urlencode(params)}'
return '{schema}://_/?{params}'.format(
schema=self.schema,
params=NotifyDBus.urlencode(params),
)

@staticmethod
def parse_url(url):
"""
There are no parameters necessary for this protocol; simply having
There are no parameters nessisary for this protocol; simply having
gnome:// is all you need. This function just makes sure that
is in place.
Expand Down
Loading

0 comments on commit 31e2a73

Please sign in to comment.