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 __future__ import absolute_import
from powerline.lib.monotonic import monotonic
from threading import Thread, Lock, Event from threading import Thread, Lock, Event
from types import MethodType
from powerline.lib.monotonic import monotonic
from powerline.segments import Segment
class MultiRunnedThread(object): class MultiRunnedThread(object):
@ -28,12 +30,14 @@ class MultiRunnedThread(object):
return None return None
class ThreadedSegment(MultiRunnedThread): class ThreadedSegment(Segment, MultiRunnedThread):
min_sleep_time = 0.1 min_sleep_time = 0.1
update_first = True update_first = True
interval = 1 interval = 1
daemon = False daemon = False
argmethods = ('render', 'set_state')
def __init__(self): def __init__(self):
super(ThreadedSegment, self).__init__() super(ThreadedSegment, self).__init__()
self.run_once = True self.run_once = True
@ -145,10 +149,34 @@ class ThreadedSegment(MultiRunnedThread):
def debug(self, *args, **kwargs): def debug(self, *args, **kwargs):
self.pl.debug(prefix=self.__class__.__name__, *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): class KwThreadedSegment(ThreadedSegment):
update_first = True update_first = True
argmethods = ('render', 'set_state', 'key', 'render_one')
def __init__(self): def __init__(self):
super(KwThreadedSegment, self).__init__() super(KwThreadedSegment, self).__init__()
self.updated = True self.updated = True

View File

@ -1,72 +1,63 @@
# vim:fileencoding=utf-8:noet # vim:fileencoding=utf-8:noet
from __future__ import absolute_import from __future__ import absolute_import
from inspect import ArgSpec, getargspec from inspect import ArgSpec, getargspec
from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment
from itertools import count from itertools import count
from powerline.lib.threaded import ThreadedSegment, KwThreadedSegment
from powerline.segments import Segment
def getconfigargspec(obj): def getconfigargspec(obj):
if isinstance(obj, ThreadedSegment): if hasattr(obj, 'powerline_origin'):
args = ['interval'] obj = obj.powerline_origin
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))
else: else:
if hasattr(obj, 'powerline_origin'): obj = obj
obj = obj.powerline_origin
else:
obj = obj
remove_self = False args = []
try: defaults = []
argspec = getargspec(obj)
except TypeError: # Workaround for now_playing segment if isinstance(obj, Segment):
# TODO For NowPlayingSegment for linter: merge in information about additional_args = obj.additional_args()
# player-specific arguments argspecobjs = obj.argspecobjs()
argspec = getargspec(obj.__call__) get_omitted_args = obj.omitted_args
remove_self = True else:
args = [] additional_args = ()
defaults = [] argspecobjs = ((None, obj),)
for i, arg in zip(count(1), reversed(argspec.args)): 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 ( if (
(arg == 'segment_info' and getattr(obj, 'powerline_requires_segment_info', None)) largs - (i + 1) in omitted_args
or arg == 'pl' 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 continue
if argspec.defaults and len(argspec.defaults) >= i: if argspec.defaults and len(argspec.defaults) > i:
default = 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) defaults.append(default)
args.append(arg) args.append(arg)
else: else:
args.insert(0, arg) if arg not in args:
argspec = ArgSpec(args=args, varargs=argspec.varargs, keywords=argspec.keywords, defaults=tuple(defaults)) 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 # vim:fileencoding=utf-8:noet
from __future__ import absolute_import, unicode_literals, division, print_function from __future__ import absolute_import, unicode_literals, division, print_function
from powerline.lib.file_watcher import create_file_watcher
import sys import sys
from powerline.lib.file_watcher import create_file_watcher
def list_segment_key_values(segment, theme_configs, key, module=None, default=None): def list_segment_key_values(segment, theme_configs, key, module=None, default=None):
try: 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 pkgutil import extend_path
from types import MethodType
__path__ = extend_path(__path__, __name__) __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.humanize_bytes import humanize_bytes
from powerline.lib.unicode import u from powerline.lib.unicode import u
from powerline.theme import requires_segment_info, requires_filesystem_watcher from powerline.theme import requires_segment_info, requires_filesystem_watcher
from powerline.segments import Segment
from collections import namedtuple 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): def __call__(self, player='mpd', format='{state_symbol} {artist} - {title} ({total})', state_symbols=STATE_SYMBOLS, **kwargs):
player_func = getattr(self, 'player_{0}'.format(player)) player_func = getattr(self, 'player_{0}'.format(player))
stats = { stats = {