[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [arm/master] Revised color util handling
commit 7a15738d01cb62e47e776ed41e4fb0a227f33dbb
Author: Damian Johnson <atagar@xxxxxxxxxxxxxx>
Date: Sun Jan 26 13:24:26 2014 -0800
Revised color util handling
Cleaning up our ui_tools' color functions.
---
arm/config/strings.cfg | 7 +-
arm/controller.py | 6 +-
arm/util/tracker.py | 4 +-
arm/util/ui_tools.py | 280 ++++++++++++++++++++++++------------------------
4 files changed, 151 insertions(+), 146 deletions(-)
diff --git a/arm/config/strings.cfg b/arm/config/strings.cfg
index 60194d2..17a9cd7 100644
--- a/arm/config/strings.cfg
+++ b/arm/config/strings.cfg
@@ -12,6 +12,8 @@
#
################################################################################
+msg.wrap {text}
+
msg.config.unable_to_load_settings Unable to load arm's internal configurations: {error}
msg.config.unable_to_read_file Failed to load configuration (using defaults): "{error}"
msg.config.nothing_loaded No armrc loaded, using defaults. You can customize arm by placing a configuration file at {path} (see the armrc.sample for its options).
@@ -34,6 +36,8 @@ msg.setup.set_freebsd_chroot Adjusting paths to account for Tor running in a Fre
msg.setup.tor_is_running_as_root Tor is currently running with root permissions. This isn't a good idea, nor should it be necessary. See the 'User UID' option on Tor's man page for an easy method of reducing its permissions after startup.
msg.setup.unable_to_determine_pid Unable to determine Tor's pid. Some information, like its resource usage will be unavailable.
msg.setup.unknown_event_types arm doesn't recognize the following event types: {event_types} (log 'UNKNOWN' events to see them)
+msg.setup.color_support_available Terminal color support detected and enabled
+msg.setup.color_support_unavailable Terminal color support unavailable
msg.tracker.abort_getting_resources Failed three attempts to get process resource usage from {resolver}, {response} ({exc})
msg.tracker.abort_getting_port_usage Failed three attempts to determine the process using active ports ({exc})
@@ -45,8 +49,9 @@ msg.tracker.unable_to_use_resolver Unable to query connections with {old_resolve
msg.usage.invalid_arguments {error} (for usage provide --help)
msg.usage.not_a_valid_address '{address_input}' isn't a valid IPv4 address
-msg.not_a_valid_port '{port_input}' isn't a valid port number
+msg.usage.not_a_valid_port '{port_input}' isn't a valid port number
msg.usage.unrecognized_log_flags Unrecognized event flags: {flags}
+msg.usage.unable_to_set_color_override "{color}" isn't a valid color
msg.connect.missing_password_bug
|BUG: You provided a password but despite this stem reported that it was
diff --git a/arm/controller.py b/arm/controller.py
index 98b5adb..e684215 100644
--- a/arm/controller.py
+++ b/arm/controller.py
@@ -24,7 +24,7 @@ import arm.util.tracker
from stem.control import State
-from arm.util import panel, tor_config, tor_tools
+from arm.util import panel, tor_config, tor_tools, ui_tools
from stem.util import conf, enum, log, system
@@ -41,6 +41,7 @@ def conf_handler(key, value):
CONFIG = conf.config_dict("arm", {
"startup.events": "N3",
"startup.data_directory": "~/.arm",
+ "features.acsSupport": True,
"features.panels.show.graph": True,
"features.panels.show.log": True,
"features.panels.show.connection": True,
@@ -587,6 +588,9 @@ def start_arm(stdscr):
init_controller(stdscr, start_time)
control = get_controller()
+ if not CONFIG["features.acsSupport"]:
+ ui_tools.disable_acs()
+
# provides notice about any unused config keys
for key in conf.get_config("arm").unused_keys():
diff --git a/arm/util/tracker.py b/arm/util/tracker.py
index e56630d..a10d291 100644
--- a/arm/util/tracker.py
+++ b/arm/util/tracker.py
@@ -40,7 +40,7 @@ import time
import threading
from stem.control import State
-from stem.util import conf, connection, log, proc, str_tools, system
+from stem.util import conf, connection, proc, str_tools, system
from arm.util import tor_controller, debug, info, notice
@@ -465,7 +465,7 @@ class ConnectionTracker(Daemon):
return True
except IOError as exc:
- log.info(exc)
+ info('wrap', text = exc)
# Fail over to another resolver if we've repeatedly been unable to use
# this one.
diff --git a/arm/util/ui_tools.py b/arm/util/ui_tools.py
index 1d90001..9e4a622 100644
--- a/arm/util/ui_tools.py
+++ b/arm/util/ui_tools.py
@@ -1,8 +1,5 @@
"""
-Toolkit for common ui tasks when working with curses. This provides a quick and
-easy method of providing the following interface components:
-- preinitialized curses color attributes
-- unit conversion for labels
+Toolkit for working with curses.
"""
import sys
@@ -10,88 +7,70 @@ import curses
from curses.ascii import isprint
-from stem.util import conf, enum, log, system
+from arm.util import info, msg
-# colors curses can handle
+from stem.util import conf, enum, system
COLOR_LIST = {
- "red": curses.COLOR_RED,
- "green": curses.COLOR_GREEN,
- "yellow": curses.COLOR_YELLOW,
- "blue": curses.COLOR_BLUE,
- "cyan": curses.COLOR_CYAN,
- "magenta": curses.COLOR_MAGENTA,
- "black": curses.COLOR_BLACK,
- "white": curses.COLOR_WHITE,
+ 'red': curses.COLOR_RED,
+ 'green': curses.COLOR_GREEN,
+ 'yellow': curses.COLOR_YELLOW,
+ 'blue': curses.COLOR_BLUE,
+ 'cyan': curses.COLOR_CYAN,
+ 'magenta': curses.COLOR_MAGENTA,
+ 'black': curses.COLOR_BLACK,
+ 'white': curses.COLOR_WHITE,
}
-# boolean for if we have color support enabled, None not yet determined
+DEFAULT_COLOR_ATTR = dict([(color, 0) for color in COLOR_LIST])
+COLOR_ATTR = None
-COLOR_IS_SUPPORTED = None
-
-# mappings for get_color() - this uses the default terminal color scheme if
-# color support is unavailable
-
-COLOR_ATTR_INITIALIZED = False
-COLOR_ATTR = dict([(color, 0) for color in COLOR_LIST])
-
-Ending = enum.Enum("ELLIPSE", "HYPHEN")
+Ending = enum.Enum('ELLIPSE', 'HYPHEN')
SCROLL_KEYS = (curses.KEY_UP, curses.KEY_DOWN, curses.KEY_PPAGE, curses.KEY_NPAGE, curses.KEY_HOME, curses.KEY_END)
def conf_handler(key, value):
- if key == "features.color_override" and value != "none":
- try:
- set_color_override(value)
- except ValueError as exc:
- log.notice(exc)
+ if key == 'features.color_override':
+ if value not in COLOR_LIST.keys() and value != 'none':
+ raise ValueError(msg('usage.unable_to_set_color_override', color = value))
-CONFIG = conf.config_dict("arm", {
- "features.color_override": "none",
- "features.colorInterface": True,
- "features.acsSupport": True,
+CONFIG = conf.config_dict('arm', {
+ 'features.color_override': 'none',
+ 'features.colorInterface': True,
}, conf_handler)
-def get_printable(line, keep_newlines = True):
- """
- Provides the line back with non-printable characters stripped.
-
- Arguments:
- line - string to be processed
- stripNewlines - retains newlines if true, stripped otherwise
- """
-
- line = line.replace('\xc2', "'")
- line = "".join([char for char in line if (isprint(char) or (keep_newlines and char == "\n"))])
-
- return line
-
-
def is_color_supported():
"""
- True if the display supports showing color, false otherwise.
- """
+ Checks if curses presently supports rendering colors.
- if COLOR_IS_SUPPORTED is None:
- _init_colors()
+ :returns: **True** if colors can be rendered, **False** otherwise
+ """
- return COLOR_IS_SUPPORTED
+ return _color_attr() != DEFAULT_COLOR_ATTR
def get_color(color):
"""
Provides attribute corresponding to a given text color. Supported colors
include:
- red green yellow blue
- cyan magenta black white
+
+ * red
+ * green
+ * yellow
+ * blue
+ * cyan
+ * magenta
+ * black
+ * white
If color support isn't available or colors can't be initialized then this uses the
terminal's default coloring scheme.
- Arguments:
- color - name of the foreground color to be returned
+ :param str color: color attributes to be provided
+
+ :returns: **tuple** color pair used by curses to render the color
"""
color_override = get_color_override()
@@ -99,43 +78,111 @@ def get_color(color):
if color_override:
color = color_override
- if not COLOR_ATTR_INITIALIZED:
- _init_colors()
-
- return COLOR_ATTR[color]
+ return _color_attr()[color]
def set_color_override(color = None):
"""
- Overwrites all requests for color with the given color instead. This raises
- a ValueError if the color is invalid.
+ Overwrites all requests for color with the given color instead.
- Arguments:
- color - name of the color to overwrite requests with, None to use normal
- coloring
+ :param str color: color to override all requests with, **None** if color
+ requests shouldn't be overwritten
+
+ :raises: **ValueError** if the color name is invalid
"""
+ arm_config = conf.get_config('arm')
+
if color is None:
- CONFIG["features.color_override"] = "none"
+ arm_config.set('features.color_override', 'none')
elif color in COLOR_LIST.keys():
- CONFIG["features.color_override"] = color
+ arm_config.set('features.color_override', color)
else:
- raise ValueError("\"%s\" isn't a valid color" % color)
+ raise ValueError(msg('usage.unable_to_set_color_override', color = color))
def get_color_override():
"""
- Provides the override color used by the interface, None if it isn't set.
+ Provides the override color used by the interface.
+
+ :returns: **str** for the color requrests will be overwritten with, **None**
+ if no override is set
"""
- color_override = CONFIG.get("features.color_override", "none")
+ color_override = CONFIG.get('features.color_override', 'none')
- if color_override == "none":
+ if color_override == 'none':
return None
else:
return color_override
+def _color_attr():
+ """
+ Initializes color mappings usable by curses. This can only be done after
+ calling curses.initscr().
+ """
+
+ global COLOR_ATTR
+
+ if COLOR_ATTR is None:
+ if not CONFIG['features.colorInterface']:
+ COLOR_ATTR = DEFAULT_COLOR_ATTR
+ elif curses.has_colors():
+ color_attr = dict([(color, 0) for color in COLOR_LIST])
+
+ for color_pair, color_name in enumerate(COLOR_LIST):
+ foreground_color = COLOR_LIST[color_name]
+ background_color = -1 # allows for default (possibly transparent) background
+ curses.init_pair(color_pair + 1, foreground_color, background_color)
+ color_attr[color_name] = curses.color_pair(color_pair + 1)
+
+ info('setup.color_support_available')
+ COLOR_ATTR = color_attr
+ else:
+ info('setup.color_support_unavailable')
+ COLOR_ATTR = DEFAULT_COLOR_ATTR
+
+ return COLOR_ATTR
+
+
+def disable_acs():
+ """
+ Replaces the curses ACS characters. This can be preferable if curses is
+ unable to render them...
+
+ http://www.atagar.com/arm/images/acs_display_failure.png
+ """
+
+ for item in curses.__dict__:
+ if item.startswith('ACS_'):
+ curses.__dict__[item] = ord('+')
+
+ # replace a few common border pipes that are better rendered as '|' or
+ # '-' instead
+
+ curses.ACS_SBSB = ord('|')
+ curses.ACS_VLINE = ord('|')
+ curses.ACS_BSBS = ord('-')
+ curses.ACS_HLINE = ord('-')
+
+
+def get_printable(line, keep_newlines = True):
+ """
+ Provides the line back with non-printable characters stripped.
+
+ :param str line: string to be processed
+ :param str keep_newlines: retains newlines if **True**, stripped otherwise
+
+ :returns: **str** of the line with only printable content
+ """
+
+ line = line.replace('\xc2', "'")
+ line = filter(lambda char: isprint(char) or (keep_newlines and char == '\n'), line)
+
+ return line
+
+
def crop_str(msg, size, min_word_length = 4, min_crop = 0, end_type = Ending.ELLIPSE, get_remainder = False):
"""
Provides the msg constrained to the given length, truncating on word breaks.
@@ -143,29 +190,29 @@ def crop_str(msg, size, min_word_length = 4, min_crop = 0, end_type = Ending.ELL
isn't room for even a truncated single word (or one word plus the ellipse if
including those) then this provides an empty string. If a cropped string ends
with a comma or period then it's stripped (unless we're providing the
- remainder back). Examples:
+ remainder back). For example...
- crop_str("This is a looooong message", 17)
- "This is a looo..."
+ >>> crop_str("This is a looooong message", 17)
+ "This is a looo..."
- crop_str("This is a looooong message", 12)
- "This is a..."
+ >>> crop_str("This is a looooong message", 12)
+ "This is a..."
- crop_str("This is a looooong message", 3)
- ""
+ >>> crop_str("This is a looooong message", 3)
+ ""
- Arguments:
- msg - source text
- size - room available for text
- min_word_length - minimum characters before which a word is dropped, requires
- whole word if None
- min_crop - minimum characters that must be dropped if a word's cropped
- end_type - type of ending used when truncating:
- None - blank ending
- Ending.ELLIPSE - includes an ellipse
- Ending.HYPHEN - adds hyphen when breaking words
- get_remainder - returns a tuple instead, with the second part being the
- cropped portion of the message
+ :param str msg: text to be processed
+ :param int size: space available for text
+ :param int min_word_length: minimum characters before which a word is
+ dropped, requires whole word if **None**
+ :param int min_crop: minimum characters that must be dropped if a word is
+ cropped
+ :param Ending end_type: type of ending used when truncating, no special
+ truncation is used if **None**
+ :param bool get_remainder: returns a tuple with the second part being the
+ cropped portion of the message
+
+ :returns: **str** of the text truncated to the given length
"""
# checks if there's room for the whole message
@@ -512,54 +559,3 @@ def is_wide_characters_supported():
pass
return False
-
-
-def _init_colors():
- """
- Initializes color mappings usable by curses. This can only be done after
- calling curses.initscr().
- """
-
- global COLOR_ATTR_INITIALIZED, COLOR_IS_SUPPORTED
-
- if not COLOR_ATTR_INITIALIZED:
- # hack to replace all ACS characters with '+' if ACS support has been
- # manually disabled
-
- if not CONFIG["features.acsSupport"]:
- for item in curses.__dict__:
- if item.startswith("ACS_"):
- curses.__dict__[item] = ord('+')
-
- # replace a few common border pipes that are better rendered as '|' or
- # '-' instead
-
- curses.ACS_SBSB = ord('|')
- curses.ACS_VLINE = ord('|')
- curses.ACS_BSBS = ord('-')
- curses.ACS_HLINE = ord('-')
-
- COLOR_ATTR_INITIALIZED = True
- COLOR_IS_SUPPORTED = False
-
- if not CONFIG["features.colorInterface"]:
- return
-
- try:
- COLOR_IS_SUPPORTED = curses.has_colors()
- except curses.error:
- return # initscr hasn't been called yet
-
- # initializes color mappings if color support is available
- if COLOR_IS_SUPPORTED:
- colorpair = 0
- log.info("Terminal color support detected and enabled")
-
- for color_name in COLOR_LIST:
- foreground_color = COLOR_LIST[color_name]
- background_color = -1 # allows for default (possibly transparent) background
- colorpair += 1
- curses.init_pair(colorpair, foreground_color, background_color)
- COLOR_ATTR[color_name] = curses.color_pair(colorpair)
- else:
- log.info("Terminal color support unavailable")
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits