Refactor IPython bindings

- Replaced two powerline objects with one powerline object utilizing
  RendererProxy proxy class that holds two Renderer instances.
- Made .setup() functions do something more meaningful.
This commit is contained in:
ZyX 2014-08-19 19:54:54 +04:00
parent b93f8d9b7b
commit 06175dcd74
7 changed files with 90 additions and 62 deletions

View File

@ -1,4 +1,7 @@
# vim:fileencoding=utf-8:noet # vim:fileencoding=utf-8:noet
from weakref import ref
from powerline.ipython import IPythonPowerline, RewriteResult from powerline.ipython import IPythonPowerline, RewriteResult
from IPython.core.prompts import PromptManager from IPython.core.prompts import PromptManager
@ -15,18 +18,13 @@ class IPythonInfo(object):
class PowerlinePromptManager(PromptManager): class PowerlinePromptManager(PromptManager):
def __init__(self, prompt_powerline, non_prompt_powerline, shell): def __init__(self, powerline, shell):
prompt_powerline.setup('prompt_powerline', self)
non_prompt_powerline.setup('non_prompt_powerline', self)
self.powerline_segment_info = IPythonInfo(shell) self.powerline_segment_info = IPythonInfo(shell)
self.shell = shell self.shell = shell
def render(self, name, color=True, *args, **kwargs): def render(self, name, color=True, *args, **kwargs):
if name == 'out' or name == 'rewrite': res = self.powerline.render(
powerline = self.non_prompt_powerline is_prompt=name.startswith('in'),
else:
powerline = self.prompt_powerline
res = powerline.render(
side='left', side='left',
output_width=True, output_width=True,
output_raw=not color, output_raw=not color,
@ -43,12 +41,12 @@ class PowerlinePromptManager(PromptManager):
class ConfigurableIPythonPowerline(IPythonPowerline): class ConfigurableIPythonPowerline(IPythonPowerline):
def init(self, ip, is_prompt, old_widths): def init(self, ip):
config = ip.config.Powerline config = ip.config.Powerline
self.config_overrides = config.get('config_overrides') self.config_overrides = config.get('config_overrides')
self.theme_overrides = config.get('theme_overrides', {}) self.theme_overrides = config.get('theme_overrides', {})
self.paths = config.get('paths') self.paths = config.get('paths')
super(ConfigurableIPythonPowerline, self).init(is_prompt, old_widths) super(ConfigurableIPythonPowerline, self).init()
old_prompt_manager = None old_prompt_manager = None
@ -58,19 +56,16 @@ def load_ipython_extension(ip):
global old_prompt_manager global old_prompt_manager
old_prompt_manager = ip.prompt_manager old_prompt_manager = ip.prompt_manager
old_widths = {} powerline = ConfigurableIPythonPowerline(ip)
prompt_powerline = ConfigurableIPythonPowerline(ip, True, old_widths)
non_prompt_powerline = ConfigurableIPythonPowerline(ip, False, old_widths)
ip.prompt_manager = PowerlinePromptManager( ip.prompt_manager = PowerlinePromptManager(
prompt_powerline=prompt_powerline, powerline=powerline,
non_prompt_powerline=non_prompt_powerline, shell=ip.prompt_manager.shell,
shell=ip.prompt_manager.shell
) )
powerline.setup(ref(ip.prompt_manager))
def shutdown_hook(): def shutdown_hook():
prompt_powerline.shutdown() powerline.shutdown()
non_prompt_powerline.shutdown()
raise TryNext() raise TryNext()
ip.hooks.shutdown_hook.add(shutdown_hook) ip.hooks.shutdown_hook.add(shutdown_hook)

View File

@ -1,12 +1,16 @@
# vim:fileencoding=utf-8:noet # vim:fileencoding=utf-8:noet
import re
from weakref import ref
from powerline.ipython import IPythonPowerline, RewriteResult from powerline.ipython import IPythonPowerline, RewriteResult
from powerline.lib.unicode import string from powerline.lib.unicode import string
from IPython.Prompts import BasePrompt from IPython.Prompts import BasePrompt
from IPython.ipapi import get as get_ipython from IPython.ipapi import get as get_ipython
from IPython.ipapi import TryNext from IPython.ipapi import TryNext
import re
class IPythonInfo(object): class IPythonInfo(object):
def __init__(self, cache): def __init__(self, cache):
@ -18,9 +22,7 @@ class IPythonInfo(object):
class PowerlinePrompt(BasePrompt): class PowerlinePrompt(BasePrompt):
def __init__(self, powerline, other_powerline, powerline_last_in, old_prompt): def __init__(self, powerline, powerline_last_in, old_prompt):
powerline.setup('powerline', self)
other_powerline.setup('other_powerline', self)
self.powerline_last_in = powerline_last_in self.powerline_last_in = powerline_last_in
self.powerline_segment_info = IPythonInfo(old_prompt.cache) self.powerline_segment_info = IPythonInfo(old_prompt.cache)
self.cache = old_prompt.cache self.cache = old_prompt.cache
@ -35,6 +37,7 @@ class PowerlinePrompt(BasePrompt):
def set_p_str(self): def set_p_str(self):
self.p_str, self.p_str_nocolor, self.powerline_prompt_width = ( self.p_str, self.p_str_nocolor, self.powerline_prompt_width = (
self.powerline.render( self.powerline.render(
is_prompt=self.powerline_is_prompt,
side='left', side='left',
output_raw=True, output_raw=True,
output_width=True, output_width=True,
@ -50,6 +53,7 @@ class PowerlinePrompt(BasePrompt):
class PowerlinePrompt1(PowerlinePrompt): class PowerlinePrompt1(PowerlinePrompt):
powerline_prompt_type = 'in' powerline_prompt_type = 'in'
powerline_is_prompt = True
rspace = re.compile(r'(\s*)$') rspace = re.compile(r'(\s*)$')
def __str__(self): def __str__(self):
@ -64,7 +68,8 @@ class PowerlinePrompt1(PowerlinePrompt):
self.powerline_last_in['nrspaces'] = self.nrspaces self.powerline_last_in['nrspaces'] = self.nrspaces
def auto_rewrite(self): def auto_rewrite(self):
return RewriteResult(self.other_powerline.render( return RewriteResult(self.powerline.render(
is_prompt=False,
side='left', side='left',
matcher_info='rewrite', matcher_info='rewrite',
segment_info=self.powerline_segment_info) + (' ' * self.nrspaces) segment_info=self.powerline_segment_info) + (' ' * self.nrspaces)
@ -73,6 +78,7 @@ class PowerlinePrompt1(PowerlinePrompt):
class PowerlinePromptOut(PowerlinePrompt): class PowerlinePromptOut(PowerlinePrompt):
powerline_prompt_type = 'out' powerline_prompt_type = 'out'
powerline_is_prompt = False
def set_p_str(self): def set_p_str(self):
super(PowerlinePromptOut, self).set_p_str() super(PowerlinePromptOut, self).set_p_str()
@ -83,37 +89,46 @@ class PowerlinePromptOut(PowerlinePrompt):
class PowerlinePrompt2(PowerlinePromptOut): class PowerlinePrompt2(PowerlinePromptOut):
powerline_prompt_type = 'in2' powerline_prompt_type = 'in2'
powerline_is_prompt = True
class ConfigurableIPythonPowerline(IPythonPowerline): class ConfigurableIPythonPowerline(IPythonPowerline):
def init(self, is_prompt, old_widths, config_overrides=None, theme_overrides={}, paths=None): def init(self, config_overrides=None, theme_overrides={}, paths=None):
self.config_overrides = config_overrides self.config_overrides = config_overrides
self.theme_overrides = theme_overrides self.theme_overrides = theme_overrides
self.paths = paths self.paths = paths
super(ConfigurableIPythonPowerline, self).init(is_prompt, old_widths) super(ConfigurableIPythonPowerline, self).init()
def do_setup(self, wrefs):
for wref in wrefs:
obj = wref()
if obj is not None:
setattr(obj, 'powerline', self)
def setup(**kwargs): def setup(**kwargs):
ip = get_ipython() ip = get_ipython()
old_widths = {} old_widths = {}
prompt_powerline = ConfigurableIPythonPowerline(True, old_widths, **kwargs) powerline = ConfigurableIPythonPowerline(**kwargs)
non_prompt_powerline = ConfigurableIPythonPowerline(False, old_widths, **kwargs)
def late_startup_hook(): def late_startup_hook():
last_in = {'nrspaces': 0} last_in = {'nrspaces': 0}
for attr, prompt_class, powerline, other_powerline in ( prompts = []
('prompt1', PowerlinePrompt1, prompt_powerline, non_prompt_powerline), for attr, prompt_class in (
('prompt2', PowerlinePrompt2, prompt_powerline, None), ('prompt1', PowerlinePrompt1),
('prompt_out', PowerlinePromptOut, non_prompt_powerline, None) ('prompt2', PowerlinePrompt2),
('prompt_out', PowerlinePromptOut)
): ):
old_prompt = getattr(ip.IP.outputcache, attr) old_prompt = getattr(ip.IP.outputcache, attr)
setattr(ip.IP.outputcache, attr, prompt_class(powerline, other_powerline, last_in, old_prompt)) prompt = prompt_class(powerline, last_in, old_prompt)
setattr(ip.IP.outputcache, attr, prompt)
prompts.append(ref(prompt))
powerline.setup(prompts)
raise TryNext() raise TryNext()
def shutdown_hook(): def shutdown_hook():
prompt_powerline.shutdown() powerline.shutdown()
non_prompt_powerline.shutdown()
raise TryNext() raise TryNext()
ip.IP.hooks.late_startup_hook.add(late_startup_hook) ip.IP.hooks.late_startup_hook.add(late_startup_hook)

View File

@ -23,17 +23,11 @@ class RewriteResult(object):
class IPythonPowerline(Powerline): class IPythonPowerline(Powerline):
def init(self, is_prompt, old_widths): def init(self):
super(IPythonPowerline, self).init( super(IPythonPowerline, self).init(
'ipython', 'ipython',
renderer_module=('.prompt' if is_prompt else None),
use_daemon_threads=True use_daemon_threads=True
) )
self.old_widths = old_widths
def create_renderer(self, *args, **kwargs):
super(IPythonPowerline, self).create_renderer(*args, **kwargs)
self.renderer.old_widths = self.old_widths
def get_config_paths(self): def get_config_paths(self):
if self.paths: if self.paths:
@ -56,5 +50,7 @@ class IPythonPowerline(Powerline):
mergedicts(r, self.theme_overrides[name]) mergedicts(r, self.theme_overrides[name])
return r return r
def do_setup(self, attr, obj): def do_setup(self, wref):
setattr(obj, attr, self) obj = wref()
if obj:
setattr(obj, 'powerline', self)

View File

@ -37,4 +37,38 @@ class IPythonRenderer(ShellRenderer):
return super(ShellRenderer, self).render(*args, **kwargs) return super(ShellRenderer, self).render(*args, **kwargs)
renderer = IPythonRenderer class IPythonPromptRenderer(IPythonRenderer):
'''Powerline ipython prompt (in and in2) renderer'''
escape_hl_start = '\x01'
escape_hl_end = '\x02'
class IPythonNonPromptRenderer(IPythonRenderer):
'''Powerline ipython non-prompt (out and rewrite) renderer'''
pass
class RendererProxy(object):
'''Powerline IPython renderer proxy which chooses appropriate renderer
Instantiates two renderer objects: one will be used for prompts and the
other for non-prompts.
'''
def __init__(self, **kwargs):
old_widths = {}
self.non_prompt_renderer = IPythonNonPromptRenderer(old_widths=old_widths, **kwargs)
self.prompt_renderer = IPythonPromptRenderer(old_widths=old_widths, **kwargs)
def render_above_lines(self, *args, **kwargs):
return self.non_prompt_renderer.render_above_lines(*args, **kwargs)
def render(self, is_prompt, *args, **kwargs):
return (self.prompt_renderer if is_prompt else self.non_prompt_renderer).render(
*args, **kwargs)
def shutdown(self, *args, **kwargs):
self.prompt_renderer.shutdown(*args, **kwargs)
self.non_prompt_renderer.shutdown(*args, **kwargs)
renderer = RendererProxy

View File

@ -1,12 +0,0 @@
# vim:fileencoding=utf-8:noet
from powerline.renderers.ipython import IPythonRenderer
class IPythonPromptRenderer(IPythonRenderer):
'''Powerline ipython prompt renderer'''
escape_hl_start = '\x01'
escape_hl_end = '\x02'
renderer = IPythonPromptRenderer

View File

@ -24,9 +24,9 @@ class ShellRenderer(Renderer):
character_translations = Renderer.character_translations.copy() character_translations = Renderer.character_translations.copy()
def __init__(self, *args, **kwargs): def __init__(self, old_widths=None, **kwargs):
super(ShellRenderer, self).__init__(*args, **kwargs) super(ShellRenderer, self).__init__(**kwargs)
self.old_widths = {} self.old_widths = old_widths if old_widths is not None else {}
def render(self, segment_info, **kwargs): def render(self, segment_info, **kwargs):
local_theme = segment_info.get('local_theme') local_theme = segment_info.get('local_theme')

View File

@ -118,11 +118,11 @@ class TestConfig(TestCase):
segment_info = Args(prompt_count=1) segment_info = Args(prompt_count=1)
with IpyPowerline(True, {}) as powerline: with IpyPowerline() as powerline:
for prompt_type in ['in', 'in2']: for prompt_type in ['in', 'in2']:
powerline.render(matcher_info=prompt_type, segment_info=segment_info) powerline.render(matcher_info=prompt_type, segment_info=segment_info)
powerline.render(matcher_info=prompt_type, segment_info=segment_info) powerline.render(matcher_info=prompt_type, segment_info=segment_info)
with IpyPowerline(False, {}) as powerline: with IpyPowerline() as powerline:
for prompt_type in ['out', 'rewrite']: for prompt_type in ['out', 'rewrite']:
powerline.render(matcher_info=prompt_type, segment_info=segment_info) powerline.render(matcher_info=prompt_type, segment_info=segment_info)
powerline.render(matcher_info=prompt_type, segment_info=segment_info) powerline.render(matcher_info=prompt_type, segment_info=segment_info)