# local from blessed.colorspace import CGA_COLORS, X11_COLORNAMES_TO_RGB
# isort: off # curses if platform.system() == 'Windows': import jinxed as curses # pylint: disable=import-error else: import curses
def _make_colors(): """ Return set of valid colors and their derivatives.
:rtype: set
:returns: Color names with prefixes """
colors = set() # basic CGA foreground color, background, high intensity, and bold # background ('iCE colors' in my day). for cga_color in CGA_COLORS:
colors.add(cga_color)
colors.add('on_' + cga_color)
colors.add('bright_' + cga_color)
colors.add('on_bright_' + cga_color)
# foreground and background VGA color for vga_color in X11_COLORNAMES_TO_RGB:
colors.add(vga_color)
colors.add('on_' + vga_color) return colors
#: Valid colors and their background (on), bright, and bright-background #: derivatives.
COLORS = _make_colors()
#: Attributes that may be compounded with colors, by underscore, such as #: 'reverse_indigo'.
COMPOUNDABLES = set('bold underline reverse blink italic standout'.split())
class ParameterizingString(six.text_type):
r"""
A Unicode string which can be called as a parameterizing termcap.
For example::
>>> from blessed import Terminal
>>> term = Terminal()
>>> color = ParameterizingString(term.color, term.normal, 'color')
>>> color(9)('color #9')
u'\x1b[91mcolor #9\x1b(B\x1b[m' """
:arg str cap: parameterized string suitable for curses.tparm()
:arg str normal: terminating sequence for this capability (optional).
:arg str name: name of this terminal capability (optional). """
new = six.text_type.__new__(cls, cap)
new._normal = normal
new._name = name return new
def __call__(self, *args): """
Returning :class:`FormattingString` instance for given parameters.
Return evaluated terminal capability (self), receiving arguments
``*args``, followed by the terminating sequence (self.normal) into
a :class:`FormattingString` capable of being called.
:raises TypeError: Mismatch between capability and arguments
:raises curses.error: :func:`curses.tparm` raised an exception
:rtype: :class:`FormattingString` or :class:`NullCallableString`
:returns: Callable string for given parameters """ try: # Re-encode the cap, because tparm() takes a bytestring in Python # 3. However, appear to be a plain Unicode string otherwise so # concats work.
attr = curses.tparm(self.encode('latin1'), *args).decode('latin1') return FormattingString(attr, self._normal) except TypeError as err: # If the first non-int (i.e. incorrect) arg was a string, suggest # something intelligent: if args and isinstance(args[0], six.string_types): raise TypeError( "Unknown terminal capability, %r, or, TypeError " "for arguments %r: %s" % (self._name, args, err)) # Somebody passed a non-string; I don't feel confident # guessing what they were trying to do. raise except curses.error as err: # ignore 'tparm() returned NULL', you won't get any styling, # even if does_styling is True. This happens on win32 platforms # with http://www.lfd.uci.edu/~gohlke/pythonlibs/#curses installed if"tparm() returned NULL"notin six.text_type(err): raise return NullCallableString()
class ParameterizingProxyString(six.text_type):
r"""
A Unicode string which can be called to proxy missing termcap entries.
This class supports the function :func:`get_proxy_string`, and mirrors
the behavior of :class:`ParameterizingString`, except that instead of
a capability name, receives a format string, and callable to filter the
given positional ``*args`` of :meth:`ParameterizingProxyString.__call__`
into a terminal sequence.
:arg tuple fmt_pair: Two element tuple containing:
- format string suitable for displaying terminal sequences
- callable suitable for receiving __call__ arguments for formatting string
:arg str normal: terminating sequence for this capability (optional).
:arg str name: name of this terminal capability (optional). """ assert isinstance(fmt_pair, tuple), fmt_pair assert callable(fmt_pair[1]), fmt_pair[1]
new = six.text_type.__new__(cls, fmt_pair[0])
new._fmt_args = fmt_pair[1]
new._normal = normal
new._name = name return new
def __call__(self, *args): """
Returning :class:`FormattingString` instance for given parameters.
Arguments are determined by the capability. For example, ``hpa``
(move_x) receives only a single integer, whereas ``cup`` (move)
receives two integers. See documentation in terminfo(5) for the
given capability.
:rtype: FormattingString
:returns: Callable string for given parameters """ return FormattingString(self.format(*self._fmt_args(*args)),
self._normal)
class FormattingString(six.text_type):
r"""
A Unicode string which doubles as a callable.
This is used for terminal attributes, so that it may be used both
directly, oras a callable. When used directly, it simply emits
the given terminal sequence. When used as a callable, it wraps the
given (string) argument with the 2nd argument used by the class
constructor::
:arg str sequence: terminal attribute sequence.
:arg str normal: terminating sequence for this attribute (optional). """
new = six.text_type.__new__(cls, sequence)
new._normal = normal return new
def __call__(self, *args): """ Return ``text`` joined by ``sequence`` and ``normal``.
:raises TypeError: Not a string type
:rtype: str
:returns: Arguments wrapped in sequence and normal """ # Jim Allman brings us this convenience of allowing existing # unicode strings to be joined as a call parameter to a formatting # string result, allowing nestation: # # >>> t.red('This is ', t.bold('extremely'), ' dangerous!') for idx, ucs_part in enumerate(args): ifnot isinstance(ucs_part, six.string_types):
expected_types = ', '.join(_type.__name__ for _type in six.string_types) raise TypeError( "TypeError for FormattingString argument, " "%r, at position %s: expected type %s, " "got %s" % (ucs_part, idx, expected_types,
type(ucs_part).__name__))
postfix = u'' if self and self._normal:
postfix = self._normal
_refresh = self._normal + self
args = [_refresh.join(ucs_part.split(self._normal)) for ucs_part in args]
return self + u''.join(args) + postfix
class FormattingOtherString(six.text_type):
r"""
A Unicode string which doubles as a callable for another sequence when called.
This is used for the :meth:`~.Terminal.move_up`, ``down``, ``left``, and ``right()``
family of functions::
:arg str direct: capability name for direct formatting, eg ``('x' + term.right)``.
:arg str target: capability name for callable, eg ``('x' + term.right(99))``. """
new = six.text_type.__new__(cls, direct)
new._callable = target return new
def __getnewargs__(self): # return arguments used for the __new__ method upon unpickling. return six.text_type.__new__(six.text_type, self), self._callable
def __call__(self, *args): """Return ``text`` by ``target``.""" if args: return self._callable(*args) return self
class NullCallableString(six.text_type): """
A dummy callable Unicode alternative to :class:`FormattingString`.
This is used for colors on terminals that do not support colors, it is just a basic form of
unicode that may also act as a callable. """
def __call__(self, *args): """
Allow empty string to be callable, returning given string, if any.
When called with an int as the first arg, return an empty Unicode. An
int is a good hint that I am a :class:`ParameterizingString`, as there
are only about half a dozen string-returning capabilities listed in
terminfo(5) which accept non-int arguments, they are seldom used.
When called with a non-int as the first arg (no no args at all), return
the first arg, acting in place of :class:`FormattingString` without
any attributes. """ ifnot args or isinstance(args[0], int): # As a NullCallableString, even when provided with a parameter, # such as t.color(5), we must also still be callable, fe: # # >>> t.color(5)('shmoo') # # is actually simplified result of NullCallable()() on terminals # without color support, so turtles all the way down: we return # another instance. return NullCallableString() return u''.join(args)
:arg Terminal term: :class:`~.Terminal` instance.
:arg str attr: terminal capability name that may be proxied.
:rtype: Noneor :class:`ParameterizingProxyString`.
:returns: :class:`ParameterizingProxyString` for some attributes
of some terminal types that support it, where the terminfo(5)
database would otherwise come up empty, such as ``move_x``
attribute for ``term.kind`` of ``screen``. Otherwise, None. """ # normalize 'screen-256color', or 'ansi.sys' to its basic names
term_kind = next(iter(_kind for _kind in ('screen', 'ansi',) if term.kind.startswith(_kind)), term)
_proxy_table = { # pragma: no cover 'screen': { # proxy move_x/move_y for 'screen' terminal type, used by tmux(1). 'hpa': ParameterizingProxyString(
(u'\x1b[{0}G', lambda *arg: (arg[0] + 1,)), term.normal, attr), 'vpa': ParameterizingProxyString(
(u'\x1b[{0}d', lambda *arg: (arg[0] + 1,)), term.normal, attr),
}, 'ansi': { # proxy show/hide cursor for 'ansi' terminal type. There is some # demand for a richly working ANSI terminal type for some reason. 'civis': ParameterizingProxyString(
(u'\x1b[?25l', lambda *arg: ()), term.normal, attr), 'cnorm': ParameterizingProxyString(
(u'\x1b[?25h', lambda *arg: ()), term.normal, attr), 'hpa': ParameterizingProxyString(
(u'\x1b[{0}G', lambda *arg: (arg[0] + 1,)), term.normal, attr), 'vpa': ParameterizingProxyString(
(u'\x1b[{0}d', lambda *arg: (arg[0] + 1,)), term.normal, attr), 'sc': '\x1b[s', 'rc': '\x1b[u',
}
} return _proxy_table.get(term_kind, {}).get(attr, None)
def split_compound(compound): """
Split compound formating string into segments.
:arg str compound: a string that may contain compounds, separated by
underline (``_``).
:rtype: list
:returns: List of formating string segments """
merged_segs = [] # These occur only as prefixes, so they can always be merged:
mergeable_prefixes = ['on', 'bright', 'on_bright'] for segment in compound.split('_'): if merged_segs and merged_segs[-1] in mergeable_prefixes:
merged_segs[-1] += '_' + segment else:
merged_segs.append(segment) return merged_segs
def resolve_capability(term, attr): """
Resolve a raw terminal capability using :func:`tigetstr`.
:arg Terminal term: :class:`~.Terminal` instance.
:arg str attr: terminal capability name.
:returns: string of the given terminal capability named by ``attr``,
which may be empty (u'') ifnot found ornot supported by the
given :attr:`~.Terminal.kind`.
:rtype: str """ ifnot term.does_styling: return u''
val = curses.tigetstr(term._sugar.get(attr, attr)) # pylint: disable=protected-access # Decode sequences as latin1, as they are always 8-bit bytes, so when # b'\xff' is returned, this is decoded as u'\xff'. return u''if val isNoneelse val.decode('latin1')
def resolve_color(term, color): """
Resolve a simple color name to a callable capability.
This function supports :func:`resolve_attribute`.
:arg Terminal term: :class:`~.Terminal` instance.
:arg str color: any string found in set :const:`COLORS`.
:returns: a string class instance which emits the terminal sequence for the given color, and may be used as a callable to wrap the
given string with such sequence.
:returns: :class:`NullCallableString` when
:attr:`~.Terminal.number_of_colors` is 0,
otherwise :class:`FormattingString`.
:rtype: :class:`NullCallableString` or :class:`FormattingString` """ # pylint: disable=protected-access if term.number_of_colors == 0: return NullCallableString()
# fg/bg capabilities terminals that support 0-256+ colors.
vga_color_cap = (term._background_color if'on_'in color else
term._foreground_color)
base_color = color.rsplit('_', 1)[-1] if base_color in CGA_COLORS: # curses constants go up to only 7, so add an offset to get at the # bright colors at 8-15:
offset = 8 if'bright_'in color else 0
base_color = color.rsplit('_', 1)[-1]
attr = 'COLOR_%s' % (base_color.upper(),)
fmt_attr = vga_color_cap(getattr(curses, attr) + offset) return FormattingString(fmt_attr, term.normal)
assert base_color in X11_COLORNAMES_TO_RGB, ( 'color not known', base_color)
rgb = X11_COLORNAMES_TO_RGB[base_color]
# downconvert X11 colors to CGA, EGA, or VGA color spaces if term.number_of_colors <= 256:
fmt_attr = vga_color_cap(term.rgb_downconvert(*rgb)) return FormattingString(fmt_attr, term.normal)
# Modern 24-bit color terminals are written pretty basically. The # foreground and background sequences are: # - ^[38;2;<r>;<g>;<b>m # - ^[48;2;<r>;<g>;<b>m
fgbg_seq = ('48'if'on_'in color else'38') assert term.number_of_colors == 1 << 24
fmt_attr = u'\x1b[' + fgbg_seq + ';2;{0};{1};{2}m' return FormattingString(fmt_attr.format(*rgb), term.normal)
def resolve_attribute(term, attr): """
Resolve a terminal attribute name into a capability class.
:arg Terminal term: :class:`~.Terminal` instance.
:arg str attr: Sugary, ordinary, or compound formatted terminal
capability, such as"red_on_white", "normal", "red", or "bold_on_black".
:returns: a string class instance which emits the terminal sequence for the given terminal capability, or may be used as a callable to
wrap the given string with such sequence.
:returns: :class:`NullCallableString` when
:attr:`~.Terminal.number_of_colors` is 0,
otherwise :class:`FormattingString`.
:rtype: :class:`NullCallableString` or :class:`FormattingString` """ if attr in COLORS: return resolve_color(term, attr)
# A direct compoundable, such as `bold' or `on_red'. if attr in COMPOUNDABLES:
sequence = resolve_capability(term, attr) return FormattingString(sequence, term.normal)
# Given `bold_on_red', resolve to ('bold', 'on_red'), RECURSIVE # call for each compounding section, joined and returned as # a completed completed FormattingString.
formatters = split_compound(attr) if all((fmt in COLORS or fmt in COMPOUNDABLES) for fmt in formatters):
resolution = (resolve_attribute(term, fmt) for fmt in formatters) return FormattingString(u''.join(resolution), term.normal)
# otherwise, this is our end-game: given a sequence such as 'csr' # (change scrolling region), return a ParameterizingString instance, # that when called, performs and returns the final string after curses # capability lookup is performed.
tparm_capseq = resolve_capability(term, attr) ifnot tparm_capseq: # and, for special terminals, such as 'screen', provide a Proxy # ParameterizingString for attributes they do not claim to support, # but actually do! (such as 'hpa' and 'vpa').
proxy = get_proxy_string(term,
term._sugar.get(attr, attr)) # pylint: disable=protected-access if proxy isnotNone: return proxy
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.