def test_cap_missing(self): """ Return None if capability is missing for terminal """ jinxed.setupterm('xterm') self.assertEqual(jinxed.tigetstr('howmuchwoodawoodchuckwillchuck'), None)
def test_unsupported_term(self): """ Raise error if no terminfo for terminal """ with self.assertRaisesRegex(jinxed.error, 'Could not find terminal foobar'): jinxed.setupterm('foobar')
def test_bad_term_type(self): """ Raise error if term is not a string """ with self.assertRaisesRegex(TypeError, 'term must be a string or None'): jinxed.setupterm(100)
def test_cap_unknown(self): """ Return -1 if capability is unknown """ jinxed.setupterm('xterm') self.assertEqual(jinxed.tigetflag('howmuchwoodawoodchuckwillchuck'), -1)
def test_fd_error(self): """ Set fd to None if known error is encountered """ with mock.patch.object(sys, 'stdout', wraps=sys.stdout) as mockstdout: for error in (AttributeError, TypeError, io.UnsupportedOperation): mockstdout.fileno.side_effect = error() jinxed.setupterm('xterm') self.assertIs(jinxed._terminal.TERM.stream_fd, None)
def test_term_none(self): """ If term is none, determine dynamically """ term = os.environ.get('TERM', None) jinxed._terminal.TERM = None try: os.environ['TERM'] = 'xterm' jinxed.setupterm() self.assertIs(jinxed._terminal.TERM.terminfo, jinxed.terminfo.xterm) finally: if term is None: del os.environ['TERM'] else: os.environ['TERM'] = term
def __init__(self, kind=None, stream=None, force_styling=False): """ Initialize the terminal. :arg str kind: A terminal string as taken by :func:`curses.setupterm`. Defaults to the value of the ``TERM`` environment variable. .. note:: Terminals withing a single process must share a common ``kind``. See :obj:`_CUR_TERM`. :arg file stream: A file-like object representing the Terminal output. Defaults to the original value of :obj:`sys.__stdout__`, like :func:`curses.initscr` does. If ``stream`` is not a tty, empty Unicode strings are returned for all capability values, so things like piping your program output to a pipe or file does not emit terminal sequences. :arg bool force_styling: Whether to force the emission of capabilities even if :obj:`sys.__stdout__` does not seem to be connected to a terminal. If you want to force styling to not happen, use ``force_styling=None``. This comes in handy if users are trying to pipe your output through something like ``less -r`` or build systems which support decoding of terminal sequences. """ # pylint: disable=global-statement,too-many-branches global _CUR_TERM self._keyboard_fd = None # Default stream is stdout, keyboard valid as stdin only when # output stream is stdout or stderr and is a tty. if stream is None: stream = sys.__stdout__ if stream in (sys.__stdout__, sys.__stderr__): self._keyboard_fd = sys.__stdin__.fileno() # we assume our input stream to be line-buffered until either the # cbreak of raw context manager methods are entered with an # attached tty. self._line_buffered = True try: stream_fd = (stream.fileno() if hasattr(stream, 'fileno') and callable(stream.fileno) else None) except io.UnsupportedOperation: stream_fd = None self._stream = stream self._is_a_tty = stream_fd is not None and os.isatty(stream_fd) self._does_styling = ((self.is_a_tty or force_styling) and force_styling is not None) # _keyboard_fd only non-None if both stdin and stdout is a tty. self._keyboard_fd = (self._keyboard_fd if self._keyboard_fd is not None and self.is_a_tty and os.isatty(self._keyboard_fd) else None) self._normal = None # cache normal attr, preventing recursive lookups # The descriptor to direct terminal initialization sequences to. self._init_descriptor = (sys.__stdout__.fileno() if stream_fd is None else stream_fd) if platform.system() == 'Windows': self._kind = kind or curses.get_term(self._init_descriptor) else: self._kind = kind or os.environ.get('TERM', 'unknown') if self.does_styling: # Initialize curses (call setupterm). # # Make things like tigetstr() work. Explicit args make setupterm() # work even when -s is passed to nosetests. Lean toward sending # init sequences to the stream if it has a file descriptor, and # send them to stdout as a fallback, since they have to go # somewhere. try: 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: if _CUR_TERM is None or self._kind == _CUR_TERM: _CUR_TERM = self._kind else: warnings.warn( 'A terminal of kind "%s" has been requested; due to an' ' internal python curses bug, terminal capabilities' ' for a terminal of kind "%s" will continue to be' ' returned for the remainder of this process.' % ( self._kind, _CUR_TERM, )) # initialize capabilities and terminal keycodes database self.__init__capabilities() self.__init__keycodes()
def test_fd_explicit(self): """ Use fd is given explicitly """ jinxed.setupterm('xterm', fd=sys.stderr.fileno()) self.assertEqual(jinxed._terminal.TERM.stream_fd, sys.stderr.fileno())
def test_bad_fd_type(self): """ Raise error if fd is not an integer """ with self.assertRaisesRegex(TypeError, 'fd must be an integer'): jinxed.setupterm('xterm', fd='banana')
def test_cap_present(self): """ Return capability if present for terminal """ jinxed.setupterm('xterm') self.assertEqual(jinxed.tigetstr('bold'), b'\x1b[1m')
def test_cap_missing(self): """ Return 0 if capability is missing for terminal """ jinxed.setupterm('xterm') self.assertEqual(jinxed.tigetnum('bitwin'), -1)
def test_cap_present(self): """ Return 1 if capability is present for terminal """ jinxed.setupterm('xterm') self.assertEqual(jinxed.tigetnum('colors'), 8)
def test_cap_missing(self): """ Return 0 if capability is missing for terminal """ jinxed.setupterm('xterm') self.assertEqual(jinxed.tigetflag('hz'), 0)