1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/python/blessings/README.rst Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,399 @@ 1.4 +========= 1.5 +Blessings 1.6 +========= 1.7 + 1.8 +Coding with Blessings looks like this... :: 1.9 + 1.10 + from blessings import Terminal 1.11 + 1.12 + t = Terminal() 1.13 + 1.14 + print t.bold('Hi there!') 1.15 + print t.bold_red_on_bright_green('It hurts my eyes!') 1.16 + 1.17 + with t.location(0, t.height - 1): 1.18 + print 'This is at the bottom.' 1.19 + 1.20 +Or, for byte-level control, you can drop down and play with raw terminal 1.21 +capabilities:: 1.22 + 1.23 + print '{t.bold}All your {t.red}bold and red base{t.normal}'.format(t=t) 1.24 + print t.wingo(2) 1.25 + 1.26 +The Pitch 1.27 +========= 1.28 + 1.29 +Blessings lifts several of curses_' limiting assumptions, and it makes your 1.30 +code pretty, too: 1.31 + 1.32 +* Use styles, color, and maybe a little positioning without clearing the whole 1.33 + screen first. 1.34 +* Leave more than one screenful of scrollback in the buffer after your program 1.35 + exits, like a well-behaved command-line app should. 1.36 +* Get rid of all those noisy, C-like calls to ``tigetstr`` and ``tparm``, so 1.37 + your code doesn't get crowded out by terminal bookkeeping. 1.38 +* Act intelligently when somebody redirects your output to a file, omitting the 1.39 + terminal control codes the user doesn't want to see (optional). 1.40 + 1.41 +.. _curses: http://docs.python.org/library/curses.html 1.42 + 1.43 +Before And After 1.44 +---------------- 1.45 + 1.46 +Without Blessings, this is how you'd print some underlined text at the bottom 1.47 +of the screen:: 1.48 + 1.49 + from curses import tigetstr, setupterm, tparm 1.50 + from fcntl import ioctl 1.51 + from os import isatty 1.52 + import struct 1.53 + import sys 1.54 + from termios import TIOCGWINSZ 1.55 + 1.56 + # If we want to tolerate having our output piped to other commands or 1.57 + # files without crashing, we need to do all this branching: 1.58 + if hasattr(sys.stdout, 'fileno') and isatty(sys.stdout.fileno()): 1.59 + setupterm() 1.60 + sc = tigetstr('sc') 1.61 + cup = tigetstr('cup') 1.62 + rc = tigetstr('rc') 1.63 + underline = tigetstr('smul') 1.64 + normal = tigetstr('sgr0') 1.65 + else: 1.66 + sc = cup = rc = underline = normal = '' 1.67 + print sc # Save cursor position. 1.68 + if cup: 1.69 + # tigetnum('lines') doesn't always update promptly, hence this: 1.70 + height = struct.unpack('hhhh', ioctl(0, TIOCGWINSZ, '\000' * 8))[0] 1.71 + print tparm(cup, height - 1, 0) # Move cursor to bottom. 1.72 + print 'This is {under}underlined{normal}!'.format(under=underline, 1.73 + normal=normal) 1.74 + print rc # Restore cursor position. 1.75 + 1.76 +Phew! That was long and full of incomprehensible trash! Let's try it again, 1.77 +this time with Blessings:: 1.78 + 1.79 + from blessings import Terminal 1.80 + 1.81 + term = Terminal() 1.82 + with term.location(0, term.height - 1): 1.83 + print 'This is', term.underline('pretty!') 1.84 + 1.85 +Much better. 1.86 + 1.87 +What It Provides 1.88 +================ 1.89 + 1.90 +Blessings provides just one top-level object: ``Terminal``. Instantiating a 1.91 +``Terminal`` figures out whether you're on a terminal at all and, if so, does 1.92 +any necessary terminal setup. After that, you can proceed to ask it all sorts 1.93 +of things about the terminal. Terminal terminal terminal. 1.94 + 1.95 +Simple Formatting 1.96 +----------------- 1.97 + 1.98 +Lots of handy formatting codes ("capabilities" in low-level parlance) are 1.99 +available as attributes on a ``Terminal``. For example:: 1.100 + 1.101 + from blessings import Terminal 1.102 + 1.103 + term = Terminal() 1.104 + print 'I am ' + term.bold + 'bold' + term.normal + '!' 1.105 + 1.106 +You can also use them as wrappers so you don't have to say ``normal`` 1.107 +afterward:: 1.108 + 1.109 + print 'I am', term.bold('bold') + '!' 1.110 + 1.111 +Or, if you want fine-grained control while maintaining some semblance of 1.112 +brevity, you can combine it with Python's string formatting, which makes 1.113 +attributes easy to access:: 1.114 + 1.115 + print 'All your {t.red}base {t.underline}are belong to us{t.normal}'.format(t=term) 1.116 + 1.117 +Simple capabilities of interest include... 1.118 + 1.119 +* ``bold`` 1.120 +* ``reverse`` 1.121 +* ``underline`` 1.122 +* ``no_underline`` (which turns off underlining) 1.123 +* ``blink`` 1.124 +* ``normal`` (which turns off everything, even colors) 1.125 +* ``clear_eol`` (clear to the end of the line) 1.126 +* ``clear_bol`` (clear to beginning of line) 1.127 +* ``clear_eos`` (clear to end of screen) 1.128 + 1.129 +Here are a few more which are less likely to work on all terminals: 1.130 + 1.131 +* ``dim`` 1.132 +* ``italic`` and ``no_italic`` 1.133 +* ``shadow`` and ``no_shadow`` 1.134 +* ``standout`` and ``no_standout`` 1.135 +* ``subscript`` and ``no_subscript`` 1.136 +* ``superscript`` and ``no_superscript`` 1.137 +* ``flash`` (which flashes the screen once) 1.138 + 1.139 +Note that, while the inverse of ``underline`` is ``no_underline``, the only way 1.140 +to turn off ``bold`` or ``reverse`` is ``normal``, which also cancels any 1.141 +custom colors. This is because there's no way to tell the terminal to undo 1.142 +certain pieces of formatting, even at the lowest level. 1.143 + 1.144 +You might notice that the above aren't the typical incomprehensible terminfo 1.145 +capability names; we alias a few of the harder-to-remember ones for 1.146 +readability. However, you aren't limited to these: you can reference any 1.147 +string-returning capability listed on the `terminfo man page`_ by the name 1.148 +under the "Cap-name" column: for example, ``term.rum``. 1.149 + 1.150 +.. _`terminfo man page`: http://www.manpagez.com/man/5/terminfo/ 1.151 + 1.152 +Color 1.153 +----- 1.154 + 1.155 +16 colors, both foreground and background, are available as easy-to-remember 1.156 +attributes:: 1.157 + 1.158 + from blessings import Terminal 1.159 + 1.160 + term = Terminal() 1.161 + print term.red + term.on_green + 'Red on green? Ick!' + term.normal 1.162 + print term.bright_red + term.on_bright_blue + 'This is even worse!' + term.normal 1.163 + 1.164 +You can also call them as wrappers, which sets everything back to normal at the 1.165 +end:: 1.166 + 1.167 + print term.red_on_green('Red on green? Ick!') 1.168 + print term.yellow('I can barely see it.') 1.169 + 1.170 +The available colors are... 1.171 + 1.172 +* ``black`` 1.173 +* ``red`` 1.174 +* ``green`` 1.175 +* ``yellow`` 1.176 +* ``blue`` 1.177 +* ``magenta`` 1.178 +* ``cyan`` 1.179 +* ``white`` 1.180 + 1.181 +You can set the background color instead of the foreground by prepending 1.182 +``on_``, as in ``on_blue``. There is also a ``bright`` version of each color: 1.183 +for example, ``on_bright_blue``. 1.184 + 1.185 +There is also a numerical interface to colors, which takes an integer from 1.186 +0-15:: 1.187 + 1.188 + term.color(5) + 'Hello' + term.normal 1.189 + term.on_color(3) + 'Hello' + term.normal 1.190 + 1.191 + term.color(5)('Hello') 1.192 + term.on_color(3)('Hello') 1.193 + 1.194 +If some color is unsupported (for instance, if only the normal colors are 1.195 +available, not the bright ones), trying to use it will, on most terminals, have 1.196 +no effect: the foreground and background colors will stay as they were. You can 1.197 +get fancy and do different things depending on the supported colors by checking 1.198 +`number_of_colors`_. 1.199 + 1.200 +.. _`number_of_colors`: http://packages.python.org/blessings/#blessings.Terminal.number_of_colors 1.201 + 1.202 +Compound Formatting 1.203 +------------------- 1.204 + 1.205 +If you want to do lots of crazy formatting all at once, you can just mash it 1.206 +all together:: 1.207 + 1.208 + from blessings import Terminal 1.209 + 1.210 + term = Terminal() 1.211 + print term.bold_underline_green_on_yellow + 'Woo' + term.normal 1.212 + 1.213 +Or you can use your newly coined attribute as a wrapper, which implicitly sets 1.214 +everything back to normal afterward:: 1.215 + 1.216 + print term.bold_underline_green_on_yellow('Woo') 1.217 + 1.218 +This compound notation comes in handy if you want to allow users to customize 1.219 +the formatting of your app: just have them pass in a format specifier like 1.220 +"bold_green" on the command line, and do a quick ``getattr(term, 1.221 +that_option)('Your text')`` when you do your formatting. 1.222 + 1.223 +I'd be remiss if I didn't credit couleur_, where I probably got the idea for 1.224 +all this mashing. 1.225 + 1.226 +.. _couleur: http://pypi.python.org/pypi/couleur 1.227 + 1.228 +Parametrized Capabilities 1.229 +------------------------- 1.230 + 1.231 +Some capabilities take parameters. Rather than making you dig up ``tparm()`` 1.232 +all the time, we simply make such capabilities into callable strings. You can 1.233 +pass the parameters right in:: 1.234 + 1.235 + from blessings import Terminal 1.236 + 1.237 + term = Terminal() 1.238 + print term.move(10, 1) 1.239 + 1.240 +Here are some of interest: 1.241 + 1.242 +``move`` 1.243 + Position the cursor elsewhere. Parameters are y coordinate, then x 1.244 + coordinate. 1.245 +``move_x`` 1.246 + Move the cursor to the given column. 1.247 +``move_y`` 1.248 + Move the cursor to the given row. 1.249 + 1.250 +You can also reference any other string-returning capability listed on the 1.251 +`terminfo man page`_ by its name under the "Cap-name" column. 1.252 + 1.253 +.. _`terminfo man page`: http://www.manpagez.com/man/5/terminfo/ 1.254 + 1.255 +Height and Width 1.256 +---------------- 1.257 + 1.258 +It's simple to get the height and width of the terminal, in characters:: 1.259 + 1.260 + from blessings import Terminal 1.261 + 1.262 + term = Terminal() 1.263 + height = term.height 1.264 + width = term.width 1.265 + 1.266 +These are newly updated each time you ask for them, so they're safe to use from 1.267 +SIGWINCH handlers. 1.268 + 1.269 +Temporary Repositioning 1.270 +----------------------- 1.271 + 1.272 +Sometimes you need to flit to a certain location, print something, and then 1.273 +return: for example, when updating a progress bar at the bottom of the screen. 1.274 +``Terminal`` provides a context manager for doing this concisely:: 1.275 + 1.276 + from blessings import Terminal 1.277 + 1.278 + term = Terminal() 1.279 + with term.location(0, term.height - 1): 1.280 + print 'Here is the bottom.' 1.281 + print 'This is back where I came from.' 1.282 + 1.283 +Parameters to ``location()`` are ``x`` and then ``y``, but you can also pass 1.284 +just one of them, leaving the other alone. For example... :: 1.285 + 1.286 + with term.location(y=10): 1.287 + print 'We changed just the row.' 1.288 + 1.289 +If you want to reposition permanently, see ``move``, in an example above. 1.290 + 1.291 +Pipe Savvy 1.292 +---------- 1.293 + 1.294 +If your program isn't attached to a terminal, like if it's being piped to 1.295 +another command or redirected to a file, all the capability attributes on 1.296 +``Terminal`` will return empty strings. You'll get a nice-looking file without 1.297 +any formatting codes gumming up the works. 1.298 + 1.299 +If you want to override this--like if you anticipate your program being piped 1.300 +through ``less -r``, which handles terminal escapes just fine--pass 1.301 +``force_styling=True`` to the ``Terminal`` constructor. 1.302 + 1.303 +In any case, there is an ``is_a_tty`` attribute on ``Terminal`` that lets you 1.304 +see whether the attached stream seems to be a terminal. If it's false, you 1.305 +might refrain from drawing progress bars and other frippery, since you're 1.306 +apparently headed into a pipe:: 1.307 + 1.308 + from blessings import Terminal 1.309 + 1.310 + term = Terminal() 1.311 + if term.is_a_tty: 1.312 + with term.location(0, term.height - 1): 1.313 + print 'Progress: [=======> ]' 1.314 + print term.bold('Important stuff') 1.315 + 1.316 +Shopping List 1.317 +============= 1.318 + 1.319 +There are decades of legacy tied up in terminal interaction, so attention to 1.320 +detail and behavior in edge cases make a difference. Here are some ways 1.321 +Blessings has your back: 1.322 + 1.323 +* Uses the terminfo database so it works with any terminal type 1.324 +* Provides up-to-the-moment terminal height and width, so you can respond to 1.325 + terminal size changes (SIGWINCH signals). (Most other libraries query the 1.326 + ``COLUMNS`` and ``LINES`` environment variables or the ``cols`` or ``lines`` 1.327 + terminal capabilities, which don't update promptly, if at all.) 1.328 +* Avoids making a mess if the output gets piped to a non-terminal 1.329 +* Works great with standard Python string templating 1.330 +* Provides convenient access to all terminal capabilities, not just a sugared 1.331 + few 1.332 +* Outputs to any file-like object, not just stdout 1.333 +* Keeps a minimum of internal state, so you can feel free to mix and match with 1.334 + calls to curses or whatever other terminal libraries you like 1.335 + 1.336 +Blessings does not provide... 1.337 + 1.338 +* Native color support on the Windows command prompt. However, it should work 1.339 + when used in concert with colorama_. 1.340 + 1.341 +.. _colorama: http://pypi.python.org/pypi/colorama/0.2.4 1.342 + 1.343 +Bugs 1.344 +==== 1.345 + 1.346 +Bugs or suggestions? Visit the `issue tracker`_. 1.347 + 1.348 +.. _`issue tracker`: https://github.com/erikrose/blessings/issues/new 1.349 + 1.350 +License 1.351 +======= 1.352 + 1.353 +Blessings is under the MIT License. See the LICENSE file. 1.354 + 1.355 +Version History 1.356 +=============== 1.357 + 1.358 +1.3 1.359 + * Add ``number_of_colors``, which tells you how many colors the terminal 1.360 + supports. 1.361 + * Made ``color(n)`` and ``on_color(n)`` callable to wrap a string, like the 1.362 + named colors can. Also, make them both fall back to the ``setf`` and 1.363 + ``setb`` capabilities (like the named colors do) if the ANSI ``setaf`` and 1.364 + ``setab`` aren't available. 1.365 + * Allow ``color`` attr to act as an unparametrized string, not just a 1.366 + callable. 1.367 + * Make ``height`` and ``width`` examine any passed-in stream before falling 1.368 + back to stdout. (This rarely if ever affects actual behavior; it's mostly 1.369 + philosophical.) 1.370 + * Make caching simpler and slightly more efficient. 1.371 + * Get rid of a reference cycle between Terminals and FormattingStrings. 1.372 + * Update docs to reflect that terminal addressing (as in ``location()``) is 1.373 + 0-based. 1.374 + 1.375 +1.2 1.376 + * Added support for Python 3! We need 3.2.3 or greater, because the curses 1.377 + library couldn't decide whether to accept strs or bytes before that 1.378 + (http://bugs.python.org/issue10570). 1.379 + * Everything that comes out of the library is now unicode. This lets us 1.380 + support Python 3 without making a mess of the code, and Python 2 should 1.381 + continue to work unless you were testing types (and badly). Please file a 1.382 + bug if this causes trouble for you. 1.383 + * Changed to the MIT License for better world domination. 1.384 + * Added Sphinx docs. 1.385 + 1.386 +1.1 1.387 + * Added nicely named attributes for colors. 1.388 + * Introduced compound formatting. 1.389 + * Added wrapper behavior for styling and colors. 1.390 + * Let you force capabilities to be non-empty, even if the output stream is 1.391 + not a terminal. 1.392 + * Added the ``is_a_tty`` attribute for telling whether the output stream is a 1.393 + terminal. 1.394 + * Sugared the remaining interesting string capabilities. 1.395 + * Let ``location()`` operate on just an x *or* y coordinate. 1.396 + 1.397 +1.0 1.398 + * Extracted Blessings from nose-progressive, my `progress-bar-having, 1.399 + traceback-shortcutting, rootin', tootin' testrunner`_. It provided the 1.400 + tootin' functionality. 1.401 + 1.402 +.. _`progress-bar-having, traceback-shortcutting, rootin', tootin' testrunner`: http://pypi.python.org/pypi/nose-progressive/