Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: Send notifies directly using python-dbus replacing notify-send #1156

Merged
merged 29 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
0e86488
Send notifies directly using python-dbus replacing notify-send
Zocker1999NET Apr 23, 2021
fad3baf
Resolve notify interface dynamically
Zocker1999NET Sep 25, 2022
b299a72
Ignore DBuxException when sending a notification
Zocker1999NET Sep 25, 2022
da55b65
Merge branch 'dev' into dbus-notifications
buhtz Jun 5, 2023
bd2360f
removed dbus import workaround
buhtz Jun 5, 2023
f8b301e
travis: install dbus-python via pip
buhtz Jun 5, 2023
b5f4a84
dbus install tweaks
buhtz Jun 5, 2023
c6d38a1
dbus on travis
buhtz Jun 5, 2023
355fb18
Merge branch 'dev' into dbus-notifications
buhtz Jun 15, 2023
6962747
Merge branch 'dev' into dbus-notifications
buhtz Jan 7, 2024
deec684
Merge branch 'dev' into dbus-notifications
buhtz May 15, 2024
302cde9
fixed unused import / little bit pep8
buhtz May 15, 2024
a4783a8
Merge branch 'dev' into dbus-notifications
buhtz Aug 19, 2024
1629e7b
Merge branch 'dev' into dbus-notifications
buhtz Sep 2, 2024
19d9fb3
Merge branch 'dev' into dbus-notifications
buhtz Sep 11, 2024
a8dcf95
changelog and typos [skip ci]
buhtz Sep 11, 2024
662742b
remove notify-send dependency [skip ci]
buhtz Sep 11, 2024
8643cf4
refactoring
buhtz Sep 11, 2024
8553848
fix message level 0
buhtz Sep 11, 2024
869599e
Merge branch 'dev' into dbus-notifications
buhtz Sep 13, 2024
d9fe525
[skip ci]
buhtz Sep 20, 2024
edc3cb1
minor linting
buhtz Sep 20, 2024
7ff5234
remove debug code [skip ci]
buhtz Sep 20, 2024
446772b
fix linter tests
buhtz Sep 20, 2024
9489db1
x
buhtz Sep 20, 2024
e878261
Merge branch 'dev' into dbus-notifications
buhtz Sep 20, 2024
4645647
lint
buhtz Sep 20, 2024
d3a2f8b
remove no-self-use rule
buhtz Sep 20, 2024
ce2d8a7
green [skip ci]
buhtz Sep 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ Version 1.5.3-dev (development of upcoming release)
* Refactor: General tab and its Schedule section
* Refactor: Remove class OrderedSet
* Refactor: Remove os.system() from class Execute
* Refactor: Systray notifications send utilize DBUS instead of notify-send (#1156) (Felix Stupp @Zocker1999NET)
* Dependency: Remove libnotify-bin (notify-send) (#1156)

Version 1.5.2 (2024-08-06)
* Fix: Ensure crontab with ending newline (#781)
Expand Down Expand Up @@ -117,6 +119,7 @@ Version 1.4.0 (2023-09-14)
* Feature: Rearranged menu bar and its entries in the main window (#1487, #1478).
* Feature: Configure user interface language via config file and GUI.
* Documentation: Removed outdated docbook (#1345).
* Testing: TravisCI now can use dbus
* Build: Introduced .readthedocs.yaml as asked by ReadTheDocs.org (#1443).
* Dependency: The oxygen icons should be installed with the BiT Qt GUI since they are used as fallback in case of missing icons
* Fix bug: Add support for ChainerBackend class as keyring which iterates over all supported keyring backends (#1410)
Expand Down
7 changes: 5 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,6 @@ the packages provided by the official repository of your GNU/Linux distribution.
- `x11-utils`
- `python3-pyqt6` (not from _PyPi_ via `pip`)
- `python3-dbus.mainloop.pyqt6` (not available from _PyPi_ via `pip`)
- `libnotify-bin`
- `policykit-1`
- `qttranslations6-l10n`
- `qtwayland6` (if Wayland is used as display server instead of X11)
Expand All @@ -150,7 +149,11 @@ the packages provided by the official repository of your GNU/Linux distribution.
- `gzip`
- `gettext`
- `python3-pyfakefs`
- `pylint`
- Optional but recommended:
- `pylint` (>= 3.3.0)
- `pycodestyle`
- `ruff` (>= 0.6.6)
- `codespell`

## Build and install via `make` system (recommended)

Expand Down
4 changes: 2 additions & 2 deletions FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
* [What happens if I hibernate the computer while a backup is running?](#what-happens-if-i-hibernate-the-computer-while-a-backup-is-running)
* [What happens if I power down the computer while a backup is running, or if a power outage happens?](#what-happens-if-i-power-down-the-computer-while-a-backup-is-running-or-if-a-power-outage-happens)
* [What happens if there is not enough disk space for the current backup?](#what-happens-if-there-is-not-enough-disk-space-for-the-current-backup)
* [NTFS Compatability](#ntfs-compatability)
* [NTFS Compatibility](#ntfs-compatibility)
- [user-callback and other PlugIns](#user-callback-and-other-plugins)
* [How to backup Debian/Ubuntu Package selection?](#how-to-backup-debianubuntu-package-selection)
* [How to restore Debian/Ubuntu Package selection?](#how-to-restore-debianubuntu-package-selection)
Expand Down Expand Up @@ -588,7 +588,7 @@ snapshot will be kept and marked ``With Errors`` or it will be removed.
By default, *Back In Time* will finally remove the oldest snapshots until there is
more than 1 GiB free space again.

## NTFS Compatability
## NTFS Compatibility
Although devices formatted with the NTFS file system can generally be used with *Back In Time*, there are some limitations to be aware of.

NTFS File systems do not support the following characters in filenames or directories:
Expand Down
2 changes: 1 addition & 1 deletion common/doc-dev/2_localization.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ correct = _("Hello 'World'")
problematic = _('One\nTwo')`
correct = _('One') + '\n' + _('Two') # <- Separation into multiple strings is
# no problem, because the translator
# whill have a screenshot.
# will have a screenshot.

# Provide meaningful placeholder names
problematic = _('Can not delete {var}.')
Expand Down
60 changes: 23 additions & 37 deletions common/pluginmanager.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,13 @@
# Back In Time
# Copyright (C) 2008-2022 Oprea Dan, Bart de Koning, Richard Bailey, Germar Reitze
# SPDX-FileCopyrightText: © 2008-2022 Oprea Dan
# SPDX-FileCopyrightText: © 2008-2022 Bart de Koning
# SPDX-FileCopyrightText: © 2008-2022 Richard Bailey
# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# SPDX-License-Identifier: GPL-2.0-or-later
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.


# This file is part of the program "Back In Time" which is released under GNU
# General Public License v2 (GPLv2). See file/folder LICENSE or go to
# <https://spdx.org/licenses/GPL-2.0-or-later.html>.
import os
import sys
import tools
Expand All @@ -28,6 +20,7 @@
import logger
from exceptions import StopException


class Plugin:
""" Interface methods to customize behavior for different backup steps

Expand All @@ -43,8 +36,6 @@ class that inherits from this one and implement all

Plugins are loaded by calling :py:func:`PluginManager.load`.
"""
def __init__(self):
return

def init(self, snapshots):
return True
Expand Down Expand Up @@ -128,21 +119,17 @@ def newSnapshot(self, snapshot_id, snapshot_path):
return

def message(self, profile_id, profile_name, level, message, timeout):
""" Called to send snapshot-related messages to plugins
"""Send snapshot-related messages to plugins.

Args:
profile_id: Profile ID from configuration
profile_name: Profile name from the configuration
level: 0 = INFO, 1 = ERROR
message: Message text
timeout: Requested timeout in seconds to process
the message. Not used at the moment.
(default -1 means "no timeout")

Returns:
``None`` (return value will be ignored anyhow)
profile_id: Profile ID from configuration.
profile_name: Profile name from the configuration.
level: 0 = INFO, 1 = ERROR
message: Message text.
timeout: Requested timeout in seconds to process the message.
Not used at the moment. (default -1 means "no timeout")
"""
return
pass

def appStart(self):
""" Called when the GUI of Back In Time was started.
Expand Down Expand Up @@ -184,6 +171,7 @@ def unmount(self, profileID = None):
"""
return


class PluginManager:
""" Central interface for loading plugins and calling their API

Expand All @@ -200,13 +188,14 @@ class PluginManager:
call this plugin function for all loaded plugins.
"""
# TODO 09/28/2022: Should inherit from + implement class "Plugin"

def __init__(self):
self.plugins = []
self.hasGuiPlugins = False
self.loaded = False

def load(self, snapshots = None, cfg = None, force = False):
""" Loads plugins
def load(self, snapshots=None, cfg=None, force=False):
"""Loads plugins

Loads all plugins from python source code files that are stored
in one of these plugin sub folders in the installation
Expand All @@ -220,11 +209,8 @@ def load(self, snapshots = None, cfg = None, force = False):
Args:
snapshots (snapshots.Snapshots): Snapshot info
cfg (config.Config): Current configuration
force (bool): ``True`` to enforce reloading all plugins
(``False`` does only load if not already done)

Returns:
``None``
force (bool): ``True`` to enforce reloading all plugins (``False``
does only load if not already done)
"""
if self.loaded and not force:
return
Expand Down
15 changes: 10 additions & 5 deletions common/test/test_lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,11 @@
PYCODESTYLE_AVAILABLE,
))

# "common" directory
_base_dir = pathlib.Path(__file__).resolve().parent.parent

# Files in this lists will get the full battery of linters and rule sets.
full_test_files = [pathlib.Path(fp) for fp in (
full_test_files = [_base_dir / fp for fp in (
'bitbase.py',
'schedule.py',
'version.py',
Expand Down Expand Up @@ -136,9 +139,12 @@ def test010_ruff_default_ruleset(self):
cmd = [
'ruff',
'check',
# Additionally activate subset of PyLint (PL)
# and PyCodestyle (E, W) rules
'--extend-select=PL,E,W',
# Additionally activate subset of sepcial rules:
# - PyLint (PL)
# - PyCodestyle (E, W)
# - flake8-gettext (INT)
# - useless noqua (RUF100)
'--extend-select=PL,E,W,INT,RUF100',
# Ignore: redefined-loop-name
'--ignore=PLW2901',
'--line-length', str(PEP8_MAX_LINE_LENGTH),
Expand Down Expand Up @@ -248,7 +254,6 @@ def test050_pylint_exclusive_ruleset(self):
'W1515', # forgotten-debug-statement
'W4902', # deprecated-method
'W4904', # deprecated-class
'R0201', # no-self-use
'R0202', # no-classmethod-decorator
'R0203', # no-staticmethod-decorator
'R0801', # duplicate-code
Expand Down
4 changes: 2 additions & 2 deletions qt/aboutdlg.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
# SPDX-License-Identifier: GPL-2.0-or-later
#
# This file is part of the program "Back In Time" which is released under GNU
# General Public License v2 (GPLv2).
# See file LICENSE or go to <https://www.gnu.org/licenses/#GPL>.
# General Public License v2 (GPLv2). See file/folder LICENSE or go to
# <https://spdx.org/licenses/GPL-2.0-or-later.html>.
"""The About dialog."""
import re
import pathlib
Expand Down
113 changes: 62 additions & 51 deletions qt/plugins/notifyplugin.py
Original file line number Diff line number Diff line change
@@ -1,62 +1,73 @@
# Back In Time
# Copyright (C) 2008-2022 Oprea Dan, Bart de Koning, Richard Bailey, Germar Reitze
# SPDX-FileCopyrightText: © 2008-2022 Oprea Dan
# SPDX-FileCopyrightText: © 2008-2022 Bart de Koning
# SPDX-FileCopyrightText: © 2008-2022 Richard Bailey
# SPDX-FileCopyrightText: © 2008-2022 Germar Reitze
# SPDX-FileCopyrightText: © 2021 Felix Stupp
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# SPDX-License-Identifier: GPL-2.0-or-later
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.


import os
# This file is part of the program "Back In Time" which is released under GNU
# General Public License v2 (GPLv2). See file/folder LICENSE or go to
# <https://spdx.org/licenses/GPL-2.0-or-later.html>.
"""Notify plugin module"""
import getpass
import dbus
import pluginmanager
import subprocess
import logger


class NotifyPlugin(pluginmanager.Plugin):
def __init__(self):
self.user = ''
"""Plugin used to create notification bubbles in systray.

try:
self.user = os.getlogin()
except:
pass

if not self.user:
try:
self.user = os.environ['USER']
except:
pass

if not self.user:
try:
self.user = os.environ['LOGNAME']
except:
pass
The plugin use DBUS to send notifications. See its base class for more
details.
"""

def isGui(self):
return True

def message(self, profile_id, profile_name, level, message, timeout):
if 1 == level:
cmd = ['notify-send']
if timeout > 0:
cmd.extend(['-t', str(1000 * timeout)])

title = "Back In Time (%s) : %s" % (self.user, profile_name)
message = message.replace("\n", ' ')
message = message.replace("\r", '')

cmd.append(title)
cmd.append(message)
print(' '.join(cmd))
subprocess.Popen(cmd).communicate()
return
# pylint: disable-next=too-many-arguments,too-many-positional-arguments
def message(self,
profile_id,
profile_name,
level,
message,
timeout):
# 1 is ERROR, 0 is INFO
if level != 1:
# Dev note (2024-10, buhtz):
# Message with level 0/INFO for example generatet by
# setTakeSnapshotMessage()
# Not clear to me why the notify plugin should only process
# errors.
return

try:
notify_interface = dbus.Interface(
object=dbus.SessionBus().get_object(
"org.freedesktop.Notifications",
"/org/freedesktop/Notifications"),
dbus_interface="org.freedesktop.Notifications"
)

except dbus.exceptions.DBusException as exc:
logger.error('Unexpected DBusException while initiating '
f'dbus.Interface(): {exc}')
return

if timeout > 0:
timeout = 1000 * timeout
else:
# let timeout default to notification server settings
timeout = -1

title = f'Back In Time ({getpass.getuser()}) : {profile_name}'
message = message.replace('\n', ' ')
message = message.replace('\r', '')

try:
notify_interface.Notify(
'Back In Time', 0, '', title, message, [], {}, timeout)

except dbus.exceptions.DBusException as exc:
logger.error(f'Unexpected DBusException while Notify(): {exc}')
16 changes: 11 additions & 5 deletions qt/test/test_lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,15 @@
PYCODESTYLE_AVAILABLE,
))

# "qt" directory
_base_dir = pathlib.Path(__file__).resolve().parent.parent

# Files in this lists will get the full battery of linters and rule sets.
full_test_files = [pathlib.Path(fp) for fp in (
full_test_files = [_base_dir / fp for fp in (
'aboutdlg.py',
'combobox.py',
'encfsmsgbox.py',
'plugins/notifyplugin.py',
'test/test_lint.py',
)]

Expand Down Expand Up @@ -134,9 +138,12 @@ def test010_ruff_default_ruleset(self):
cmd = [
'ruff',
'check',
# Additionally activate subset of PyLint (PL)
# and PyCodestyle (E, W) rules
'--extend-select=PL,E,W',
# Additionally activate subset of sepcial rules:
# - PyLint (PL)
# - PyCodestyle (E, W)
# - flake8-gettext (INT)
# - useless noqua (RUF100)
'--extend-select=PL,E,W,INT,RUF100',
# Ignore: redefined-loop-name
'--ignore=PLW2901',
'--line-length', str(PEP8_MAX_LINE_LENGTH),
Expand Down Expand Up @@ -243,7 +250,6 @@ def test050_pylint_exclusive_ruleset(self):
# Enable asap. This list is selection of existing (not all!)
# problems currently exiting in the BIT code base. Quit easy to fix
# because there count is low.
# 'R0201', # no-self-use
# 'R0202', # no-classmethod-decorator
# 'R0203', # no-staticmethod-decorator
'R0801', # duplicate-code
Expand Down