Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-py3status for openSUSE:Factory checked in at 2023-06-30 20:00:08 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-py3status (Old) and /work/SRC/openSUSE:Factory/.python-py3status.new.13546 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "python-py3status" Fri Jun 30 20:00:08 2023 rev:12 rq:1096164 version:3.51 Changes: -------- --- /work/SRC/openSUSE:Factory/python-py3status/python-py3status.changes 2023-04-19 17:44:12.300621601 +0200 +++ /work/SRC/openSUSE:Factory/.python-py3status.new.13546/python-py3status.changes 2023-06-30 20:00:33.242243650 +0200 @@ -1,0 +2,31 @@ +Fri Jun 30 15:06:00 UTC 2023 - Matej Cepl <mcepl@suse.com> + +- Clean up SPEC file. + +------------------------------------------------------------------- +Fri Jun 30 14:31:54 UTC 2023 - Dawid Adam <nyslay@gmail.com> +- Update to 3.51: + * NEW: thanks to Andreas Grapentin, py3status can now run in lots of other containers (tmux, term, dzen2, lemonbar...) + * IMPORTANT: modules are moving away from the obsolete pydbus library + * core: implement handling of output_format in py3status for i3bar, dzen2, xmobar, lemonbar, tmux, term, none (#2104), by Andreas Grapentin + * core: autodetect output_format (#2202), by lasers + * fix(module): report module post_config_hook errors thx to @lasers (#2208) + * docs(user): add a section to specify that modules dependencies are up to the users + * docs(mkdocs): drop mkdocs-simple-hooks closes #2195 + * docs: fix two DeprecationWarning (#2191), by lasers + * docs(audiosink): fix screenshots (#2194), by lasers + * docs(autodoc): remove dead code thx to @lasers, closes #2183 (#2193) + * arch_updates module: add support for pikaur (#2182), by vim + * arch_updates module: remove cower support (gone) (#2190), by lasers + * backlight module: drop pydbus, switch to dbus-python + * bluetooth module: report battery percentage if available (#2185), by Alex Tsitsimpis + * clock module: added timezone information to times implicitly using local system timezone (#2197), by Andreas Grapentin + * clock module: minor patch to get the local timezone. (#2189), by Josh Sixsmith + * diskdata module: fix variable init type + * diskdata module: make module work on FreeBSD (#2200), by Bj��rn Busse + * mpd_status module: fix UnboundLocalError (#2199), by Bj��rn Busse + * ns_checker module: replace query() with resolve() (#2207), by Bj��rn Busse + * systemd module: drop pydbus, switch to dbus-python + * vpn_status module: drop pydbus, switch to dbus-python + +------------------------------------------------------------------- Old: ---- py3status-3.50.tar.gz New: ---- py3status-3.51.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-py3status.spec ++++++ --- /var/tmp/diff_new_pack.gAOaqe/_old 2023-06-30 20:00:33.922247695 +0200 +++ /var/tmp/diff_new_pack.gAOaqe/_new 2023-06-30 20:00:33.930247743 +0200 @@ -17,24 +17,25 @@ %define skip_python2 1 -%{?!python_module:%define python_module() python-%{**} python3-%{**}} Name: python-py3status -Version: 3.50 +Version: 3.51 Release: 0 Summary: Python extensible i3status wrapper License: BSD-3-Clause URL: https://github.com/ultrabug/py3status Source: https://files.pythonhosted.org/packages/source/p/py3status/py3status-%{version}.tar.gz BuildRequires: %{python_module gevent >= 1.1} +BuildRequires: %{python_module pip} BuildRequires: %{python_module pytest} BuildRequires: %{python_module pyudev >= 0.21.0} -BuildRequires: %{python_module setuptools} +BuildRequires: %{python_module wheel} BuildRequires: fdupes BuildRequires: python-rpm-macros Requires: python-setuptools Requires(post): update-alternatives Requires(postun):update-alternatives Recommends: i3status +Recommends: python-dbus-python Recommends: python-gevent >= 1.1 Recommends: python-pyudev >= 0.21.0 Provides: py3status = %{version} @@ -59,10 +60,10 @@ %setup -q -n py3status-%{version} %build -%python_build +%pyproject_wheel %install -%python_install +%pyproject_install %python_clone -a %{buildroot}%{_bindir}/py3status %python_clone -a %{buildroot}%{_bindir}/py3-cmd %python_expand %fdupes %{buildroot}%{$python_sitelib} @@ -84,6 +85,7 @@ %doc CHANGELOG README.md %python_alternative %{_bindir}/py3status %python_alternative %{_bindir}/py3-cmd -%{python_sitelib}/py3status* +%{python_sitelib}/py3status +%{python_sitelib}/py3status-%{version}*-info %changelog ++++++ py3status-3.50.tar.gz -> py3status-3.51.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/py3status-3.50/CHANGELOG new/py3status-3.51/CHANGELOG --- old/py3status-3.50/CHANGELOG 2023-04-17 18:42:48.000000000 +0200 +++ new/py3status-3.51/CHANGELOG 2023-06-27 09:27:54.000000000 +0200 @@ -1,3 +1,27 @@ +version 3.51 (2023-06-27) +* NEW: thanks to Andreas Grapentin, py3status can now run in lots of other containers (tmux, term, dzen2, lemonbar...) +* IMPORTANT: modules are moving away from the obsolete pydbus library +* core: implement handling of output_format in py3status for i3bar, dzen2, xmobar, lemonbar, tmux, term, none (#2104), by Andreas Grapentin +* core: autodetect output_format (#2202), by lasers +* fix(module): report module post_config_hook errors thx to @lasers (#2208) +* docs(user): add a section to specify that modules dependencies are up to the users +* docs(mkdocs): drop mkdocs-simple-hooks closes #2195 +* docs: fix two DeprecationWarning (#2191), by lasers +* docs(audiosink): fix screenshots (#2194), by lasers +* docs(autodoc): remove dead code thx to @lasers, closes #2183 (#2193) +* arch_updates module: add support for pikaur (#2182), by vim +* arch_updates module: remove cower support (gone) (#2190), by lasers +* backlight module: drop pydbus, switch to dbus-python +* bluetooth module: report battery percentage if available (#2185), by Alex Tsitsimpis +* clock module: added timezone information to times implicitly using local system timezone (#2197), by Andreas Grapentin +* clock module: minor patch to get the local timezone. (#2189), by Josh Sixsmith +* diskdata module: fix variable init type +* diskdata module: make module work on FreeBSD (#2200), by Bj��rn Busse +* mpd_status module: fix UnboundLocalError (#2199), by Bj��rn Busse +* ns_checker module: replace query() with resolve() (#2207), by Bj��rn Busse +* systemd module: drop pydbus, switch to dbus-python +* vpn_status module: drop pydbus, switch to dbus-python + version 3.50 (2023-04-17) * update alpine install doc (#2176), by raspbeguy * battery_level module: fix invalid indexing, skip batteries with unavailable info (#2180), by lasers diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/py3status-3.50/PKG-INFO new/py3status-3.51/PKG-INFO --- old/py3status-3.50/PKG-INFO 2023-04-17 18:44:18.737956300 +0200 +++ new/py3status-3.51/PKG-INFO 2023-06-27 10:33:46.732813100 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: py3status -Version: 3.50 +Version: 3.51 Summary: py3status: an extensible i3status wrapper written in python Home-page: https://github.com/ultrabug/py3status Author: Ultrabug diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/py3status-3.50/py3status/autodoc.py new/py3status-3.51/py3status/autodoc.py --- old/py3status-3.50/py3status/autodoc.py 2021-08-30 10:15:50.000000000 +0200 +++ new/py3status-3.51/py3status/autodoc.py 2023-06-18 11:40:47.000000000 +0200 @@ -1,117 +1,9 @@ -import ast -import inspect import re from pathlib import Path -from docutils import nodes -from docutils.parsers.rst import Directive -from pygments.lexer import RegexLexer, bygroups -import pygments.token as pygments_token - from py3status.docstrings import core_module_docstrings -from py3status.screenshots import create_screenshots, get_samples, process -from py3status.py3 import Py3 - - -# some Py3 methods have constants as defaults we need to identify them here -CONSTANT_PARAMS = [("log", "level"), ("notify_user", "level")] - - -class Py3statusLexer(RegexLexer): - """ - A simple lexer for py3status configuration files. - This helps make the documentation more beautiful - """ - - name = "Py3status" - aliases = ["py3status"] - filenames = ["*.conf"] - - tokens = { - "root": [ - (r"#.*?$", pygments_token.Comment), # comments - ( # double quoted strings - r'"(?:[^"\\]|\\.)*"', - pygments_token.String.Double, - ), - ( # single quoted strings - r"'(?:[^'\\]|\\.)*'", - pygments_token.String.Single, - ), - (r"([0-9]+)|([0-9]*)\.([0-9]*)", pygments_token.Number), # numbers - (r"[Tt]rue|[Ff]alse|[Nn]one", pygments_token.Literal), # True, False & None - (r"(\+=)|=", pygments_token.Operator), # = and += - (r"[{}\[\](),:]", pygments_token.Punctuation), # other things like (){}[],: - ( # config functions eg env(value, type) - r"(\S+)([(])(([^)\\]|\\.)*)((\s*,\s*)(\w+))?([)])", - bygroups( - pygments_token.Name.Function, - pygments_token.Punctuation, - pygments_token.Literal, - None, - None, - pygments_token.Punctuation, - pygments_token.Keyword.Type, - pygments_token.Punctuation, - ), - ), - ( # module names - r"(\S+)(\s*)([^=]*)(\s*)(\{)", - bygroups( - pygments_token.Keyword.Reserved, - pygments_token.Whitespace, - pygments_token.Keyword.Reserved, - pygments_token.Whitespace, - pygments_token.Punctuation, - ), - ), - ( # order += .... - r"^(order)(\s+)(\+=)", - bygroups( - pygments_token.Keyword.Reserved, - pygments_token.Whitespace, - pygments_token.Punctuation, - ), - ), - (r"on_click\s*\d", pygments_token.Name.Variable), # on_click x - ( # module parameters - r"(\w+)((:)(\S+))?", - bygroups( - pygments_token.Name.Variable, - None, - pygments_token.Punctuation, - pygments_token.Keyword.Type, - ), - ), - (r"\s+", pygments_token.Whitespace), # whitespace - ] - } - - -def markdown_2_rst(lines): - """ - Convert markdown to restructured text - """ - out = [] - code = False - for line in lines: - # code blocks - if line.strip() == "```": - code = not code - space = " " * (len(line.rstrip()) - 3) - if code: - out.append(f"\n\n{space}.. code-block:: none\n\n") - else: - out.append("\n") - else: - if code and line.strip(): - line = " " + line - else: - # escape any backslashes - line = line.replace("\\", "\\\\") - out.append(line) - return out +from py3status.screenshots import create_screenshots, get_samples def file_sort(my_list): @@ -177,184 +69,10 @@ return config -def get_variable_docstrings(filename): - """ - Go through the file and find all documented variables. - That is ones that have a literal expression following them. - - Also get a dict of assigned values so that we can substitute constants. - """ - - def walk_node(parent, values=None, prefix=""): - """ - walk the ast searching for docstrings/values - """ - docstrings = {} - if values is None: - values = {} - key = None - for node in ast.iter_child_nodes(parent): - if isinstance(node, ast.ClassDef): - # We are in a class so walk the class - docs = walk_node(node, values, prefix + node.name + ".")[0] - docstrings[node.name] = docs - elif isinstance(node, ast.Assign): - key = node.targets[0].id - if isinstance(node.value, ast.Num): - values[key] = node.value.n - if isinstance(node.value, ast.Str): - values[key] = node.value.s - if isinstance(node.value, ast.Name): - if node.value.id in values: - values[prefix + key] = values[node.value.id] - elif isinstance(node, ast.Expr) and key: - docstrings[key] = node.value.s - else: - key = None - return docstrings, values - - return walk_node(ast.parse(filename.read_text())) - - -def get_py3_info(): - """ - Inspect Py3 class and get constants, exceptions, methods - along with their docstrings. - """ - # get all documented constants and their values - constants, values = get_variable_docstrings(Path("../py3status/py3.py")) - # we only care about ones defined in Py3 - constants = constants["Py3"] - # sort them alphabetically - constants = sorted(constants.items()) - - # filter values as we only care about values defined in Py3 - values = {v: k[4:] for k, v in values.items() if k.startswith("Py3.")} - - def make_value(attr, arg, default): - """ - If the methods parameter is defined as a constant then do a - replacement. Otherwise return the values representation. - """ - if (attr, arg) in CONSTANT_PARAMS and default in values: - return values[default] - return repr(default) - - # inspect Py3 to find it's methods etc - py3 = Py3() - # no private ones - attrs = [x for x in dir(py3) if not x.startswith("_")] - exceptions = [] - methods = [] - for attr in attrs: - item = getattr(py3, attr) - if "method" in str(item): - # a method so we need to get the call parameters - args, vargs, kw, defaults = inspect.getargspec(item) - args = args[1:] - len_defaults = len(defaults) if defaults else 0 - len_args = len(args) - - sig = [] - for index, arg in enumerate(args): - # default values set? - if len_args - index <= len_defaults: - default = defaults[len_defaults - len_args + index] - sig.append("{}={}".format(arg, make_value(attr, arg, default))) - else: - sig.append(arg) - - definition = "{}({})".format(attr, ", ".join(sig)) - methods.append((definition, item.__doc__)) - continue - try: - # find any exceptions - if isinstance(item(), Exception): - exceptions.append((attr, item.__doc__)) - continue - except: # noqa e722 - pass - return {"methods": methods, "exceptions": exceptions, "constants": constants} - - -def auto_undent(string): - """ - Unindent a docstring. - """ - lines = string.splitlines() - while lines[0].strip() == "": - lines = lines[1:] - if not lines: - return [] - spaces = len(lines[0]) - len(lines[0].lstrip(" ")) - out = [] - for line in lines: - num_spaces = len(line) - len(line.lstrip(" ")) - out.append(line[min(spaces, num_spaces) :]) - return out - - -def create_py3_docs(): - """ - Create the include files for py3 documentation. - """ - # we want the correct .rst 'type' for our data - trans = {"methods": "function", "exceptions": "exception", "constants": "attribute"} - data = get_py3_info() - for k, v in data.items(): - output = [] - for name, desc in v: - output.append("") - output.append(f".. _{name}:") # reference for linking - output.append("") - output.append(f".. py:{trans[k]}:: {name}") - output.append("") - output.extend(auto_undent(desc)) - Path(f"../docs/py3-{k}-info.inc").write_text("\n".join(output)) - - -def create_auto_documentation(config): +def on_config(config): """ Create any include files needed for sphinx documentation """ create_screenshots(config) create_module_docs(config) return config - - -class ScreenshotDirective(Directive): - """ - Adds the ability to add screenshots dynamically in sphinx documentation - - .. screenshot:: - - {'color': '#00FF00', 'full_text': 'Example output'} - - """ - - has_content = True - - def run(self): - env = self.state.document.settings.env - - targetid = f"screenshot-{env.new_serialno('screenshot')}" - targetnode = nodes.target("", "", ids=[targetid]) - - image_name = f"_{targetid}" - try: - content = ast.literal_eval("\n".join(self.content)) - except: # noqa e722 - content = { - "color": "#990000", - "background": "#FFFF00", - "full_text": " IMAGE DATA ERROR ", - } - - process(image_name, content, False) - image_path = Path("screenshots") / (image_name + ".png") - screenshot_node = nodes.image(uri=image_path) - return [targetnode, screenshot_node] - - -if __name__ == "__main__": - create_auto_documentation() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/py3status-3.50/py3status/constants.py new/py3status-3.51/py3status/constants.py --- old/py3status-3.50/py3status/constants.py 2022-10-02 17:24:36.000000000 +0200 +++ new/py3status-3.51/py3status/constants.py 2023-06-18 14:26:19.000000000 +0200 @@ -7,7 +7,7 @@ "color_separator": "#333333", "colors": True, "interval": 5, - "output_format": "i3bar", + "output_format": None, } MAX_NESTING_LEVELS = 4 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/py3status-3.50/py3status/core.py new/py3status-3.51/py3status/core.py --- old/py3status-3.50/py3status/core.py 2022-10-27 12:10:18.000000000 +0200 +++ new/py3status-3.51/py3status/core.py 2023-06-18 14:26:19.000000000 +0200 @@ -3,7 +3,6 @@ import time from collections import deque -from json import dumps from pathlib import Path from pprint import pformat from signal import signal, Signals, SIGTERM, SIGUSR1, SIGTSTP, SIGCONT @@ -19,6 +18,7 @@ from py3status.i3status import I3status from py3status.parse_config import process_config from py3status.module import Module +from py3status.output import OutputFormat from py3status.profiling import profile from py3status.udev_monitor import UdevMonitor @@ -613,6 +613,16 @@ self.log("config file: {}".format(self.config["i3status_config_path"])) self.config["py3_config"] = process_config(config_path, self) + # autodetect output_format + output_format = self.config["py3_config"]["general"]["output_format"] + if output_format is None: + if sys.stdout.isatty(): + print("py3status: trying to auto-detect output_format setting") + print('py3status: auto-detected "term"') + output_format = "term" + + self.config["py3_config"]["general"]["output_format"] = output_format or "i3bar" + # read resources if "resources" in str(self.config["py3_config"].values()): from subprocess import check_output @@ -722,6 +732,20 @@ # load and spawn i3status.conf configured modules threads self.load_modules(self.py3_modules, user_modules) + # determine the target output format + self.output_format = OutputFormat.instance_for( + self.config["py3_config"]["general"]["output_format"] + ) + + # determine the output separator, if needed + color_separator = None + if self.config["py3_config"]["general"]["colors"]: + color_separator = self.config["py3_config"]["general"]["color_separator"] + self.output_format.format_separator( + self.config["py3_config"]["general"].get("separator", None), + color_separator, + ) + def notify_user( self, msg, @@ -1015,8 +1039,8 @@ # Color: substitute the config defined color if "color" not in output: output["color"] = color - # Create the json string output. - return ",".join(dumps(x) for x in outputs) + # format output and return + return self.output_format.format(outputs) def i3bar_stop(self, signum, frame): if ( @@ -1089,17 +1113,13 @@ # items in the bar output = [None] * len(py3_config["order"]) - write = sys.__stdout__.write - flush = sys.__stdout__.flush - # start our output header = { "version": 1, "click_events": self.config["click_events"], "stop_signal": self.stop_signal or 0, } - write(dumps(header)) - write("\n[[]\n") + self.output_format.write_header(header) update_due = None # main loop @@ -1126,8 +1146,5 @@ # store the output as json output[index] = out - # build output string - out = ",".join(x for x in output if x) - # dump the line to stdout - write(f",[{out}]\n") - flush() + # build output string and dump to stdout + self.output_format.write_line(output) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/py3status-3.50/py3status/i3status.py new/py3status-3.51/py3status/i3status.py --- old/py3status-3.50/py3status/i3status.py 2022-10-02 20:12:35.000000000 +0200 +++ new/py3status-3.51/py3status/i3status.py 2023-06-18 11:22:17.000000000 +0200 @@ -330,6 +330,12 @@ value = TZTIME_FORMAT if key == "format_time": continue + # Set output_format to i3bar in general section so that we + # receive predictable output from i3status, regardless of our + # own output_format configuration + if section_name == "general": + if key == "output_format": + value = "i3bar" if isinstance(value, bool): value = f"{value}".lower() self.write_in_tmpfile(f' {key} = "{value}"\n', tmpfile) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/py3status-3.50/py3status/module.py new/py3status-3.51/py3status/module.py --- old/py3status-3.50/py3status/module.py 2023-01-09 18:39:16.000000000 +0100 +++ new/py3status-3.51/py3status/module.py 2023-06-27 09:04:06.000000000 +0200 @@ -150,12 +150,10 @@ self.error_messages = [ self.module_nice_name, - "{}: {}".format( - self.module_nice_name, str(e) or e.__class__.__name__ - ), + f"{e}", ] - self.runtime_error(self.error_messages[0], "post_config_hook") - msg = f"Exception in `{self.module_full_name}` post_config_hook()" + self.runtime_error(self.error_messages[1], "post_config_hook") + msg = f"Exception in `{self.module_full_name}` post_config_hook() : {self.error_messages}" self._py3_wrapper.report_exception(msg, notify_user=False) self._py3_wrapper.log(f"terminating module {self.module_full_name}") self.enabled = True @@ -175,7 +173,7 @@ # only show first line of error msg = msg.splitlines()[0] - errors = [self.module_nice_name, f"{self.module_nice_name}: {msg}"] + errors = [self.module_nice_name, f"{self.module_nice_name}[{method}]: {msg}"] # if we have shown this error then keep in the same state if self.error_messages != errors: @@ -202,7 +200,11 @@ "name": self.module_name, } for method in self.methods.values(): - if method_affected and method["method"] != method_affected: + if ( + method_affected + and method["method"] != method_affected + and method_affected != "post_config_hook" + ): continue method["last_output"] = [error] @@ -338,11 +340,17 @@ separator = fn(self.module_full_name, "separator") if not hasattr(separator, "none_setting"): - if not isinstance(separator, bool): - err = "Invalid `separator` attribute, should be a boolean. " - err += f"Got `{separator}`." - raise TypeError(err) - self.i3bar_module_options["separator"] = separator + # HACK: separator is a valid setting in the general section + # of the configuration. but it's a string, not a boolean. + # revisit how i3status and py3status differ in this regard. + # if not isinstance(separator, bool): + + # err = "Invalid `separator` attribute, should be a boolean. " + # err += f"Got `{separator}`." + # raise TypeError(err) + # self.i3bar_module_options["separator"] = separator + if isinstance(separator, bool): + self.i3bar_module_options["separator"] = separator separator_block_width = fn(self.module_full_name, "separator_block_width") if not hasattr(separator_block_width, "none_setting"): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/py3status-3.50/py3status/modules/arch_updates.py new/py3status-3.51/py3status/modules/arch_updates.py --- old/py3status-3.50/py3status/modules/arch_updates.py 2022-10-02 16:25:48.000000000 +0200 +++ new/py3status-3.51/py3status/modules/arch_updates.py 2023-06-05 13:45:55.000000000 +0200 @@ -17,6 +17,7 @@ trizen: lightweight pacman wrapper and AUR helper yay: yet another yogurt. pacman wrapper and aur helper written in go paru: feature packed AUR helper + pikaur: pacman wrapper and AUR helper written in python @author Iain Tatch <iain.tatch@gmail.com> @license BSD @@ -46,7 +47,7 @@ helper = { "pacman": self.py3.check_commands(["checkupdates"]), "aur": self.py3.check_commands( - ["auracle", "trizen", "yay", "cower", "paru"] + ["auracle", "trizen", "yay", "paru", "pikaur"] ), } if self.format: @@ -88,13 +89,6 @@ except self.py3.CommandError as ce: return None if ce.error else 0 - def _get_cower_updates(self): - try: - self.py3.command_output(["cower", "-u"]) - return None - except self.py3.CommandError as ce: - return len(ce.output.splitlines()) - def _get_trizen_updates(self): try: updates = self.py3.command_output(["trizen", "-Suaq"]) @@ -115,6 +109,13 @@ return len(updates.splitlines()) except self.py3.CommandError as ce: return None if ce.error else 0 + + def _get_pikaur_updates(self): + try: + updates = self.py3.command_output(["pikaur", "-Qua"]) + return len(updates.splitlines()) + except self.py3.CommandError as ce: + return None if ce.error else 0 def arch_updates(self): pacman, aur, total, full_text = None, None, None, "" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/py3status-3.50/py3status/modules/audiosink.py new/py3status-3.51/py3status/modules/audiosink.py --- old/py3status-3.50/py3status/modules/audiosink.py 2022-08-25 16:38:50.000000000 +0200 +++ new/py3status-3.51/py3status/modules/audiosink.py 2023-06-18 10:05:11.000000000 +0200 @@ -26,12 +26,14 @@ } ``` +@author Jens Brandt <py3status@brandt-george.de> +@license BSD + SAMPLE OUTPUT {'full_text': 'Dock'} -{'full_text': 'Int'} -@author Jens Brandt <py3status@brandt-george.de> -@license BSD +int +{'full_text': 'Int'} """ import os diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/py3status-3.50/py3status/modules/backlight.py new/py3status-3.51/py3status/modules/backlight.py --- old/py3status-3.50/py3status/modules/backlight.py 2023-02-02 16:30:54.000000000 +0100 +++ new/py3status-3.51/py3status/modules/backlight.py 2023-06-27 09:04:06.000000000 +0200 @@ -35,7 +35,7 @@ Requires: one of xbacklight: need for changing brightness, not detection light: program to easily change brightness on backlight-controllers - pydbus + logind v243: logind to change brightness without X + dbus-python + logind v243: logind to change brightness without X @author Tjaart van der Walt (github:tjaartvdwalt), J��r��my Rosen (github:boucman) @license BSD @@ -46,10 +46,7 @@ from pathlib import Path -try: - from pydbus import SystemBus -except ImportError: - pass +import dbus STRING_NOT_AVAILABLE = "no available device" @@ -103,9 +100,9 @@ def post_config_hook(self): try: - self._logind_proxy = SystemBus().get( - bus_name="org.freedesktop.login1", - object_path="/org/freedesktop/login1/session/self", + bus = dbus.SystemBus() + self._logind_proxy = bus.get_object( + "org.freedesktop.login1", "/org/freedesktop/login1/session/self" ) except NameError: self._logind_proxy = None diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/py3status-3.50/py3status/modules/bluetooth.py new/py3status-3.51/py3status/modules/bluetooth.py --- old/py3status-3.50/py3status/modules/bluetooth.py 2023-01-10 13:36:52.000000000 +0100 +++ new/py3status-3.51/py3status/modules/bluetooth.py 2023-05-30 19:01:27.000000000 +0200 @@ -39,6 +39,7 @@ {address} eg, 00:00:00:00:00:00 {addresstype} eg, public {alias} eg, MSFT Mouse + {battery} eg, 95 {class} eg, 1234 {connected} eg, False {icon} eg, input-mouse @@ -130,7 +131,8 @@ for name, match in self.names_and_matches: if match in interface_keys: interface = {k.lower(): v for k, v in interfaces[match].items()} - interface.update({"path": path, "uuids": []}) + battery = interfaces.get("org.bluez.Battery1", {}).get("Percentage") + interface.update({"path": path, "uuids": [], "battery": battery}) temporary.setdefault(name, []).append(interface) break diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/py3status-3.50/py3status/modules/clock.py new/py3status-3.51/py3status/modules/clock.py --- old/py3status-3.50/py3status/modules/clock.py 2022-10-27 10:35:49.000000000 +0200 +++ new/py3status-3.51/py3status/modules/clock.py 2023-06-18 11:23:36.000000000 +0200 @@ -189,10 +189,7 @@ """ # special Local timezone if tz == "Local": - try: - return zoneinfo.ZoneInfo("localtime") - except zoneinfo.ZoneInfoNotFoundError: - return "?" + return None # get the timezone try: zone = zoneinfo.ZoneInfo(tz) @@ -251,7 +248,12 @@ idx = int(h / self.block_hours * len(self.blocks)) icon = self.blocks[idx] - timezone = zone.key + # special case for handling Local timezone + if zone is None: + t = t.astimezone() + timezone = t.tzname() + else: + timezone = zone.key tzname = timezone.split("/")[-1].replace("_", " ") if self.multiple_tz: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/py3status-3.50/py3status/modules/diskdata.py new/py3status-3.51/py3status/modules/diskdata.py --- old/py3status-3.50/py3status/modules/diskdata.py 2022-10-02 16:25:48.000000000 +0200 +++ new/py3status-3.51/py3status/modules/diskdata.py 2023-06-18 11:28:19.000000000 +0200 @@ -6,7 +6,7 @@ disk: show stats for disk or partition, i.e. `sda1`. None for all disks. (default None) format: display format for this module. - (default "{disk}: {used_percent}% ({total})") + (default "{disk}: {used_percent}%[ ({total})]") format_rate: display format for rates value (default "[\?min_length=11 {value:.1f} {unit}]") format_space: display format for disk space values @@ -65,7 +65,7 @@ # available configuration parameters cache_timeout = 10 disk = None - format = "{disk}: {used_percent}% ({total})" + format = "{disk}: {used_percent}%[ ({total})]" format_rate = r"[\?min_length=11 {value:.1f} {unit}]" format_space = r"[\?min_length=5 {value:.1f}]" sector_size = 512 @@ -90,8 +90,11 @@ self.init[name] = {"placeholders": placeholders, "keys": match} if self.init["diskstats"]: - self.last_diskstats = self._get_diskstats(self.disk) - self.last_time = time.monotonic() + try: + self.last_diskstats = self._get_diskstats(self.disk) + self.last_time = time.monotonic() + except Exception: + self.init["diskstats"] = {} self.thresholds_init = self.py3.get_color_names_list(self.format) @@ -102,12 +105,16 @@ df_usages = ce.output total, used, free, devs = 0, 0, 0, [] - if disk and not disk.startswith("/dev/"): - disk = "/dev/" + disk + disk_dev = None + + if disk: + disk_dev = "/dev/" + disk for line in df_usages.splitlines(): - if (disk and line.startswith(disk)) or ( - disk is None and line.startswith("/dev/") + if ( + (disk and line.startswith(disk)) + or (disk_dev and line.startswith(disk_dev)) + or (disk is None and line.startswith("/dev/")) ): data = line.split() if data[0] in devs: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/py3status-3.50/py3status/modules/mpd_status.py new/py3status-3.51/py3status/modules/mpd_status.py --- old/py3status-3.50/py3status/modules/mpd_status.py 2022-10-02 16:25:48.000000000 +0200 +++ new/py3status-3.51/py3status/modules/mpd_status.py 2023-06-18 11:20:01.000000000 +0200 @@ -256,8 +256,7 @@ text = "Failed to authenticate to mpd!" self._get_mpd(disconnect=True) - state = None - self.current_status = (text, status) + self.current_status = (text, None) return finally: self.py3.update() # to propagate error message diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/py3status-3.50/py3status/modules/ns_checker.py new/py3status-3.51/py3status/modules/ns_checker.py --- old/py3status-3.50/py3status/modules/ns_checker.py 2022-10-02 16:25:48.000000000 +0200 +++ new/py3status-3.51/py3status/modules/ns_checker.py 2023-06-27 09:04:06.000000000 +0200 @@ -72,7 +72,7 @@ if self.resolvers: my_resolver.nameservers = self.resolvers - my_ns = my_resolver.query(self.domain, "NS") + my_ns = my_resolver.resolve(self.domain, "NS") # Insert each NS ip address in nameservers for ns in my_ns: @@ -84,7 +84,7 @@ for ns in nameservers: my_resolver.nameservers = [ns] try: - my_resolver.query(self.domain, "A") + my_resolver.resolve(self.domain, "A") count_ok += 1 except: # noqa e722 count_nok += 1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/py3status-3.50/py3status/modules/systemd.py new/py3status-3.51/py3status/modules/systemd.py --- old/py3status-3.50/py3status/modules/systemd.py 2022-10-02 16:25:48.000000000 +0200 +++ new/py3status-3.51/py3status/modules/systemd.py 2023-06-27 09:04:06.000000000 +0200 @@ -25,7 +25,8 @@ color_degraded: unit not-found Requires: - pydbus: pythonic dbus library + dbus-python: to interact with dbus + pygobject: which in turn requires libcairo2-dev, libgirepository1.0-dev Examples: ``` @@ -51,7 +52,7 @@ {'color': '#FFFF00', 'full_text': 'sshd.service: not-found'} """ -from pydbus import SessionBus, SystemBus +import dbus class Py3status: @@ -67,16 +68,30 @@ def post_config_hook(self): if self.user: - bus = SessionBus() + bus = dbus.SessionBus() else: - bus = SystemBus() - systemd = bus.get("org.freedesktop.systemd1") - self.systemd_unit = bus.get(".systemd1", systemd.LoadUnit(self.unit)) + bus = dbus.SystemBus() + systemd = bus.get_object( + "org.freedesktop.systemd1", "/org/freedesktop/systemd1" + ) + systemd_unit = systemd.LoadUnit( + self.unit, dbus_interface="org.freedesktop.systemd1.Manager" + ) + unit_proxy = bus.get_object("org.freedesktop.systemd1", systemd_unit) + self.systemd_interface = dbus.Interface( + unit_proxy, "org.freedesktop.DBus.Properties" + ) def systemd(self): - status = self.systemd_unit.Get("org.freedesktop.systemd1.Unit", "ActiveState") - exists = self.systemd_unit.Get("org.freedesktop.systemd1.Unit", "LoadState") - state = self.systemd_unit.Get("org.freedesktop.systemd1.Unit", "UnitFileState") + status = self.systemd_interface.Get( + "org.freedesktop.systemd1.Unit", "ActiveState" + ) + exists = self.systemd_interface.Get( + "org.freedesktop.systemd1.Unit", "LoadState" + ) + state = self.systemd_interface.Get( + "org.freedesktop.systemd1.Unit", "UnitFileState" + ) if exists == "not-found": color = self.py3.COLOR_DEGRADED diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/py3status-3.50/py3status/modules/vpn_status.py new/py3status-3.51/py3status/modules/vpn_status.py --- old/py3status-3.50/py3status/modules/vpn_status.py 2023-04-17 18:41:49.000000000 +0200 +++ new/py3status-3.51/py3status/modules/vpn_status.py 2023-06-27 09:04:06.000000000 +0200 @@ -22,7 +22,8 @@ color_good: VPN down Requires: - pydbus: Which further requires PyGi. Check your distribution's repositories. + dbus-python: to interact with dbus + pygobject: which in turn requires libcairo2-dev, libgirepository1.0-dev @author Nathan Smith <nathan AT praisetopia.org> @@ -33,12 +34,14 @@ {'color': '#FF0000', 'full_text': u'VPN: no'} """ -from pydbus import SystemBus -from gi.repository import GObject +from dbus.mainloop.glib import DBusGMainLoop +from gi.repository import GLib from threading import Thread from time import sleep from pathlib import Path +import dbus + class Py3status: """ """ @@ -66,15 +69,25 @@ def _start_loop(self): """Starts main event handler loop, run in handler thread t.""" # Create our main loop, get our bus, and add the signal handler - loop = GObject.MainLoop() - bus = SystemBus() - manager = bus.get(".NetworkManager") - manager.onPropertiesChanged = self._vpn_signal_handler + loop = DBusGMainLoop(set_as_default=True) + dbus.set_default_main_loop(loop) - # initialize active connections, some of them might be VPNs - self.active = manager.ActiveConnections + bus = dbus.SystemBus() + bus.add_signal_receiver( + self._vpn_signal_handler, path="/org/freedesktop/NetworkManager" + ) + # Initialize the already active connections + manager = bus.get_object( + "org.freedesktop.NetworkManager", + "/org/freedesktop/NetworkManager", + ) + interface = dbus.Interface(manager, "org.freedesktop.DBus.Properties") + self.active = interface.Get( + "org.freedesktop.NetworkManager", "ActiveConnections" + ) - # Loop forever + # Loop forever to listen for events + loop = GLib.MainLoop() loop.run() def _vpn_signal_handler(self, *args): @@ -97,12 +110,23 @@ # Sleep for a bit to let any changes in state finish sleep(0.3) # Check if any active connections are a VPN - bus = SystemBus() + bus = dbus.SystemBus() ids = [] for name in self.active: - conn = bus.get(".NetworkManager", name) - if conn.Vpn or conn.Type == "wireguard": - ids.append(conn.Id) + manager = bus.get_object( + "org.freedesktop.NetworkManager", + name, + ) + interface = dbus.Interface(manager, "org.freedesktop.DBus.Properties") + try: + properties = interface.GetAll( + "org.freedesktop.NetworkManager.Connection.Active" + ) + if properties.get("Vpn") or properties.get("Type") == "wireguard": + ids.append(properties.get("Id")) + except dbus.DBusException: + # the connection id has disappeared + pass # No active VPN return ids diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/py3status-3.50/py3status/output.py new/py3status-3.51/py3status/output.py --- old/py3status-3.50/py3status/output.py 1970-01-01 01:00:00.000000000 +0100 +++ new/py3status-3.51/py3status/output.py 2023-06-18 11:22:17.000000000 +0200 @@ -0,0 +1,269 @@ +import sys +from json import dumps + + +class OutputFormat: + """ + A base class for formatting the output of py3status for various + different consumers + """ + + @classmethod + def instance_for(cls, output_format): + """ + A factory for OutputFormat objects + """ + supported_output_formats = { + "dzen2": Dzen2OutputFormat, + "i3bar": I3barOutputFormat, + "lemonbar": LemonbarOutputFormat, + "none": NoneOutputFormat, + "term": TermOutputFormat, + "tmux": TmuxOutputFormat, + "xmobar": XmobarOutputFormat, + } + + if output_format in supported_output_formats: + return supported_output_formats[output_format]() + raise ValueError( + f"Invalid `output_format` attribute, should be one of `{'`, `'.join(supported_output_formats.keys())}`. Got `{output_format}`." + ) + + def __init__(self): + """ + Constructor + """ + self.separator = None + + def format_separator(self, separator, color): + """ + Produce a formatted and colorized separator for the output format, + if the output_format requires it, and None otherwise. + """ + pass + + def format(self, outputs): + """ + Produce a line of output from a list of module output dictionaries + """ + raise NotImplementedError() + + def write_header(self, header): + """ + Write the header to output, if supported by the output_format + """ + raise NotImplementedError() + + def write_line(self, output): + """ + Write a line of py3status containing the given module output + """ + raise NotImplementedError() + + +class I3barOutputFormat(OutputFormat): + """ + Format the output for consumption by i3bar + """ + + def format(self, outputs): + """ + Produce a line of output from a list of module outputs for + consumption by i3bar. separator is ignored. + """ + return ",".join(dumps(x) for x in outputs) + + def write_header(self, header): + """ + Write the i3bar header to output + """ + write = sys.__stdout__.write + flush = sys.__stdout__.flush + + write(dumps(header)) + write("\n[[]\n") + flush() + + def write_line(self, output): + """ + Write a line of py3status output for consumption by i3bar + """ + write = sys.__stdout__.write + flush = sys.__stdout__.flush + + out = ",".join(x for x in output if x) + write(f",[{out}]\n") + flush() + + +class SeparatedOutputFormat(OutputFormat): + """ + Base class for formatting output as an enriched string containing + separators + """ + + def begin_color(self, color): + """ + Produce a format string for a colorized output for the output format + """ + raise NotImplementedError() + + def end_color(self): + """ + Produce a format string for ending a colorized output for the output format + """ + raise NotImplementedError() + + def end_color_quick(self): + """ + Produce a format string for ending a colorized output, but only + if it is syntactically required. (for example because a new color + declaration immediately follows) + """ + return self.end_color() + + def get_default_separator(self): + """ + Produce the default separator for the output format + """ + return " | " + + def format_separator(self, separator, color): + """ + Format the given separator with the given color + """ + if separator is None: + separator = self.get_default_separator() + if color is not None: + separator = self.begin_color(color) + separator + self.end_color() + self.separator = separator + + def format_color(self, block): + """ + Format the given block of module output + """ + full_text = block["full_text"] + if "color" in block: + full_text = ( + self.begin_color(block["color"]) + full_text + self.end_color_quick() + ) + return full_text + + def format(self, outputs): + """ + Produce a line of output from a list of module outputs by + concatenating individual blocks of formatted output + """ + return "".join(self.format_color(x) for x in outputs) + + def write_header(self, header): + """ + Not supported in separated output formats + """ + pass + + def write_line(self, output): + """ + Write a line of py3status output separated by the formatted separator + """ + write = sys.__stdout__.write + flush = sys.__stdout__.flush + + out = self.separator.join(x for x in output if x) + write(f"{out}\n") + flush() + + +class Dzen2OutputFormat(SeparatedOutputFormat): + """ + Format the output for consumption by dzen2 + """ + + def begin_color(self, color): + return f"^fg({color})" + + def end_color(self): + return "^fg()" + + def end_color_quick(self): + return "" + + def get_default_separator(self): + """ + Produce the default separator for the output format + """ + return "^p(5;-2)^ro(2)^p()^p(5)" + + +class XmobarOutputFormat(SeparatedOutputFormat): + """ + Format the output for consumption by xmobar + """ + + def begin_color(self, color): + return f"<fc={color}>" + + def end_color(self): + return "</fc>" + + +class LemonbarOutputFormat(SeparatedOutputFormat): + """ + Format the output for consumption by lemonbar + """ + + def begin_color(self, color): + return f"%{{F{color}}}" + + def end_color(self): + return "%{F-}" + + def end_color_quick(self): + return "" + + +class TmuxOutputFormat(SeparatedOutputFormat): + """ + Format the output for consumption by tmux + """ + + def begin_color(self, color): + return f"#[fg={color.lower()}]" + + def end_color(self): + return "#[default]" + + def end_color_quick(self): + return "" + + +class TermOutputFormat(SeparatedOutputFormat): + """ + Format the output using terminal escapes + """ + + def begin_color(self, color): + col = int(color[1:], 16) + r = (col & (0xFF << 0)) // 0x80 + g = (col & (0xFF << 8)) // 0x8000 + b = (col & (0xFF << 16)) // 0x800000 + col = (r << 2) | (g << 1) | b + return f"\033[3{col};1m" + + def end_color(self): + return "\033[0m" + + def end_color_quick(self): + return "" + + +class NoneOutputFormat(SeparatedOutputFormat): + """ + Format the output without colors + """ + + def begin_color(self, color): + return "" + + def end_color(self): + return "" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/py3status-3.50/py3status/screenshots.py new/py3status-3.51/py3status/screenshots.py --- old/py3status-3.50/py3status/screenshots.py 2022-10-02 16:25:48.000000000 +0200 +++ new/py3status-3.51/py3status/screenshots.py 2023-06-05 13:45:55.000000000 +0200 @@ -148,7 +148,7 @@ color = COLOR_URGENT background = COLOR_URGENT_BG - size = font.getsize(text) + size = font.getbbox(text)[-2:] if background: d.rectangle( @@ -167,7 +167,7 @@ d_text = ImageDraw.Draw(txt) d_text.text((0, 0), text, font=font, fill=color) # resize to actual size wanted and add to image - txt = txt.resize((size[0] // SCALE, size[1] // SCALE), Image.ANTIALIAS) + txt = txt.resize((size[0] // SCALE, size[1] // SCALE), Image.LANCZOS) img.paste(txt, (WIDTH - x, TOP_BAR_HEIGHT + PADDING)) if separator: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/py3status-3.50/py3status/version.py new/py3status-3.51/py3status/version.py --- old/py3status-3.50/py3status/version.py 2023-04-17 18:43:02.000000000 +0200 +++ new/py3status-3.51/py3status/version.py 2023-06-27 09:28:29.000000000 +0200 @@ -1 +1 @@ -version = "3.50" +version = "3.51" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/py3status-3.50/py3status.egg-info/PKG-INFO new/py3status-3.51/py3status.egg-info/PKG-INFO --- old/py3status-3.50/py3status.egg-info/PKG-INFO 2023-04-17 18:44:18.000000000 +0200 +++ new/py3status-3.51/py3status.egg-info/PKG-INFO 2023-06-27 10:33:46.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: py3status -Version: 3.50 +Version: 3.51 Summary: py3status: an extensible i3status wrapper written in python Home-page: https://github.com/ultrabug/py3status Author: Ultrabug diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/py3status-3.50/py3status.egg-info/SOURCES.txt new/py3status-3.51/py3status.egg-info/SOURCES.txt --- old/py3status-3.50/py3status.egg-info/SOURCES.txt 2023-04-17 18:44:18.000000000 +0200 +++ new/py3status-3.51/py3status.egg-info/SOURCES.txt 2023-06-27 10:33:46.000000000 +0200 @@ -30,6 +30,7 @@ py3status/i3status.py py3status/module.py py3status/module_test.py +py3status/output.py py3status/parse_config.py py3status/private.py py3status/profiling.py
participants (1)
-
Source-Sync