Repository: xonsh/xonsh

Issue:
Cannot set stderr prefix/postfix; KeyError: 'rightred'
I cannot set `$XONSH_STDERR_PREFIX` and `$XONSH_STDERR_POSTFIX` without xonsh crashing.

## xonfig

```                    
+------------------+-----------------+
| xonsh            | 0.7.5           |
| Python           | 3.6.6           |
| PLY              | 3.7             |
| have readline    | True            |
| prompt toolkit   | 2.0.4           |
| shell type       | prompt_toolkit2 |
| pygments         | 2.2.0           |
| on posix         | True            |
| on linux         | True            |
| distro           | unknown         |
| on darwin        | False           |
| on windows       | False           |
| on cygwin        | False           |
| on msys2         | False           |
| is superuser     | False           |
| default encoding | utf-8           |
| xonsh encoding   | utf-8           |
| encoding errors  | surrogateescape |
+------------------+-----------------+
```

## Expected Behavior
stderr output gets styled according to prefix and postfix

## Current Behavior
xonsh crashes as soon as I set both:

```
$ XONSH_DEBUG=1 xonsh --no-rc
...
monk@cola ~ $ $XONSH_STDERR_POSTFIX = 'a'                                       
monk@cola ~ $ $XONSH_STDERR_PREFIX = 'a'                                        
Traceback (most recent call last):
  File "/nix/store/kgibmzwk0bgr3hnfdckmsbwg2ajpyb7x-xonsh-0.7.5/lib/python3.6/site-packages/xonsh/main.py", line 343, in main
    return main_xonsh(args)
  File "/nix/store/kgibmzwk0bgr3hnfdckmsbwg2ajpyb7x-xonsh-0.7.5/lib/python3.6/site-packages/xonsh/main.py", line 369, in main_xonsh
    shell.shell.cmdloop()
  File "/nix/store/kgibmzwk0bgr3hnfdckmsbwg2ajpyb7x-xonsh-0.7.5/lib/python3.6/site-packages/xonsh/ptk2/shell.py", line 170, in cmdloop
    self.default(line)
  File "/nix/store/kgibmzwk0bgr3hnfdckmsbwg2ajpyb7x-xonsh-0.7.5/lib/python3.6/site-packages/xonsh/base_shell.py", line 327, in default
    tee = Tee(encoding=enc, errors=err)
  File "/nix/store/kgibmzwk0bgr3hnfdckmsbwg2ajpyb7x-xonsh-0.7.5/lib/python3.6/site-packages/xonsh/base_shell.py", line 237, in __init__
    poststderr = format_std_prepost(env.get('XONSH_STDERR_POSTFIX'))
  File "/nix/store/kgibmzwk0bgr3hnfdckmsbwg2ajpyb7x-xonsh-0.7.5/lib/python3.6/site-packages/xonsh/tools.py", line 1792, in format_std_prepost
    s = shell.format_color(invis + s + invis, force_string=True)
  File "/nix/store/kgibmzwk0bgr3hnfdckmsbwg2ajpyb7x-xonsh-0.7.5/lib/python3.6/site-packages/xonsh/ptk2/shell.py", line 265, in format_color
    formatter = pyghooks.XonshTerminal256Formatter(style=proxy_style)
  File "/nix/store/kgibmzwk0bgr3hnfdckmsbwg2ajpyb7x-xonsh-0.7.5/lib/python3.6/site-packages/xonsh/lazyasd.py", line 85, in __call__
    return obj(*args, **kwargs)
  File "/nix/store/kgibmzwk0bgr3hnfdckmsbwg2ajpyb7x-xonsh-0.7.5/lib/python3.6/site-packages/xonsh/pyghooks.py", line 1350, in __init__
    super().__init__(*args, **kwargs)
  File "/nix/store/w7p41gs88mkpk8k0bwbgg7bc3970fpbl-python3.6-Pygments-2.2.0/lib/python3.6/site-packages/pygments/formatters/terminal256.py", line 134, in __init__
    self._setup_styles()  # convert selected style's colors to term. colors
  File "/nix/store/w7p41gs88mkpk8k0bwbgg7bc3970fpbl-python3.6-Pygments-2.2.0/lib/python3.6/site-packages/pygments/formatters/terminal256.py", line 224, in _setup_styles
    self.style_string[str(ttype)] = (escape.color_string(),
  File "/nix/store/w7p41gs88mkpk8k0bwbgg7bc3970fpbl-python3.6-Pygments-2.2.0/lib/python3.6/site-packages/pygments/formatters/terminal256.py", line 53, in color_string
    esc = codes[self.fg[5:]]
KeyError: 'rightblue'
Xonsh encountered an issue during launch
Failback to /run/current-system/sw/bin/bash
```

The particular truncated color name varies from run to run.

## Steps to Reproduce

```
$XONSH_STDERR_POSTFIX = 'a'                                       
$XONSH_STDERR_PREFIX = 'a'  
```


Discussion:
Works as expected with ptk 1.0.15 though.
Thanks for reporting @t184256. I think I know why this happens. With PTK2 the ansi-color names changed. Xonsh follows the new names and convert them back when using the readline and PTK1 backend. I think I overlooked a place where xonsh does coloring without going through PTK2 and the colors are therefore not converted back.  That causes a problem with pygments which doesn't understand the new names. 

We have put in a PR to update pygments, but that will take a while. We can probably make a workaround in xonsh in the meantime. 

I will try to fix this when I get a little more time later this week. 

Resolving patch:
diff --git a/xonsh/platform.py b/xonsh/platform.py
--- a/xonsh/platform.py
+++ b/xonsh/platform.py
@@ -114,6 +114,15 @@ def pygments_version():
     return v
 
 
+@functools.lru_cache(1)
+def pygments_version_info():
+    """ Returns `pygments`'s version as tuple of integers. """
+    if HAS_PYGMENTS:
+        return tuple(int(x) for x in pygments_version().strip('<>+-=.').split('.'))
+    else:
+        return None
+
+
 @functools.lru_cache(1)
 def has_prompt_toolkit():
     """Tests if the `prompt_toolkit` is available."""
diff --git a/xonsh/pyghooks.py b/xonsh/pyghooks.py
--- a/xonsh/pyghooks.py
+++ b/xonsh/pyghooks.py
@@ -23,14 +23,21 @@
     intensify_colors_for_cmd_exe,
     ansicolors_to_ptk1_names,
     ANSICOLOR_NAMES_MAP,
-    hardcode_colors_for_win10
+    PTK_NEW_OLD_COLOR_MAP,
+    hardcode_colors_for_win10,
 )
 
 from xonsh.color_tools import (RE_BACKGROUND, BASE_XONSH_COLORS, make_palette,
                                find_closest_color)
 from xonsh.style_tools import norm_name
 from xonsh.lazyimps import terminal256
-from xonsh.platform import os_environ, win_ansi_support
+from xonsh.platform import (
+    os_environ,
+    win_ansi_support,
+    ptk_version_info,
+    pygments_version_info,
+)
+
 from xonsh.pygments_cache import get_style_by_name
 
 
@@ -1335,12 +1342,43 @@ def pygments_style_by_name(name):
     return astyle
 
 
+def _monkey_patch_pygments_codes():
+    """ Monky patch pygments' dict of console codes,
+        with new color names
+    """
+    import pygments.console
+    if 'brightblack' in pygments.console.codes:
+        # Assume that colors are already fixed in pygments
+        # for example when using pygments from source
+        return
+
+    if not getattr(pygments.console, "_xonsh_patched", False):
+        patched_codes = {}
+        for new, old in PTK_NEW_OLD_COLOR_MAP.items():
+            if old in pygments.console.codes:
+                patched_codes[new[1:]] = pygments.console.codes[old]
+        pygments.console.codes.update(patched_codes)
+        pygments.console._xonsh_patched = True
+
+
 #
 # Formatter
 #
 
 @lazyobject
 def XonshTerminal256Formatter():
+
+    if (
+        ptk_version_info()
+        and ptk_version_info() > (2, 0)
+        and pygments_version_info()
+        and (2, 2) <= pygments_version_info() < (2, 3)
+    ):
+        # Monky patch pygments' dict of console codes
+        # with the new color names used by PTK2
+        # Can be removed once pygment names get fixed.
+        _monkey_patch_pygments_codes()
+
     class XonshTerminal256FormatterProxy(terminal256.Terminal256Formatter):
         """Proxy class for xonsh terminal256 formatting that understands.
         xonsh color tokens.
diff --git a/xonsh/tools.py b/xonsh/tools.py
--- a/xonsh/tools.py
+++ b/xonsh/tools.py
@@ -1638,25 +1638,31 @@ def _get_color_indexes(style_map):
                 rgb = None
             yield token, index, rgb
 
+
+# Map of new PTK2 color names to PTK1 variants
+PTK_NEW_OLD_COLOR_MAP = LazyObject(lambda: {
+    "black": "black",
+    "red": "darkred",
+    "green": "darkgreen",
+    "yellow": "brown",
+    "blue": "darkblue",
+    "magenta": "purple",
+    "cyan": "teal",
+    "gray": "lightgray",
+    "brightblack": "darkgray",
+    "brightred": "red",
+    "brightgreen": "green",
+    "brightyellow": "yellow",
+    "brightblue": "blue",
+    "brightmagenta": "fuchsia",
+    "brightcyan": "turquoise",
+    "white": "white",
+}, globals(), "PTK_NEW_OLD_COLOR_MAP")
+
 # Map of new ansicolor names to old PTK1 names
 ANSICOLOR_NAMES_MAP = LazyObject(lambda: {
-    'ansiblack': '#ansiblack',
-    'ansired': '#ansidarkred',
-    'ansigreen': '#ansidarkgreen',
-    'ansiyellow': '#ansibrown',
-    'ansiblue': '#ansidarkblue',
-    'ansimagenta': '#ansipurple',
-    'ansicyan': '#ansiteal',
-    'ansigray': '#ansilightgray',
-    'ansibrightblack': '#ansidarkgray',
-    'ansibrightred': '#ansired',
-    'ansibrightgreen': '#ansigreen',
-    'ansibrightyellow': '#ansiyellow',
-    'ansibrightblue': '#ansiblue',
-    'ansibrightmagenta': '#ansifuchsia',
-    'ansibrightcyan': '#ansiturquoise',
-    'ansiwhite': '#ansiwhite',
-}, globals(), 'ANSICOLOR_NAMES_MAP')
+    "ansi" + k: "#ansi" + v for k, v in PTK_NEW_OLD_COLOR_MAP.items()
+}, globals(), "ANSICOLOR_NAMES_MAP")
 
 
 def _win10_color_map():


Test patch: