summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xpy3status/__init__.py137
-rw-r--r--py3status/modules/xrandr.py4
2 files changed, 106 insertions, 35 deletions
diff --git a/py3status/__init__.py b/py3status/__init__.py
index 226448b..25f608a 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
@@ -338,7 +347,7 @@ class I3status(Thread):
# add mendatory items in i3status time format wrt issue #18
time_fmt = time_format
for fmt in ['%Y', '%m', '%d']:
- if not fmt in time_format:
+ if fmt not in time_format:
time_fmt = '{} {}'.format(time_fmt, fmt)
i3s_time = '{} {}'.format(
i3s_time, datetime.now().strftime(fmt)
@@ -627,7 +636,7 @@ class Events(Thread):
"""
Force a cache expiration for all the methods of the given module.
- We rate limit the i3status refresh to 100ms for obvious abusive behavior.
+ We rate limit the i3status refresh to 100ms.
"""
module = self.modules.get(module_name)
if module is not None:
@@ -1031,7 +1040,7 @@ class Module(Thread):
raise KeyError('missing "name" key in response')
# validate the response
- if not 'full_text' in result:
+ if 'full_text' not in result:
raise KeyError('missing "full_text" key in response')
# initialize method object
@@ -1120,6 +1129,29 @@ 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 +1159,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 +1182,13 @@ 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='*', help=argparse.SUPPRESS)
+
options = parser.parse_args()
+ if options.cli_command:
+ config['cli_command'] = options.cli_command
+
# only asked for version
if options.version:
from platform import python_version
@@ -1169,36 +1207,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 +1292,11 @@ class Py3statusWrapper():
# setup configuration
self.config = self.get_config()
+
+ if self.config.get('cli_command'):
+ self.handle_cli_command(self.config['cli_command'])
+ sys.exit()
+
if self.config['debug']:
syslog(
LOG_INFO,
@@ -1578,6 +1592,63 @@ class Py3statusWrapper():
delta += 0.1
sleep(0.1)
+ @staticmethod
+ def print_module_description(details, 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())
+
+ docstring = ast.get_docstring(module, clean=True)
+ if docstring:
+ short_description = docstring.split('\n')[0].rstrip('.')
+ print_stderr(' %-22s %s.' % (mod_name, short_description))
+ if details:
+ for description in docstring.split('\n')[1:]:
+ print_stderr(' ' * 25 + '%s' % description)
+ print_stderr(' ' * 25 + '---')
+ else:
+ print_stderr(' %-22s No docstring in %s' % (mod_name, path))
+ except Exception:
+ print_stderr(' %-22s Unable to parse %s' % (mod_name, path))
+
+ def handle_cli_command(self, cmd):
+ """Handle a command from the CLI.
+ """
+ # aliases
+ if cmd[0] in ['mod', 'module', 'modules']:
+ cmd[0] = 'modules'
+
+ # allowed cli commands
+ if cmd[:2] in (['modules', 'list'], ['modules', 'details']):
+ try:
+ py3_modules_path = imp.find_module('py3status')[1]
+ py3_modules_path += '/modules/'
+ if os.path.isdir(py3_modules_path):
+ self.config['include_paths'].append(py3_modules_path)
+ except:
+ print_stderr('Unable to locate py3status modules !')
+
+ details = cmd[1] == 'details'
+ user_modules = self.get_user_modules()
+
+ print_stderr('Available modules:')
+ for mod_name, mod_path in sorted(user_modules.items()):
+ if mod_name == 'empty_class':
+ continue
+ self.print_module_description(details, mod_name, mod_path)
+ elif cmd[:2] in (['modules', 'enable'], ['modules', 'disable']):
+ # TODO: to be implemented
+ pass
+ else:
+ print_stderr('Error: unknown command')
+ sys.exit(1)
+
def main():
try:
diff --git a/py3status/modules/xrandr.py b/py3status/modules/xrandr.py
index 2d3e757..4ddd856 100644
--- a/py3status/modules/xrandr.py
+++ b/py3status/modules/xrandr.py
@@ -75,7 +75,7 @@ class Py3status:
def _get_layout(self):
"""
- Get the current outputs layout from xrandr and try to detect the
+ Get the outputs layout from xrandr and try to detect the
currently active layout as best as we can on start.
"""
connected = list()
@@ -88,7 +88,7 @@ class Py3status:
}
)
- current = Popen(['xrandr', '--current'], stdout=PIPE)
+ current = Popen(['xrandr'], stdout=PIPE)
for line in current.stdout.readlines():
try:
# python3