Bug 1428362 - Vendor blessings via |mach vendor python|; r?ahal draft
authorDave Hunt <dhunt@mozilla.com>
Thu, 31 May 2018 13:54:43 +0100
changeset 806521 87d9c3612dc0c3634e8b12b72fcc718ebf15f7ec
parent 806520 4235735b19d07d831f8655949c3bb65d3358c469
child 806522 a9f4b733e7d515e673f34833fb1abcfe4359c81e
push id112906
push userbmo:dave.hunt@gmail.com
push dateMon, 11 Jun 2018 01:42:31 +0000
Bug 1428362 - Vendor blessings via |mach vendor python|; r?ahal MozReview-Commit-ID: 7suX72iyYEV
--- a/Pipfile
+++ b/Pipfile
@@ -2,16 +2,17 @@
 url = "https://pypi.org/simple"
 verify_ssl = true
 name = "pypi"
 attrs = "==18.1.0"
+blessings = "==1.6.1"
 jsmin = "==2.1.0"
 json-e = "==2.5.0"
 pipenv = "==2018.5.18"
 pytest = "==3.2.5"
 python-hglib = "==2.4"
 requests = "==2.9.1"
 six = "==1.10.0"
 virtualenv = "==15.2.0"
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,12 +1,12 @@
     "_meta": {
         "hash": {
-            "sha256": "eb8b0a9771d4420f83fbbabf9952dc783aeefe9be455559de2f3ebff27caa93f"
+            "sha256": "60a5696cfb1b1bbe1626821180076b0b69d563cb0404effec56fe1902c768c1e"
         "pipfile-spec": 6,
         "requires": {},
         "sources": [
                 "name": "pypi",
                 "url": "https://pypi.org/simple",
                 "verify_ssl": true
@@ -17,16 +17,25 @@
         "attrs": {
             "hashes": [
             "index": "pypi",
             "version": "==18.1.0"
+        "blessings": {
+            "hashes": [
+                "sha256:26dbaf2f89c3e6dee11c10f7c0b85756ed75cf602b1bb7935b4efd8ed67a000f",
+                "sha256:466e43ff45723b272311de0437649df464b33b4daba7a54c69493212958e19c7",
+                "sha256:74919575885552e14bc24a68f8b539690bd1b5629180faa830b1a25b8c7fb6ea"
+            ],
+            "index": "pypi",
+            "version": "==1.6.1"
+        },
         "certifi": {
             "hashes": [
             "version": "==2018.4.16"
         "jsmin": {
new file mode 100644
--- /dev/null
+++ b/third_party/python/blessings/MANIFEST.in
@@ -0,0 +1,3 @@
+include README.rst
+include LICENSE
+include tox.ini
new file mode 100644
--- /dev/null
+++ b/third_party/python/blessings/README.rst
@@ -0,0 +1,527 @@
+Coding with Blessings looks like this... ::
+    from blessings import Terminal
+    t = Terminal()
+    print t.bold('Hi there!')
+    print t.bold_red_on_bright_green('It hurts my eyes!')
+    with t.location(0, t.height - 1):
+        print 'This is at the bottom.'
+Or, for byte-level control, you can drop down and play with raw terminal
+    print '{t.bold}All your {t.red}bold and red base{t.normal}'.format(t=t)
+    print t.wingo(2)
+`Full API Reference <https://blessings.readthedocs.io/>`_
+The Pitch
+Blessings lifts several of curses_' limiting assumptions, and it makes your
+code pretty, too:
+* Use styles, color, and maybe a little positioning without necessarily
+  clearing the whole
+  screen first.
+* Leave more than one screenful of scrollback in the buffer after your program
+  exits, like a well-behaved command-line app should.
+* Get rid of all those noisy, C-like calls to ``tigetstr`` and ``tparm``, so
+  your code doesn't get crowded out by terminal bookkeeping.
+* Act intelligently when somebody redirects your output to a file, omitting the
+  terminal control codes the user doesn't want to see (optional).
+.. _curses: http://docs.python.org/library/curses.html
+Before And After
+Without Blessings, this is how you'd print some underlined text at the bottom
+of the screen::
+    from curses import tigetstr, setupterm, tparm
+    from fcntl import ioctl
+    from os import isatty
+    import struct
+    import sys
+    from termios import TIOCGWINSZ
+    # If we want to tolerate having our output piped to other commands or
+    # files without crashing, we need to do all this branching:
+    if hasattr(sys.stdout, 'fileno') and isatty(sys.stdout.fileno()):
+        setupterm()
+        sc = tigetstr('sc')
+        cup = tigetstr('cup')
+        rc = tigetstr('rc')
+        underline = tigetstr('smul')
+        normal = tigetstr('sgr0')
+    else:
+        sc = cup = rc = underline = normal = ''
+    print sc  # Save cursor position.
+    if cup:
+        # tigetnum('lines') doesn't always update promptly, hence this:
+        height = struct.unpack('hhhh', ioctl(0, TIOCGWINSZ, '\000' * 8))[0]
+        print tparm(cup, height - 1, 0)  # Move cursor to bottom.
+    print 'This is {under}underlined{normal}!'.format(under=underline,
+                                                      normal=normal)
+    print rc  # Restore cursor position.
+That was long and full of incomprehensible trash! Let's try it again, this time
+with Blessings::
+    from blessings import Terminal
+    term = Terminal()
+    with term.location(0, term.height - 1):
+        print 'This is', term.underline('pretty!')
+Much better.
+What It Provides
+Blessings provides just one top-level object: ``Terminal``. Instantiating a
+``Terminal`` figures out whether you're on a terminal at all and, if so, does
+any necessary terminal setup. After that, you can proceed to ask it all sorts
+of things about the terminal. Terminal terminal terminal.
+Simple Formatting
+Lots of handy formatting codes ("capabilities" in low-level parlance) are
+available as attributes on a ``Terminal``. For example::
+    from blessings import Terminal
+    term = Terminal()
+    print 'I am ' + term.bold + 'bold' + term.normal + '!'
+Though they are strings at heart, you can also use them as callable wrappers so
+you don't have to say ``normal`` afterward::
+    print 'I am', term.bold('bold') + '!'
+Or, if you want fine-grained control while maintaining some semblance of
+brevity, you can combine it with Python's string formatting, which makes
+attributes easy to access::
+    print 'All your {t.red}base {t.underline}are belong to us{t.normal}'.format(t=term)
+Simple capabilities of interest include...
+* ``bold``
+* ``reverse``
+* ``underline``
+* ``no_underline`` (which turns off underlining)
+* ``blink``
+* ``normal`` (which turns off everything, even colors)
+Here are a few more which are less likely to work on all terminals:
+* ``dim``
+* ``italic`` and ``no_italic``
+* ``shadow`` and ``no_shadow``
+* ``standout`` and ``no_standout``
+* ``subscript`` and ``no_subscript``
+* ``superscript`` and ``no_superscript``
+* ``flash`` (which flashes the screen once)
+Note that, while the inverse of ``underline`` is ``no_underline``, the only way
+to turn off ``bold`` or ``reverse`` is ``normal``, which also cancels any
+custom colors. This is because there's no portable way to tell the terminal to
+undo certain pieces of formatting, even at the lowest level.
+You might also notice that the above aren't the typical incomprehensible
+terminfo capability names; we alias a few of the harder-to-remember ones for
+readability. However, you aren't limited to these: you can reference any
+string-returning capability listed on the `terminfo man page`_ by the name
+under the "Cap-name" column: for example, ``term.rum``.
+.. _`terminfo man page`: http://www.manpagez.com/man/5/terminfo/
+16 colors, both foreground and background, are available as easy-to-remember
+    from blessings import Terminal
+    term = Terminal()
+    print term.red + term.on_green + 'Red on green? Ick!' + term.normal
+    print term.bright_red + term.on_bright_blue + 'This is even worse!' + term.normal
+You can also call them as wrappers, which sets everything back to normal at the
+    print term.red_on_green('Red on green? Ick!')
+    print term.yellow('I can barely see it.')
+The available colors are...
+* ``black``
+* ``red``
+* ``green``
+* ``yellow``
+* ``blue``
+* ``magenta``
+* ``cyan``
+* ``white``
+You can set the background color instead of the foreground by prepending
+``on_``, as in ``on_blue``. There is also a ``bright`` version of each color:
+for example, ``on_bright_blue``.
+There is also a numerical interface to colors, which takes an integer from
+    term.color(5) + 'Hello' + term.normal
+    term.on_color(3) + 'Hello' + term.normal
+    term.color(5)('Hello')
+    term.on_color(3)('Hello')
+If some color is unsupported (for instance, if only the normal colors are
+available, not the bright ones), trying to use it will, on most terminals, have
+no effect: the foreground and background colors will stay as they were. You can
+get fancy and do different things depending on the supported colors by checking
+.. _`number_of_colors`: http://packages.python.org/blessings/#blessings.Terminal.number_of_colors
+Compound Formatting
+If you want to do lots of crazy formatting all at once, you can just mash it
+all together::
+    from blessings import Terminal
+    term = Terminal()
+    print term.bold_underline_green_on_yellow + 'Woo' + term.normal
+Or you can use your newly coined attribute as a wrapper, which implicitly sets
+everything back to normal afterward::
+    print term.bold_underline_green_on_yellow('Woo')
+This compound notation comes in handy if you want to allow users to customize
+the formatting of your app: just have them pass in a format specifier like
+"bold_green" on the command line, and do a quick ``getattr(term,
+that_option)('Your text')`` when you do your formatting.
+I'd be remiss if I didn't credit couleur_, where I probably got the idea for
+all this mashing.
+.. _couleur: http://pypi.python.org/pypi/couleur
+Moving The Cursor
+When you want to move the cursor to output text at a specific spot, you have
+a few choices.
+Moving Temporarily
+Most often, you'll need to flit to a certain location, print something, and
+then return: for example, when updating a progress bar at the bottom of the
+screen. ``Terminal`` provides a context manager for doing this concisely::
+    from blessings import Terminal
+    term = Terminal()
+    with term.location(0, term.height - 1):
+        print 'Here is the bottom.'
+    print 'This is back where I came from.'
+Parameters to ``location()`` are ``x`` and then ``y``, but you can also pass
+just one of them, leaving the other alone. For example... ::
+    with term.location(y=10):
+        print 'We changed just the row.'
+If you're doing a series of ``move`` calls (see below) and want to return the
+cursor to its original position afterward, call ``location()`` with no
+arguments, and it will do only the position restoring::
+    with term.location():
+        print term.move(1, 1) + 'Hi'
+        print term.move(9, 9) + 'Mom'
+Note that, since ``location()`` uses the terminal's built-in
+position-remembering machinery, you can't usefully nest multiple calls. Use
+``location()`` at the outermost spot, and use simpler things like ``move``
+Moving Permanently
+If you just want to move and aren't worried about returning, do something like
+    from blessings import Terminal
+    term = Terminal()
+    print term.move(10, 1) + 'Hi, mom!'
+  Position the cursor elsewhere. Parameters are y coordinate, then x
+  coordinate.
+  Move the cursor to the given column.
+  Move the cursor to the given row.
+How does all this work? These are simply more terminal capabilities, wrapped to
+give them nicer names. The added wrinkle--that they take parameters--is also
+given a pleasant treatment: rather than making you dig up ``tparm()`` all the
+time, we simply make these capabilities into callable strings. You'd get the
+raw capability strings if you were to just print them, but they're fully
+parametrized if you pass params to them as if they were functions.
+Consequently, you can also reference any other string-returning capability
+listed on the `terminfo man page`_ by its name under the "Cap-name" column.
+.. _`terminfo man page`: http://www.manpagez.com/man/5/terminfo/
+One-Notch Movement
+Finally, there are some parameterless movement capabilities that move the
+cursor one character in various directions:
+* ``move_left``
+* ``move_right``
+* ``move_up``
+* ``move_down``
+For example... ::
+    print term.move_up + 'Howdy!'
+Height And Width
+It's simple to get the height and width of the terminal, in characters::
+    from blessings import Terminal
+    term = Terminal()
+    height = term.height
+    width = term.width
+These are newly updated each time you ask for them, so they're safe to use from
+SIGWINCH handlers.
+Clearing The Screen
+Blessings provides syntactic sugar over some screen-clearing capabilities:
+  Clear the whole screen.
+  Clear to the end of the line.
+  Clear backward to the beginning of the line.
+  Clear to the end of screen.
+Full-Screen Mode
+Perhaps you have seen a full-screen program, such as an editor, restore the
+exact previous state of the terminal upon exiting, including, for example, the
+command-line prompt from which it was launched. Curses pretty much forces you
+into this behavior, but Blessings makes it optional. If you want to do the
+state-restoration thing, use these capabilities:
+    Switch to the terminal mode where full-screen output is sanctioned. Print
+    this before you do any output.
+    Switch back to normal mode, restoring the exact state from before
+    ``enter_fullscreen`` was used.
+Using ``exit_fullscreen`` will wipe away any trace of your program's output, so
+reserve it for when you don't want to leave anything behind in the scrollback.
+There's also a context manager you can use as a shortcut::
+    from blessings import Terminal
+    term = Terminal()
+    with term.fullscreen():
+        # Print some stuff.
+Besides brevity, another advantage is that it switches back to normal mode even
+if an exception is raised in the ``with`` block.
+Pipe Savvy
+If your program isn't attached to a terminal, like if it's being piped to
+another command or redirected to a file, all the capability attributes on
+``Terminal`` will return empty strings. You'll get a nice-looking file without
+any formatting codes gumming up the works.
+If you want to override this--like if you anticipate your program being piped
+through ``less -r``, which handles terminal escapes just fine--pass
+``force_styling=True`` to the ``Terminal`` constructor.
+In any case, there is a ``does_styling`` attribute on ``Terminal`` that lets
+you see whether your capabilities will return actual, working formatting codes.
+If it's false, you should refrain from drawing progress bars and other frippery
+and just stick to content, since you're apparently headed into a pipe::
+    from blessings import Terminal
+    term = Terminal()
+    if term.does_styling:
+        with term.location(0, term.height - 1):
+            print 'Progress: [=======>   ]'
+    print term.bold('Important stuff')
+Shopping List
+There are decades of legacy tied up in terminal interaction, so attention to
+detail and behavior in edge cases make a difference. Here are some ways
+Blessings has your back:
+* Uses the terminfo database so it works with any terminal type
+* Provides up-to-the-moment terminal height and width, so you can respond to
+  terminal size changes (SIGWINCH signals). (Most other libraries query the
+  ``COLUMNS`` and ``LINES`` environment variables or the ``cols`` or ``lines``
+  terminal capabilities, which don't update promptly, if at all.)
+* Avoids making a mess if the output gets piped to a non-terminal
+* Works great with standard Python string templating
+* Provides convenient access to all terminal capabilities, not just a sugared
+  few
+* Outputs to any file-like object, not just stdout
+* Keeps a minimum of internal state, so you can feel free to mix and match with
+  calls to curses or whatever other terminal libraries you like
+Blessings does not provide...
+* Native color support on the Windows command prompt. However, it should work
+  when used in concert with colorama_.
+.. _colorama: http://pypi.python.org/pypi/colorama/0.2.4
+Bugs or suggestions? Visit the `issue tracker`_.
+.. _`issue tracker`: https://github.com/erikrose/blessings/issues/
+Blessings tests are run automatically by `Travis CI`_.
+.. _`Travis CI`: https://travis-ci.org/erikrose/blessings/
+.. image:: https://travis-ci.org/erikrose/blessings.svg?branch=master
+    :target: https://travis-ci.org/erikrose/blessings
+Blessings is under the MIT License. See the LICENSE file.
+Version History
+  * Don't crash if ``number_of_colors()`` is called when run in a non-terminal
+    or when ``does_styling`` is otherwise false.
+  * Add ``does_styling`` property. This takes ``force_styling`` into account
+    and should replace most uses of ``is_a_tty``.
+  * Make ``is_a_tty`` a read-only property, like ``does_styling``. Writing to
+    it never would have done anything constructive.
+  * Add ``fullscreen()`` and ``hidden_cursor()`` to the auto-generated docs.
+  * Fall back to ``LINES`` and ``COLUMNS`` environment vars to find height and
+    width. (jquast)
+  * Support terminal types, such as kermit and avatar, that use bytes 127-255
+    in their escape sequences. (jquast)
+  * Clean up fabfile, removing the redundant ``test`` command.
+  * Add Travis support.
+  * Make ``python setup.py test`` work without spurious errors on 2.6.
+  * Work around a tox parsing bug in its config file.
+  * Make context managers clean up after themselves even if there's an
+    exception. (Vitja Makarov)
+  * Parametrizing a capability no longer crashes when there is no tty. (Vitja
+    Makarov)
+  * Add syntactic sugar and documentation for ``enter_fullscreen`` and
+    ``exit_fullscreen``.
+  * Add context managers ``fullscreen()`` and ``hidden_cursor()``.
+  * Now you can force a ``Terminal`` never to emit styles by passing
+    ``force_styling=None``.
+  * Add syntactic sugar for cursor visibility control and single-space-movement
+    capabilities.
+  * Endorse the ``location()`` idiom for restoring cursor position after a
+    series of manual movements.
+  * Fix a bug in which ``location()`` wouldn't do anything when passed zeroes.
+  * Allow tests to be run with ``python setup.py test``.
+  * Added ``number_of_colors``, which tells you how many colors the terminal
+    supports.
+  * Made ``color(n)`` and ``on_color(n)`` callable to wrap a string, like the
+    named colors can. Also, make them both fall back to the ``setf`` and
+    ``setb`` capabilities (like the named colors do) if the ANSI ``setaf`` and
+    ``setab`` aren't available.
+  * Allowed ``color`` attr to act as an unparametrized string, not just a
+    callable.
+  * Made ``height`` and ``width`` examine any passed-in stream before falling
+    back to stdout. (This rarely if ever affects actual behavior; it's mostly
+    philosophical.)
+  * Made caching simpler and slightly more efficient.
+  * Got rid of a reference cycle between Terminals and FormattingStrings.
+  * Updated docs to reflect that terminal addressing (as in ``location()``) is
+    0-based.
+  * Added support for Python 3! We need 3.2.3 or greater, because the curses
+    library couldn't decide whether to accept strs or bytes before that
+    (http://bugs.python.org/issue10570).
+  * Everything that comes out of the library is now unicode. This lets us
+    support Python 3 without making a mess of the code, and Python 2 should
+    continue to work unless you were testing types (and badly). Please file a
+    bug if this causes trouble for you.
+  * Changed to the MIT License for better world domination.
+  * Added Sphinx docs.
+  * Added nicely named attributes for colors.
+  * Introduced compound formatting.
+  * Added wrapper behavior for styling and colors.
+  * Let you force capabilities to be non-empty, even if the output stream is
+    not a terminal.
+  * Added the ``is_a_tty`` attribute for telling whether the output stream is a
+    terminal.
+  * Sugared the remaining interesting string capabilities.
+  * Let ``location()`` operate on just an x *or* y coordinate.
+  * Extracted Blessings from nose-progressive, my `progress-bar-having,
+    traceback-shortcutting, rootin', tootin' testrunner`_. It provided the
+    tootin' functionality.
+.. _`progress-bar-having, traceback-shortcutting, rootin', tootin' testrunner`: http://pypi.python.org/pypi/nose-progressive/
--- a/third_party/python/blessings/blessings/__init__.py
+++ b/third_party/python/blessings/blessings/__init__.py
@@ -1,40 +1,34 @@
 """A thin, practical wrapper around terminal coloring, styling, and
+from __future__ import absolute_import
 from contextlib import contextmanager
 import curses
 from curses import setupterm, tigetnum, tigetstr, tparm
 from fcntl import ioctl
+import six
     from io import UnsupportedOperation as IOUnsupportedOperation
 except ImportError:
     class IOUnsupportedOperation(Exception):
         """A dummy exception to take the place of Python 3's
         ``io.UnsupportedOperation`` in Python 2"""
 from os import isatty, environ
-from platform import python_version_tuple
 import struct
 import sys
 from termios import TIOCGWINSZ
 __all__ = ['Terminal']
-if ('3', '0', '0') <= python_version_tuple() < ('3', '2', '2+'):  # Good till
-                                                                  # 3.2.10
-    # Python 3.x < 3.2.3 has a bug in which tparm() erroneously takes a string.
-    raise ImportError('Blessings needs Python 3.2.3 or greater for Python 3 '
-                      'support due to http://bugs.python.org/issue10570.')
 class Terminal(object):
     """An abstraction around terminal capabilities
     Unlike curses, this doesn't require clearing the screen before doing
     anything, and it's friendlier to use. It keeps the endless calls to
     ``tigetstr()`` and ``tparm()`` out of your code, and it acts intelligently
     when somebody pipes your output to a non-terminal.
@@ -423,29 +417,29 @@ def derivative_colors(colors):
 COLORS = set(['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan',
                  set(['bold', 'underline', 'reverse', 'blink', 'dim', 'italic',
                       'shadow', 'standout', 'subscript', 'superscript']))
-class ParametrizingString(unicode):
+class ParametrizingString(six.text_type):
     """A Unicode string which can be called to parametrize it as a terminal
     def __new__(cls, formatting, normal=None):
         :arg normal: If non-None, indicates that, once parametrized, this can
             be used as a ``FormattingString``. The value is used as the
             "normal" capability.
-        new = unicode.__new__(cls, formatting)
+        new = six.text_type.__new__(cls, formatting)
         new._normal = normal
         return new
     def __call__(self, *args):
             # Re-encode the cap, because tparm() takes a bytestring in Python
             # 3. However, appear to be a plain Unicode string otherwise so
             # concats work.
@@ -464,56 +458,56 @@ class ParametrizingString(unicode):
             # Catch "must call (at least) setupterm() first" errors, as when
             # running simply `nosetests` (without progressive) on nose-
             # progressive. Perhaps the terminal has gone away between calling
             # tigetstr and calling tparm.
             return u''
         except TypeError:
             # If the first non-int (i.e. incorrect) arg was a string, suggest
             # something intelligent:
-            if len(args) == 1 and isinstance(args[0], basestring):
+            if len(args) == 1 and isinstance(args[0], six.string_types):
                 raise TypeError(
                     'A native or nonexistent capability template received '
                     '%r when it was expecting ints. You probably misspelled a '
                     'formatting call like bright_red_on_white(...).' % args)
                 # Somebody passed a non-string; I don't feel confident
                 # guessing what they were trying to do.
-class FormattingString(unicode):
+class FormattingString(six.text_type):
     """A Unicode string which can be called upon a piece of text to wrap it in
     def __new__(cls, formatting, normal):
-        new = unicode.__new__(cls, formatting)
+        new = six.text_type.__new__(cls, formatting)
         new._normal = normal
         return new
     def __call__(self, text):
         """Return a new string that is ``text`` formatted with my contents.
         At the beginning of the string, I prepend the formatting that is my
         contents. At the end, I append the "normal" sequence to set everything
         back to defaults. The return value is always a Unicode.
         return self + text + self._normal
-class NullCallableString(unicode):
+class NullCallableString(six.text_type):
     """A dummy callable Unicode to stand in for ``FormattingString`` and
     We use this when there is no tty and thus all capabilities should be blank.
     def __new__(cls):
-        new = unicode.__new__(cls, u'')
+        new = six.text_type.__new__(cls, u'')
         return new
     def __call__(self, *args):
         """Return a Unicode or whatever you passed in as the first arg
         (hopefully a string of some kind).
         When called with an int as the first arg, return an empty Unicode. An
         int is a good hint that I am a ``ParametrizingString``, as there are
--- a/third_party/python/blessings/blessings/tests.py
+++ b/third_party/python/blessings/blessings/tests.py
@@ -5,23 +5,24 @@ It was tempting to mock out curses to ge
 but there are concrete integration-testing benefits in not doing so. For
 instance, ``tigetstr`` changed its return type in Python 3.2.3. So instead, we
 simply create all our test ``Terminal`` instances with a known terminal type.
 All we require from the host machine is that a standard terminfo definition of
 xterm-256color exists.
 from __future__ import with_statement  # Make 2.5-compatible
+from __future__ import absolute_import
 from curses import tigetstr, tparm
 from functools import partial
-from StringIO import StringIO
 import sys
 from nose import SkipTest
 from nose.tools import eq_
+from six import StringIO
 # This tests that __all__ is correct, since we use below everything that should
 # be imported:
 from blessings import *
 TestTerminal = partial(Terminal, kind='xterm-256color')
@@ -224,32 +225,32 @@ def test_formatting_functions_without_tt
     eq_(t.on_bright_red_bold_bright_green_underline('meh'), u'meh')
 def test_nice_formatting_errors():
     """Make sure you get nice hints if you misspell a formatting wrapper."""
     t = TestTerminal()
-    except TypeError, e:
+    except TypeError as e:
         assert 'probably misspelled' in e.args[0]
         t.bold_misspelled(u'hey')  # unicode
-    except TypeError, e:
+    except TypeError as e:
         assert 'probably misspelled' in e.args[0]
         t.bold_misspelled(None)  # an arbitrary non-string
-    except TypeError, e:
+    except TypeError as e:
         assert 'probably misspelled' not in e.args[0]
         t.bold_misspelled('a', 'b')  # >1 string arg
-    except TypeError, e:
+    except TypeError as e:
         assert 'probably misspelled' not in e.args[0]
 def test_init_descriptor_always_initted():
     """We should be able to get a height and width even on no-tty Terminals."""
     t = Terminal(stream=StringIO())
     eq_(type(t.height), int)
new file mode 100644
--- /dev/null
+++ b/third_party/python/blessings/setup.py
@@ -0,0 +1,49 @@
+import sys
+# Prevent spurious errors during `python setup.py test`, a la
+# http://www.eby-sarna.com/pipermail/peak/2010-May/003357.html:
+    import multiprocessing
+except ImportError:
+    pass
+from setuptools import setup, find_packages
+    name='blessings',
+    version='1.6.1',
+    description='A thin, practical wrapper around terminal coloring, styling, and positioning',
+    long_description=open('README.rst').read(),
+    author='Erik Rose',
+    author_email='erikrose@grinchcentral.com',
+    license='MIT',
+    packages=find_packages(exclude=['ez_setup']),
+    install_requires=['six'],
+    tests_require=['nose'],
+    test_suite='nose.collector',
+    url='https://github.com/erikrose/blessings',
+    include_package_data=True,
+    python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*',
+    classifiers=[
+        'Intended Audience :: Developers',
+        'Natural Language :: English',
+        'Development Status :: 5 - Production/Stable',
+        'Environment :: Console',
+        'Environment :: Console :: Curses',
+        'License :: OSI Approved :: MIT License',
+        'Operating System :: POSIX',
+        'Programming Language :: Python :: 2',
+        'Programming Language :: Python :: 2.7',
+        'Programming Language :: Python :: 3',
+        'Programming Language :: Python :: 3.4',
+        'Programming Language :: Python :: 3.5',
+        'Programming Language :: Python :: 3.6',
+        'Programming Language :: Python :: Implementation :: CPython',
+        'Programming Language :: Python :: Implementation :: PyPy',
+        'Topic :: Software Development :: Libraries',
+        'Topic :: Software Development :: User Interfaces',
+        'Topic :: Terminals'
+        ],
+    keywords=['terminal', 'tty', 'curses', 'ncurses', 'formatting', 'style', 'color', 'console'],
new file mode 100644
--- /dev/null
+++ b/third_party/python/blessings/tox.ini
@@ -0,0 +1,8 @@
+envlist = py27, py34, py36
+commands = nosetests blessings
+deps =
+    nose
+    six