Refactor powerline.lint.inspect: do not special-case *ThreadedSegment

Fixes now_playing segment handling in python-3.4
This commit is contained in:
ZyX 2014-08-06 12:52:52 +04:00
parent d4735c87df
commit 1a87006310
5 changed files with 130 additions and 61 deletions

View File

@ -2,9 +2,11 @@
from __future__ import absolute_import
from powerline.lib.monotonic import monotonic
from threading import Thread, Lock, Event
from types import MethodType
from powerline.lib.monotonic import monotonic
from powerline.segments import Segment
class MultiRunnedThread(object):
@ -28,12 +30,14 @@ class MultiRunnedThread(object):
return None
class ThreadedSegment(MultiRunnedThread):
class ThreadedSegment(Segment, MultiRunnedThread):
min_sleep_time = 0.1
update_first = True
interval = 1
daemon = False
argmethods = ('render', 'set_state')
def __init__(self):
super(ThreadedSegment, self).__init__()
self.run_once = True
@ -145,10 +149,34 @@ class ThreadedSegment(MultiRunnedThread):
def debug(self, *args, **kwargs):
self.pl.debug(prefix=self.__class__.__name__, *args, **kwargs)
def argspecobjs(self):
for name in self.argmethods:
try:
yield name, getattr(self, name)
except AttributeError:
pass
def additional_args(self):
return (('interval', self.interval),)
def omitted_args(self, name, method):
if isinstance(getattr(self, name, None), MethodType):
omitted_indexes = (0,)
else:
omitted_indexes = ()
if name.startswith('render'):
if omitted_indexes:
omitted_indexes += (1,)
else:
omitted_indexes = (0,)
return omitted_indexes
class KwThreadedSegment(ThreadedSegment):
update_first = True
argmethods = ('render', 'set_state', 'key', 'render_one')
def __init__(self):
super(KwThreadedSegment, self).__init__()
self.updated = True

View File

@ -1,72 +1,63 @@
# vim:fileencoding=utf-8:noet
from __future__ import absolute_import
from inspect import ArgSpec, getargspec
from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment
from itertools import count
from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment
from powerline.segments import Segment
def getconfigargspec(obj):
if isinstance(obj, ThreadedSegment):
args = ['interval']
defaults = [getattr(obj, 'interval', 1)]
if obj.update_first:
args.append('update_first')
defaults.append(True)
methods = ['render', 'set_state']
if isinstance(obj, KwThreadedSegment):
methods += ['key', 'render_one']
for method in methods:
if hasattr(obj, method):
# Note: on <python-2.6 it may return simple tuple, not
# ArgSpec instance.
argspec = getargspec(getattr(obj, method))
for i, arg in zip(count(1), reversed(argspec.args)):
if (arg == 'self' or
(arg == 'segment_info' and
getattr(obj, 'powerline_requires_segment_info', None)) or
(arg == 'create_watcher' and
getattr(obj, 'powerline_requires_filesystem_watcher', None)) or
(arg == 'pl') or
(method.startswith('render') and (1 if argspec.args[0] == 'self' else 0) + i == len(argspec.args)) or
arg in args):
continue
if argspec.defaults and len(argspec.defaults) >= i:
default = argspec.defaults[-i]
defaults.append(default)
args.append(arg)
else:
args.insert(0, arg)
argspec = ArgSpec(args=args, varargs=None, keywords=None, defaults=tuple(defaults))
if hasattr(obj, 'powerline_origin'):
obj = obj.powerline_origin
else:
if hasattr(obj, 'powerline_origin'):
obj = obj.powerline_origin
else:
obj = obj
obj = obj
remove_self = False
try:
argspec = getargspec(obj)
except TypeError: # Workaround for now_playing segment
# TODO For NowPlayingSegment for linter: merge in information about
# player-specific arguments
argspec = getargspec(obj.__call__)
remove_self = True
args = []
defaults = []
for i, arg in zip(count(1), reversed(argspec.args)):
args = []
defaults = []
if isinstance(obj, Segment):
additional_args = obj.additional_args()
argspecobjs = obj.argspecobjs()
get_omitted_args = obj.omitted_args
else:
additional_args = ()
argspecobjs = ((None, obj),)
get_omitted_args = lambda *args: ()
for arg in additional_args:
args.append(arg[0])
if len(arg) > 1:
defaults.append(arg[1])
requires_segment_info = getattr(obj, 'powerline_requires_segment_info', False)
requires_filesystem_watcher = getattr(obj, 'powerline_requires_filesystem_watcher', False)
for name, method in argspecobjs:
argspec = getargspec(method)
omitted_args = get_omitted_args(name, method)
largs = len(argspec.args)
for i, arg in enumerate(reversed(argspec.args)):
if (
(arg == 'segment_info' and getattr(obj, 'powerline_requires_segment_info', None))
largs - (i + 1) in omitted_args
or arg == 'pl'
or (arg == 'self' and remove_self)
or (arg == 'create_watcher' and requires_filesystem_watcher)
or (arg == 'segment_info' and requires_segment_info)
):
continue
if argspec.defaults and len(argspec.defaults) >= i:
default = argspec.defaults[-i]
if argspec.defaults and len(argspec.defaults) > i:
if arg in args:
idx = args.index(arg)
if len(args) - idx > len(defaults):
args.pop(idx)
else:
continue
default = argspec.defaults[-(i + 1)]
defaults.append(default)
args.append(arg)
else:
args.insert(0, arg)
argspec = ArgSpec(args=args, varargs=argspec.varargs, keywords=argspec.keywords, defaults=tuple(defaults))
if arg not in args:
args.insert(0, arg)
return argspec
return ArgSpec(args=args, varargs=None, keywords=None, defaults=tuple(defaults))

View File

@ -1,10 +1,10 @@
# vim:fileencoding=utf-8:noet
from __future__ import absolute_import, unicode_literals, division, print_function
from powerline.lib.file_watcher import create_file_watcher
import sys
from powerline.lib.file_watcher import create_file_watcher
def list_segment_key_values(segment, theme_configs, key, module=None, default=None):
try:

View File

@ -1,2 +1,51 @@
# vim:fileencoding=utf-8:noet
from __future__ import absolute_import
import sys
from pkgutil import extend_path
from types import MethodType
__path__ = extend_path(__path__, __name__)
class Segment(object):
'''Base class for any segment that is not a function
Required for powerline.lint.inspect to work properly.
'''
if sys.version_info < (3, 4):
def argspecobjs(self):
yield '__call__', self.__call__
else:
def argspecobjs(self): # NOQA
yield '__call__', self
argspecobjs.__doc__ = (
'''Return a list of valid arguments for inspect.getargspec
Used to determine function arguments.
'''
)
def omitted_args(self, name, method):
'''List arguments which should be omitted
Returns a tuple with indexes of omitted arguments.
.. note::``segment_info``, ``create_watcher`` and ``pl`` will be omitted
regardless of the below return (for ``segment_info`` and
``create_watcher``: only if object was marked to require segment
info or filesystem watcher).
'''
if isinstance(self.__call__, MethodType):
return (0,)
else:
return ()
@staticmethod
def additional_args():
'''Returns a list of (additional argument name[, default value]) tuples.
'''
return ()

View File

@ -19,6 +19,7 @@ from powerline.lib.monotonic import monotonic
from powerline.lib.humanize_bytes import humanize_bytes
from powerline.lib.unicode import u
from powerline.theme import requires_segment_info, requires_filesystem_watcher
from powerline.segments import Segment
from collections import namedtuple
@ -904,7 +905,7 @@ STATE_SYMBOLS = {
}
class NowPlayingSegment(object):
class NowPlayingSegment(Segment):
def __call__(self, player='mpd', format='{state_symbol} {artist} - {title} ({total})', state_symbols=STATE_SYMBOLS, **kwargs):
player_func = getattr(self, 'player_{0}'.format(player))
stats = {