summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xpy3status/__init__.py122
1 files changed, 92 insertions, 30 deletions
diff --git a/py3status/__init__.py b/py3status/__init__.py
index 226448b..2027882 100755
--- a/py3status/__init__.py
+++ b/py3status/__init__.py
@@ -1,4 +1,7 @@
+from __future__ import print_function
+
import argparse
+import ast
import imp
import locale
import os
@@ -40,6 +43,12 @@ def print_line(line):
sys.__stdout__.flush()
+def print_stderr(line):
+ """Print line to stderr
+ """
+ print(line, file=sys.stderr)
+
+
class IOPoller:
"""
This class implements a predictive and timing-out I/O reader
@@ -1120,6 +1129,27 @@ class Py3statusWrapper():
version = 'unknown'
config['version'] = version
+ # i3status config file default detection
+ # respect i3status' file detection order wrt issue #43
+ i3status_config_file_candidates = [
+ '{}/.i3status.conf'.format(home_path),
+ '{}/.config/i3status/config'.format(
+ os.environ.get('XDG_CONFIG_HOME', home_path)
+ ),
+ '/etc/i3status.conf',
+ '{}/i3status/config'.format(
+ os.environ.get('XDG_CONFIG_DIRS', '/etc/xdg')
+ )
+ ]
+ for fn in i3status_config_file_candidates:
+ if os.path.isfile(fn):
+ i3status_config_file_default = fn
+ break
+ else:
+ # if none of the default files exists, we will default
+ # to ~/.i3/i3status.conf
+ i3status_config_file_default = '{}/.i3/i3status.conf'.format(home_path)
+
# command line options
parser = argparse.ArgumentParser(
description='The agile, python-powered, i3status wrapper')
@@ -1127,6 +1157,7 @@ class Py3statusWrapper():
parser.add_argument('-c', '--config', action="store",
dest="i3status_conf",
type=str,
+ default=i3status_config_file_default,
help="path to i3status config file")
parser.add_argument('-d', '--debug', action="store_true",
help="be verbose in syslog")
@@ -1149,8 +1180,19 @@ class Py3statusWrapper():
(default 60 sec)""")
parser.add_argument('-v', '--version', action="store_true",
help="""show py3status version and exit""")
+
+ parser.add_argument('cli_command', nargs='?',
+ choices=[
+ 'list-modules',
+ ])
+ parser.add_argument('cli_command_arg', nargs='?', help=argparse.SUPPRESS)
+
options = parser.parse_args()
+ if options.cli_command:
+ config['cli_command'] = options.cli_command
+ config['cli_command_arg'] = options.cli_command_arg
+
# only asked for version
if options.version:
from platform import python_version
@@ -1169,36 +1211,7 @@ class Py3statusWrapper():
config['include_paths'] = options.include_paths
config['interval'] = int(options.interval)
config['standalone'] = options.standalone
-
- # i3status config file path setup or default detection
- if options.i3status_conf:
- config['i3status_config_path'] = options.i3status_conf
- else:
- # find i3status default config file
- # respect i3status' file detection order wrt issue #43
- i3status_config_files = [
- '{}/.i3status.conf'.format(home_path),
- '{}/.config/i3status/config'.format(
- os.environ.get('XDG_CONFIG_HOME', home_path)
- ),
- '/etc/i3status.conf',
- '{}/i3status/config'.format(
- os.environ.get('XDG_CONFIG_DIRS', '/etc/xdg')
- )
- ]
- i3status_config_files = list(
- filter(
- os.path.isfile,
- i3status_config_files
- )
- )
-
- # if none of the default files exists, we will default
- # to ~/.i3/i3status.conf
- config['i3status_config_path'] = (
- i3status_config_files[0] if i3status_config_files
- else '{}/.i3/i3status.conf'.format(home_path)
- )
+ config['i3status_config_path'] = options.i3status_conf
# all done
return config
@@ -1283,6 +1296,14 @@ class Py3statusWrapper():
# setup configuration
self.config = self.get_config()
+
+ if 'cli_command' in self.config:
+ self.handle_cli_command(
+ self.config['cli_command'],
+ self.config['cli_command_arg']
+ )
+ sys.exit()
+
if self.config['debug']:
syslog(
LOG_INFO,
@@ -1578,6 +1599,47 @@ class Py3statusWrapper():
delta += 0.1
sleep(0.1)
+ @staticmethod
+ def print_module_description(mod_name, mod_path):
+ """Print module description extracted from its docstring.
+ """
+ if mod_name == '__init__':
+ return
+
+ path = os.path.join(*mod_path)
+ try:
+ with open(path) as f:
+ module = ast.parse(f.read())
+
+ items = [i for i in module.body if isinstance(i, ast.Expr)]
+ if items:
+ ds = items[0].value.s
+ ds = ds.lstrip()
+ ds = ds.split('\n\n',1)[0] # Filter only up to an empty line.
+ ds = ds.split('.', 1)[0] # And the first dot
+ ds = ds.replace('\n', ' ')
+ print_stderr(" %-22s %s." % (mod_name, ds))
+
+ else:
+ print_stderr(" %-22s No docstring in %s" % (mod_name, path))
+
+ except Exception as e:
+ print_stderr(" %-22s Unable to parse %s" % (mod_name, path))
+
+ def handle_cli_command(self, cmd, cmd_arg):
+ """Handle a command from the CLI.
+ """
+ if cmd == 'list-modules':
+ user_modules = self.get_user_modules()
+ print_stderr("Available modules:")
+ for mod_name, mod_path in sorted(user_modules.items()):
+ self.print_module_description(mod_name, mod_path)
+
+ sys.exit()
+
+ print_stderr("Error: unknown command")
+ sys.exit(1)
+
def main():
try: