diff options
| -rwxr-xr-x | py3status/__init__.py | 137 | ||||
| -rw-r--r-- | py3status/modules/xrandr.py | 4 |
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 |
