bumblebee-status/bumblebee_status/core/config.py

306 lines
9.4 KiB
Python
Raw Normal View History

import os
2020-05-02 13:57:13 +02:00
import ast
from configparser import RawConfigParser
import sys
import glob
import textwrap
import argparse
import logging
import core.theme
import util.store
import util.format
import modules.core
import modules.contrib
log = logging.getLogger(__name__)
MODULE_HELP = "Specify a space-separated list of modules to load. The order of the list determines their order in the i3bar (from left to right). Use <module>:<alias> to provide an alias in case you want to load the same module multiple times, but specify different parameters."
PARAMETER_HELP = (
"Provide configuration parameters in the form of <module>.<key>=<value>"
)
THEME_HELP = "Specify the theme to use for drawing modules"
def all_modules():
2020-05-10 13:25:13 +02:00
"""Returns a list of all available modules (either core or contrib)
:return: list of modules
:rtype: list of strings
"""
result = {}
for path in [modules.core.__file__, modules.contrib.__file__]:
path = os.path.dirname(path)
for mod in glob.iglob("{}/*.py".format(path)):
result[os.path.basename(mod).replace(".py", "")] = 1
res = list(result.keys())
res.sort()
return res
class print_usage(argparse.Action):
def __init__(self, option_strings, dest, nargs=None, **kwargs):
argparse.Action.__init__(self, option_strings, dest, nargs, **kwargs)
self._indent = " " * 2
def __call__(self, parser, namespace, value, option_string=None):
if value == "modules":
self._args = namespace
self._format = "plain"
self.print_modules()
elif value == "modules-rst":
self._args = namespace
self._format = "rst"
self.print_modules()
elif value == "themes":
self.print_themes()
sys.exit(0)
def print_themes(self):
print(", ".join(core.theme.themes()))
def print_modules(self):
2020-05-03 16:56:23 +02:00
basepath = os.path.abspath(
os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")
)
2020-05-05 20:53:04 +02:00
rst = {}
for m in all_modules():
try:
2020-05-05 20:53:04 +02:00
module_type = "core"
filename = os.path.join(basepath, "modules", "core", "{}.py".format(m))
if not os.path.exists(filename):
filename = os.path.join(
basepath, "modules", "contrib", "{}.py".format(m)
)
2020-05-05 20:53:04 +02:00
module_type = "contrib"
if not os.path.exists(filename):
log.warning("module {} not found".format(m))
continue
doc = None
with open(filename) as f:
tree = ast.parse(f.read())
doc = ast.get_docstring(tree)
if not doc:
log.warning("failed to find docstring for {}".format(m))
continue
if self._format == "rst":
2020-05-04 20:12:02 +02:00
if os.path.exists(
2020-05-23 16:55:24 +02:00
os.path.join(basepath, "..", "screenshots", "{}.png".format(m))
2020-05-04 20:12:02 +02:00
):
doc = "{}\n\n.. image:: ../screenshots/{}.png".format(doc, m)
2020-05-05 20:53:04 +02:00
rst[module_type] = rst.get(module_type, [])
2020-05-09 21:24:28 +02:00
rst[module_type].append({"module": m, "content": doc})
else:
print(
textwrap.fill(
"{}:".format(m),
80,
initial_indent=self._indent * 2,
subsequent_indent=self._indent * 2,
)
)
for line in doc.split("\n"):
print(
textwrap.fill(
line,
80,
initial_indent=self._indent * 3,
subsequent_indent=self._indent * 6,
)
)
except Exception as e:
log.warning(e)
2020-05-05 20:53:04 +02:00
if self._format == "rst":
print("List of modules\n===============")
2020-05-09 21:24:28 +02:00
for k in ["core", "contrib"]:
print("\n{}\n{}\n".format(k, "-" * len(k)))
2020-05-05 20:53:04 +02:00
for mod in rst[k]:
2020-05-09 21:24:28 +02:00
print("\n{}\n{}\n".format(mod["module"], "~" * len(mod["module"])))
2020-05-05 20:53:04 +02:00
print(mod["content"])
class Config(util.store.Store):
2020-05-10 13:25:13 +02:00
"""Represents the configuration of bumblebee-status (either via config file or via CLI)
:param args: The arguments passed via the commandline
"""
def __init__(self, args):
super(Config, self).__init__()
parser = argparse.ArgumentParser(
description="bumblebee-status is a modular, theme-able status line generator for the i3 window manager. https://github.com/tobi-wan-kenobi/bumblebee-status/wiki"
)
parser.add_argument(
"-m", "--modules", nargs="+", action="append", default=[], help=MODULE_HELP
)
parser.add_argument(
"-p",
"--parameters",
nargs="+",
action="append",
default=[],
help=PARAMETER_HELP,
)
parser.add_argument("-t", "--theme", default=None, help=THEME_HELP)
parser.add_argument(
"-i",
"--iconset",
default="auto",
help="Specify the name of an iconset to use (overrides theme default)",
)
parser.add_argument(
"-a",
"--autohide",
nargs="+",
default=[],
help="Specify a list of modules to hide when not in warning/error state",
)
parser.add_argument(
"-d", "--debug", action="store_true", help="Add debug fields to i3 output"
)
parser.add_argument(
"-f",
"--logfile",
help="destination for the debug log file, if -d|--debug is specified; defaults to stderr",
)
parser.add_argument(
"-r",
"--right-to-left",
action="store_true",
help="Draw widgets from right to left, rather than left to right (which is the default)",
)
parser.add_argument(
"-l",
"--list",
choices=["modules", "themes", "modules-rst"],
help="Display a list of available themes or available modules, along with their parameters",
action=print_usage,
)
2020-05-01 15:17:55 +02:00
self.__args = parser.parse_args(args)
for cfg in [
"~/.bumblebee-status.conf",
"~/.config/bumblebee-status.conf",
"~/.config/bumblebee-status/config",
]:
cfg = os.path.expanduser(cfg)
self.load_config(cfg)
parameters = [item for sub in self.__args.parameters for item in sub]
for param in parameters:
if not "=" in param:
log.error(
'missing value for parameter "{}" - ignoring this parameter'.format(
param
)
)
continue
key, value = param.split("=", 1)
self.set(key, value)
2020-05-10 13:25:13 +02:00
"""Loads parameters from an init-style configuration file
:param filename: path to the file to load
"""
def load_config(self, filename):
if os.path.exists(filename):
log.info("loading {}".format(filename))
tmp = RawConfigParser()
tmp.read(u"{}".format(filename))
if tmp.has_section("module-parameters"):
for key, value in tmp.items("module-parameters"):
self.set(key, value)
2020-05-10 13:25:13 +02:00
"""Returns a list of configured modules
:return: list of configured (active) modules
:rtype: list of strings
"""
def modules(self):
2020-07-06 10:50:41 +02:00
list_of_modules = self.get('modules', None)
if (list_of_modules is None) or (type(list_of_modules) != list):
list_of_modules = [item for sub in self.__args.modules for item in sub]
return list_of_modules
2020-05-10 13:25:13 +02:00
"""Returns the global update interval
:return: update interval in seconds
:rtype: float
"""
def interval(self, default=1):
return util.format.seconds(self.get("interval", default))
2020-05-10 13:25:13 +02:00
"""Returns whether debug mode is enabled
:return: True if debug is enabled, False otherwise
:rtype: boolean
"""
def debug(self):
return self.__args.debug
2020-05-10 13:25:13 +02:00
"""Returns whether module order should be reversed/inverted
:return: True if modules should be reversed, False otherwise
:rtype: boolean
"""
2020-05-01 15:17:55 +02:00
def reverse(self):
return self.__args.right_to_left
2020-05-10 13:25:13 +02:00
"""Returns the logfile location
:return: location where the logfile should be written
:rtype: string
"""
def logfile(self):
return self.__args.logfile
2020-05-10 13:25:13 +02:00
"""Returns the configured theme name
:return: name of the configured theme
:rtype: string
"""
def theme(self):
return self.__args.theme or self.get("theme") or "default"
2020-05-10 13:25:13 +02:00
"""Returns the configured iconset name
:return: name of the configured iconset
:rtype: string
"""
def iconset(self):
return self.__args.iconset
2020-05-10 13:25:13 +02:00
"""Returns which modules should be hidden if their state is not warning/critical
:return: list of modules to hide automatically
:rtype: list of strings
"""
def autohide(self, name):
return name in self.__args.autohide
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4