diff options
-rw-r--r-- | .prospector.yaml | 2 | ||||
-rw-r--r-- | README.rst | 4 | ||||
-rw-r--r-- | blessed/_binterms.py | 872 | ||||
-rw-r--r-- | blessed/sequences.py | 14 | ||||
-rw-r--r-- | blessed/terminal.py | 24 | ||||
-rw-r--r-- | blessed/tests/accessories.py | 52 | ||||
-rw-r--r-- | blessed/tests/test_core.py | 4 | ||||
-rw-r--r-- | blessed/tests/test_keyboard.py | 25 | ||||
-rw-r--r-- | blessed/tests/test_length_sequence.py | 11 | ||||
-rw-r--r-- | blessed/tests/test_sequences.py | 86 | ||||
-rw-r--r-- | blessed/tests/test_wrap.py | 148 | ||||
-rw-r--r-- | tox.ini | 8 |
12 files changed, 1036 insertions, 214 deletions
diff --git a/.prospector.yaml b/.prospector.yaml index 3ccfbda..83c0c35 100644 --- a/.prospector.yaml +++ b/.prospector.yaml @@ -29,8 +29,6 @@ pep257: pep8: # style checking run: true - options: - max-line-length: 100 pyflakes: # preferring 'frosted' instead (a fork of) @@ -2,9 +2,9 @@ :alt: Travis Continous Integration :target: https://travis-ci.org/jquast/blessed -.. image:: https://img.shields.io/coveralls/jquast/blessed.svg +.. image:: https://coveralls.io/repos/jquast/blessed/badge.png?branch=master :alt: Coveralls Code Coverage - :target: https://coveralls.io/r/jquast/blessed + :target: https://coveralls.io/r/jquast/blessed?branch=master .. image:: https://img.shields.io/pypi/v/blessed.svg :alt: Latest Version diff --git a/blessed/_binterms.py b/blessed/_binterms.py new file mode 100644 index 0000000..84f58fe --- /dev/null +++ b/blessed/_binterms.py @@ -0,0 +1,872 @@ +""" Exports a list of binary terminals blessed is not able to cope with. """ +#: This list of terminals is manually managed, it describes all of the terminals +#: that blessed cannot measure the sequence length for; they contain +#: binary-packed capabilities instead of numerics, so it is not possible to +#: build regular expressions in the way that sequences.py does. +#: +#: This may be generated by exporting TEST_BINTERMS, then analyzing the +#: jUnit result xml written to the project folder. +binary_terminals = u""" +9term +aaa+dec +aaa+rv +aaa+rv-100 +aaa+rv-30 +aaa-rv-unk +abm80 +abm85 +abm85e +abm85h +abm85h-old +act4 +act5 +addrinfo +adds980 +adm+sgr +adm+sgr-100 +adm+sgr-30 +adm11 +adm1178 +adm12 +adm1a +adm2 +adm20 +adm21 +adm22 +adm3 +adm31 +adm31-old +adm3a +adm3a+ +adm42 +adm42-ns +adm5 +aepro +aj510 +aj830 +alto-h19 +altos4 +altos7 +altos7pc +ampex175 +ampex175-b +ampex210 +ampex232 +ampex232w +ampex80 +annarbor4080 +ansi+arrows +ansi+csr +ansi+cup +ansi+enq +ansi+erase +ansi+idc +ansi+idl +ansi+idl1 +ansi+inittabs +ansi+local +ansi+local1 +ansi+pp +ansi+rca +ansi+rep +ansi+sgr +ansi+sgrbold +ansi+sgrdim +ansi+sgrso +ansi+sgrul +ansi+tabs +ansi-color-2-emx +ansi-color-3-emx +ansi-emx +ansi-mini +ansi-mr +ansi-mtabs +ansi-nt +ansi.sys +ansi.sys-old +ansi.sysk +ansi77 +apollo +apple-80 +apple-ae +apple-soroc +apple-uterm +apple-uterm-vb +apple-videx +apple-videx2 +apple-videx3 +apple-vm80 +apple2e +apple2e-p +apple80p +appleII +appleIIgs +atari +att4415+nl +att4420 +att4424m +att5310 +att5310-100 +att5310-30 +att5620-s +avatar +avatar0 +avatar0+ +avt+s +aws +awsc +bantam +basis +beacon +beehive +blit +bq300-8 +bq300-8-pc +bq300-8-pc-rv +bq300-8-pc-w +bq300-8-pc-w-rv +bq300-8rv +bq300-8w +bq300-w-8rv +c100 +c100-rv +c108 +c108-4p +c108-rv +c108-rv-4p +c108-w +ca22851 +cbblit +cbunix +cci +cci-100 +cci-30 +cdc456 +cdc721 +cdc721-esc +cdc721-esc-100 +cdc721-esc-30 +cdc721ll +cdc752 +cdc756 +cit101e +cit101e-132 +cit101e-n +cit101e-n132 +cit80 +citoh +citoh-6lpi +citoh-8lpi +citoh-comp +citoh-elite +citoh-pica +citoh-prop +coco3 +color_xterm +commodore +contel300 +contel301 +cops10 +ct8500 +ctrm +ctrm-100 +ctrm-30 +cyb110 +cyb83 +d132 +d200 +d200-100 +d200-30 +d210-dg +d210-dg-100 +d210-dg-30 +d211-dg +d211-dg-100 +d211-dg-30 +d216-dg +d216-dg-100 +d216-dg-30 +d216-unix +d216-unix-25 +d217-unix +d217-unix-25 +d220 +d220-100 +d220-30 +d220-7b +d220-7b-100 +d220-7b-30 +d220-dg +d220-dg-100 +d220-dg-30 +d230c +d230c-100 +d230c-30 +d230c-dg +d230c-dg-100 +d230c-dg-30 +d400 +d400-100 +d400-30 +d410-dg +d410-dg-100 +d410-dg-30 +d412-dg +d412-dg-100 +d412-dg-30 +d412-unix +d412-unix-25 +d412-unix-s +d412-unix-sr +d412-unix-w +d413-unix +d413-unix-25 +d413-unix-s +d413-unix-sr +d413-unix-w +d414-unix +d414-unix-25 +d414-unix-s +d414-unix-sr +d414-unix-w +d430c-dg +d430c-dg-100 +d430c-dg-30 +d430c-dg-ccc +d430c-dg-ccc-100 +d430c-dg-ccc-30 +d430c-unix +d430c-unix-100 +d430c-unix-25 +d430c-unix-25-100 +d430c-unix-25-30 +d430c-unix-25-ccc +d430c-unix-30 +d430c-unix-ccc +d430c-unix-s +d430c-unix-s-ccc +d430c-unix-sr +d430c-unix-sr-ccc +d430c-unix-w +d430c-unix-w-ccc +d470c +d470c-7b +d470c-dg +d555-dg +d577-dg +d800 +delta +dg+ccc +dg+color +dg+color8 +dg+fixed +dg-generic +dg200 +dg210 +dg211 +dg450 +dg460-ansi +dg6053 +dg6053-old +dgkeys+11 +dgkeys+15 +dgkeys+7b +dgkeys+8b +dgmode+color +dgmode+color8 +dgunix+ccc +dgunix+fixed +diablo1620 +diablo1620-m8 +diablo1640 +diablo1640-lm +diablo1740-lm +digilog +djgpp203 +dm1520 +dm2500 +dm3025 +dm3045 +dmchat +dmterm +dp8242 +dt100 +dt100w +dt110 +dt80-sas +dtc300s +dtc382 +dumb +dw1 +dw2 +dw3 +dw4 +dwk +ecma+color +ecma+sgr +elks +elks-glasstty +elks-vt52 +emu +ep40 +ep48 +esprit +esprit-am +ex155 +f100 +f100-rv +f110 +f110-14 +f110-14w +f110-w +f200 +f200-w +f200vi +f200vi-w +falco +falco-p +fos +fox +gator-52 +gator-52t +glasstty +gnome +gnome+pcfkeys +gnome-2007 +gnome-2008 +gnome-256color +gnome-fc5 +gnome-rh72 +gnome-rh80 +gnome-rh90 +go140 +go140w +gs6300 +gsi +gt40 +gt42 +guru+rv +guru+s +h19 +h19-bs +h19-g +h19-u +h19-us +h19k +ha8675 +ha8686 +hft-old +hmod1 +hp+arrows +hp+color +hp+labels +hp+pfk+arrows +hp+pfk+cr +hp+pfk-cr +hp+printer +hp2 +hp236 +hp262x +hp2641a +hp300h +hp700-wy +hp70092 +hp9837 +hp9845 +hpterm +hpterm-color +hz1000 +hz1420 +hz1500 +hz1510 +hz1520 +hz1520-noesc +hz1552 +hz1552-rv +hz2000 +i100 +i400 +ibcs2 +ibm+16color +ibm+color +ibm-apl +ibm-system1 +ibm3101 +ibm3151 +ibm3161 +ibm3161-C +ibm3162 +ibm3164 +ibm327x +ibm5081-c +ibm8514-c +ibmaed +ibmapa8c +ibmapa8c-c +ibmega +ibmega-c +ibmmono +ibmvga +ibmvga-c +icl6404 +icl6404-w +ifmr +ims-ansi +ims950 +ims950-b +ims950-rv +intertube +intertube2 +intext +intext2 +kaypro +kermit +kermit-am +klone+acs +klone+color +klone+koi8acs +klone+sgr +klone+sgr-dumb +klone+sgr8 +konsole +konsole+pcfkeys +konsole-16color +konsole-256color +konsole-base +konsole-linux +konsole-solaris +konsole-vt100 +konsole-vt420pc +konsole-xf3x +konsole-xf4x +kt7 +kt7ix +ln03 +ln03-w +lpr +luna +megatek +mgterm +microb +mime +mime-fb +mime-hb +mime2a +mime2a-s +mime314 +mime3a +mime3ax +minitel1 +minitel1b +mlterm+pcfkeys +mm340 +modgraph2 +msk227 +msk22714 +msk227am +mt70 +ncr160vppp +ncr160vpwpp +ncr160wy50+pp +ncr160wy50+wpp +ncr160wy60pp +ncr160wy60wpp +ncr260vppp +ncr260vpwpp +ncr260wy325pp +ncr260wy325wpp +ncr260wy350pp +ncr260wy350wpp +ncr260wy50+pp +ncr260wy50+wpp +ncr260wy60pp +ncr260wy60wpp +ncr7900i +ncr7900iv +ncr7901 +ncrvt100an +ncrvt100wan +ndr9500 +ndr9500-25 +ndr9500-25-mc +ndr9500-25-mc-nl +ndr9500-25-nl +ndr9500-mc +ndr9500-mc-nl +ndr9500-nl +nec5520 +newhpkeyboard +nextshell +northstar +nsterm+c +nsterm+c41 +nsterm+s +nwp511 +oblit +oc100 +oldpc3 +origpc3 +osborne +osborne-w +osexec +otek4112 +owl +p19 +pc-coherent +pc-venix +pc6300plus +pcix +pckermit +pckermit120 +pe1251 +pe7000c +pe7000m +pilot +pmcons +prism2 +prism4 +prism5 +pro350 +psterm-fast +psterm-fast-100 +psterm-fast-30 +pt100 +pt210 +pt250 +pty +qansi +qansi-g +qansi-m +qansi-t +qansi-w +qdss +qnx +qnx-100 +qnx-30 +qnxm +qnxm-100 +qnxm-30 +qnxt +qnxt-100 +qnxt-30 +qnxt2 +qnxtmono +qnxtmono-100 +qnxtmono-30 +qnxw +qnxw-100 +qnxw-30 +qume5 +qvt101 +qvt101+ +qvt102 +qvt119+ +qvt119+-25 +qvt119+-25-w +qvt119+-w +rbcomm +rbcomm-nam +rbcomm-w +rca +regent100 +regent20 +regent25 +regent40 +regent40+ +regent60 +rt6221 +rt6221-w +rtpc +rxvt+pcfkeys +scanset +screen+fkeys +screen-16color +screen-16color-bce +screen-16color-bce-s +screen-16color-s +screen-256color +screen-256color-bce +screen-256color-bce-s +screen-256color-s +screen-bce +screen-s +screen-w +screen.linux +screen.rxvt +screen.teraterm +screen.xterm-r6 +screen2 +screen3 +screwpoint +sibo +simterm +soroc120 +soroc140 +st52 +superbee-xsb +superbeeic +superbrain +swtp +synertek +t10 +t1061 +t1061f +t3700 +t3800 +tandem6510 +tandem653 +tandem653-100 +tandem653-30 +tek +tek4013 +tek4014 +tek4014-sm +tek4015 +tek4015-sm +tek4023 +tek4105 +tek4107 +tek4113-nd +tek4205 +tek4205-100 +tek4205-30 +tek4207-s +teraterm +teraterm2.3 +teraterm4.59 +terminet1200 +ti700 +ti931 +trs16 +trs2 +tt +tty33 +tty37 +tty43 +tvi803 +tvi9065 +tvi910 +tvi910+ +tvi912 +tvi912b +tvi912b+2p +tvi912b+dim +tvi912b+dim-100 +tvi912b+dim-30 +tvi912b+mc +tvi912b+mc-100 +tvi912b+mc-30 +tvi912b+printer +tvi912b+vb +tvi912b-2p +tvi912b-2p-mc +tvi912b-2p-mc-100 +tvi912b-2p-mc-30 +tvi912b-2p-p +tvi912b-2p-unk +tvi912b-mc +tvi912b-mc-100 +tvi912b-mc-30 +tvi912b-p +tvi912b-unk +tvi912b-vb +tvi912b-vb-mc +tvi912b-vb-mc-100 +tvi912b-vb-mc-30 +tvi912b-vb-p +tvi912b-vb-unk +tvi920b +tvi920b+fn +tvi920b-2p +tvi920b-2p-mc +tvi920b-2p-mc-100 +tvi920b-2p-mc-30 +tvi920b-2p-p +tvi920b-2p-unk +tvi920b-mc +tvi920b-mc-100 +tvi920b-mc-30 +tvi920b-p +tvi920b-unk +tvi920b-vb +tvi920b-vb-mc +tvi920b-vb-mc-100 +tvi920b-vb-mc-30 +tvi920b-vb-p +tvi920b-vb-unk +tvi921 +tvi924 +tvi925 +tvi925-hi +tvi92B +tvi92D +tvi950 +tvi950-2p +tvi950-4p +tvi950-rv +tvi950-rv-2p +tvi950-rv-4p +tvipt +unknown +vanilla +vc303 +vc404 +vc404-s +vc414 +vc415 +vi200 +vi200-f +vi200-rv +vi50 +vi500 +vi50adm +vi55 +viewpoint +vp3a+ +vp60 +vp90 +vremote +vt100+enq +vt100+fnkeys +vt100+keypad +vt100+pfkeys +vt100-s +vt102+enq +vt200-js +vt220+keypad +vt50h +vt52 +vt61 +wsiris +wy100 +wy100q +wy120 +wy120-25 +wy120-vb +wy160 +wy160-25 +wy160-42 +wy160-43 +wy160-tek +wy160-tek-100 +wy160-tek-30 +wy160-vb +wy30 +wy30-mc +wy30-mc-100 +wy30-mc-30 +wy30-vb +wy325 +wy325-25 +wy325-42 +wy325-43 +wy325-vb +wy350 +wy350-100 +wy350-30 +wy350-vb +wy350-vb-100 +wy350-vb-30 +wy350-w +wy350-w-100 +wy350-w-30 +wy350-wvb +wy350-wvb-100 +wy350-wvb-30 +wy370 +wy370-100 +wy370-105k +wy370-105k-100 +wy370-105k-30 +wy370-30 +wy370-EPC +wy370-EPC-100 +wy370-EPC-30 +wy370-nk +wy370-nk-100 +wy370-nk-30 +wy370-rv +wy370-rv-100 +wy370-rv-30 +wy370-tek +wy370-tek-100 +wy370-tek-30 +wy370-vb +wy370-vb-100 +wy370-vb-30 +wy370-w +wy370-w-100 +wy370-w-30 +wy370-wvb +wy370-wvb-100 +wy370-wvb-30 +wy50 +wy50-mc +wy50-mc-100 +wy50-mc-30 +wy50-vb +wy60 +wy60-25 +wy60-42 +wy60-43 +wy60-vb +wy99-ansi +wy99a-ansi +wy99f +wy99fa +wy99gt +wy99gt-25 +wy99gt-vb +wy99gt-tek +wyse-vp +xerox1720 +xerox820 +xfce +xnuppc+100x37 +xnuppc+112x37 +xnuppc+128x40 +xnuppc+128x48 +xnuppc+144x48 +xnuppc+160x64 +xnuppc+200x64 +xnuppc+200x75 +xnuppc+256x96 +xnuppc+80x25 +xnuppc+80x30 +xnuppc+90x30 +xnuppc+c +xnuppc+c-100 +xnuppc+c-30 +xtalk +xtalk-100 +xtalk-30 +xterm+256color +xterm+256color-100 +xterm+256color-30 +xterm+88color +xterm+88color-100 +xterm+88color-30 +xterm+app +xterm+edit +xterm+noapp +xterm+pc+edit +xterm+pcc0 +xterm+pcc1 +xterm+pcc2 +xterm+pcc3 +xterm+pce2 +xterm+pcf0 +xterm+pcf2 +xterm+pcfkeys +xterm+r6f2 +xterm+vt+edit +xterm-vt52 +z100 +z100bw +z29 +zen30 +zen50 +ztx +""".split() + +__all__ = ('binary_terminals',) diff --git a/blessed/sequences.py b/blessed/sequences.py index 2447e53..2862197 100644 --- a/blessed/sequences.py +++ b/blessed/sequences.py @@ -14,12 +14,15 @@ import math import sys import re +# local +from ._binterms import binary_terminals as _BINTERM_UNSUPPORTED + # 3rd-party import wcwidth # https://github.com/jquast/wcwidth -_BINTERM_UNSUPPORTED = ('kermit', 'avatar') -_BINTERM_UNSUPPORTED_MSG = ('sequence-awareness for terminals emitting ' - 'binary-packed capabilities are not supported.') +_BINTERM_UNSUPPORTED_MSG = ( + u"Terminal kind {0!r} contains binary-packed capabilities, blessed " + u"is likely to fail to measure the length of its sequences.") if sys.version_info[0] == 3: text_type = str @@ -263,7 +266,7 @@ def init_sequence_patterns(term): printable length of a string. """ if term.kind in _BINTERM_UNSUPPORTED: - warnings.warn(_BINTERM_UNSUPPORTED_MSG) + warnings.warn(_BINTERM_UNSUPPORTED_MSG.format(term.kind)) # Build will_move, a list of terminal capabilities that have # indeterminate effects on the terminal cursor position. @@ -322,9 +325,6 @@ def init_sequence_patterns(term): class SequenceTextWrapper(textwrap.TextWrapper): def __init__(self, width, term, **kwargs): self.term = term - assert kwargs.get('break_long_words', False) is False, ( - 'break_long_words is not sequence-safe') - kwargs['break_long_words'] = False textwrap.TextWrapper.__init__(self, width, **kwargs) def _wrap_chunks(self, chunks): diff --git a/blessed/terminal.py b/blessed/terminal.py index 8f65321..1e17d73 100644 --- a/blessed/terminal.py +++ b/blessed/terminal.py @@ -4,6 +4,7 @@ import collections import contextlib import functools import warnings +import platform import codecs import curses import locale @@ -180,9 +181,16 @@ class Terminal(object): # send them to stdout as a fallback, since they have to go # somewhere. try: - curses.setupterm(self._kind, self._init_descriptor) - except curses.error: - warnings.warn('Failed to setupterm(kind=%s)' % (self._kind,)) + if (platform.python_implementation() == 'PyPy' and + isinstance(self._kind, unicode)): + # pypy/2.4.0_2/libexec/lib_pypy/_curses.py, line 1131 + # TypeError: initializer for ctype 'char *' must be a str + curses.setupterm(self._kind.encode('ascii'), self._init_descriptor) + else: + curses.setupterm(self._kind, self._init_descriptor) + except curses.error as err: + warnings.warn('Failed to setupterm(kind={0!r}): {1}' + .format(self._kind, err)) self._kind = None self._does_styling = False else: @@ -515,16 +523,8 @@ class Terminal(object): Returns a list of strings that may contain escape sequences. See ``textwrap.TextWrapper`` for all available additional kwargs to customize wrapping behavior such as ``subsequent_indent``. - - Note that the keyword argument ``break_long_words`` may not be set, - it is not sequence-safe! """ - - _blw = 'break_long_words' - assert (_blw not in kwargs or not kwargs[_blw]), ( - "keyword argument, '{}' is not sequence-safe".format(_blw)) - - width = width is None and self.width or width + width = self.width if width is None else width lines = [] for line in text.splitlines(): lines.extend( diff --git a/blessed/tests/accessories.py b/blessed/tests/accessories.py index 6785827..9c06688 100644 --- a/blessed/tests/accessories.py +++ b/blessed/tests/accessories.py @@ -3,9 +3,11 @@ # std from __future__ import with_statement import contextlib +import subprocess import functools import traceback import termios +import random import codecs import curses import sys @@ -27,16 +29,25 @@ TestTerminal = functools.partial(Terminal, kind='xterm-256color') SEND_SEMAPHORE = SEMAPHORE = b'SEMAPHORE\n' RECV_SEMAPHORE = b'SEMAPHORE\r\n' all_xterms_params = ['xterm', 'xterm-256color'] -all_terms_params = ['screen', 'vt220', 'rxvt', 'cons25', 'linux', 'ansi'] -binpacked_terminal_params = ['avatar', 'kermit'] many_lines_params = [30, 100] -many_columns_params = [10, 100] -if os.environ.get('TRAVIS', None) is None: - # TRAVIS-CI has a limited type of terminals, the others ... - all_terms_params.extend(['avatar', 'kermit', 'dtterm', 'wyse520', - 'minix', 'eterm', 'aixterm', 'putty']) -all_standard_terms_params = (set(all_terms_params) - - set(binpacked_terminal_params)) +many_columns_params = [1, 25, 50] +from blessed._binterms import binary_terminals +default_all_terms = ['screen', 'vt220', 'rxvt', 'cons25', 'linux', 'ansi'] +try: + available_terms = [ + _term.split(None, 1)[0].decode('ascii') for _term in + subprocess.Popen(["toe"], stdout=subprocess.PIPE, close_fds=True) + .communicate()[0].splitlines()] + if not os.environ.get('TEST_ALLTERMS'): + # we just pick 3 random terminal types, they're all as good as any so + # long as they're not in the binary_terminals list. + random.shuffle(available_terms) + available_terms = available_terms[:3] + all_terms_params = list(set(available_terms) - ( + set(binary_terminals) if not os.environ.get('TEST_BINTERMS') + else set())) or default_all_terms +except OSError: + all_terms_params = default_all_terms class as_subprocess(object): @@ -174,7 +185,10 @@ def echo_off(fd): def unicode_cap(cap): """Return the result of ``tigetstr`` except as Unicode.""" - val = curses.tigetstr(cap) + try: + val = curses.tigetstr(cap) + except curses.error: + val = None if val: return val.decode('latin1') return u'' @@ -182,15 +196,21 @@ def unicode_cap(cap): def unicode_parm(cap, *parms): """Return the result of ``tparm(tigetstr())`` except as Unicode.""" - cap = curses.tigetstr(cap) + try: + cap = curses.tigetstr(cap) + except curses.error: + cap = None if cap: - val = curses.tparm(cap, *parms) + try: + val = curses.tparm(cap, *parms) + except curses.error: + val = None if val: return val.decode('latin1') return u'' -@pytest.fixture(params=binpacked_terminal_params) +@pytest.fixture(params=binary_terminals) def unsupported_sequence_terminals(request): """Terminals that emit warnings for unsupported sequence-awareness.""" return request.param @@ -208,12 +228,6 @@ def all_terms(request): return request.param -@pytest.fixture(params=all_standard_terms_params) -def all_standard_terms(request): - """Common kind values for all kinds of terminals (except binary-packed).""" - return request.param - - @pytest.fixture(params=many_lines_params) def many_lines(request): """Various number of lines for screen height.""" diff --git a/blessed/tests/test_core.py b/blessed/tests/test_core.py index effaa13..befcea3 100644 --- a/blessed/tests/test_core.py +++ b/blessed/tests/test_core.py @@ -212,7 +212,9 @@ def test_setupterm_invalid_issue39(): term = TestTerminal(kind='unknown', force_styling=True) except UserWarning: err = sys.exc_info()[1] - assert err.args[0] == 'Failed to setupterm(kind=unknown)' + assert err.args[0] == ( + "Failed to setupterm(kind='unknown'): " + "setupterm: could not find terminal") else: assert not term.is_a_tty and not term.does_styling, ( 'Should have thrown exception') diff --git a/blessed/tests/test_keyboard.py b/blessed/tests/test_keyboard.py index e079c88..c81154f 100644 --- a/blessed/tests/test_keyboard.py +++ b/blessed/tests/test_keyboard.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- "Tests for keyboard support." +# std imports import functools import tempfile try: @@ -7,15 +8,17 @@ try: except ImportError: import io StringIO = io.StringIO +import platform import signal import curses import time import math -import tty +import tty # NOQA import pty import sys import os +# local from .accessories import ( read_until_eof, read_until_semaphore, @@ -29,6 +32,8 @@ from .accessories import ( xterms, ) +# 3rd-party +import pytest import mock if sys.version_info[0] == 3: @@ -422,6 +427,9 @@ def test_inkey_0s_cbreak_multibyte_utf8(): assert math.floor(time.time() - stime) == 0.0 +@pytest.mark.skipif(os.environ.get('TRAVIS', None) is not None or + platform.python_implementation() == 'PyPy', + reason="travis-ci nor pypy handle ^C very well.") def test_inkey_0s_raw_ctrl_c(): "0-second inkey with raw allows receiving ^C." pid, master_fd = pty.fork() @@ -449,17 +457,9 @@ def test_inkey_0s_raw_ctrl_c(): stime = time.time() output = read_until_eof(master_fd) pid, status = os.waitpid(pid, 0) - if os.environ.get('TRAVIS', None) is not None: - # For some reason, setraw has no effect travis-ci, - # is still accepts ^C, causing system exit on py26, - # but exit 0 on py27, and either way on py33 - # .. strange, huh? - assert output in (u'', u'\x03') - assert os.WEXITSTATUS(status) in (0, 2) - else: - assert (output == u'\x03' or - output == u'' and not os.isatty(0)) - assert os.WEXITSTATUS(status) == 0 + assert (output == u'\x03' or + output == u'' and not os.isatty(0)) + assert os.WEXITSTATUS(status) == 0 assert math.floor(time.time() - stime) == 0.0 @@ -698,7 +698,6 @@ def test_cuf1_and_cub1_as_RIGHT_LEFT(all_terms): term = TestTerminal(kind=kind, force_styling=True) keymap = get_keyboard_sequences(term) if term._cuf1: - assert term._cuf1 != u' ' assert term._cuf1 in keymap assert keymap[term._cuf1] == term.KEY_RIGHT if term._cub1: diff --git a/blessed/tests/test_length_sequence.py b/blessed/tests/test_length_sequence.py index 93aa2d1..88096dc 100644 --- a/blessed/tests/test_length_sequence.py +++ b/blessed/tests/test_length_sequence.py @@ -12,12 +12,11 @@ except ImportError: from io import StringIO from .accessories import ( - all_standard_terms, + all_terms, as_subprocess, TestTerminal, many_columns, many_lines, - all_terms, ) import pytest @@ -244,7 +243,7 @@ def test_winsize(many_lines, many_columns): @pytest.mark.skipif(platform.python_implementation() == 'PyPy', reason='PyPy fails TIOCSWINSZ') -def test_Sequence_alignment(all_terms, many_lines): +def test_Sequence_alignment(all_terms): """Tests methods related to Sequence class, namely ljust, rjust, center.""" @as_subprocess def child(kind, lines=25, cols=80): @@ -270,7 +269,7 @@ def test_Sequence_alignment(all_terms, many_lines): assert (t.length(radjusted.strip()) == pony_len) assert (t.length(radjusted) == len(pony_msg.rjust(t.width))) - child(kind=all_terms, lines=many_lines) + child(kind=all_terms) def test_sequence_is_movement_false(all_terms): @@ -310,7 +309,7 @@ def test_sequence_is_movement_false(all_terms): child_mnemonics_wontmove(all_terms) -def test_sequence_is_movement_true(all_standard_terms): +def test_sequence_is_movement_true(all_terms): """Test parsers about sequences that move the cursor.""" @as_subprocess def child_mnemonics_willmove(kind): @@ -340,4 +339,4 @@ def test_sequence_is_movement_true(all_standard_terms): assert not t.clear or (len(t.clear) == measure_length(t.clear, t)) - child_mnemonics_willmove(all_standard_terms) + child_mnemonics_willmove(all_terms) diff --git a/blessed/tests/test_sequences.py b/blessed/tests/test_sequences.py index 7a14ba0..6de77ec 100644 --- a/blessed/tests/test_sequences.py +++ b/blessed/tests/test_sequences.py @@ -1,16 +1,19 @@ # -*- coding: utf-8 -*- """Tests for Terminal() sequences and sequence-awareness.""" +# std imports try: from StringIO import StringIO except ImportError: from io import StringIO import platform +import random import sys import os +# local from .accessories import ( unsupported_sequence_terminals, - all_standard_terms, + all_terms, as_subprocess, TestTerminal, unicode_parm, @@ -19,6 +22,7 @@ from .accessories import ( many_lines, ) +# 3rd-party import pytest import mock @@ -89,12 +93,14 @@ def test_stream_attr(): @pytest.mark.skipif(os.environ.get('TRAVIS', None) is not None, reason="travis-ci does not have binary-packed terminals.") -def test_emit_warnings_about_binpacked(unsupported_sequence_terminals): +def test_emit_warnings_about_binpacked(): """Test known binary-packed terminals (kermit, avatar) emit a warning.""" + from blessed.sequences import _BINTERM_UNSUPPORTED_MSG + from blessed._binterms import binary_terminals + @as_subprocess def child(kind): import warnings - from blessed.sequences import _BINTERM_UNSUPPORTED_MSG warnings.filterwarnings("error", category=RuntimeWarning) warnings.filterwarnings("error", category=UserWarning) @@ -102,30 +108,33 @@ def test_emit_warnings_about_binpacked(unsupported_sequence_terminals): TestTerminal(kind=kind, force_styling=True) except UserWarning: err = sys.exc_info()[1] - assert (err.args[0] == _BINTERM_UNSUPPORTED_MSG or - err.args[0].startswith('Unknown parameter in ') + assert (err.args[0] == _BINTERM_UNSUPPORTED_MSG.format(kind) or + err.args[0].startswith('Unknown parameter in ') or + err.args[0].startswith('Failed to setupterm(') ), err else: assert 'warnings should have been emitted.' warnings.resetwarnings() - child(unsupported_sequence_terminals) + # any binary terminal should do. + child(binary_terminals[random.randrange(len(binary_terminals))]) -def test_unit_binpacked_unittest(unsupported_sequence_terminals): +def test_unit_binpacked_unittest(): """Unit Test known binary-packed terminals emit a warning (travis-safe).""" import warnings + from blessed._binterms import binary_terminals from blessed.sequences import (_BINTERM_UNSUPPORTED_MSG, init_sequence_patterns) warnings.filterwarnings("error", category=UserWarning) term = mock.Mock() - term.kind = unsupported_sequence_terminals + term.kind = binary_terminals[random.randrange(len(binary_terminals))] try: init_sequence_patterns(term) except UserWarning: err = sys.exc_info()[1] - assert err.args[0] == _BINTERM_UNSUPPORTED_MSG + assert err.args[0] == _BINTERM_UNSUPPORTED_MSG.format(term.kind) else: assert False, 'Previous stmt should have raised exception.' warnings.resetwarnings() @@ -139,7 +148,7 @@ def test_merge_sequences(): assert (_merge_sequences(input_list) == output_expected) -def test_location_with_styling(all_standard_terms): +def test_location_with_styling(all_terms): """Make sure ``location()`` works on all terminals.""" @as_subprocess def child_with_styling(kind): @@ -152,7 +161,7 @@ def test_location_with_styling(all_standard_terms): u'hi', unicode_cap('rc'))) assert (t.stream.getvalue() == expected_output) - child_with_styling(all_standard_terms) + child_with_styling(all_terms) def test_location_without_styling(): @@ -170,7 +179,7 @@ def test_location_without_styling(): child_without_styling() -def test_horizontal_location(all_standard_terms): +def test_horizontal_location(all_terms): """Make sure we can move the cursor horizontally without changing rows.""" @as_subprocess def child(kind): @@ -181,14 +190,15 @@ def test_horizontal_location(all_standard_terms): (unicode_cap('sc'), unicode_parm('hpa', 5), unicode_cap('rc'))) - assert (t.stream.getvalue() == expected_output) + assert (t.stream.getvalue() == expected_output), ( + repr(t.stream.getvalue()), repr(expected_output)) # skip 'screen', hpa is proxied (see later tests) - if all_standard_terms != 'screen': - child(all_standard_terms) + if all_terms != 'screen': + child(all_terms) -def test_vertical_location(all_standard_terms): +def test_vertical_location(all_terms): """Make sure we can move the cursor horizontally without changing rows.""" @as_subprocess def child(kind): @@ -202,8 +212,8 @@ def test_vertical_location(all_standard_terms): assert (t.stream.getvalue() == expected_output) # skip 'screen', vpa is proxied (see later tests) - if all_standard_terms != 'screen': - child(all_standard_terms) + if all_terms != 'screen': + child(all_terms) def test_inject_move_x(): @@ -262,7 +272,7 @@ def test_inject_civis_and_cnorm_for_ansi(): child('ansi') -def test_zero_location(all_standard_terms): +def test_zero_location(all_terms): """Make sure ``location()`` pays attention to 0-valued args.""" @as_subprocess def child(kind): @@ -275,10 +285,10 @@ def test_zero_location(all_standard_terms): unicode_cap('rc'))) assert (t.stream.getvalue() == expected_output) - child(all_standard_terms) + child(all_terms) -def test_mnemonic_colors(all_standard_terms): +def test_mnemonic_colors(all_terms): """Make sure color shortcuts work.""" @as_subprocess def child(kind): @@ -300,10 +310,10 @@ def test_mnemonic_colors(all_standard_terms): assert (t.on_bright_black == on_color(t, 8)) assert (t.on_bright_green == on_color(t, 10)) - child(all_standard_terms) + child(all_terms) -def test_callable_numeric_colors(all_standard_terms): +def test_callable_numeric_colors(all_terms): """``color(n)`` should return a formatting wrapper.""" @as_subprocess def child(kind): @@ -333,10 +343,10 @@ def test_callable_numeric_colors(all_standard_terms): else: assert t.on_color(6)('smoo') == 'smoo' - child(all_standard_terms) + child(all_terms) -def test_null_callable_numeric_colors(all_standard_terms): +def test_null_callable_numeric_colors(all_terms): """``color(n)`` should be a no-op on null terminals.""" @as_subprocess def child(kind): @@ -344,20 +354,20 @@ def test_null_callable_numeric_colors(all_standard_terms): assert (t.color(5)('smoo') == 'smoo') assert (t.on_color(6)('smoo') == 'smoo') - child(all_standard_terms) + child(all_terms) -def test_naked_color_cap(all_standard_terms): +def test_naked_color_cap(all_terms): """``term.color`` should return a stringlike capability.""" @as_subprocess def child(kind): t = TestTerminal(kind=kind) assert (t.color + '' == t.setaf + '') - child(all_standard_terms) + child(all_terms) -def test_formatting_functions(all_standard_terms): +def test_formatting_functions(all_terms): """Test simple and compound formatting wrappers.""" @as_subprocess def child(kind): @@ -388,10 +398,10 @@ def test_formatting_functions(all_standard_terms): assert (t.subscript(u'[1]') == expected_output) - child(all_standard_terms) + child(all_terms) -def test_compound_formatting(all_standard_terms): +def test_compound_formatting(all_terms): """Test simple and compound formatting wrappers.""" @as_subprocess def child(kind): @@ -411,10 +421,10 @@ def test_compound_formatting(all_standard_terms): assert (t.on_bright_red_bold_bright_green_underline('meh') == expected_output) - child(all_standard_terms) + child(all_terms) -def test_formatting_functions_without_tty(all_standard_terms): +def test_formatting_functions_without_tty(all_terms): """Test crazy-ass formatting wrappers when there's no tty.""" @as_subprocess def child(kind): @@ -426,10 +436,10 @@ def test_formatting_functions_without_tty(all_standard_terms): assert (t.bold_underline_green_on_red('loo') == u'loo') assert (t.on_bright_red_bold_bright_green_underline('meh') == u'meh') - child(all_standard_terms) + child(all_terms) -def test_nice_formatting_errors(all_standard_terms): +def test_nice_formatting_errors(all_terms): """Make sure you get nice hints if you misspell a formatting wrapper.""" @as_subprocess def child(kind): @@ -463,10 +473,10 @@ def test_nice_formatting_errors(all_standard_terms): e = sys.exc_info()[1] assert 'probably misspelled' in e.args[0], e.args - child(all_standard_terms) + child(all_terms) -def test_null_callable_string(all_standard_terms): +def test_null_callable_string(all_terms): """Make sure NullCallableString tolerates all kinds of args.""" @as_subprocess def child(kind): @@ -480,7 +490,7 @@ def test_null_callable_string(all_standard_terms): assert (t.uhh(9876) == '') assert (t.clear('x') == 'x') - child(all_standard_terms) + child(all_terms) def test_bnc_parameter_emits_warning(): diff --git a/blessed/tests/test_wrap.py b/blessed/tests/test_wrap.py index 36e752d..dee3ebc 100644 --- a/blessed/tests/test_wrap.py +++ b/blessed/tests/test_wrap.py @@ -35,122 +35,50 @@ def test_SequenceWrapper_invalid_width(): child() -def test_SequenceWrapper_drop_whitespace_subsequent_indent(): +@pytest.mark.parametrize("kwargs", [ + dict(break_long_words=False, + drop_whitespace=False, + subsequent_indent=''), + dict(break_long_words=False, + drop_whitespace=True, + subsequent_indent=''), + dict(break_long_words=False, + drop_whitespace=False, + subsequent_indent=' '), + dict(break_long_words=False, + drop_whitespace=True, + subsequent_indent=' '), + # dict(break_long_words=True, + # drop_whitespace=False, + # subsequent_indent=''), + # dict(break_long_words=True, + # drop_whitespace=True, + # subsequent_indent=''), + # dict(break_long_words=True, + # drop_whitespace=False, + # subsequent_indent=' '), + # dict(break_long_words=True, + # drop_whitespace=True, + # subsequent_indent=' '), +]) +def test_SequenceWrapper(all_terms, many_columns, kwargs): """Test that text wrapping matches internal extra options.""" - WIDTH = 10 - @as_subprocess - def child(): + def child(term, width, kwargs): # build a test paragraph, along with a very colorful version t = TestTerminal() - pgraph = u' '.join( - ('a', 'ab', 'abc', 'abcd', 'abcde', 'abcdef', 'abcdefgh', - 'abcdefghi', 'abcdefghij', 'abcdefghijk', 'abcdefghijkl', - 'abcdefghijklm', 'abcdefghijklmn', 'abcdefghijklmno ',) - * 4) - - pgraph_colored = u''.join([ - t.color(n % 7) + t.bold + ch if ch != ' ' else ' ' - for n, ch in enumerate(pgraph)]) - - internal_wrapped = textwrap.wrap(pgraph, width=WIDTH, - break_long_words=False, - drop_whitespace=True, - subsequent_indent=u' '*3) - my_wrapped = t.wrap(pgraph, width=WIDTH, - drop_whitespace=True, - subsequent_indent=u' '*3) - my_wrapped_colored = t.wrap(pgraph_colored, width=WIDTH, - drop_whitespace=True, - subsequent_indent=u' '*3) - - # ensure we textwrap ascii the same as python - assert (internal_wrapped == my_wrapped) - - # ensure our first and last line wraps at its ends - first_l = internal_wrapped[0] - last_l = internal_wrapped[-1] - my_first_l = my_wrapped_colored[0] - my_last_l = my_wrapped_colored[-1] - assert (len(first_l) == t.length(my_first_l)) - assert (len(last_l) == t.length(my_last_l)), (internal_wrapped, - my_wrapped_colored) - assert (len(internal_wrapped[-1]) == t.length(my_wrapped_colored[-1])) - - # ensure our colored textwrap is the same line length - assert (len(internal_wrapped) == len(my_wrapped_colored)) - - child() - - -@pytest.mark.skipif(platform.python_implementation() == 'PyPy', - reason='PyPy fails TIOCSWINSZ') -def test_SequenceWrapper(all_terms, many_columns): - """Test that text wrapping accounts for sequences correctly.""" - @as_subprocess - def child(kind, lines=25, cols=80): - - # set the pty's virtual window size - val = struct.pack('HHHH', lines, cols, 0, 0) - fcntl.ioctl(sys.__stdout__.fileno(), termios.TIOCSWINSZ, val) - - # build a test paragraph, along with a very colorful version - t = TestTerminal(kind=kind) - pgraph = u' '.join( - ('a', 'ab', 'abc', 'abcd', 'abcde', 'abcdef', 'abcdefgh', - 'abcdefghi', 'abcdefghij', 'abcdefghijk', 'abcdefghijkl', - 'abcdefghijklm', 'abcdefghijklmn', 'abcdefghijklmno',) * 4) - pgraph_colored = u''.join([ - t.color(n % 7) + t.bold + ch - for n, ch in enumerate(pgraph)]) - - internal_wrapped = textwrap.wrap(pgraph, t.width, - break_long_words=False) - my_wrapped = t.wrap(pgraph) - my_wrapped_colored = t.wrap(pgraph_colored) - - # ensure we textwrap ascii the same as python - assert (internal_wrapped == my_wrapped) - - # ensure our first and last line wraps at its ends - first_l = internal_wrapped[0] - last_l = internal_wrapped[-1] - my_first_l = my_wrapped_colored[0] - my_last_l = my_wrapped_colored[-1] - assert (len(first_l) == t.length(my_first_l)) - assert (len(last_l) == t.length(my_last_l)) - assert (len(internal_wrapped[-1]) == t.length(my_wrapped_colored[-1])) - - child(kind=all_terms, lines=25, cols=many_columns) - - -def test_SequenceWrapper_27(all_terms): - """Test that text wrapping accounts for sequences correctly.""" - WIDTH = 27 - - @as_subprocess - def child(kind): - # build a test paragraph, along with a very colorful version - t = TestTerminal(kind=kind) - pgraph = u' '.join( - ('a', 'ab', 'abc', 'abcd', 'abcde', 'abcdef', 'abcdefgh', - 'abcdefghi', 'abcdefghij', 'abcdefghijk', 'abcdefghijkl', - 'abcdefghijklm', 'abcdefghijklmn', 'abcdefghijklmno ',) - * 8) + pgraph = u' '.join(( + 'a', 'bc', 'def', 'ghij', 'klmno', 'pqrstu', 'vwxyz012', + '34567890A', 'BCDEFGHIJK', 'LMNOPQRSTUV', 'WXYZabcdefgh', + 'ijklmnopqrstu', 'vwxyz123456789', '0ABCDEFGHIJKLMN ')) pgraph_colored = u''.join([ - t.color(n % 7) + t.bold + ch - for n, ch in enumerate(pgraph)]) + t.color(idx % 7)(char) if char != ' ' else ' ' + for idx, char in enumerate(pgraph)]) - internal_wrapped = textwrap.wrap(pgraph, width=WIDTH, - break_long_words=False, - drop_whitespace=False) - my_wrapped = t.wrap(pgraph, width=WIDTH, - break_long_words=False, - drop_whitespace=False) - my_wrapped_colored = t.wrap(pgraph_colored, width=WIDTH, - break_long_words=False, - drop_whitespace=False) + internal_wrapped = textwrap.wrap(pgraph, width=width, **kwargs) + my_wrapped = t.wrap(pgraph, width=width, **kwargs) + my_wrapped_colored = t.wrap(pgraph_colored, width=width, **kwargs) # ensure we textwrap ascii the same as python assert (internal_wrapped == my_wrapped) @@ -167,4 +95,4 @@ def test_SequenceWrapper_27(all_terms): # ensure our colored textwrap is the same line length assert (len(internal_wrapped) == len(my_wrapped_colored)) - child(kind=all_terms) + child(all_terms, many_columns, kwargs) @@ -12,19 +12,19 @@ skip_missing_interpreters = true whitelist_externals = /bin/bash /bin/mv setenv = PYTHONIOENCODING=UTF8 deps = pytest-flakes + pytest-xdist pytest-pep8 pytest-cov pytest mock commands = {envbindir}/py.test \ - -x --strict --pep8 --flakes \ - --junit-xml=results.{envname}.xml \ - --verbose --verbose \ + --strict --pep8 --flakes \ + --junit-xml=results.{envname}.xml --verbose \ --cov blessed blessed/tests {posargs} /bin/mv {toxinidir}/.coverage {toxinidir}/.coverage.{envname} [testenv:static_analysis] -deps = prospector[with_pep257,with_pyroma,with_vulture] +deps = prospector[with_everything] commands = prospector \ --die-on-tool-error \ --test-warnings \ |