302 lines
9.2 KiB
Python
302 lines
9.2 KiB
Python
import os
|
|
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():
|
|
"""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):
|
|
basepath = os.path.abspath(
|
|
os.path.join(os.path.dirname(os.path.realpath(__file__)), "..")
|
|
)
|
|
|
|
rst = {}
|
|
for m in all_modules():
|
|
try:
|
|
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)
|
|
)
|
|
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":
|
|
if os.path.exists(
|
|
os.path.join(basepath, "..", "screenshots", "{}.png".format(m))
|
|
):
|
|
doc = "{}\n\n.. image:: ../screenshots/{}.png".format(doc, m)
|
|
|
|
rst[module_type] = rst.get(module_type, [])
|
|
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)
|
|
|
|
if self._format == "rst":
|
|
print("List of modules\n===============")
|
|
for k in ["core", "contrib"]:
|
|
print("\n{}\n{}\n".format(k, "-" * len(k)))
|
|
for mod in rst[k]:
|
|
print("\n{}\n{}\n".format(mod["module"], "~" * len(mod["module"])))
|
|
print(mod["content"])
|
|
|
|
|
|
class Config(util.store.Store):
|
|
"""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="default", 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,
|
|
)
|
|
|
|
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)
|
|
|
|
"""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(filename)
|
|
|
|
if tmp.has_section("module-parameters"):
|
|
for key, value in tmp.items("module-parameters"):
|
|
self.set(key, value)
|
|
|
|
"""Returns a list of configured modules
|
|
|
|
:return: list of configured (active) modules
|
|
:rtype: list of strings
|
|
"""
|
|
|
|
def modules(self):
|
|
return [item for sub in self.__args.modules for item in sub]
|
|
|
|
"""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))
|
|
|
|
"""Returns whether debug mode is enabled
|
|
|
|
:return: True if debug is enabled, False otherwise
|
|
:rtype: boolean
|
|
"""
|
|
|
|
def debug(self):
|
|
return self.__args.debug
|
|
|
|
"""Returns whether module order should be reversed/inverted
|
|
|
|
:return: True if modules should be reversed, False otherwise
|
|
:rtype: boolean
|
|
"""
|
|
|
|
def reverse(self):
|
|
return self.__args.right_to_left
|
|
|
|
"""Returns the logfile location
|
|
|
|
:return: location where the logfile should be written
|
|
:rtype: string
|
|
"""
|
|
|
|
def logfile(self):
|
|
return self.__args.logfile
|
|
|
|
"""Returns the configured theme name
|
|
|
|
:return: name of the configured theme
|
|
:rtype: string
|
|
"""
|
|
|
|
def theme(self):
|
|
return self.__args.theme
|
|
|
|
"""Returns the configured iconset name
|
|
|
|
:return: name of the configured iconset
|
|
:rtype: string
|
|
"""
|
|
|
|
def iconset(self):
|
|
return self.__args.iconset
|
|
|
|
"""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
|