diff --git a/client/powerline.c b/client/powerline.c index d6858770..cafd3d62 100644 --- a/client/powerline.c +++ b/client/powerline.c @@ -42,6 +42,26 @@ void do_write(int sd, const char *raw, size_t len) { } } +inline size_t true_sun_len(const struct sockaddr_un *ptr) { +#ifdef __linux__ + /* Because SUN_LEN uses strlen and abstract namespace paths begin + * with a null byte, SUN_LEN is broken for these. Passing the full + * struct size also fails on Linux, so compute manually. The + * abstract namespace is Linux-only. */ + if (ptr->sun_path[0] == '\0') { + return sizeof(ptr->sun_family) + strlen(ptr->sun_path + 1) + 1; + } +#endif +#ifdef SUN_LEN + /* If the vendor provided SUN_LEN, we may as well use it. */ + return SUN_LEN(ptr); +#else + /* SUN_LEN is not POSIX, so if it was not provided, use the struct + * size as a fallback. */ + return sizeof(struct sockaddr_un); +#endif +} + #ifdef __linux__ # define ADDRESS_TEMPLATE "powerline-ipc-%d" # define A +1 @@ -91,7 +111,7 @@ int main(int argc, char *argv[]) { server.sun_family = AF_UNIX; strncpy(server.sun_path A, address, strlen(address)); - if (connect(sd, (struct sockaddr *) &server, (socklen_t) (sizeof(server.sun_family) + strlen(address) A)) < 0) { + if (connect(sd, (struct sockaddr *) &server, true_sun_len(&server)) < 0) { close(sd); /* We failed to connect to the daemon, execute powerline instead */ argc = (argc < NEW_ARGV_SIZE - 1) ? argc : NEW_ARGV_SIZE - 1; diff --git a/docs/source/commands/lint.rst b/docs/source/commands/lint.rst index 2ea5ee6b..92d676d6 100644 --- a/docs/source/commands/lint.rst +++ b/docs/source/commands/lint.rst @@ -1,5 +1,7 @@ :orphan: +.. _command-powerline-lint: + powerline-lint manual page ========================== diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index d3754905..7c20eb17 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -16,7 +16,7 @@ Powerline provides default configurations in the following locations: :ref:`Main configuration ` :file:`powerline/config.json` -:ref:`Colorschemes ` +:ref:`Colorschemes ` :file:`powerline/colorschemes/{name}.json`, :file:`powerline/colorscheme/{extension}/__main__.json`, :file:`powerline/colorschemes/{extension}/{name}.json` @@ -33,6 +33,8 @@ corresponds to :file:`~/.config/powerline` on both platforms. If per-instance configuration is needed please refer to :ref:`Local configuration overrides `. +.. _configuration-merging: + .. note:: Existing multiple configuration files that have the same name, but are placed in different directories, will be merged. Merging happens in the following @@ -63,20 +65,21 @@ Quick setup guide This guide will help you with the initial configuration of Powerline. -Start by copying the entire set of default configuration files to the -corresponding path in your user config directory: - -.. code-block:: sh - - mkdir ~/.config/powerline - cp -R /path/to/powerline/config_files/* ~/.config/powerline +Look at configuration in :file:`{powerline_root}/powerline/config_files`. If you +want to modify some file you can create :file:`~/.config/powerline` directory +and put modifications there: all configuration files are :ref:`merged +` with each other. Each extension (vim, tmux, etc.) has its own theme, and they are located in -:file:`{config directory}/themes/{extension}/default.json`. +:file:`{config directory}/themes/{extension}/default.json`. Best way to modify +it is to copy this theme as a whole, remove ``segment_data`` key with +corresponding value if present (unless you need to modify it, in which case only +modifications must be left) and do necessary modifications in the list of +segments (lists are not subject to merging: this is why you need a copy). -If you want to move, remove or customize any of the provided segments, you -can do that by updating the segment dictionary in the theme you want to -customize. A segment dictionary looks like this: +If you want to move, remove or customize any of the provided segments in the +copy, you can do that by updating the segment dictionary in the theme you want +to customize. A segment dictionary looks like this: .. code-block:: javascript @@ -93,6 +96,11 @@ prompt or statusline. is valid JSON! It’s strongly recommended that you run your configuration files through ``jsonlint`` after changing them. +.. note:: + If your modifications appear not to work, run :ref:`powerline-lint script + `. This script should show you the location of the + error. + Some segments need a user configuration to work properly. Here’s a couple of segments that you may want to customize right away: diff --git a/docs/source/configuration/listers.rst b/docs/source/configuration/listers.rst index dc544b91..04e5371e 100644 --- a/docs/source/configuration/listers.rst +++ b/docs/source/configuration/listers.rst @@ -23,3 +23,9 @@ Vim listers .. automodule:: powerline.listers.vim :members: + +Pdb listers +----------- + +.. automodule:: powerline.listers.pdb + :members: diff --git a/docs/source/configuration/local.rst b/docs/source/configuration/local.rst index 3eec3ee0..5ea233f4 100644 --- a/docs/source/configuration/local.rst +++ b/docs/source/configuration/local.rst @@ -239,3 +239,9 @@ In order to keep shell prompt, but avoid launching Python twice to get unused In order to remove additional space from the end of the right prompt in fish that was added in order to support multiline prompt ``$POWERLINE_NO_FISH_ABOVE`` or ``$POWERLINE_NO_SHELL_ABOVE`` variable should be set. + +PDB overrides +============= + +Like shell bindings :ref:`PDB bindings ` take overrides from +:ref:`environment variables `. diff --git a/docs/source/configuration/segments/pdb.rst b/docs/source/configuration/segments/pdb.rst new file mode 100644 index 00000000..b9a104b2 --- /dev/null +++ b/docs/source/configuration/segments/pdb.rst @@ -0,0 +1,7 @@ +************ +PDB segments +************ + +.. automodule:: powerline.segments.pdb + :members: + diff --git a/docs/source/develop/segments.rst b/docs/source/develop/segments.rst index 509eb7f7..3fea00a2 100644 --- a/docs/source/develop/segments.rst +++ b/docs/source/develop/segments.rst @@ -469,6 +469,21 @@ Ipython Attribute ``prompt_count`` contains the so-called “history count” (equivalent to ``\N`` in ``in_template``). +Pdb +--- + +``pdb`` + Currently active :py:class:`pdb.Pdb` instance. + +``curframe`` + Frame which will be run next. Note: due to the existence of + :py:func:`powerline.listers.pdb.frame_lister` one must not use + ``segment_info['pdb'].curframe``. + +``initial_stack_length`` + Equal to the length of :py:attr:`pdb.Pdb.stack` at the first invocation of + the prompt decremented by one. + Segment class ============= diff --git a/docs/source/usage/other.rst b/docs/source/usage/other.rst index cbdab712..c160339f 100644 --- a/docs/source/usage/other.rst +++ b/docs/source/usage/other.rst @@ -124,3 +124,41 @@ the used profile: IPython=0.11* is not supported and does not work. IPython<0.10 was not tested (not installable by pip). + +.. _pdb-prompt: + +PDB prompt +========== + +To use Powerline with PDB prompt you need to use custom class. Inherit your +class from :py:class:`pdb.Pdb` and decorate it with +:py:func:`powerline.bindings.pdb.use_powerline_prompt`: + +.. code-block:: Python + + import pdb + + from powerline.bindings.pdb import use_powerline_prompt + + @use_powerline_prompt + class MyPdb(pdb.Pdb): + pass + + MyPdb.run('some.code.to.debug()') + +. Alternatively you may use + +.. code-block:: bash + + python -mpowerline.bindings.pdb path/to/script.py + +just like you used ``python -m pdb``. + +.. note: + If you are using Python-2.6 you need to use ``python + -mpowerline.bindings.pdb.__main__``, not what is shown above. + +.. warning: + Using PyPy (not PyPy3) forces ASCII-only prompts. In other cases unicode + characters are allowed, even if you use `pdbpp + `_. diff --git a/docs/source/usage/wm-widgets.rst b/docs/source/usage/wm-widgets.rst index 0a734ea7..bcbf206f 100644 --- a/docs/source/usage/wm-widgets.rst +++ b/docs/source/usage/wm-widgets.rst @@ -44,13 +44,31 @@ Add the following to :file:`~/.config/qtile/config.py`: ), ] +.. _bar-usage: + +LemonBoy’s bar +============== + +To run the bar simply pipe the output of the binding script into ``bar`` and +specify appropriate options, for example like this:: + + python /path/to/powerline/bindings/bar/powerline-bar.py | bar + +to run with i3, simply ``exec`` this in i3 config file:: + + exec python /path/to/powerline/bindings/bar/powerline-bar.py --i3 | bar + +See the `bar documentation `_ for more +information and options. + I3 bar ====== .. note:: - Until the patch is done in i3 a custom ``i3bar`` build called ``i3bgbar`` is - needed. The source is available `in S0lll0s/i3bgbar github repository - `_. + As the patch to include background-colors in i3bar is likely not to be + merged, it is recommended to instead run ``bar`` (see above). The source for + i3bgbar is however still available `here + `_. Add the following to :file:`~/.i3/config`:: diff --git a/powerline/bindings/bar/powerline-bar.py b/powerline/bindings/bar/powerline-bar.py new file mode 100755 index 00000000..898c5cc6 --- /dev/null +++ b/powerline/bindings/bar/powerline-bar.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import sys +import time + +from threading import Lock +from argparse import ArgumentParser + +from powerline import Powerline +from powerline.lib.monotonic import monotonic +from powerline.lib.encoding import get_unicode_writer + + +class BarPowerline(Powerline): + get_encoding = staticmethod(lambda: 'utf-8') + + def init(self): + super(BarPowerline, self).init(ext='wm', renderer_module='bar') + + +def render(event=None, data=None, sub=None): + global lock + with lock: + write(powerline.render()) + write('\n') + sys.stdout.flush() + + +if __name__ == '__main__': + parser = ArgumentParser(description='Powerline BAR bindings.') + parser.add_argument( + '--i3', action='store_true', + help='Subscribe for i3 events.' + ) + args = parser.parse_args() + powerline = BarPowerline() + + interval = 0.5 + lock = Lock() + + write = get_unicode_writer(encoding='utf-8') + + if args.i3: + import i3 + sub = i3.Subscription(render, 'workspace') + + while True: + start_time = monotonic() + render() + time.sleep(max(interval - (monotonic() - start_time), 0.1)) diff --git a/powerline/bindings/fish/powerline-setup.fish b/powerline/bindings/fish/powerline-setup.fish index dc631c5e..4c80cae7 100644 --- a/powerline/bindings/fish/powerline-setup.fish +++ b/powerline/bindings/fish/powerline-setup.fish @@ -76,7 +76,6 @@ function powerline-setup " _powerline_set_columns end - _powerline_bind_mode _powerline_set_default_mode _powerline_update end diff --git a/powerline/bindings/pdb/__init__.py b/powerline/bindings/pdb/__init__.py new file mode 100644 index 00000000..4033e611 --- /dev/null +++ b/powerline/bindings/pdb/__init__.py @@ -0,0 +1,183 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import sys +import pdb + +from powerline.pdb import PDBPowerline +from powerline.lib.encoding import get_preferred_output_encoding +from powerline.lib.unicode import unicode + + +if sys.version_info < (3,): + # XXX The below classes make code compatible with PDBpp which uses pyrepl + # which does not expect unicode or something above ASCII. They are + # completely not needed if pdbpp is not used, but that’s not always the + # case. + class PowerlineRenderBytesResult(bytes): + def __new__(cls, s, encoding=None): + encoding = encoding or s.encoding + if isinstance(s, PowerlineRenderResult): + return s.encode(encoding) + self = bytes.__new__(cls, s.encode(encoding) if isinstance(s, unicode) else s) + self.encoding = encoding + return self + + for meth in ( + '__contains__', + 'partition', 'rpartition', + 'split', 'rsplit', + 'count', 'join', + ): + exec(( + 'def {0}(self, *args):\n' + ' if any((isinstance(arg, unicode) for arg in args)):\n' + ' return self.__unicode__().{0}(*args)\n' + ' else:\n' + ' return bytes.{0}(self, *args)' + ).format(meth)) + + for meth in ( + 'find', 'rfind', + 'index', 'rindex', + ): + exec(( + 'def {0}(self, *args):\n' + ' if any((isinstance(arg, unicode) for arg in args)):\n' + ' args = [arg.encode(self.encoding) if isinstance(arg, unicode) else arg for arg in args]\n' + ' return bytes.{0}(self, *args)' + ).format(meth)) + + def __len__(self): + return len(self.decode(self.encoding)) + + def __getitem__(self, *args): + return PowerlineRenderBytesResult(bytes.__getitem__(self, *args), encoding=self.encoding) + + def __getslice__(self, *args): + return PowerlineRenderBytesResult(bytes.__getslice__(self, *args), encoding=self.encoding) + + @staticmethod + def add(encoding, *args): + if any((isinstance(arg, unicode) for arg in args)): + return PowerlineRenderResult(''.join(( + arg + if isinstance(arg, unicode) + else arg.decode(encoding) + for arg in args + )), encoding) + else: + return PowerlineRenderBytesResult(b''.join(args), encoding=encoding) + + def __add__(self, other): + return self.add(self.encoding, self, other) + + def __radd__(self, other): + return self.add(self.encoding, other, self) + + def __unicode__(self): + return PowerlineRenderResult(self) + + class PowerlineRenderResult(unicode): + def __new__(cls, s, encoding=None): + encoding = ( + encoding + or getattr(s, 'encoding', None) + or get_preferred_output_encoding() + ) + if isinstance(s, unicode): + self = unicode.__new__(cls, s) + else: + self = unicode.__new__(cls, s, encoding, 'replace') + self.encoding = encoding + return self + + def __str__(self): + return PowerlineRenderBytesResult(self) + + def __getitem__(self, *args): + return PowerlineRenderResult(unicode.__getitem__(self, *args)) + + def __getslice__(self, *args): + return PowerlineRenderResult(unicode.__getslice__(self, *args)) + + @staticmethod + def add(encoding, *args): + return PowerlineRenderResult(''.join(( + arg + if isinstance(arg, unicode) + else arg.decode(encoding) + for arg in args + )), encoding) + + def __add__(self, other): + return self.add(self.encoding, self, other) + + def __radd__(self, other): + return self.add(self.encoding, other, self) + + def encode(self, *args, **kwargs): + return PowerlineRenderBytesResult(unicode.encode(self, *args, **kwargs), args[0]) +else: + PowerlineRenderResult = str + + +def use_powerline_prompt(cls): + '''Decorator that installs powerline prompt to the class + + :param pdb.Pdb cls: + Class that should be decorated. + + :return: + ``cls`` argument or a class derived from it. Latter is used to turn + old-style classes into new-style classes. + ''' + @property + def prompt(self): + try: + powerline = self.powerline + except AttributeError: + powerline = PDBPowerline() + powerline.setup(self) + self.powerline = powerline + return PowerlineRenderResult(powerline.render(side='left')) + + @prompt.setter + def prompt(self, _): + pass + + @prompt.deleter + def prompt(self): + pass + + if not hasattr(cls, '__class__'): + # Old-style class: make it new-style or @property will not work. + old_cls = cls + + class cls(cls, object): + __module__ = cls.__module__ + __doc__ = cls.__doc__ + + cls.__name__ = old_cls.__name__ + + cls.prompt = prompt + + return cls + + +def main(): + '''Run module as a script + + Uses :py:func:`pdb.main` function directly, but prior to that it mocks + :py:class:`pdb.Pdb` class with powerline-specific class instance. + ''' + orig_pdb = pdb.Pdb + + @use_powerline_prompt + class Pdb(pdb.Pdb, object): + def __init__(self): + orig_pdb.__init__(self) + + pdb.Pdb = Pdb + + return pdb.main() diff --git a/powerline/bindings/pdb/__main__.py b/powerline/bindings/pdb/__main__.py new file mode 100755 index 00000000..768b2f29 --- /dev/null +++ b/powerline/bindings/pdb/__main__.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.bindings.pdb import main + + +if __name__ == '__main__': + main() diff --git a/powerline/bindings/rc/powerline.rc b/powerline/bindings/rc/powerline.rc index 732d40c8..15b313a9 100644 --- a/powerline/bindings/rc/powerline.rc +++ b/powerline/bindings/rc/powerline.rc @@ -15,7 +15,7 @@ fn _powerline_continuation_prompt { _powerline_prompt --renderer-arg 'local_theme=continuation' $* } fn _powerline_prompt { - $POWERLINE_COMMAND $POWERLINE_COMMAND_ARGS shell aboveleft -r.rcsh --last-pipe-status $^_POWERLINE_STATUS --last-exit-code $_POWERLINE_STATUS($#_POWERLINE_STATUS) --jobnum $_POWERLINE_JOBNUM --renderer-arg 'client_id='$pid $* + $POWERLINE_COMMAND $POWERLINE_COMMAND_ARGS shell aboveleft -r.readline --last-pipe-status $^_POWERLINE_STATUS --last-exit-code $_POWERLINE_STATUS($#_POWERLINE_STATUS) --jobnum $_POWERLINE_JOBNUM --renderer-arg 'client_id='$pid $* } fn _powerline_set_prompt { _POWERLINE_STATUS = ( $status ) diff --git a/powerline/bindings/tmux/powerline.conf b/powerline/bindings/tmux/powerline.conf index 7ea407a5..29ec6a47 100644 --- a/powerline/bindings/tmux/powerline.conf +++ b/powerline/bindings/tmux/powerline.conf @@ -1,2 +1,2 @@ -if-shell 'test -z "$POWERLINE_CONFIG_COMMAND"' 'set-environment -g POWERLINE_CONFIG_COMMAND powerline-config; run-shell "env \"\$POWERLINE_CONFIG_COMMAND\" tmux setup"' 'run-shell "env \"\$POWERLINE_CONFIG_COMMAND\" tmux setup"' +if-shell 'env "$POWERLINE_CONFIG_COMMAND" tmux setup' '' 'run-shell "powerline-config tmux setup"' # vim: ft=tmux diff --git a/powerline/bindings/zsh/__init__.py b/powerline/bindings/zsh/__init__.py index 291a4c39..cbe5f957 100644 --- a/powerline/bindings/zsh/__init__.py +++ b/powerline/bindings/zsh/__init__.py @@ -101,7 +101,20 @@ class Environment(object): return False -environ = getattr(zsh, 'environ', Environment()) +if hasattr(getattr(zsh, 'environ', None), '__contains__'): + environ = zsh.environ +else: + environ = Environment() + + +if hasattr(zsh, 'expand') and zsh.expand('${:-}') == '': + zsh_expand = zsh.expand +else: + def zsh_expand(s): + zsh.eval('_POWERLINE_REPLY="' + s + '"') + ret = zsh.getvalue('_POWERLINE_REPLY') + zsh.setvalue('_POWERLINE_REPLY', None) + return ret class ZshPowerline(ShellPowerline): @@ -135,8 +148,8 @@ class Prompt(object): self.theme = theme def __str__(self): - zsh.eval('_POWERLINE_PARSER_STATE="${(%):-%_}"') - zsh.eval('_POWERLINE_SHORTENED_PATH="${(%):-%~}"') + parser_state = u(zsh_expand('${(%):-%_}')) + shortened_path = u(zsh_expand('${(%):-%~}')) try: mode = u(zsh.getvalue('_POWERLINE_MODE')) except IndexError: @@ -150,13 +163,11 @@ class Prompt(object): 'environ': environ, 'client_id': 1, 'local_theme': self.theme, - 'parser_state': zsh.getvalue('_POWERLINE_PARSER_STATE'), - 'shortened_path': zsh.getvalue('_POWERLINE_SHORTENED_PATH'), + 'parser_state': parser_state, + 'shortened_path': shortened_path, 'mode': mode, 'default_mode': default_mode, } - zsh.setvalue('_POWERLINE_PARSER_STATE', None) - zsh.setvalue('_POWERLINE_SHORTENED_PATH', None) try: zle_rprompt_indent = zsh.getvalue('ZLE_RPROMPT_INDENT') except IndexError: @@ -194,7 +205,7 @@ def set_prompt(powerline, psvar, side, theme, above=False): savedps = None zpyvar = 'ZPYTHON_POWERLINE_' + psvar prompt = Prompt(powerline, side, theme, psvar, savedps, above) - zsh.eval('unset ' + zpyvar) + zsh.setvalue(zpyvar, None) zsh.set_special_string(zpyvar, prompt) zsh.setvalue(psvar, '${' + zpyvar + '}') return ref(prompt) diff --git a/powerline/commands/main.py b/powerline/commands/main.py index 605098c6..8c41b476 100644 --- a/powerline/commands/main.py +++ b/powerline/commands/main.py @@ -150,7 +150,7 @@ def get_argparser(ArgumentParser=argparse.ArgumentParser): return parser -def write_output(args, powerline, segment_info, write, encoding): +def write_output(args, powerline, segment_info, write): if args.renderer_arg: segment_info.update(args.renderer_arg) if args.side.startswith('above'): @@ -159,8 +159,7 @@ def write_output(args, powerline, segment_info, write, encoding): segment_info=segment_info, mode=segment_info.get('mode', None), ): - write(line.encode(encoding, 'replace')) - write(b'\n') + write(line + '\n') args.side = args.side[len('above'):] if args.side: @@ -170,4 +169,4 @@ def write_output(args, powerline, segment_info, write, encoding): segment_info=segment_info, mode=segment_info.get('mode', None), ) - write(rendered.encode(encoding, 'replace')) + write(rendered) diff --git a/powerline/config_files/colorschemes/default.json b/powerline/config_files/colorschemes/default.json index fbea5532..b1d653c0 100644 --- a/powerline/config_files/colorschemes/default.json +++ b/powerline/config_files/colorschemes/default.json @@ -1,13 +1,19 @@ { "name": "Default", "groups": { + "information:additional": { "fg": "gray9", "bg": "gray4", "attrs": [] }, + "information:regular": { "fg": "gray10", "bg": "gray4", "attrs": ["bold"] }, + "information:highlighted": { "fg": "white", "bg": "gray4", "attrs": [] }, + "information:priority": { "fg": "brightyellow", "bg": "mediumorange", "attrs": [] }, + "critical:failure": { "fg": "white", "bg": "darkestred", "attrs": [] }, + "critical:success": { "fg": "white", "bg": "darkestgreen", "attrs": [] }, "background": { "fg": "white", "bg": "gray0", "attrs": [] }, "background:divider": { "fg": "gray5", "bg": "gray0", "attrs": [] }, "session": { "fg": "black", "bg": "gray10", "attrs": ["bold"] }, "date": { "fg": "gray8", "bg": "gray2", "attrs": [] }, "time": { "fg": "gray10", "bg": "gray2", "attrs": ["bold"] }, "time:divider": { "fg": "gray5", "bg": "gray2", "attrs": [] }, - "email_alert": { "fg": "white", "bg": "brightred", "attrs": ["bold"] }, + "email_alert": "warning:regular", "email_alert_gradient": { "fg": "white", "bg": "yellow_orange_red", "attrs": ["bold"] }, "hostname": { "fg": "black", "bg": "gray10", "attrs": ["bold"] }, "weather": { "fg": "gray8", "bg": "gray0", "attrs": [] }, @@ -31,13 +37,12 @@ "battery_empty": { "fg": "white", "bg": "gray0", "attrs": [] }, "player": { "fg": "gray10", "bg": "black", "attrs": [] }, "user": { "fg": "white", "bg": "darkblue", "attrs": ["bold"] }, - "superuser": { "fg": "white", "bg": "brightred", "attrs": ["bold"] }, "branch": { "fg": "gray9", "bg": "gray2", "attrs": [] }, "branch_dirty": { "fg": "brightyellow", "bg": "gray2", "attrs": [] }, "branch_clean": { "fg": "gray9", "bg": "gray2", "attrs": [] }, "branch:divider": { "fg": "gray7", "bg": "gray2", "attrs": [] }, - "cwd": { "fg": "gray9", "bg": "gray4", "attrs": [] }, - "cwd:current_folder": { "fg": "gray10", "bg": "gray4", "attrs": ["bold"] }, + "cwd": "information:additional", + "cwd:current_folder": "information:regular", "cwd:divider": { "fg": "gray7", "bg": "gray4", "attrs": [] }, "virtualenv": { "fg": "white", "bg": "darkcyan", "attrs": [] }, "attached_clients": { "fg": "gray8", "bg": "gray0", "attrs": [] } diff --git a/powerline/config_files/colorschemes/ipython/__main__.json b/powerline/config_files/colorschemes/ipython/__main__.json new file mode 100644 index 00000000..982ea355 --- /dev/null +++ b/powerline/config_files/colorschemes/ipython/__main__.json @@ -0,0 +1,6 @@ +{ + "groups": { + "prompt": "information:additional", + "prompt_count": "information:highlighted" + } +} diff --git a/powerline/config_files/colorschemes/ipython/default.json b/powerline/config_files/colorschemes/ipython/default.json deleted file mode 100644 index a5da8ffc..00000000 --- a/powerline/config_files/colorschemes/ipython/default.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "name": "Default color scheme for IPython prompt", - "groups": { - "prompt": { "fg": "gray9", "bg": "gray4", "attrs": [] }, - "prompt_count": { "fg": "white", "bg": "gray4", "attrs": [] } - } -} diff --git a/powerline/config_files/colorschemes/pdb/__main__.json b/powerline/config_files/colorschemes/pdb/__main__.json new file mode 100644 index 00000000..01a51fe5 --- /dev/null +++ b/powerline/config_files/colorschemes/pdb/__main__.json @@ -0,0 +1,8 @@ +{ + "groups": { + "current_code_name": "information:additional", + "current_context": "current_code_name", + "current_line": "information:regular", + "current_file": "information:regular" + } +} diff --git a/powerline/config_files/colorschemes/pdb/default.json b/powerline/config_files/colorschemes/pdb/default.json new file mode 100644 index 00000000..b97acf7f --- /dev/null +++ b/powerline/config_files/colorschemes/pdb/default.json @@ -0,0 +1,5 @@ +{ + "groups": { + "stack_depth": { "fg": "gray1", "bg": "gray10", "attrs": ["bold"] } + } +} diff --git a/powerline/config_files/colorschemes/pdb/solarized.json b/powerline/config_files/colorschemes/pdb/solarized.json new file mode 100644 index 00000000..2e1c787c --- /dev/null +++ b/powerline/config_files/colorschemes/pdb/solarized.json @@ -0,0 +1,5 @@ +{ + "groups": { + "stack_depth": { "fg": "solarized:base03", "bg": "solarized:base2", "attrs": ["bold"] } + } +} diff --git a/powerline/config_files/colorschemes/shell/__main__.json b/powerline/config_files/colorschemes/shell/__main__.json index c8c2aaa2..6e3856f5 100644 --- a/powerline/config_files/colorschemes/shell/__main__.json +++ b/powerline/config_files/colorschemes/shell/__main__.json @@ -1,6 +1,10 @@ { "groups": { "continuation": "cwd", - "continuation:current": "cwd:current_folder" + "continuation:current": "cwd:current_folder", + "exit_fail": "critical:failure", + "exit_success": "critical:success", + "jobnum": "information:priority", + "superuser": "warning:regular" } } diff --git a/powerline/config_files/colorschemes/shell/default.json b/powerline/config_files/colorschemes/shell/default.json index 09f3d211..1126febf 100644 --- a/powerline/config_files/colorschemes/shell/default.json +++ b/powerline/config_files/colorschemes/shell/default.json @@ -2,9 +2,6 @@ "name": "Default color scheme for shell prompts", "groups": { "hostname": { "fg": "brightyellow", "bg": "mediumorange", "attrs": [] }, - "jobnum": { "fg": "brightyellow", "bg": "mediumorange", "attrs": [] }, - "exit_fail": { "fg": "white", "bg": "darkestred", "attrs": [] }, - "exit_success": { "fg": "white", "bg": "darkestgreen", "attrs": [] }, "environment": { "fg": "white", "bg": "darkestgreen", "attrs": [] }, "mode": { "fg": "darkestgreen", "bg": "brightgreen", "attrs": ["bold"] }, "attached_clients": { "fg": "white", "bg": "darkestgreen", "attrs": [] } diff --git a/powerline/config_files/colorschemes/shell/solarized.json b/powerline/config_files/colorschemes/shell/solarized.json index 43b1666a..69dcab1d 100644 --- a/powerline/config_files/colorschemes/shell/solarized.json +++ b/powerline/config_files/colorschemes/shell/solarized.json @@ -1,10 +1,7 @@ { "name": "Solarized dark for shell", "groups": { - "jobnum": { "fg": "solarized:base3", "bg": "solarized:base01", "attrs": [] }, - "exit_fail": { "fg": "solarized:base3", "bg": "solarized:red", "attrs": [] }, - "exit_success": { "fg": "solarized:base3", "bg": "solarized:green", "attrs": [] }, - "mode": { "fg": "solarized:base3", "bg": "solarized:green", "attrs": ["bold"] } + "mode": { "fg": "solarized:base3", "bg": "solarized:green", "attrs": ["bold"] } }, "mode_translations": { "vicmd": { diff --git a/powerline/config_files/colorschemes/solarized.json b/powerline/config_files/colorschemes/solarized.json index 9730e825..6a573dcd 100644 --- a/powerline/config_files/colorschemes/solarized.json +++ b/powerline/config_files/colorschemes/solarized.json @@ -1,18 +1,34 @@ { "name": "Solarized dark", "groups": { - "background": { "fg": "solarized:base3", "bg": "solarized:base02", "attrs": [] }, - "user": { "fg": "solarized:base3", "bg": "solarized:blue", "attrs": ["bold"] }, - "superuser": { "fg": "solarized:base3", "bg": "solarized:red", "attrs": ["bold"] }, - "virtualenv": { "fg": "solarized:base3", "bg": "solarized:green", "attrs": [] }, - "branch": { "fg": "solarized:base1", "bg": "solarized:base02", "attrs": [] }, - "branch_dirty": { "fg": "solarized:yellow", "bg": "solarized:base02", "attrs": [] }, - "branch_clean": { "fg": "solarized:base1", "bg": "solarized:base02", "attrs": [] }, - "cwd": { "fg": "solarized:base2", "bg": "solarized:base01", "attrs": [] }, - "cwd:current_folder": { "fg": "solarized:base3", "bg": "solarized:base01", "attrs": ["bold"] }, - "cwd:divider": { "fg": "solarized:base1", "bg": "solarized:base01", "attrs": [] }, - "hostname": { "fg": "solarized:base3", "bg": "solarized:base01", "attrs": [] }, - "environment": { "fg": "solarized:base3", "bg": "solarized:green", "attrs": [] }, - "attached_clients": { "fg": "solarized:base3", "bg": "solarized:green", "attrs": [] } + "information:additional": { "fg": "solarized:base2", "bg": "solarized:base01", "attrs": [] }, + "information:regular": { "fg": "solarized:base3", "bg": "solarized:base01", "attrs": ["bold"] }, + "information:highlighted": { "fg": "solarized:base3", "bg": "solarized:base01", "attrs": ["bold"]}, + "information:priority": { "fg": "solarized:base3", "bg": "solarized:yellow", "attrs": [] }, + "warning:regular": { "fg": "solarized:base3", "bg": "solarized:red", "attrs": [] }, + "critical:failure": { "fg": "solarized:base3", "bg": "solarized:red", "attrs": [] }, + "critical:success": { "fg": "solarized:base3", "bg": "solarized:green", "attrs": [] }, + "background": { "fg": "solarized:base3", "bg": "solarized:base02", "attrs": [] }, + "background:divider": { "fg": "solarized:base1", "bg": "solarized:base02", "attrs": [] }, + "user": { "fg": "solarized:base3", "bg": "solarized:blue", "attrs": ["bold"] }, + "virtualenv": { "fg": "solarized:base3", "bg": "solarized:green", "attrs": [] }, + "branch": { "fg": "solarized:base1", "bg": "solarized:base02", "attrs": [] }, + "branch_dirty": { "fg": "solarized:yellow", "bg": "solarized:base02", "attrs": [] }, + "branch_clean": { "fg": "solarized:base1", "bg": "solarized:base02", "attrs": [] }, + "email_alert_gradient": { "fg": "solarized:base3", "bg": "yellow_orange_red", "attrs": [] }, + "email_alert": "warning:regular", + "cwd": "information:additional", + "cwd:current_folder": "information:regular", + "cwd:divider": { "fg": "solarized:base1", "bg": "solarized:base01", "attrs": [] }, + "hostname": { "fg": "solarized:base3", "bg": "solarized:base01", "attrs": [] }, + "environment": { "fg": "solarized:base3", "bg": "solarized:green", "attrs": [] }, + "attached_clients": { "fg": "solarized:base3", "bg": "solarized:green", "attrs": [] }, + "date": { "fg": "solarized:base1", "bg": "solarized:base02", "attrs": [] }, + "time": { "fg": "solarized:base1", "bg": "solarized:base02", "attrs": ["bold"] }, + "time:divider": { "fg": "solarized:base1", "bg": "solarized:base02", "attrs": [] }, + "system_load": { "fg": "solarized:base1", "bg": "solarized:base03", "attrs": [] }, + "weather_temp_gradient": { "fg": "blue_red", "bg": "solarized:base03", "attrs": [] }, + "weather": { "fg": "solarized:base1", "bg": "solarized:base03", "attrs": [] }, + "uptime": { "fg": "solarized:base1", "bg": "solarized:base03", "attrs": [] } } } diff --git a/powerline/config_files/config.json b/powerline/config_files/config.json index 9502129a..bd2cf2bb 100644 --- a/powerline/config_files/config.json +++ b/powerline/config_files/config.json @@ -12,6 +12,10 @@ "in2": "in2" } }, + "pdb": { + "colorscheme": "default", + "theme": "default" + }, "shell": { "colorscheme": "default", "theme": "default", diff --git a/powerline/config_files/themes/pdb/default.json b/powerline/config_files/themes/pdb/default.json new file mode 100644 index 00000000..dcae1080 --- /dev/null +++ b/powerline/config_files/themes/pdb/default.json @@ -0,0 +1,27 @@ +{ + "default_module": "powerline.segments.pdb", + "segments": { + "left": [ + { + "function": "stack_depth" + }, + { + "type": "segment_list", + "function": "powerline.listers.pdb.frame_lister", + "segments": [ + { + "function": "current_file", + "after": ":" + }, + { + "function": "current_line", + "after": " " + }, + { + "function": "current_code_name" + } + ] + } + ] + } +} diff --git a/powerline/lib/encoding.py b/powerline/lib/encoding.py index 1ca48ce6..798fc396 100644 --- a/powerline/lib/encoding.py +++ b/powerline/lib/encoding.py @@ -86,3 +86,28 @@ def get_preferred_environment_encoding(): locale.getpreferredencoding() or 'utf-8' ) + + +def get_unicode_writer(stream=sys.stdout, encoding=None, errors='replace'): + '''Get function which will write unicode string to the given stream + + Writing is done using encoding returned by + :py:func:`get_preferred_output_encoding`. + + :param file stream: + Stream to write to. Default value is :py:attr:`sys.stdout`. + :param str encoding: + Determines which encoding to use. If this argument is specified then + :py:func:`get_preferred_output_encoding` is not used. + :param str errors: + Determines what to do with characters which cannot be encoded. See + ``errors`` argument of :py:func:`codecs.encode`. + + :return: Callable which writes unicode string to the given stream using + the preferred output encoding. + ''' + encoding = encoding or get_preferred_output_encoding() + if sys.version_info < (3,) or not hasattr(stream, 'buffer'): + return lambda s: stream.write(s.encode(encoding, errors)) + else: + return lambda s: stream.buffer.write(s.encode(encoding, errors)) diff --git a/powerline/lint/__init__.py b/powerline/lint/__init__.py index d6ba0bfe..340e69a5 100644 --- a/powerline/lint/__init__.py +++ b/powerline/lint/__init__.py @@ -233,6 +233,7 @@ segment_spec = Spec( segments=sub_segments_spec, ).func(check_full_segment_data) sub_segments_spec.optional().list(segment_spec) +del sub_segments_spec segments_spec = Spec().optional().list(segment_spec).copy segdict_spec = Spec( left=segments_spec().context_message('Error while loading segments from left side (key {key})'), @@ -546,6 +547,8 @@ def check(paths=None, debug=False, echoerr=echoerr, require_ext=None): econfigs.get('__main__'), ecconfigs, ) + if not (mconfigs[0] or mconfigs[2]): + continue config = None for mconfig in mconfigs: if not mconfig: @@ -554,7 +557,7 @@ def check(paths=None, debug=False, echoerr=echoerr, require_ext=None): config = mergedicts_copy(config, mconfig) else: config = mconfig - colorscheme_configs[colorscheme] = config + colorscheme_configs[ext][colorscheme] = config theme_configs = dict2(loaded_configs['themes']) top_theme_configs = dict(loaded_configs['top_themes']) diff --git a/powerline/lint/checks.py b/powerline/lint/checks.py index 24c91f43..4fcd71dd 100644 --- a/powerline/lint/checks.py +++ b/powerline/lint/checks.py @@ -499,6 +499,56 @@ def check_segment_function(function_name, data, context, echoerr): return True, False, False +def hl_group_in_colorscheme(hl_group, cconfig, allow_gradients, data, context, echoerr): + havemarks(hl_group, cconfig) + if hl_group not in cconfig.get('groups', {}): + return False + elif not allow_gradients or allow_gradients == 'force': + group_config = cconfig['groups'][hl_group] + while isinstance(group_config, unicode): + try: + group_config = cconfig['groups'][group_config] + except KeyError: + # No such group. Error was already reported when checking + # colorschemes. + return True + havemarks(group_config) + hadgradient = False + for ckey in ('fg', 'bg'): + color = group_config.get(ckey) + if not color: + # No color. Error was already reported when checking + # colorschemes. + return True + havemarks(color) + # Gradients are only allowed for function segments. Note that + # whether *either* color or gradient exists should have been + # already checked + hascolor = color in data['colors_config'].get('colors', {}) + hasgradient = color in data['colors_config'].get('gradients', {}) + if hasgradient: + hadgradient = True + if allow_gradients is False and not hascolor and hasgradient: + echoerr( + context='Error while checking highlight group in theme (key {key})'.format( + key=context.key), + context_mark=hl_group.mark, + problem='group {0} is using gradient {1} instead of a color'.format(hl_group, color), + problem_mark=color.mark + ) + return False + if allow_gradients == 'force' and not hadgradient: + echoerr( + context='Error while checking highlight group in theme (key {key})'.format( + key=context.key), + context_mark=hl_group.mark, + problem='group {0} should have at least one gradient color, but it has no'.format(hl_group), + problem_mark=group_config.mark + ) + return False + return True + + def hl_exists(hl_group, data, context, echoerr, allow_gradients=False): havemarks(hl_group) ext = data['ext'] @@ -507,45 +557,14 @@ def hl_exists(hl_group, data, context, echoerr, allow_gradients=False): # twice return [] r = [] + found = False for colorscheme, cconfig in data['colorscheme_configs'][ext].items(): - if hl_group not in cconfig.get('groups', {}): + if hl_group_in_colorscheme(hl_group, cconfig, allow_gradients, data, context, echoerr): + found = True + else: r.append(colorscheme) - elif not allow_gradients or allow_gradients == 'force': - group_config = cconfig['groups'][hl_group] - havemarks(group_config) - hadgradient = False - for ckey in ('fg', 'bg'): - color = group_config.get(ckey) - if not color: - # No color. Error was already reported. - continue - havemarks(color) - # Gradients are only allowed for function segments. Note that - # whether *either* color or gradient exists should have been - # already checked - hascolor = color in data['colors_config'].get('colors', {}) - hasgradient = color in data['colors_config'].get('gradients', {}) - if hasgradient: - hadgradient = True - if allow_gradients is False and not hascolor and hasgradient: - echoerr( - context='Error while checking highlight group in theme (key {key})'.format( - key=context.key), - context_mark=hl_group.mark, - problem='group {0} is using gradient {1} instead of a color'.format(hl_group, color), - problem_mark=color.mark - ) - r.append(colorscheme) - continue - if allow_gradients == 'force' and not hadgradient: - echoerr( - context='Error while checking highlight group in theme (key {key})'.format( - key=context.key), - context_mark=hl_group.mark, - problem='group {0} should have at least one gradient color, but it has no'.format(hl_group), - problem_mark=group_config.mark - ) - r.append(colorscheme) + if not found: + pass return r diff --git a/powerline/listers/pdb.py b/powerline/listers/pdb.py new file mode 100644 index 00000000..24e11eaf --- /dev/null +++ b/powerline/listers/pdb.py @@ -0,0 +1,37 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.theme import requires_segment_info + + +@requires_segment_info +def frame_lister(pl, segment_info, full_stack=False, maxframes=3): + '''List all frames in segment_info format + + :param bool full_stack: + If true, then all frames in the stack are listed. Normally N first + frames are discarded where N is a number of frames present at the first + invocation of the prompt minus one. + :param int maxframes: + Maximum number of frames to display. + ''' + if full_stack: + initial_stack_length = 0 + frames = segment_info['pdb'].stack + else: + initial_stack_length = segment_info['initial_stack_length'] + frames = segment_info['pdb'].stack[initial_stack_length:] + + if len(frames) > maxframes: + frames = frames[-maxframes:] + + return ( + ( + { + 'curframe': frame[0], + 'initial_stack_length': initial_stack_length, + }, + {} + ) + for frame in frames + ) diff --git a/powerline/pdb.py b/powerline/pdb.py new file mode 100644 index 00000000..b1e13cea --- /dev/null +++ b/powerline/pdb.py @@ -0,0 +1,48 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import sys +import platform +import os + +from powerline import Powerline +from powerline.lib.overrides import parse_override_var +from powerline.lib.dict import mergeargs, mergedicts + + +class PDBPowerline(Powerline): + '''PDB-specific powerline bindings + ''' + def init(self, **kwargs): + return super(PDBPowerline, self).init( + ext='pdb', + renderer_module='pdb', + **kwargs + ) + + def do_setup(self, pdb): + self.update_renderer() + self.renderer.set_pdb(pdb) + + def load_main_config(self): + r = super(PDBPowerline, self).load_main_config() + config_overrides = os.environ.get('POWERLINE_CONFIG_OVERRIDES') + if config_overrides: + mergedicts(r, mergeargs(parse_override_var(config_overrides))) + return r + + def load_theme_config(self, name): + r = super(PDBPowerline, self).load_theme_config(name) + theme_overrides = os.environ.get('POWERLINE_THEME_OVERRIDES') + if theme_overrides: + theme_overrides_dict = mergeargs(parse_override_var(theme_overrides)) + if name in theme_overrides_dict: + mergedicts(r, theme_overrides_dict[name]) + return r + + def get_config_paths(self): + paths = [path for path in os.environ.get('POWERLINE_CONFIG_PATHS', '').split(':') if path] + return paths or super(PDBPowerline, self).get_config_paths() + + if sys.version_info < (3,) and platform.python_implementation() == 'PyPy': + get_encoding = staticmethod(lambda: 'ascii') diff --git a/powerline/renderer.py b/powerline/renderer.py index 6d9b56a7..c03c42e8 100644 --- a/powerline/renderer.py +++ b/powerline/renderer.py @@ -124,8 +124,10 @@ class Renderer(object): 'getcwd': getattr(os, 'getcwdu', os.getcwd), 'home': os.environ.get('HOME'), } - '''Basic segment info. Is merged with local segment information by - ``.get_segment_info()`` method. Keys: + '''Basic segment info + + Is merged with local segment information by :py:meth:`get_segment_info` + method. Keys: ``environ`` Object containing environment variables. Must define at least the @@ -279,9 +281,10 @@ class Renderer(object): string_width)``. Returns a three-tuple if ``output_raw`` is also ``True``: ``(colored_string, colorless_string, string_width)``. :param dict segment_info: - Segment information. See also ``.get_segment_info()`` method. + Segment information. See also :py:meth:`get_segment_info` method. :param matcher_info: - Matcher information. Is processed in ``.get_theme()`` method. + Matcher information. Is processed in :py:meth:`get_segment_info` + method. ''' theme = self.get_theme(matcher_info) return self.do_render( @@ -504,7 +507,7 @@ class Renderer(object): def hl(self, contents, fg=None, bg=None, attrs=None): '''Output highlighted chunk. - This implementation just outputs ``.hlstyle()`` joined with + This implementation just outputs :py:meth:`hlstyle` joined with ``contents``. ''' return self.hlstyle(fg, bg, attrs) + (contents or '') diff --git a/powerline/renderers/bar.py b/powerline/renderers/bar.py new file mode 100644 index 00000000..7e1e5d36 --- /dev/null +++ b/powerline/renderers/bar.py @@ -0,0 +1,45 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.renderer import Renderer +from powerline.colorscheme import ATTR_UNDERLINE + + +class BarRenderer(Renderer): + '''bar (bar ain't recursive) renderer + + + See documentation of `bar `_ and :ref:`the usage instructions ` + ''' + + character_translations = Renderer.character_translations.copy() + character_translations[ord('%')] = '%%' + + @staticmethod + def hlstyle(*args, **kwargs): + # We don’t need to explicitly reset attributes, so skip those calls + return '' + + def hl(self, contents, fg=None, bg=None, attrs=None): + text = '' + + if fg is not None: + if fg is not False and fg[1] is not False: + text += '%{{F#ff{0:06x}}}'.format(fg[1]) + if bg is not None: + if bg is not False and bg[1] is not False: + text += '%{{B#ff{0:06x}}}'.format(bg[1]) + + if attrs & ATTR_UNDERLINE: + text += '%{+u}' + + return text + contents + '%{F-B--u}' + + def render(self): + return '%{{l}}{0}%{{r}}{1}'.format( + super(BarRenderer, self).render(side='left'), + super(BarRenderer, self).render(side='right'), + ) + + +renderer = BarRenderer diff --git a/powerline/renderers/ipython/__init__.py b/powerline/renderers/ipython/__init__.py index 3547fd2e..a84418f1 100644 --- a/powerline/renderers/ipython/__init__.py +++ b/powerline/renderers/ipython/__init__.py @@ -2,6 +2,7 @@ from __future__ import (unicode_literals, division, absolute_import, print_function) from powerline.renderers.shell import ShellRenderer +from powerline.renderers.shell.readline import ReadlineRenderer from powerline.theme import Theme @@ -45,10 +46,9 @@ class IPythonRenderer(ShellRenderer): ) -class IPythonPromptRenderer(IPythonRenderer): +class IPythonPromptRenderer(IPythonRenderer, ReadlineRenderer): '''Powerline ipython prompt (in and in2) renderer''' - escape_hl_start = '\x01' - escape_hl_end = '\x02' + pass class IPythonNonPromptRenderer(IPythonRenderer): diff --git a/powerline/renderers/pdb.py b/powerline/renderers/pdb.py new file mode 100644 index 00000000..040f0e1a --- /dev/null +++ b/powerline/renderers/pdb.py @@ -0,0 +1,50 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import sys +import platform + +from powerline.renderers.shell.readline import ReadlineRenderer +from powerline.renderer import Renderer + + +class PDBRenderer(ReadlineRenderer): + '''PDB-specific powerline renderer + ''' + pdb = None + initial_stack_length = None + + def get_segment_info(self, segment_info, mode): + r = self.segment_info.copy() + r['pdb'] = self.pdb + r['initial_stack_length'] = self.initial_stack_length + r['curframe'] = self.pdb.curframe + return r + + def set_pdb(self, pdb): + '''Record currently used :py:class:`pdb.Pdb` instance + + Must be called before first calling :py:meth:`render` method. + + :param pdb.Pdb pdb: + Used :py:class:`pdb.Pdb` instance. This instance will later be used + by :py:meth:`get_segment_info` for patching :ref:`segment_info + ` dictionary. + ''' + self.pdb = pdb + + def render(self, **kwargs): + if self.initial_stack_length is None: + self.initial_stack_length = len(self.pdb.stack) - 1 + return Renderer.render(self, **kwargs) + + if sys.version_info < (3,) and platform.python_implementation() == 'PyPy': + def do_render(self, **kwargs): + # Make sure that only ASCII characters survive + ret = super(PDBRenderer, self).do_render(**kwargs) + ret = ret.encode('ascii', 'replace') + ret = ret.decode('ascii') + return ret + + +renderer = PDBRenderer diff --git a/powerline/renderers/shell/rcsh.py b/powerline/renderers/shell/rcsh.py index 4bc01412..75ccb223 100644 --- a/powerline/renderers/shell/rcsh.py +++ b/powerline/renderers/shell/rcsh.py @@ -1,12 +1,7 @@ # vim:fileencoding=utf-8:noet from __future__ import (unicode_literals, division, absolute_import, print_function) -from powerline.renderers.shell import ShellRenderer +from powerline.renderers.shell.readline import ReadlineRenderer -class RcshRenderer(ShellRenderer): - '''Powerline rcsh prompt renderer''' - escape_hl_start = '\x01' - escape_hl_end = '\x02' - -renderer = RcshRenderer +renderer = ReadlineRenderer diff --git a/powerline/renderers/shell/readline.py b/powerline/renderers/shell/readline.py new file mode 100644 index 00000000..a72dff05 --- /dev/null +++ b/powerline/renderers/shell/readline.py @@ -0,0 +1,14 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +from powerline.renderers.shell import ShellRenderer + + +class ReadlineRenderer(ShellRenderer): + '''Renderer useful for some applications that use readline + ''' + escape_hl_start = '\x01' + escape_hl_end = '\x02' + + +renderer = ReadlineRenderer diff --git a/powerline/segments/pdb.py b/powerline/segments/pdb.py new file mode 100644 index 00000000..bd6a38b7 --- /dev/null +++ b/powerline/segments/pdb.py @@ -0,0 +1,61 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import os + +from powerline.theme import requires_segment_info + + +@requires_segment_info +def current_line(pl, segment_info): + '''Displays line number that is next to be run + ''' + return str(segment_info['curframe'].f_lineno) + + +@requires_segment_info +def current_file(pl, segment_info, basename=True): + '''Displays current file name + + :param bool basename: + If true only basename is displayed. + ''' + filename = segment_info['curframe'].f_code.co_filename + if basename: + filename = os.path.basename(filename) + return filename + + +@requires_segment_info +def current_code_name(pl, segment_info): + '''Displays name of the code object of the current frame + ''' + return segment_info['curframe'].f_code.co_name + + +@requires_segment_info +def current_context(pl, segment_info): + '''Displays currently executed context name + + This is similar to :py:func:`current_code_name`, but gives more details. + + Currently it only gives module file name if code_name happens to be + ````. + ''' + name = segment_info['curframe'].f_code.co_name + if name == '': + name = os.path.basename(segment_info['curframe'].f_code.co_filename) + return name + + +@requires_segment_info +def stack_depth(pl, segment_info, full_stack=False): + '''Displays current stack depth + + Result is relative to the stack depth at the time prompt was first run. + + :param bool full_stack: + If true then absolute depth is used. + ''' + return str(len(segment_info['pdb'].stack) - ( + 0 if full_stack else segment_info['initial_stack_length'])) diff --git a/powerline/segments/vim/plugin/commandt.py b/powerline/segments/vim/plugin/commandt.py index 7b8007f3..7e5262ef 100644 --- a/powerline/segments/vim/plugin/commandt.py +++ b/powerline/segments/vim/plugin/commandt.py @@ -50,6 +50,8 @@ def finder(pl): $command_t to add them). All Command-T finders have ``CommandT::`` module prefix, but it is stripped out (actually, any ``CommandT::`` substring will be stripped out). + + Highlight groups used: ``commandt:finder``. ''' initialize() vim.command('ruby $powerline.commandt_set_active_finder') @@ -80,6 +82,8 @@ def path(pl): $command_t.active_finder is required in order to omit displaying path for finders ``MRUBufferFinder``, ``BufferFinder``, ``TagFinder`` and ``JumpFinder`` (pretty much any finder, except ``FileFinder``). + + Highlight groups used: ``commandt:path``. ''' initialize() vim.command('ruby $powerline.commandt_set_active_finder') diff --git a/powerline/vim.py b/powerline/vim.py index b93788a8..d4f5c2d6 100644 --- a/powerline/vim.py +++ b/powerline/vim.py @@ -217,10 +217,6 @@ class VimPowerline(Powerline): for args in _local_themes: self.add_local_theme(*args) - @staticmethod - def get_segment_info(): - return {} - def reset_highlight(self): try: self.renderer.reset_highlight() diff --git a/scripts/powerline-daemon b/scripts/powerline-daemon index 82841878..10635c97 100755 --- a/scripts/powerline-daemon +++ b/scripts/powerline-daemon @@ -17,7 +17,7 @@ from io import BytesIO from powerline.shell import ShellPowerline from powerline.commands.main import finish_args, write_output from powerline.lib.monotonic import monotonic -from powerline.lib.encoding import get_preferred_output_encoding +from powerline.lib.encoding import get_preferred_output_encoding, get_preferred_arguments_encoding, get_unicode_writer from powerline.commands.main import get_argparser as get_main_argparser from powerline.commands.daemon import get_argparser as get_daemon_argparser @@ -107,7 +107,7 @@ def render(args, environ, cwd): else: return 'Failed to render {0}: {1}'.format(str(key), str(e)) s = BytesIO() - write_output(args, powerline, segment_info, s.write, encoding) + write_output(args, powerline, segment_info, get_unicode_writer(stream=s)) s.seek(0) return s.read() @@ -150,10 +150,7 @@ def do_write(conn, result): pass -encoding = get_preferred_output_encoding() - - -def safe_bytes(o, encoding=encoding): +def safe_bytes(o, encoding=get_preferred_output_encoding()): '''Return bytes instance without ever throwing an exception.''' try: try: @@ -171,7 +168,7 @@ def safe_bytes(o, encoding=encoding): return safe_bytes(str(e), encoding) -def parse_args(req): +def parse_args(req, encoding=get_preferred_arguments_encoding()): args = [x.decode(encoding) for x in req.split(b'\0') if x] numargs = int(args[0], 16) shell_args = parser.parse_args(args[1:numargs + 1]) diff --git a/scripts/powerline-release.py b/scripts/powerline-release.py index 0c3eba10..242beccd 100755 --- a/scripts/powerline-release.py +++ b/scripts/powerline-release.py @@ -48,24 +48,57 @@ def parse_version(s): return tuple(map(int, s.split('.'))) +def setup_py_filter(filter_func): + with codecs.open('.setup.py.new', 'w', encoding='utf-8') as NS: + with codecs.open('setup.py', 'r', encoding='utf-8') as OS: + for line in OS: + line = filter_func(line) + NS.write(line) + + os.unlink('setup.py') + os.rename('.setup.py.new', 'setup.py') + + +def setup_py_develop_filter(line, version_string): + if line.startswith('\tbase_version = '): + line = '\tbase_version = \'' + version_string + '\'\n' + return line + + +def setup_py_master_filter(line, version_string): + if line.startswith('\tversion='): + line = '\tversion=\'' + version_string + '\',\n' + elif 'Development Status' in line: + line = '\t\t\'Development Status :: 5 - Production/Stable\',\n' + return line + + def merge(version_string, rev, **kwargs): + check_call(['git', 'checkout', rev]) + + temp_branch_name = 'release-' + version_string + check_call(['git', 'checkout', '-b', temp_branch_name]) + setup_py_filter(lambda line: setup_py_develop_filter(line, version_string)) + check_call(['git', 'add', 'setup.py']) + check_call(['git', 'commit', '-m', 'Update base version']) + check_call(['git', 'checkout', rev]) + check_call(['git', 'merge', '--no-ff', + '--strategy', 'recursive', + '--strategy-option', 'theirs', + '--commit', + '-m', 'Merge branch \'{0}\' into {1}'.format(temp_branch_name, rev), + temp_branch_name]) + check_call(['git', 'branch', '-d', temp_branch_name]) + + rev = check_output(['git', 'rev-parse', 'HEAD']).strip() + check_call(['git', 'checkout', 'master']) try: check_call(['git', 'merge', '--no-ff', '--no-commit', '--log', rev]) except CalledProcessError: check_call(['git', 'mergetool', '--tool', 'vimdiff2']) - with codecs.open('.setup.py.new', 'w', encoding='utf-8') as NS: - with codecs.open('setup.py', 'r', encoding='utf-8') as OS: - for line in OS: - if line.startswith('\tversion='): - line = '\tversion=\'' + version_string + '\',\n' - elif 'Development Status' in line: - line = '\t\t\'Development Status :: 5 - Production/Stable\',\n' - NS.write(line) - - os.unlink('setup.py') - os.rename('.setup.py.new', 'setup.py') + setup_py_filter(lambda line: setup_py_master_filter(line, version_string)) check_call(['git', 'add', 'setup.py']) check_call(['git', 'commit']) diff --git a/scripts/powerline-render b/scripts/powerline-render index 07016705..4b8a3a0a 100755 --- a/scripts/powerline-render +++ b/scripts/powerline-render @@ -13,7 +13,7 @@ except ImportError: from powerline.shell import ShellPowerline from powerline.commands.main import get_argparser, finish_args, write_output -from powerline.lib.unicode import get_preferred_output_encoding +from powerline.lib.encoding import get_unicode_writer if sys.version_info < (3,): @@ -27,4 +27,4 @@ if __name__ == '__main__': finish_args(os.environ, args) powerline = ShellPowerline(args, run_once=True) segment_info = {'args': args, 'environ': os.environ} - write_output(args, powerline, segment_info, write, get_preferred_output_encoding()) + write_output(args, powerline, segment_info, get_unicode_writer()) diff --git a/setup.py b/setup.py index 06ffce2b..74bdca23 100644 --- a/setup.py +++ b/setup.py @@ -8,6 +8,7 @@ import subprocess import logging import shlex +from traceback import print_exc from setuptools import setup, find_packages @@ -28,7 +29,7 @@ def compile_client(): else: from distutils.ccompiler import new_compiler compiler = new_compiler().compiler - cflags = os.environ.get('CFLAGS', '-O3') + cflags = os.environ.get('CFLAGS', str('-O3')) # A normal split would do a split on each space which might be incorrect. The # shlex will not split if a space occurs in an arguments value. subprocess.check_call(compiler + shlex.split(cflags) + ['client/powerline.c', '-o', 'scripts/powerline']) @@ -58,15 +59,18 @@ else: def get_version(): + base_version = '2.1' + base_version += '.dev9999' try: - return 'dev-' + subprocess.check_output(['git', 'rev-parse', 'HEAD']).strip() - except Exception as e: - return 'dev' + return base_version + '+git.' + str(subprocess.check_output(['git', 'rev-parse', 'HEAD']).strip()) + except Exception: + print_exc() + return base_version setup( name='powerline-status', - version='2.0', + version='2.1', description='The ultimate statusline/prompt utility.', long_description=README, classifiers=[ diff --git a/tests/lib/config_mock.py b/tests/lib/config_mock.py index dd19ff11..9e0edf6d 100644 --- a/tests/lib/config_mock.py +++ b/tests/lib/config_mock.py @@ -182,10 +182,17 @@ def select_renderer(simpler_renderer=False): renderer = EvenSimplerRenderer if simpler_renderer else SimpleRenderer -def get_powerline_raw(helpers, PowerlineClass, **kwargs): +def get_powerline_raw(helpers, PowerlineClass, replace_gcp=False, **kwargs): if not isinstance(helpers, TestHelpers): helpers = TestHelpers(helpers) select_renderer(kwargs.pop('simpler_renderer', False)) + + if replace_gcp: + class PowerlineClass(PowerlineClass): + @staticmethod + def get_config_paths(): + return ['/'] + pl = PowerlineClass( config_loader=TestConfigLoader( _helpers=helpers, diff --git a/tests/test_configuration.py b/tests/test_configuration.py index efc21476..c0708c3d 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -43,6 +43,10 @@ config = { 'theme': 'default', 'colorscheme': 'default', }, + 'wm': { + 'theme': 'default', + 'colorscheme': 'default', + }, }, }, 'colors': { @@ -91,6 +95,14 @@ config = { 'environment': {'fg': 'col3', 'bg': 'col4', 'attrs': ['underline']}, }, }, + 'colorschemes/wm/default': { + 'groups': { + 'hl1': {'fg': 'col1', 'bg': 'col2', 'attrs': ['underline']}, + 'hl2': {'fg': 'col2', 'bg': 'col1', 'attrs': []}, + 'hl3': {'fg': 'col3', 'bg': 'col1', 'attrs': ['underline']}, + 'hl4': {'fg': 'col2', 'bg': 'col4', 'attrs': []}, + }, + }, 'themes/test/default': { 'segments': { 'left': [ @@ -142,6 +154,19 @@ config = { ], }, }, + 'themes/wm/default': { + 'default_module': 'powerline.segments.common', + 'segments': { + 'left': [ + highlighted_string('A', 'hl1'), + highlighted_string('B', 'hl2'), + ], + 'right': [ + highlighted_string('C', 'hl3'), + highlighted_string('D', 'hl4'), + ], + }, + }, } @@ -750,26 +775,26 @@ class TestVim(TestCase): def test_environ_update(self): # Regression test: test that segment obtains environment from vim, not # from os.environ. - from powerline.vim import VimPowerline - import powerline as powerline_module - import vim - vim.vars['powerline_config_paths'] = ['/'] - with swap_attributes(config, powerline_module): - with vim_module._with('environ', TEST='abc'): - with get_powerline_raw(config, VimPowerline) as powerline: - window = vim_module.current.window - window_id = 1 - winnr = window.number - self.assertEqual(powerline.render(window, window_id, winnr), b'%#Pl_3_8404992_4_192_underline#\xc2\xa0abc%#Pl_4_192_NONE_None_NONE#>>') - vim_module._environ['TEST'] = 'def' - self.assertEqual(powerline.render(window, window_id, winnr), b'%#Pl_3_8404992_4_192_underline#\xc2\xa0def%#Pl_4_192_NONE_None_NONE#>>') + import tests.vim as vim_module + with vim_module._with('globals', powerline_config_paths=['/']): + from powerline.vim import VimPowerline + import powerline as powerline_module + with swap_attributes(config, powerline_module): + with vim_module._with('environ', TEST='abc'): + with get_powerline_raw(config, VimPowerline) as powerline: + window = vim_module.current.window + window_id = 1 + winnr = window.number + self.assertEqual(powerline.render(window, window_id, winnr), b'%#Pl_3_8404992_4_192_underline#\xc2\xa0abc%#Pl_4_192_NONE_None_NONE#>>') + vim_module._environ['TEST'] = 'def' + self.assertEqual(powerline.render(window, window_id, winnr), b'%#Pl_3_8404992_4_192_underline#\xc2\xa0def%#Pl_4_192_NONE_None_NONE#>>') def test_local_themes(self): # Regression test: VimPowerline.add_local_theme did not work properly. from powerline.vim import VimPowerline import powerline as powerline_module with swap_attributes(config, powerline_module): - with get_powerline_raw(config, VimPowerline) as powerline: + with get_powerline_raw(config, VimPowerline, replace_gcp=True) as powerline: powerline.add_local_theme('tests.matchers.always_true', { 'segment_data': { 'foo': { @@ -800,6 +825,31 @@ class TestVim(TestCase): sys.path.pop(0) +class TestBar(TestRender): + def test_bar(self): + import powerline as powerline_module + with swap_attributes(config, powerline_module): + with get_powerline_raw(config, powerline_module.Powerline, replace_gcp=True, ext='wm', renderer_module='bar') as powerline: + self.assertRenderEqual( + powerline, + '%{l}%{F#ffc00000}%{B#ff008000}%{+u} A%{F-B--u}%{F#ff008000}%{B#ffc00000}>>%{F-B--u}%{F#ff008000}%{B#ffc00000}B%{F-B--u}%{F#ffc00000}>>%{F-B--u}%{r}%{F#ffc00000}<<%{F-B--u}%{F#ff804000}%{B#ffc00000}%{+u}C%{F-B--u}%{F#ff0000c0}%{B#ffc00000}<<%{F-B--u}%{F#ff008000}%{B#ff0000c0}D %{F-B--u}' + ) + + @with_new_config + def test_bar_escape(self, config): + import powerline as powerline_module + config['themes/wm/default']['segments']['left'] = ( + highlighted_string('%{asd}', 'hl1'), + highlighted_string('10% %', 'hl2'), + ) + with swap_attributes(config, powerline_module): + with get_powerline_raw(config, powerline_module.Powerline, replace_gcp=True, ext='wm', renderer_module='bar') as powerline: + self.assertRenderEqual( + powerline, + '%{l}%{F#ffc00000}%{B#ff008000}%{+u} %%{asd}%{F-B--u}%{F#ff008000}%{B#ffc00000}>>%{F-B--u}%{F#ff008000}%{B#ffc00000}10%% %%%{F-B--u}%{F#ffc00000}>>%{F-B--u}%{r}%{F#ffc00000}<<%{F-B--u}%{F#ff804000}%{B#ffc00000}%{+u}C%{F-B--u}%{F#ff0000c0}%{B#ffc00000}<<%{F-B--u}%{F#ff008000}%{B#ff0000c0}D %{F-B--u}' + ) + + if __name__ == '__main__': from tests import main main() diff --git a/tests/test_in_vterm/test_tmux.py b/tests/test_in_vterm/test_tmux.py index eb8de65f..2562939d 100755 --- a/tests/test_in_vterm/test_tmux.py +++ b/tests/test_in_vterm/test_tmux.py @@ -27,6 +27,60 @@ def cell_properties_key_to_shell_escape(cell_properties_key): )) +def test_expected_result(p, expected_result, cols, rows): + last_line = [] + for col in range(cols): + last_line.append(p[rows - 1, col]) + attempts = 10 + result = None + while attempts: + result = tuple(( + (key, ''.join((i.text for i in subline))) + for key, subline in groupby(last_line, lambda i: i.cell_properties_key) + )) + if result == expected_result: + return True + attempts -= 1 + print('Actual result does not match expected. Attempts left: {0}.'.format(attempts)) + sleep(2) + print('Result:') + shesc_result = ''.join(( + '{0}{1}\x1b[m'.format(cell_properties_key_to_shell_escape(key), text) + for key, text in result + )) + print(shesc_result) + print('Expected:') + shesc_expected_result = ''.join(( + '{0}{1}\x1b[m'.format(cell_properties_key_to_shell_escape(key), text) + for key, text in expected_result + )) + print(shesc_expected_result) + p.send(b'powerline-config tmux setup\n') + sleep(5) + print('Screen:') + screen = [] + for i in range(rows): + screen.append([]) + for j in range(cols): + screen[-1].append(p[i, j]) + print('\n'.join( + ''.join(( + '{0}{1}\x1b[m'.format( + cell_properties_key_to_shell_escape(i.cell_properties_key), + i.text + ) for i in line + )) + for line in screen + )) + a = shesc_result.replace('\x1b', '\\e') + '\n' + b = shesc_expected_result.replace('\x1b', '\\e') + '\n' + print('_' * 80) + print('Diff:') + print('=' * 80) + print(''.join((u(line) for line in ndiff([a], [b])))) + return False + + def main(): VTERM_TEST_DIR = os.path.abspath('tests/vterm') vterm_path = os.path.join(VTERM_TEST_DIR, 'path') @@ -103,14 +157,7 @@ def main(): }, ) p.start() - sleep(10) - last_line = [] - for col in range(cols): - last_line.append(p[rows - 1, col]) - result = tuple(( - (key, ''.join((i.text for i in subline))) - for key, subline in groupby(last_line, lambda i: i.cell_properties_key) - )) + sleep(2) expected_result_new = ( (((0, 0, 0), (243, 243, 243), 1, 0, 0), ' 0 '), (((243, 243, 243), (11, 11, 11), 0, 0, 0), ' '), @@ -151,50 +198,12 @@ def main(): (((88, 88, 88), (11, 11, 11), 0, 0, 0), ' '), (((199, 199, 199), (88, 88, 88), 0, 0, 0), ' S1 string here '), ) - print('Result:') - shesc_result = ''.join(( - '{0}{1}\x1b[m'.format(cell_properties_key_to_shell_escape(key), text) - for key, text in result - )) - print(shesc_result) - print('Expected:') tmux_version = get_tmux_version(get_fallback_logger()) if tmux_version < (1, 8): expected_result = expected_result_old else: expected_result = expected_result_new - shesc_expected_result = ''.join(( - '{0}{1}\x1b[m'.format(cell_properties_key_to_shell_escape(key), text) - for key, text in expected_result - )) - print(shesc_expected_result) - if result == expected_result: - return True - else: - p.send(b'powerline-config tmux setup\n') - sleep(5) - print('Screen:') - screen = [] - for i in range(rows): - screen.append([]) - for j in range(cols): - screen[-1].append(p[i, j]) - print('\n'.join( - ''.join(( - '{0}{1}\x1b[m'.format( - cell_properties_key_to_shell_escape(i.cell_properties_key), - i.text - ) for i in line - )) - for line in screen - )) - a = shesc_result.replace('\x1b', '\\e') + '\n' - b = shesc_expected_result.replace('\x1b', '\\e') + '\n' - print('_' * 80) - print('Diff:') - print('=' * 80) - print(''.join((u(line) for line in ndiff([a], [b])))) - return False + return test_expected_result(p, expected_result, cols, rows) finally: check_call([tmux_exe, '-S', socket_path, 'kill-server'], env={ 'PATH': vterm_path, diff --git a/tests/test_segments.py b/tests/test_segments.py index 73e4bb91..08fc520b 100644 --- a/tests/test_segments.py +++ b/tests/test_segments.py @@ -7,7 +7,7 @@ import os from functools import partial from collections import namedtuple -from powerline.segments import shell, tmux +from powerline.segments import shell, tmux, pdb from powerline.lib.vcs import get_fallback_create_watcher from powerline.lib.unicode import out_u @@ -1308,6 +1308,52 @@ class TestVim(TestCase): sys.path.pop(0) +class TestPDB(TestCase): + def test_current_line(self): + pl = Pl() + self.assertEqual(pdb.current_line(pl=pl, segment_info={'curframe': Args(f_lineno=10)}), '10') + + def test_current_file(self): + pl = Pl() + cf = lambda **kwargs: pdb.current_file( + pl=pl, + segment_info={'curframe': Args(f_code=Args(co_filename='/tmp/abc.py'))}, + **kwargs + ) + self.assertEqual(cf(), 'abc.py') + self.assertEqual(cf(basename=True), 'abc.py') + self.assertEqual(cf(basename=False), '/tmp/abc.py') + + def test_current_code_name(self): + pl = Pl() + ccn = lambda **kwargs: pdb.current_code_name( + pl=pl, + segment_info={'curframe': Args(f_code=Args(co_name=''))}, + **kwargs + ) + self.assertEqual(ccn(), '') + + def test_current_context(self): + pl = Pl() + cc = lambda **kwargs: pdb.current_context( + pl=pl, + segment_info={'curframe': Args(f_code=Args(co_name='', co_filename='/tmp/abc.py'))}, + **kwargs + ) + self.assertEqual(cc(), 'abc.py') + + def test_stack_depth(self): + pl = Pl() + sd = lambda **kwargs: pdb.stack_depth( + pl=pl, + segment_info={'pdb': Args(stack=[1, 2, 3]), 'initial_stack_length': 1}, + **kwargs + ) + self.assertEqual(sd(), '2') + self.assertEqual(sd(full_stack=False), '2') + self.assertEqual(sd(full_stack=True), '3') + + old_cwd = None diff --git a/tests/test_shells/input.pdb b/tests/test_shells/input.pdb new file mode 100644 index 00000000..e9ac498c --- /dev/null +++ b/tests/test_shells/input.pdb @@ -0,0 +1,89 @@ +s + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/test_shells/pdb-main.py b/tests/test_shells/pdb-main.py new file mode 100644 index 00000000..37af785f --- /dev/null +++ b/tests/test_shells/pdb-main.py @@ -0,0 +1,24 @@ +# vim:fileencoding=utf-8:noet +from __future__ import (unicode_literals, division, absolute_import, print_function) + +import pdb +import os +import sys + +from powerline.bindings.pdb import use_powerline_prompt + + +@use_powerline_prompt +class Pdb(pdb.Pdb): + pass + + +p = Pdb() + + +script = os.path.join(os.path.dirname(__file__), 'pdb-script.py') +with open(script, 'r') as fd: + code = compile(fd.read(), script, 'exec') + + +p.run('exec(code)', globals={'code': code}) diff --git a/tests/test_shells/pdb-script.py b/tests/test_shells/pdb-script.py new file mode 100644 index 00000000..40db5e8d --- /dev/null +++ b/tests/test_shells/pdb-script.py @@ -0,0 +1,38 @@ +# vim:fileencoding=utf-8:noet +def nop(_): + pass + + +class Foo(object): + def __init__(self): + nop('__init__') + self.bar() + self.baz() + self.bra() + + @classmethod + def bar(cls): + nop(cls.__name__) + + @staticmethod + def baz(): + nop(1) + + def bra(self): + nop(self.__class__.__name__) + + +def brah(): + nop('brah') + + +f = Foo() +Foo.bar() +Foo.baz() +Foo.bra(f) + +f.bar() +f.baz() +f.bra() + +brah() diff --git a/tests/test_shells/pdb.module.ok b/tests/test_shells/pdb.module.ok new file mode 100644 index 00000000..7554dd6c --- /dev/null +++ b/tests/test_shells/pdb.module.ok @@ -0,0 +1,222 @@ + 1  pdb-script.py:6    +--Call-- +-> class Foo(object): + 2  pdb-script.py:6   pdb-script.py:6 Foo   +-> class Foo(object): + 2  pdb-script.py:6   pdb-script.py:6 Foo   +-> def __init__(self): + 2  pdb-script.py:6   pdb-script.py:7 Foo   +-> @classmethod + 2  pdb-script.py:6   pdb-script.py:13 Foo   +-> @staticmethod + 2  pdb-script.py:6   pdb-script.py:17 Foo   +-> def bra(self): + 2  pdb-script.py:6   pdb-script.py:21 Foo   +--Return-- +-> def bra(self): + 2  pdb-script.py:6   pdb-script.py:21 Foo   +-> def brah(): + 1  pdb-script.py:25    +-> f = Foo() + 1  pdb-script.py:29    +--Call-- +-> def __init__(self): + 2  pdb-script.py:29   pdb-script.py:7 __init__   +-> nop('__init__') + 2  pdb-script.py:29   pdb-script.py:8 __init__   +--Call-- +-> def nop(_): + 3  pdb-script.py:29   pdb-script.py:8 __init__  pdb-script.py:2 nop   +-> pass + 3  pdb-script.py:29   pdb-script.py:8 __init__  pdb-script.py:3 nop   +--Return-- +-> pass + 3  pdb-script.py:29   pdb-script.py:8 __init__  pdb-script.py:3 nop   +-> self.bar() + 2  pdb-script.py:29   pdb-script.py:9 __init__   +--Call-- +-> @classmethod + 3  pdb-script.py:29   pdb-script.py:9 __init__  pdb-script.py:13 bar   +-> nop(cls.__name__) + 3  pdb-script.py:29   pdb-script.py:9 __init__  pdb-script.py:15 bar   +--Call-- +-> def nop(_): + 4  pdb-script.py:9 __init__  pdb-script.py:15 bar  pdb-script.py:2 nop   +-> pass + 4  pdb-script.py:9 __init__  pdb-script.py:15 bar  pdb-script.py:3 nop   +--Return-- +-> pass + 4  pdb-script.py:9 __init__  pdb-script.py:15 bar  pdb-script.py:3 nop   +--Return-- +-> nop(cls.__name__) + 3  pdb-script.py:29   pdb-script.py:9 __init__  pdb-script.py:15 bar   +-> self.baz() + 2  pdb-script.py:29   pdb-script.py:10 __init__   +--Call-- +-> @staticmethod + 3  pdb-script.py:29   pdb-script.py:10 __init__  pdb-script.py:17 baz   +-> nop(1) + 3  pdb-script.py:29   pdb-script.py:10 __init__  pdb-script.py:19 baz   +--Call-- +-> def nop(_): + 4  pdb-script.py:10 __init__  pdb-script.py:19 baz  pdb-script.py:2 nop   +-> pass + 4  pdb-script.py:10 __init__  pdb-script.py:19 baz  pdb-script.py:3 nop   +--Return-- +-> pass + 4  pdb-script.py:10 __init__  pdb-script.py:19 baz  pdb-script.py:3 nop   +--Return-- +-> nop(1) + 3  pdb-script.py:29   pdb-script.py:10 __init__  pdb-script.py:19 baz   +-> self.bra() + 2  pdb-script.py:29   pdb-script.py:11 __init__   +--Call-- +-> def bra(self): + 3  pdb-script.py:29   pdb-script.py:11 __init__  pdb-script.py:21 bra   +-> nop(self.__class__.__name__) + 3  pdb-script.py:29   pdb-script.py:11 __init__  pdb-script.py:22 bra   +--Call-- +-> def nop(_): + 4  pdb-script.py:11 __init__  pdb-script.py:22 bra  pdb-script.py:2 nop   +-> pass + 4  pdb-script.py:11 __init__  pdb-script.py:22 bra  pdb-script.py:3 nop   +--Return-- +-> pass + 4  pdb-script.py:11 __init__  pdb-script.py:22 bra  pdb-script.py:3 nop   +--Return-- +-> nop(self.__class__.__name__) + 3  pdb-script.py:29   pdb-script.py:11 __init__  pdb-script.py:22 bra   +--Return-- +-> self.bra() + 2  pdb-script.py:29   pdb-script.py:11 __init__   +-> Foo.bar() + 1  pdb-script.py:30    +--Call-- +-> @classmethod + 2  pdb-script.py:30   pdb-script.py:13 bar   +-> nop(cls.__name__) + 2  pdb-script.py:30   pdb-script.py:15 bar   +--Call-- +-> def nop(_): + 3  pdb-script.py:30   pdb-script.py:15 bar  pdb-script.py:2 nop   +-> pass + 3  pdb-script.py:30   pdb-script.py:15 bar  pdb-script.py:3 nop   +--Return-- +-> pass + 3  pdb-script.py:30   pdb-script.py:15 bar  pdb-script.py:3 nop   +--Return-- +-> nop(cls.__name__) + 2  pdb-script.py:30   pdb-script.py:15 bar   +-> Foo.baz() + 1  pdb-script.py:31    +--Call-- +-> @staticmethod + 2  pdb-script.py:31   pdb-script.py:17 baz   +-> nop(1) + 2  pdb-script.py:31   pdb-script.py:19 baz   +--Call-- +-> def nop(_): + 3  pdb-script.py:31   pdb-script.py:19 baz  pdb-script.py:2 nop   +-> pass + 3  pdb-script.py:31   pdb-script.py:19 baz  pdb-script.py:3 nop   +--Return-- +-> pass + 3  pdb-script.py:31   pdb-script.py:19 baz  pdb-script.py:3 nop   +--Return-- +-> nop(1) + 2  pdb-script.py:31   pdb-script.py:19 baz   +-> Foo.bra(f) + 1  pdb-script.py:32    +--Call-- +-> def bra(self): + 2  pdb-script.py:32   pdb-script.py:21 bra   +-> nop(self.__class__.__name__) + 2  pdb-script.py:32   pdb-script.py:22 bra   +--Call-- +-> def nop(_): + 3  pdb-script.py:32   pdb-script.py:22 bra  pdb-script.py:2 nop   +-> pass + 3  pdb-script.py:32   pdb-script.py:22 bra  pdb-script.py:3 nop   +--Return-- +-> pass + 3  pdb-script.py:32   pdb-script.py:22 bra  pdb-script.py:3 nop   +--Return-- +-> nop(self.__class__.__name__) + 2  pdb-script.py:32   pdb-script.py:22 bra   +-> f.bar() + 1  pdb-script.py:34    +--Call-- +-> @classmethod + 2  pdb-script.py:34   pdb-script.py:13 bar   +-> nop(cls.__name__) + 2  pdb-script.py:34   pdb-script.py:15 bar   +--Call-- +-> def nop(_): + 3  pdb-script.py:34   pdb-script.py:15 bar  pdb-script.py:2 nop   +-> pass + 3  pdb-script.py:34   pdb-script.py:15 bar  pdb-script.py:3 nop   +--Return-- +-> pass + 3  pdb-script.py:34   pdb-script.py:15 bar  pdb-script.py:3 nop   +--Return-- +-> nop(cls.__name__) + 2  pdb-script.py:34   pdb-script.py:15 bar   +-> f.baz() + 1  pdb-script.py:35    +--Call-- +-> @staticmethod + 2  pdb-script.py:35   pdb-script.py:17 baz   +-> nop(1) + 2  pdb-script.py:35   pdb-script.py:19 baz   +--Call-- +-> def nop(_): + 3  pdb-script.py:35   pdb-script.py:19 baz  pdb-script.py:2 nop   +-> pass + 3  pdb-script.py:35   pdb-script.py:19 baz  pdb-script.py:3 nop   +--Return-- +-> pass + 3  pdb-script.py:35   pdb-script.py:19 baz  pdb-script.py:3 nop   +--Return-- +-> nop(1) + 2  pdb-script.py:35   pdb-script.py:19 baz   +-> f.bra() + 1  pdb-script.py:36    +--Call-- +-> def bra(self): + 2  pdb-script.py:36   pdb-script.py:21 bra   +-> nop(self.__class__.__name__) + 2  pdb-script.py:36   pdb-script.py:22 bra   +--Call-- +-> def nop(_): + 3  pdb-script.py:36   pdb-script.py:22 bra  pdb-script.py:2 nop   +-> pass + 3  pdb-script.py:36   pdb-script.py:22 bra  pdb-script.py:3 nop   +--Return-- +-> pass + 3  pdb-script.py:36   pdb-script.py:22 bra  pdb-script.py:3 nop   +--Return-- +-> nop(self.__class__.__name__) + 2  pdb-script.py:36   pdb-script.py:22 bra   +-> brah() + 1  pdb-script.py:38    +--Call-- +-> def brah(): + 2  pdb-script.py:38   pdb-script.py:25 brah   +-> nop('brah') + 2  pdb-script.py:38   pdb-script.py:26 brah   +--Call-- +-> def nop(_): + 3  pdb-script.py:38   pdb-script.py:26 brah  pdb-script.py:2 nop   +-> pass + 3  pdb-script.py:38   pdb-script.py:26 brah  pdb-script.py:3 nop   +--Return-- +-> pass + 3  pdb-script.py:38   pdb-script.py:26 brah  pdb-script.py:3 nop   +--Return-- +-> nop('brah') + 2  pdb-script.py:38   pdb-script.py:26 brah   +--Return-- +-> brah() + 1  pdb-script.py:38    +--Return-- + 0   diff --git a/tests/test_shells/pdb.subclass.ok b/tests/test_shells/pdb.subclass.ok new file mode 100644 index 00000000..d8eba5ef --- /dev/null +++ b/tests/test_shells/pdb.subclass.ok @@ -0,0 +1,217 @@ + 2  :1   pdb-script.py:6    +--Call-- +-> class Foo(object): + 3  :1   pdb-script.py:6   pdb-script.py:6 Foo   +-> class Foo(object): + 3  :1   pdb-script.py:6   pdb-script.py:6 Foo   +-> def __init__(self): + 3  :1   pdb-script.py:6   pdb-script.py:7 Foo   +-> @classmethod + 3  :1   pdb-script.py:6   pdb-script.py:13 Foo   +-> @staticmethod + 3  :1   pdb-script.py:6   pdb-script.py:17 Foo   +-> def bra(self): + 3  :1   pdb-script.py:6   pdb-script.py:21 Foo   +--Return-- +-> def bra(self): + 3  :1   pdb-script.py:6   pdb-script.py:21 Foo   +-> def brah(): + 2  :1   pdb-script.py:25    +-> f = Foo() + 2  :1   pdb-script.py:29    +--Call-- +-> def __init__(self): + 3  :1   pdb-script.py:29   pdb-script.py:7 __init__   +-> nop('__init__') + 3  :1   pdb-script.py:29   pdb-script.py:8 __init__   +--Call-- +-> def nop(_): + 4  pdb-script.py:29   pdb-script.py:8 __init__  pdb-script.py:2 nop   +-> pass + 4  pdb-script.py:29   pdb-script.py:8 __init__  pdb-script.py:3 nop   +--Return-- +-> pass + 4  pdb-script.py:29   pdb-script.py:8 __init__  pdb-script.py:3 nop   +-> self.bar() + 3  :1   pdb-script.py:29   pdb-script.py:9 __init__   +--Call-- +-> @classmethod + 4  pdb-script.py:29   pdb-script.py:9 __init__  pdb-script.py:13 bar   +-> nop(cls.__name__) + 4  pdb-script.py:29   pdb-script.py:9 __init__  pdb-script.py:15 bar   +--Call-- +-> def nop(_): + 5  pdb-script.py:9 __init__  pdb-script.py:15 bar  pdb-script.py:2 nop   +-> pass + 5  pdb-script.py:9 __init__  pdb-script.py:15 bar  pdb-script.py:3 nop   +--Return-- +-> pass + 5  pdb-script.py:9 __init__  pdb-script.py:15 bar  pdb-script.py:3 nop   +--Return-- +-> nop(cls.__name__) + 4  pdb-script.py:29   pdb-script.py:9 __init__  pdb-script.py:15 bar   +-> self.baz() + 3  :1   pdb-script.py:29   pdb-script.py:10 __init__   +--Call-- +-> @staticmethod + 4  pdb-script.py:29   pdb-script.py:10 __init__  pdb-script.py:17 baz   +-> nop(1) + 4  pdb-script.py:29   pdb-script.py:10 __init__  pdb-script.py:19 baz   +--Call-- +-> def nop(_): + 5  pdb-script.py:10 __init__  pdb-script.py:19 baz  pdb-script.py:2 nop   +-> pass + 5  pdb-script.py:10 __init__  pdb-script.py:19 baz  pdb-script.py:3 nop   +--Return-- +-> pass + 5  pdb-script.py:10 __init__  pdb-script.py:19 baz  pdb-script.py:3 nop   +--Return-- +-> nop(1) + 4  pdb-script.py:29   pdb-script.py:10 __init__  pdb-script.py:19 baz   +-> self.bra() + 3  :1   pdb-script.py:29   pdb-script.py:11 __init__   +--Call-- +-> def bra(self): + 4  pdb-script.py:29   pdb-script.py:11 __init__  pdb-script.py:21 bra   +-> nop(self.__class__.__name__) + 4  pdb-script.py:29   pdb-script.py:11 __init__  pdb-script.py:22 bra   +--Call-- +-> def nop(_): + 5  pdb-script.py:11 __init__  pdb-script.py:22 bra  pdb-script.py:2 nop   +-> pass + 5  pdb-script.py:11 __init__  pdb-script.py:22 bra  pdb-script.py:3 nop   +--Return-- +-> pass + 5  pdb-script.py:11 __init__  pdb-script.py:22 bra  pdb-script.py:3 nop   +--Return-- +-> nop(self.__class__.__name__) + 4  pdb-script.py:29   pdb-script.py:11 __init__  pdb-script.py:22 bra   +--Return-- +-> self.bra() + 3  :1   pdb-script.py:29   pdb-script.py:11 __init__   +-> Foo.bar() + 2  :1   pdb-script.py:30    +--Call-- +-> @classmethod + 3  :1   pdb-script.py:30   pdb-script.py:13 bar   +-> nop(cls.__name__) + 3  :1   pdb-script.py:30   pdb-script.py:15 bar   +--Call-- +-> def nop(_): + 4  pdb-script.py:30   pdb-script.py:15 bar  pdb-script.py:2 nop   +-> pass + 4  pdb-script.py:30   pdb-script.py:15 bar  pdb-script.py:3 nop   +--Return-- +-> pass + 4  pdb-script.py:30   pdb-script.py:15 bar  pdb-script.py:3 nop   +--Return-- +-> nop(cls.__name__) + 3  :1   pdb-script.py:30   pdb-script.py:15 bar   +-> Foo.baz() + 2  :1   pdb-script.py:31    +--Call-- +-> @staticmethod + 3  :1   pdb-script.py:31   pdb-script.py:17 baz   +-> nop(1) + 3  :1   pdb-script.py:31   pdb-script.py:19 baz   +--Call-- +-> def nop(_): + 4  pdb-script.py:31   pdb-script.py:19 baz  pdb-script.py:2 nop   +-> pass + 4  pdb-script.py:31   pdb-script.py:19 baz  pdb-script.py:3 nop   +--Return-- +-> pass + 4  pdb-script.py:31   pdb-script.py:19 baz  pdb-script.py:3 nop   +--Return-- +-> nop(1) + 3  :1   pdb-script.py:31   pdb-script.py:19 baz   +-> Foo.bra(f) + 2  :1   pdb-script.py:32    +--Call-- +-> def bra(self): + 3  :1   pdb-script.py:32   pdb-script.py:21 bra   +-> nop(self.__class__.__name__) + 3  :1   pdb-script.py:32   pdb-script.py:22 bra   +--Call-- +-> def nop(_): + 4  pdb-script.py:32   pdb-script.py:22 bra  pdb-script.py:2 nop   +-> pass + 4  pdb-script.py:32   pdb-script.py:22 bra  pdb-script.py:3 nop   +--Return-- +-> pass + 4  pdb-script.py:32   pdb-script.py:22 bra  pdb-script.py:3 nop   +--Return-- +-> nop(self.__class__.__name__) + 3  :1   pdb-script.py:32   pdb-script.py:22 bra   +-> f.bar() + 2  :1   pdb-script.py:34    +--Call-- +-> @classmethod + 3  :1   pdb-script.py:34   pdb-script.py:13 bar   +-> nop(cls.__name__) + 3  :1   pdb-script.py:34   pdb-script.py:15 bar   +--Call-- +-> def nop(_): + 4  pdb-script.py:34   pdb-script.py:15 bar  pdb-script.py:2 nop   +-> pass + 4  pdb-script.py:34   pdb-script.py:15 bar  pdb-script.py:3 nop   +--Return-- +-> pass + 4  pdb-script.py:34   pdb-script.py:15 bar  pdb-script.py:3 nop   +--Return-- +-> nop(cls.__name__) + 3  :1   pdb-script.py:34   pdb-script.py:15 bar   +-> f.baz() + 2  :1   pdb-script.py:35    +--Call-- +-> @staticmethod + 3  :1   pdb-script.py:35   pdb-script.py:17 baz   +-> nop(1) + 3  :1   pdb-script.py:35   pdb-script.py:19 baz   +--Call-- +-> def nop(_): + 4  pdb-script.py:35   pdb-script.py:19 baz  pdb-script.py:2 nop   +-> pass + 4  pdb-script.py:35   pdb-script.py:19 baz  pdb-script.py:3 nop   +--Return-- +-> pass + 4  pdb-script.py:35   pdb-script.py:19 baz  pdb-script.py:3 nop   +--Return-- +-> nop(1) + 3  :1   pdb-script.py:35   pdb-script.py:19 baz   +-> f.bra() + 2  :1   pdb-script.py:36    +--Call-- +-> def bra(self): + 3  :1   pdb-script.py:36   pdb-script.py:21 bra   +-> nop(self.__class__.__name__) + 3  :1   pdb-script.py:36   pdb-script.py:22 bra   +--Call-- +-> def nop(_): + 4  pdb-script.py:36   pdb-script.py:22 bra  pdb-script.py:2 nop   +-> pass + 4  pdb-script.py:36   pdb-script.py:22 bra  pdb-script.py:3 nop   +--Return-- +-> pass + 4  pdb-script.py:36   pdb-script.py:22 bra  pdb-script.py:3 nop   +--Return-- +-> nop(self.__class__.__name__) + 3  :1   pdb-script.py:36   pdb-script.py:22 bra   +-> brah() + 2  :1   pdb-script.py:38    +--Call-- +-> def brah(): + 3  :1   pdb-script.py:38   pdb-script.py:25 brah   +-> nop('brah') + 3  :1   pdb-script.py:38   pdb-script.py:26 brah   +--Call-- +-> def nop(_): + 4  pdb-script.py:38   pdb-script.py:26 brah  pdb-script.py:2 nop   +-> pass + 4  pdb-script.py:38   pdb-script.py:26 brah  pdb-script.py:3 nop   +--Return-- +-> pass + 4  pdb-script.py:38   pdb-script.py:26 brah  pdb-script.py:3 nop   +--Return-- +-> nop('brah') + 3  :1   pdb-script.py:38   pdb-script.py:26 brah   diff --git a/tests/test_shells/postproc.py b/tests/test_shells/postproc.py index ff9c5748..d544bf83 100755 --- a/tests/test_shells/postproc.py +++ b/tests/test_shells/postproc.py @@ -34,9 +34,15 @@ IPYPY_DEANSI_RE = re.compile(r'\033(?:\[(?:\?\d+[lh]|[^a-zA-Z]+[a-ln-zA-Z])|[=>] with codecs.open(fname, 'r', encoding='utf-8') as R: with codecs.open(new_fname, 'w', encoding='utf-8') as W: found_cd = False + i = -1 for line in (R if shell != 'fish' else R.read().split('\n')): + i += 1 if not found_cd: - found_cd = ('cd tests/shell/3rd' in line) + found_cd = ( + 'class Foo(object):' in line + if shell == 'pdb' else + 'cd tests/shell/3rd' in line + ) continue if 'true is the last line' in line: break @@ -101,4 +107,15 @@ with codecs.open(fname, 'r', encoding='utf-8') as R: elif shell == 'rc': if line == 'read() failed: Connection reset by peer\n': line = '' + elif shell == 'pdb': + if is_pypy: + if line == '\033[?1h\033=\033[?25l\033[1A\n': + line = '' + line = IPYPY_DEANSI_RE.subn('', line)[0] + if line == '\n': + line = '' + if line.startswith(('>',)): + line = '' + elif line == '-> self.quitting = 1\n': + line = '-> self.quitting = True\n' W.write(line) diff --git a/tests/test_shells/test.sh b/tests/test_shells/test.sh index 81b5edb2..14430361 100755 --- a/tests/test_shells/test.sh +++ b/tests/test_shells/test.sh @@ -1,5 +1,7 @@ #!/bin/sh -set -e +. tests/bot-ci/scripts/common/main.sh +set +x + : ${PYTHON:=python} FAIL_SUMMARY="" FAILED=0 @@ -109,14 +111,41 @@ do_run_test() { done # Wait for screen to initialize sleep 1 - while ! screen -S "$SESNAME" -p 0 -X width 300 1 ; do + local attempts=100 + while ! screen -S "$SESNAME" -p 0 -X width 300 1 >/dev/null ; do sleep 0.1s + attempts=$(( attempts - 1 )) + if test $attempts -eq 0 ; then + echo "Waiting for too long: assuming test failed" + echo "Failed ${SH}. Full output:" + echo '============================================================' + cat tests/shell/${SH}.${TEST_TYPE}.${TEST_CLIENT}.full.log + echo '____________________________________________________________' + if test "x$POWERLINE_TEST_NO_CAT_V" != "x1" ; then + echo "Full output (cat -v):" + echo '============================================================' + cat -v tests/shell/${SH}.${TEST_TYPE}.${TEST_CLIENT}.full.log + echo '____________________________________________________________' + fi + return 1 + fi done if ( \ - test "x${SH}" = "xdash" || - ( \ + test "x${SH}" = "xdash" \ + || ( \ test "x${SH}" = "xipython" \ - && ${PYTHON} -c 'import platform, sys; sys.exit(1 - (platform.python_implementation() == "PyPy"))' \ + && test "$PYTHON_IMPLEMENTATION" = "PyPy" \ + ) \ + || ( \ + test "x${SH}" = "xpdb" \ + && ( \ + ( \ + test "$PYTHON_VERSION_MAJOR" -eq 3 \ + && test "$PYTHON_VERSION_MINOR" -eq 2 \ + && test "$PYTHON_IMPLEMENTATION" = "CPython" \ + ) \ + || test "$PYTHON_IMPLEMENTATION" = "PyPy" \ + ) \ ) \ ) ; then # If I do not use this hack for dash then output will look like @@ -126,7 +155,9 @@ do_run_test() { # … # prompt1> prompt2> … while read -r line ; do - screen -S "$SESNAME" -p 0 -X stuff "$line$NL" + if test "$(screen -S "$SESNAME" -p 0 -X stuff "$line$NL")" = "No screen session found." ; then + break + fi sleep 1 done < tests/test_shells/input.$SH else @@ -185,6 +216,9 @@ run_test() { TEST_CLIENT="$2" SH="$3" local attempts=3 + if test -n "$ONLY_SHELL$ONLY_TEST_TYPE$ONLY_TEST_CLIENT" ; then + attempts=1 + fi while test $attempts -gt 0 ; do rm -f tests/shell/${SH}.${TEST_TYPE}.${TEST_CLIENT}.log rm -f tests/shell/${SH}.${TEST_TYPE}.${TEST_CLIENT}.full.log @@ -245,9 +279,11 @@ ln -s ../../test_shells/waitpid.sh tests/shell/path if which socat ; then ln -s "$(which socat)" tests/shell/path fi -for pexe in powerline powerline-config ; do +for pexe in powerline powerline-config powerline-render powerline.sh powerline.py ; do if test -e scripts/$pexe ; then ln -s "$PWD/scripts/$pexe" tests/shell/path + elif test -e client/$pexe ; then + ln -s "$PWD/client/$pexe" tests/shell/path elif which $pexe ; then ln -s "$(which $pexe)" tests/shell/path else @@ -256,6 +292,9 @@ for pexe in powerline powerline-config ; do fi done +ln -s python tests/shell/path/pdb +PDB_PYTHON=pdb + if test -z "$POWERLINE_RC_EXE" ; then if which rc-status >/dev/null ; then # On Gentoo `rc` executable is from OpenRC. Thus app-shells/rc instals @@ -282,6 +321,25 @@ export ADDRESS="powerline-ipc-test-$$" export PYTHON echo "Powerline address: $ADDRESS" +check_test_client() { + local executable="$1" + local client_type="$2" + local actual_mime_type="$(file --mime-type --brief --dereference "tests/shell/path/$executable" | cut -d/ -f1)" + local expected_mime_type + case "$client_type" in + C) expected_mime_type="application/x-executable" ;; + python) expected_mime_type="text/x-python" ;; + render) expected_mime_type="text/x-python" ;; + shell) expected_mime_type="text/x-shellscript" ;; + esac + expected_mime_type="${expected_mime_type%/*}" + if test "$expected_mime_type" != "$actual_mime_type" ; then + echo "Expected $executable to have MIME type $expected_mime_type, but got $actual_mime_type" + FAILED=1 + FAIL_SUMMARY="${FAIL_SUMMARY}${NL}M ${executable}" + fi +} + if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || test "x${ONLY_SHELL}" = xbusybox || test "x${ONLY_SHELL}" = xrc ; then scripts/powerline-config shell command @@ -308,17 +366,18 @@ if test -z "${ONLY_SHELL}" || test "x${ONLY_SHELL%sh}" != "x${ONLY_SHELL}" || te echo "> Testing $TEST_TYPE" I=-1 for POWERLINE_COMMAND in \ - $PWD/scripts/powerline \ - $PWD/scripts/powerline-render \ - $PWD/client/powerline.py \ - $PWD/client/powerline.sh + powerline \ + powerline-render \ + powerline.py \ + powerline.sh do case "$POWERLINE_COMMAND" in - *powerline) TEST_CLIENT=C ;; - *powerline-render) TEST_CLIENT=render ;; - *powerline.py) TEST_CLIENT=python ;; - *powerline.sh) TEST_CLIENT=shell ;; + powerline) TEST_CLIENT=C ;; + powerline-render) TEST_CLIENT=render ;; + powerline.py) TEST_CLIENT=python ;; + powerline.sh) TEST_CLIENT=shell ;; esac + check_test_client "$POWERLINE_COMMAND" $TEST_CLIENT if test "$TEST_CLIENT" = render && test "$TEST_TYPE" = daemon ; then continue fi @@ -418,6 +477,29 @@ if ( test "x${ONLY_SHELL}" = "x" || test "x${ONLY_SHELL}" = "xzsh" ) \ fi fi +if test "x${ONLY_SHELL}" = "x" || test "x${ONLY_SHELL}" = "xpdb" ; then + if ! ( test "$PYTHON_IMPLEMENTATION" = "PyPy" && test "$PYTHON_VERSION_MAJOR" = 2 ) ; then + if test "x${ONLY_TEST_TYPE}" = "x" || test "x${ONLY_TEST_TYPE}" = "xsubclass" ; then + echo "> pdb subclass" + if ! run_test subclass python $PDB_PYTHON "$PWD/tests/test_shells/pdb-main.py" ; then + FAILED=1 + FAIL_SUMMARY="${FAIL_SUMMARY}${NL}T pdb $PDB_PYTHON $PWD/tests/test_shells/pdb-main.py" + fi + fi + if test "x${ONLY_TEST_TYPE}" = "x" || test "x${ONLY_TEST_TYPE}" = "xmodule" ; then + echo "> pdb module" + MODULE="powerline.bindings.pdb" + if test "$PYTHON_MM" = "2.6" ; then + MODULE="powerline.bindings.pdb.__main__" + fi + if ! run_test module python $PDB_PYTHON -m$MODULE "$PWD/tests/test_shells/pdb-script.py" ; then + FAILED=1 + FAIL_SUMMARY="${FAIL_SUMMARY}${NL}T pdb $PDB_PYTHON -m$MODULE $PWD/tests/test_shells/pdb-script" + fi + fi + fi +fi + if test "x${ONLY_SHELL}" = "x" || test "x${ONLY_SHELL}" = "xipython" ; then if which ipython >/dev/null ; then # Define some overrides which should be ignored by IPython.