diff options
author | Jerome Martin <jxm@risingtidesystems.com> | 2011-06-14 11:53:55 +0200 |
---|---|---|
committer | Jerome Martin <jxm@risingtidesystems.com> | 2011-07-18 17:08:18 +0200 |
commit | 95a9c3945917c0bcefcd87854e1c3b09fd61e2fa (patch) | |
tree | 70f319f4e58f313228f7ee87fd558d1f6fd2af73 /configshell | |
parent | 257f6ec194a00809543d34957113063157e0944e (diff) | |
download | configshell-fb-95a9c3945917c0bcefcd87854e1c3b09fd61e2fa.tar.gz |
Refactored ConfigShell usage and add/del child.
* Moved ConfigNode add_child to child's __init__
. Better semantics matching actual usage.
. Allows to skip many checks in add_child.
. Knowing parent at __init__ time will allow to access some of its
attributes during __init__, useful for actual usage.
. Removed _set_name, _set_parent and corresponding setter properties.
. Added ConfigNode.remove_child().
* Moved shell property to ConfigNode.
. Root node attaches itself to a shell during __init__.
. Each node can use con, log and prefs through the shell property.
* Linebreaks cleanup: removed unneccessary backslashes.
* Catching setting signal handler failure in thread.
* Changed a list concat (l1+l2) to use l1.extend(l2).
Diffstat (limited to 'configshell')
-rw-r--r-- | configshell/node.py | 465 | ||||
-rw-r--r-- | configshell/shell.py | 67 |
2 files changed, 265 insertions, 267 deletions
diff --git a/configshell/node.py b/configshell/node.py index 115d079..88a3c13 100644 --- a/configshell/node.py +++ b/configshell/node.py @@ -16,9 +16,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. ''' import re -import log -import prefs -import console import inspect class ExecutionError(Exception): @@ -65,14 +62,36 @@ class ConfigNode(object): COMMAND} to get more information. ''' - def __init__(self): - self._name = 'config node' + def __init__(self, name, parent=None, shell=None): + ''' + @param parent: The parent ConfigNode of the new object. If None, then + the ConfigNode will be a root node. + @type parent: ConfigNode or None + @param shell: The shell to attach a root node to. + @type shell: ConfigShell + ''' + self._name = name self._children = set([]) - self._parent = None + if parent is None: + if shell is None: + raise ValueError("A root ConfigNode must have a shell.") + else: + self._parent = None + self._shell = shell + shell.attach_root_node(self) + else: + if shell is None: + self._parent = parent + self._shell = None + else: + raise ValueError("A non-root ConfigNode can't have a shell.") - self.prefs = prefs.Prefs() - self.log = log.Log() - self.con = console.Console() + if self._parent is not None: + for sibling in self._parent._children: + if sibling.name == name: + raise ValueError("Name '%s' already used by a sibling." + % self._name) + self._parent._children.add(self) self._configuration_groups = {} @@ -122,8 +141,8 @@ class ConfigNode(object): 'global', 'prompt_length', 'number', 'Max length of the shell prompt path, 0 for infinite.') - if self.prefs['bookmarks'] is None: - self.prefs['bookmarks'] = {} + if self.shell.prefs['bookmarks'] is None: + self.shell.prefs['bookmarks'] = {} # User interface types @@ -162,7 +181,7 @@ class ConfigNode(object): try: value = int(value) except ValueError: - raise ValueError("Syntax error, '%s' is not a %s." \ + raise ValueError("Syntax error, '%s' is not a %s." % (value, syntax)) else: return value @@ -202,7 +221,7 @@ class ConfigNode(object): try: value = str(value) except ValueError: - raise ValueError("Syntax error, '%s' is not a %s." \ + raise ValueError("Syntax error, '%s' is not a %s." % (value, syntax)) else: return value @@ -241,7 +260,7 @@ class ConfigNode(object): elif value == 'false': return False else: - raise ValueError("Syntax error, '%s' is not %s." \ + raise ValueError("Syntax error, '%s' is not %s." % (value, syntax)) def ui_type_loglevel(self, value=None, enum=False, reverse=False): @@ -266,7 +285,7 @@ class ConfigNode(object): else: return 'n/a' - type_enum = self.log.levels + type_enum = self.shell.log.levels syntax = '|'.join(type_enum) if value is None: if enum: @@ -276,7 +295,7 @@ class ConfigNode(object): elif value in type_enum: return value else: - raise ValueError("Syntax error, '%s' is not %s" \ + raise ValueError("Syntax error, '%s' is not %s" % (value, syntax)) def ui_type_color(self, value=None, enum=False, reverse=False): @@ -301,7 +320,7 @@ class ConfigNode(object): else: return 'default' - type_enum = self.con.colors + ['default'] + type_enum = self.shell.con.colors + ['default'] syntax = '|'.join(type_enum) if value is None: if enum: @@ -313,7 +332,7 @@ class ConfigNode(object): elif value in type_enum: return value else: - raise ValueError("Syntax error, '%s' is not %s" \ + raise ValueError("Syntax error, '%s' is not %s" % (value, syntax)) def ui_type_colordefault(self, value=None, enum=False, reverse=False): @@ -338,7 +357,7 @@ class ConfigNode(object): else: return 'none' - type_enum = self.con.colors + ['none'] + type_enum = self.shell.con.colors + ['none'] syntax = '|'.join(type_enum) if value is None: if enum: @@ -350,7 +369,7 @@ class ConfigNode(object): elif value in type_enum: return value else: - raise ValueError("Syntax error, '%s' is not %s" \ + raise ValueError("Syntax error, '%s' is not %s" % (value, syntax)) @@ -377,7 +396,7 @@ class ConfigNode(object): @param value: The value @type value: arbitrary ''' - self.prefs[parameter] = value + self.shell.prefs[parameter] = value def ui_getgroup_global(self, parameter): ''' @@ -395,7 +414,7 @@ class ConfigNode(object): @return: The parameter's value @rtype: arbitrary ''' - return self.prefs[parameter] + return self.shell.prefs[parameter] def ui_eval_param(self, ui_value, type, default): ''' @@ -450,11 +469,11 @@ class ConfigNode(object): get ''' if group is None: - self.con.epy_write(''' - AVAILABLE CONFIGURATION GROUPS - ============================== - %s - ''' % ' '.join(self.list_config_groups())) + self.shell.con.epy_write(''' + AVAILABLE CONFIGURATION GROUPS + ============================== + %s + ''' % ' '.join(self.list_config_groups())) elif not parameter: if group in self.list_config_groups(): section = "%s CONFIG GROUP" % group.upper() @@ -467,10 +486,10 @@ class ConfigNode(object): underline2 = ''.ljust(len(parameter), '-') parameters += '%s\n%s\n%s\n\n' \ % (parameter, underline2, p_def['description']) - self.con.epy_write('''%s\n%s\n%s\n''' \ - % (section, underline1, parameters)) + self.shell.con.epy_write('''%s\n%s\n%s\n''' + % (section, underline1, parameters)) else: - self.log.error("Unknown configuration group: %s" % group) + self.shell.log.error("Unknown configuration group: %s" % group) elif group in self.list_config_groups(): for param, value in parameter.iteritems(): if param in self.list_group_params(group): @@ -480,22 +499,24 @@ class ConfigNode(object): try: value = type_method(value) except ValueError, msg: - self.log.error("Not setting %s! %s" % (param, msg)) + self.shell.log.error("Not setting %s! %s" + % (param, msg)) else: group_setter = self.get_group_setter(group) group_setter(param, value) group_getter = self.get_group_getter(group) value = group_getter(param) value = type_method(value, reverse=True) - self.con.display( "Parameter %s is now '%s'." \ - % (param, value)) + self.shell.con.display("Parameter %s is now '%s'." + % (param, value)) else: - self.log.error("Parameter '%s' is read-only." % param) + self.shell.log.error("Parameter %s is read-only." + % param) else: - self.log.error("Unknown parameter '%s' in group '%s'." \ - % (param, group)) + self.shell.log.error("Unknown parameter %s in group '%s'." + % (param, group)) else: - self.log.error("Unknown configuration group: %s" % group) + self.shell.log.error("Unknown configuration group: %s" % group) def ui_complete_set(self, parameters, text, current_param): ''' @@ -511,8 +532,8 @@ class ConfigNode(object): ''' completions = [] - self.log.debug("Called with parameters=%s, text='%s', current='%s'" \ - % (str(parameters), text, current_param)) + self.shell.log.debug("Called with params=%s, text='%s', current='%s'" + % (str(parameters), text, current_param)) if current_param == 'group': completions = [group for group in self.list_config_groups() @@ -539,7 +560,7 @@ class ConfigNode(object): if len(completions) == 1 and not completions[0].endswith('='): completions = [completions[0] + ' '] - self.log.debug("Returning completions %s." % str(completions)) + self.shell.log.debug("Returning completions %s." % str(completions)) return completions def ui_command_get(self, group=None, *parameter): @@ -558,11 +579,11 @@ class ConfigNode(object): set ''' if group is None: - self.con.epy_write(''' - AVAILABLE CONFIGURATION GROUPS - ============================== - %s - ''' % ' '.join(self.list_config_groups())) + self.shell.con.epy_write(''' + AVAILABLE CONFIGURATION GROUPS + ============================== + %s + ''' % ' '.join(self.list_config_groups())) elif not parameter: if group in self.list_config_groups(): section = "%s CONFIG GROUP" % group.upper() @@ -582,14 +603,14 @@ class ConfigNode(object): parameters += '%s\n%s\n%s\n\n' \ % (parameter, underline2, p_def['description']) - self.con.epy_write('''%s\n%s\n%s\n''' \ - % (section, underline1, parameters)) + self.shell.con.epy_write('''%s\n%s\n%s\n''' + % (section, underline1, parameters)) else: - self.log.error("Unknown configuration group: %s" % group) + self.shell.log.error("Unknown configuration group: %s" % group) elif group in self.list_config_groups(): for param in parameter: if param in self.list_group_params(group): - self.log.debug("About to get the parameter's value.") + self.shell.log.debug("About to get the parameter's value.") group_getter = self.get_group_getter(group) value = group_getter(param) p_def = self.get_group_param(group, param) @@ -599,12 +620,13 @@ class ConfigNode(object): writable = "" else: writable = "[ro]" - self.con.display("%s=%s %s" % (param, value, writable)) + self.shell.con.display("%s=%s %s" + % (param, value, writable)) else: - self.log.error( - "No parameter '%s' in group '%s'." % (param, group)) + self.shell.log.error("No parameter '%s' in group '%s'." + % (param, group)) else: - self.log.error("Unknown configuration group: %s" % group) + self.shell.log.error("Unknown configuration group: %s" % group) def ui_complete_get(self, parameters, text, current_param): ''' @@ -620,8 +642,8 @@ class ConfigNode(object): ''' completions = [] - self.log.debug("Called with parameters=%s, text='%s', current='%s'" \ - % (str(parameters), text, current_param)) + self.shell.log.debug("Called with params=%s, text='%s', current='%s'" + % (str(parameters), text, current_param)) if current_param == 'group': completions = [group for group in self.list_config_groups() @@ -639,7 +661,7 @@ class ConfigNode(object): if len(completions) == 1 and not completions[0].endswith('='): completions = [completions[0] + ' '] - self.log.debug("Returning completions %s." % str(completions)) + self.shell.log.debug("Returning completions %s." % str(completions)) return completions def ui_command_ls(self, path=None, depth=None): @@ -666,20 +688,20 @@ class ConfigNode(object): try: target = self.get_node(path) except ValueError, msg: - self.log.error(msg) + self.shell.log.error(msg) return if depth is None: - depth = self.prefs['tree_max_depth'] + depth = self.shell.prefs['tree_max_depth'] try: depth = int(depth) except ValueError: - self.log.error('The tree depth must be a number.') + self.shell.log.error('The tree depth must be a number.') else: if depth == 0: depth = None tree = self._render_tree(target, depth=depth) - self.con.display(tree) + self.shell.con.display(tree) def _render_tree(self, root, margin=None, depth=None, do_list=False): ''' @@ -730,7 +752,7 @@ class ConfigNode(object): if do_list: name = root.name else: - name = self.con.render_text(root.name, color, styles=styles) + name = self.shell.con.render_text(root.name, color, styles=styles) name_len = len(root.name) (description, is_healthy) = root.summary() @@ -747,18 +769,18 @@ class ConfigNode(object): if do_list: summary = '[' else: - summary = self.con.render_text(' [', styles=['bold']) + summary = self.shell.con.render_text(' [', styles=['bold']) if is_healthy is True: if do_list: summary += description else: - summary += self.con.render_text(description, 'green') + summary += self.shell.con.render_text(description, 'green') elif is_healthy is False: if do_list: summary += description else: - summary += self.con.render_text(description, 'red', + summary += self.shell.con.render_text(description, 'red', styles=['bold']) else: summary += description @@ -766,7 +788,7 @@ class ConfigNode(object): if do_list: summary += ']' else: - summary += self.con.render_text(']', styles=['bold']) + summary += self.shell.con.render_text(']', styles=['bold']) children = list(root.children) children.sort(key=lambda child: str(child)) @@ -778,7 +800,7 @@ class ConfigNode(object): else: line = line + ''.ljust(node_shift) - if self.prefs['tree_round_nodes']: + if self.shell.prefs['tree_round_nodes']: node_char = 'o' else: node_char = '+' @@ -786,21 +808,23 @@ class ConfigNode(object): line += ' ' margin_len = len(line) - pad = (self.con.get_width() - 1 + pad = (self.shell.con.get_width() - 1 - description_len - margin_len - name_len) * '.' if not do_list: - pad = self.con.render_text(pad, color) + pad = self.shell.con.render_text(pad, color) line += name - if self.prefs['tree_status_mode']: + if self.shell.prefs['tree_status_mode']: line += ' %s%s' % (pad, summary) lines.append(line) paths.append(root.path) - if root_call and not self.prefs['tree_show_root'] and not do_list: + if root_call \ + and not self.shell.prefs['tree_show_root'] \ + and not do_list: tree = '' for child in children: tree = tree + self._render_tree(child, [False], depth) @@ -865,13 +889,14 @@ class ConfigNode(object): completions[0] = completions[0].rstrip('/') + ' ' # Bookmarks - bookmarks = ['@' + bookmark for bookmark in self.prefs['bookmarks'] + bookmarks = ['@' + bookmark for bookmark + in self.shell.prefs['bookmarks'] if ('@' + bookmark).startswith(text)] - self.log.debug("Found bookmarks %s." % str(bookmarks)) + self.shell.log.debug("Found bookmarks %s." % str(bookmarks)) if bookmarks: completions.extend(bookmarks) - self.log.debug("Completions are %s." % str(completions)) + self.shell.log.debug("Completions are %s." % str(completions)) return completions elif current_param == 'depth': @@ -879,7 +904,7 @@ class ConfigNode(object): try: int(text.strip()) except ValueError: - self.log.debug("Text is not a number.") + self.shell.log.debug("Text is not a number.") return [] return [ text + number for number in [str(num) for num in range(10)] @@ -920,24 +945,24 @@ class ConfigNode(object): ======== ls cd ''' - self.log.debug("Changing current node to '%s'." % path) + self.shell.log.debug("Changing current node to '%s'." % path) - if self.prefs['path_history'] is None: - self.prefs['path_history'] = [self.path] - self.prefs['path_history_index'] = 0 + if self.shell.prefs['path_history'] is None: + self.shell.prefs['path_history'] = [self.path] + self.shell.prefs['path_history_index'] = 0 # Go back in history to the last existing path if path == '<': - if self.prefs['path_history_index'] == 0: - self.log.info("Reached begining of path history.") + if self.shell.prefs['path_history_index'] == 0: + self.shell.log.info("Reached begining of path history.") return self exists = False while not exists: - if self.prefs['path_history_index'] > 0: - self.prefs['path_history_index'] = \ - self.prefs['path_history_index'] - 1 - index = self.prefs['path_history_index'] - path = self.prefs['path_history'][index] + if self.shell.prefs['path_history_index'] > 0: + self.shell.prefs['path_history_index'] = \ + self.shell.prefs['path_history_index'] - 1 + index = self.shell.prefs['path_history_index'] + path = self.shell.prefs['path_history'][index] try: target_node = self.get_node(path) except ValueError: @@ -946,26 +971,26 @@ class ConfigNode(object): exists = True else: path = '/' - self.prefs['path_history_index'] = 0 - self.prefs['path_history'][0] = '/' + self.shell.prefs['path_history_index'] = 0 + self.shell.prefs['path_history'][0] = '/' exists = True - self.log.info('Taking you back to %s.' % path) + self.shell.log.info('Taking you back to %s.' % path) return self.get_node(path) # Go forward in history if path == '>': - if self.prefs['path_history_index'] == \ - len(self.prefs['path_history']) - 1: - self.log.info("Reached the end of path history.") + if self.shell.prefs['path_history_index'] == \ + len(self.shell.prefs['path_history']) - 1: + self.shell.log.info("Reached the end of path history.") return self exists = False while not exists: - if self.prefs['path_history_index'] \ - < len(self.prefs['path_history']) - 1: - self.prefs['path_history_index'] = \ - self.prefs['path_history_index'] + 1 - index = self.prefs['path_history_index'] - path = self.prefs['path_history'][index] + if self.shell.prefs['path_history_index'] \ + < len(self.shell.prefs['path_history']) - 1: + self.shell.prefs['path_history_index'] = \ + self.shell.prefs['path_history_index'] + 1 + index = self.shell.prefs['path_history_index'] + path = self.shell.prefs['path_history'][index] try: target_node = self.get_node(path) except ValueError: @@ -974,11 +999,11 @@ class ConfigNode(object): exists = True else: path = self.path - self.prefs['path_history_index'] \ - = len(self.prefs['path_history']) - self.prefs['path_history'].append(path) + self.shell.prefs['path_history_index'] \ + = len(self.shell.prefs['path_history']) + self.shell.prefs['path_history'].append(path) exists = True - self.log.info('Taking you back to %s.' % path) + self.shell.log.info('Taking you back to %s.' % path) return self.get_node(path) # Use an urwid walker to select the path @@ -992,20 +1017,20 @@ class ConfigNode(object): try: target_node = self.get_node(path) except ValueError, msg: - self.log.error(msg) + self.shell.log.error(msg) return None else: - index = self.prefs['path_history_index'] - if target_node.path != self.prefs['path_history'][index]: + index = self.shell.prefs['path_history_index'] + if target_node.path != self.shell.prefs['path_history'][index]: # Truncate the hostory to retain current path as last one - self.prefs['path_history'] = \ - self.prefs['path_history'][:index+1] + self.shell.prefs['path_history'] = \ + self.shell.prefs['path_history'][:index+1] # Append the new path and update the index - self.prefs['path_history'].append(target_node.path) - self.prefs['path_history_index'] = index + 1 - self.log.debug("After cd, path history is: %s, index is %d" \ - % (str(self.prefs['path_history']), - self.prefs['path_history_index'])) + self.shell.prefs['path_history'].append(target_node.path) + self.shell.prefs['path_history_index'] = index + 1 + self.shell.log.debug("After cd, path history is: %s, index is %d" + % (str(self.shell.prefs['path_history']), + self.shell.prefs['path_history_index'])) return target_node def _lines_walker(self, lines, start_pos): @@ -1077,8 +1102,8 @@ class ConfigNode(object): ''' commands = self.list_commands() if topic is None: - msg = self.con.dedent(self.help_intro) - msg += self.con.dedent(''' + msg = self.shell.con.dedent(self.help_intro) + msg += self.shell.con.dedent(''' AVAILABLE COMMANDS ================== @@ -1088,10 +1113,10 @@ class ConfigNode(object): ''') for command in commands: msg += " - %s\n" % self.get_command_syntax(command)[0] - self.con.epy_write(msg) + self.shell.con.epy_write(msg) elif topic in commands: syntax, comments, defaults = self.get_command_syntax(topic) - msg = self.con.dedent(''' + msg = self.shell.con.dedent(''' SYNTAX ====== %s @@ -1101,20 +1126,20 @@ class ConfigNode(object): msg += comment + '\n' if defaults: - msg += self.con.dedent(''' + msg += self.shell.con.dedent(''' DEFAULT VALUES ============== %s ''' % defaults) - msg += self.con.dedent(''' + msg += self.shell.con.dedent(''' DESCRIPTION =========== ''') msg += self.get_command_description(topic) - self.con.epy_write(msg) + self.shell.con.epy_write(msg) else: - self.log.error("Cannot find help topic %s." % topic) + self.shell.log.error("Cannot find help topic %s." % topic) def ui_complete_help(self, parameters, text, current_param): ''' @@ -1185,44 +1210,46 @@ class ConfigNode(object): ls cd ''' if action == 'add' and bookmark: - if bookmark in self.prefs['bookmarks']: - self.log.error("Bookmark %s already exists." % bookmark) + if bookmark in self.shell.prefs['bookmarks']: + self.shell.log.error("Bookmark %s already exists." % bookmark) else: - self.prefs['bookmarks'][bookmark] = self.path + self.shell.prefs['bookmarks'][bookmark] = self.path # No way Prefs is going to account for that :-( - self.prefs.save() - self.log.info("Bookmarked %s as %s." % (self.path, bookmark)) + self.shell.prefs.save() + self.shell.log.info("Bookmarked %s as %s." + % (self.path, bookmark)) elif action == 'del' and bookmark: - if bookmark in self.prefs['bookmarks']: - del self.prefs['bookmarks'][bookmark] + if bookmark in self.shell.prefs['bookmarks']: + del self.shell.prefs['bookmarks'][bookmark] # No way Prefs is going to account for that deletion - self.prefs.save() - self.log.info("Deleted bookmark %s." % bookmark) + self.shell.prefs.save() + self.shell.log.info("Deleted bookmark %s." % bookmark) else: - self.log.error("No such bookmark %s." % bookmark) + self.shell.log.error("No such bookmark %s." % bookmark) elif action == 'go' and bookmark: - if bookmark in self.prefs['bookmarks']: - return self.ui_command_cd(self.prefs['bookmarks'][bookmark]) + if bookmark in self.shell.prefs['bookmarks']: + return self.ui_command_cd( + self.shell.prefs['bookmarks'][bookmark]) else: - self.log.error("No such bookmark %s." % bookmark) + self.shell.log.error("No such bookmark %s." % bookmark) elif action == 'show': - bookmarks = self.con.dedent(''' - BOOKMARKS - ========= + bookmarks = self.shell.con.dedent(''' + BOOKMARKS + ========= - ''') - if not self.prefs['bookmarks']: + ''') + if not self.shell.prefs['bookmarks']: bookmarks += "No bookmarks yet.\n" else: for (bookmark, path) \ - in self.prefs['bookmarks'].iteritems(): + in self.shell.prefs['bookmarks'].iteritems(): if len(bookmark) == 1: bookmark += '\0' underline = ''.ljust(len(bookmark), '-') bookmarks += "%s\n%s\n%s\n\n" % (bookmark, underline, path) - self.con.epy_write(bookmarks) + self.shell.con.epy_write(bookmarks) else: - self.log.error("Syntax error, see 'help bookmarks'.") + self.shell.log.error("Syntax error, see 'help bookmarks'.") def ui_complete_bookmarks(self, parameters, text, current_param): ''' @@ -1242,7 +1269,8 @@ class ConfigNode(object): elif current_param == 'bookmark': if 'action' in parameters: if parameters['action'] not in ['show', 'add']: - completions = [mark for mark in self.prefs['bookmarks'] + completions = [mark for mark + in self.shell.prefs['bookmarks'] if mark.startswith(text)] else: completions = [] @@ -1260,7 +1288,7 @@ class ConfigNode(object): ======== ls cd ''' - self.con.display(self.path) + self.shell.con.display(self.path) # Private methods @@ -1278,17 +1306,6 @@ class ConfigNode(object): ''' return self._parent - def _set_parent(self, parent): - ''' - Sets the node's parent. Works only if it does not already have one. - @param parent: The parent node to assign. - @type parent: ConfigNode - ''' - if self.is_root(): - self._parent = parent - else: - raise AttributeError("Node %s already has a parent" % self._name) - def _get_name(self): ''' @return: The node's name. @@ -1299,10 +1316,8 @@ class ConfigNode(object): def _set_name(self, name): ''' Sets the node's name. - @param name: The new node name. - @type name: str ''' - self._name = str(name) + self._name = name def _get_path(self): ''' @@ -1325,6 +1340,15 @@ class ConfigNode(object): ''' return self._children + def _get_shell(self): + ''' + Gets the shell attached to ConfigNode tree. + ''' + if self.is_root(): + return self._shell + else: + return self.get_root().shell + # Public methods def summary(self): @@ -1355,21 +1379,21 @@ class ConfigNode(object): they are interpreted by ConfigShell. @rtype: str or ConfigNode or None ''' - self.log.debug("Executing command %s " % command - + "with pparams %s " % pparams - + "and kparams %s." % kparams) + self.shell.log.debug("Executing command %s " % command + + "with pparams %s " % pparams + + "and kparams %s." % kparams) if command in self.list_commands(): method = self.get_command_method(command) else: - self.log.error("Command not found %s" % command) + self.shell.log.error("Command not found %s" % command) return try: self.assert_params(method, pparams, kparams) result = method(*pparams, **kparams) except ExecutionError, msg: - self.log.error(msg) + self.shell.log.error(msg) else: return result @@ -1401,8 +1425,8 @@ class ConfigNode(object): opt_params = args[nb_min_params:] unexpected_keywords = sorted(set(kparams) - set(args)) - missing_params = sorted(set(args[len(pparams):]) \ - - set(opt_params) \ + missing_params = sorted(set(args[len(pparams):]) + - set(opt_params) - set(kparams.keys())) nb_params = len(pparams) + len(kparams) @@ -1410,14 +1434,14 @@ class ConfigNode(object): + len([param for param in kparams if param in args]) nb_extended_params = nb_params - nb_standard_params - self.log.debug("Min params: %d" % nb_min_params) - self.log.debug("Max params: %d" % nb_max_params) - self.log.debug("Required params: %s" % ", ".join(req_params)) - self.log.debug("Optional params: %s" % ", ".join(opt_params)) - self.log.debug("Got %s standard params." % nb_standard_params) - self.log.debug("Got %s extended params." % nb_extended_params) - self.log.debug("Variable positional params: %s" % pp) - self.log.debug("Variable keyword params: %s" % kw) + self.shell.log.debug("Min params: %d" % nb_min_params) + self.shell.log.debug("Max params: %d" % nb_max_params) + self.shell.log.debug("Required params: %s" % ", ".join(req_params)) + self.shell.log.debug("Optional params: %s" % ", ".join(opt_params)) + self.shell.log.debug("Got %s standard params." % nb_standard_params) + self.shell.log.debug("Got %s extended params." % nb_extended_params) + self.shell.log.debug("Variable positional params: %s" % pp) + self.shell.log.debug("Variable keyword params: %s" % kw) if len(missing_params) == 1: raise ExecutionError( @@ -1437,8 +1461,8 @@ class ConfigNode(object): raise ExecutionError( "Unexpected keyword parameters %s." % ", ".join("'%s'" % kw for kw in unexpected_keywords)) - - all_params = args[:len(pparams)] + kparams.keys() + all_params = args[:len(pparams)] + all_params.extend(kparams.keys()) for param in all_params: if all_params.count(param) > 1: raise ExecutionError( @@ -1500,8 +1524,8 @@ class ConfigNode(object): if command in self.list_commands(): return getattr(self, '%s%s' % (prefix, command)) else: - self.log.debug('No command named %s in %s (%s)' \ - % (command, self.name, self.path)) + self.shell.log.debug('No command named %s in %s (%s)' + % (command, self.name, self.path)) raise ValueError('No command named "%s".' % command) def get_completion_method(self, command): @@ -1529,7 +1553,7 @@ class ConfigNode(object): doc = self.get_command_method(command).__doc__ if not doc: doc = "No description available." - return self.con.dedent(doc) + return self.shell.con.dedent(doc) def get_command_syntax(self, command): ''' @@ -1557,8 +1581,8 @@ class ConfigNode(object): required_parameters = parameters optional_parameters = [] - self.log.debug("Required: %s" % str(required_parameters)) - self.log.debug("Optional: %s" % str(optional_parameters)) + self.shell.log.debug("Required: %s" % str(required_parameters)) + self.shell.log.debug("Optional: %s" % str(optional_parameters)) syntax = "B{%s} " % command @@ -1574,7 +1598,7 @@ class ConfigNode(object): comments = [] #if optional_parameters: - # comments.append(self.con.dedent( + # comments.append(self.shell.con.dedent( # ''' # %s - These are optional parameters that can either be # specified in the above order as positional parameters, or in @@ -1583,7 +1607,7 @@ class ConfigNode(object): if args is not None: syntax += "[I{%s}...] " % args - # comments.append(self.con.dedent( + # comments.append(self.shell.con.dedent( # ''' # [I{%s}...] - This command accepts an arbitrary number of # parameters before any keyword=value parameter. In order to use @@ -1593,7 +1617,7 @@ class ConfigNode(object): if kwargs is not None: syntax += "[I{%s=value}...] " % (kwargs) - # comments.append(self.con.dedent( + # comments.append(self.shell.con.dedent( # ''' # This command also accepts an arbitrary number of # keyword=value parameters. See B{DESCRIPTION} below. @@ -1630,10 +1654,10 @@ class ConfigNode(object): free_kparams = kwargs else: free_kparams = False - self.log.debug("Signature is %s, %s, %s." \ - % (str(parameters), \ - str(free_pparams), \ - str(free_kparams))) + self.shell.log.debug("Signature is %s, %s, %s." + % (str(parameters), + str(free_pparams), + str(free_kparams))) return parameters, free_pparams, free_kparams def get_root(self): @@ -1732,8 +1756,10 @@ class ConfigNode(object): return dict(name=param, group=group, type=p_type, description=p_description, writable=p_writable) - name = property(_get_name, - _set_name, + shell = property(_get_shell, + doc="Gets the shell attached to ConfigNode tree.") + + name = property(_get_name, _set_name, doc="Gets or sets the node's name.") path = property(_get_path, @@ -1743,8 +1769,7 @@ class ConfigNode(object): doc="Lists the node's children.") parent = property(_get_parent, - _set_parent, - doc="Gets or sets for the first time the node's parent.") + doc="Gets the node's parent.") def is_root(self): ''' @@ -1768,9 +1793,17 @@ class ConfigNode(object): if child.name == name: return child else: - raise ValueError("No such path %s/%s" \ + raise ValueError("No such path %s/%s" % (self.path.rstrip('/'), name)) + def remove_child(self, child): + ''' + Removes a child from our children's list. + @param child: The child to remove. + @type child: ConfigNode + ''' + self._children.remove(child) + def get_node(self, path): ''' Looks up a node by path in the nodes tree. @@ -1802,8 +1835,8 @@ class ConfigNode(object): # Is it a bookmark ? if path.startswith('@'): bookmark = path.lstrip('@').strip() - if bookmark in self.prefs['bookmarks']: - path = self.prefs['bookmarks'][bookmark] + if bookmark in self.shell.prefs['bookmarks']: + path = self.shell.prefs['bookmarks'][bookmark] else: raise ValueError("No such bookmark %s" % bookmark) @@ -1811,7 +1844,7 @@ class ConfigNode(object): path = re.sub('%s+' % self._path_separator, self._path_separator, path) if len(path) > 1: path = path.rstrip(self._path_separator) - self.log.debug("Looking for path '%s'" % path) + self.shell.log.debug("Looking for path '%s'" % path) # Absolute path - make relative and pass on to root node @@ -1831,49 +1864,3 @@ class ConfigNode(object): # Path is just one of our children return adjacent_node(path) - - def add_child(self, child, name=None): - ''' - Adds a new child to the node. - Performs necessary checks to enforce our hierarchy conventions: - - A node cannot be its own child - - A new node must not already have a parent - - Our children names must be unique - - We must not be a child of this child - @param child: The new child node. - @type child: A ConfigNode object. - @param name: If specified, the new child name, else uses the one from - child. - @type name: str - @raise ValueError: if the node breaks our hierarchy conventions. - ''' - if child == self: - raise ValueError("A node cannot be it's own child.") - - if not child.is_root(): - raise ValueError("Child node already has a parent.") - - for grandchild in child.children: - if grandchild == self: - raise ValueError("Refusing to add cyclic parent link.") - - if name is not None: - child.name = name - - if child.name in [ourchild.name for ourchild in self.children]: - raise ValueError("Node already has a child named %s" % child.name) - else: - child.parent = self - self._children.add(child) - - def del_child(self, child): - ''' - Deletes a child from the node. - @param child: The new child node. - @type child: A ConfigNode object. - ''' - if child in self._children: - self._children.remove(child) - else: - raise ValueError("Cannot delete: no such child.") - diff --git a/configshell/shell.py b/configshell/shell.py index 4b9439d..af3137e 100644 --- a/configshell/shell.py +++ b/configshell/shell.py @@ -35,7 +35,12 @@ def handle_sigint(signum, frame): still have to do the translation ourselves. ''' raise KeyboardInterrupt -signal.signal(signal.SIGINT, handle_sigint) + +try: + signal.signal(signal.SIGINT, handle_sigint) +except Exception: + # In a thread, this fails + pass class ConfigShell(object): ''' @@ -96,16 +101,14 @@ class ConfigShell(object): <bookmark> := '@', var? ''' - def __init__(self, root_node, preferences_dir=None): + def __init__(self, preferences_dir=None): ''' Creates a new ConfigShell. - @param root_node: The root ConfigNode object - @type root_node: ConfigNode @param preferences_dir: Directory to load/save preferences from/to @type preferences_dir: str ''' - self._current_node = root_node - self._root_node = root_node + self._current_node = None + self._root_node = None self._exit = False self._parser = simpleparse.parser.Parser(self.grammar, root='line') @@ -125,8 +128,8 @@ class ConfigShell(object): try: open(self._cmd_history, 'w').close() except: - self.log.warning("Cannot create history file %s, " \ - % self._cmd_history \ + self.log.warning("Cannot create history file %s, " + % self._cmd_history + "command history will not be saved.") self._save_history = False @@ -134,7 +137,7 @@ class ConfigShell(object): try: readline.read_history_file(self._cmd_history) except IOError: - self.log.warning("Cannot read command history file %s." \ + self.log.warning("Cannot read command history file %s." % self._cmd_history) if self.prefs['logfile'] is None: @@ -149,8 +152,8 @@ class ConfigShell(object): try: self.prefs.load() except IOError: - self.log.warning("Could not load preferences file %s." \ - % self._prefs_file) + self.log.warning("Could not load preferences file %s." + % self._prefs_file) for pref, value in self.default_prefs.iteritems(): if pref not in self.prefs: @@ -412,7 +415,7 @@ class ConfigShell(object): if text.endswith('.'): text = text + '/' (basedir, slash, partial_name) = text.rpartition('/') - self.log.debug("Got basedir=%s, partial_name=%s" \ + self.log.debug("Got basedir=%s, partial_name=%s" % (basedir, partial_name)) basedir = basedir + slash target = self._current_node.get_node(basedir) @@ -483,8 +486,8 @@ class ConfigShell(object): current_parameters[key] = value self._completion_help_topic = command completion_method = target.get_completion_method(command) - self.log.debug("Command %s accepts parameters %s." \ - % (command, cmd_params)) + self.log.debug("Command %s accepts parameters %s." + % (command, cmd_params)) # Do we still accept positional params ? pparam_ok = True @@ -530,9 +533,9 @@ class ConfigShell(object): if param not in kparams \ if param.startswith(text)] - self.log.debug("Possible pparam values are %s." \ + self.log.debug("Possible pparam values are %s." % str(completions)) - self.log.debug("Possible kparam keywords are %s." \ + self.log.debug("Possible kparam keywords are %s." % str(keyword_completions)) if keyword_completions: @@ -616,11 +619,11 @@ class ConfigShell(object): self.log.debug("Called for text='%s'" % text) target = self._current_node.get_node(path) cmd_params = target.get_command_signature(command)[0] - self.log.debug("Command %s accepts parameters %s." \ - % (command, cmd_params)) + self.log.debug("Command %s accepts parameters %s." + % (command, cmd_params)) (keyword, sep, current_value) = text.partition('=') - self.log.debug("Completing '%s' for kparam %s" \ + self.log.debug("Completing '%s' for kparam %s" % (current_value, keyword)) self._current_parameter = keyword @@ -686,7 +689,7 @@ class ConfigShell(object): pparams, kparams, text, current_token) - self.log.debug("Returning completions %s to readline." \ + self.log.debug("Returning completions %s to readline." % str(self._current_completions)) if state < len(self._current_completions): @@ -722,11 +725,11 @@ class ConfigShell(object): ''' completions = [] - self.log.debug("Dispatching completion for %s token. " \ - % current_token \ - + "text='%s', path='%s', command='%s', " \ - % (text, path, command) \ - + "pparams=%s, kparams=%s" \ + self.log.debug("Dispatching completion for %s token. " + % current_token + + "text='%s', path='%s', command='%s', " + % (text, path, command) + + "pparams=%s, kparams=%s" % (str(pparams), str(kparams))) (path, iterall) = path.partition('*')[:2] @@ -759,7 +762,7 @@ class ConfigShell(object): self._complete_token_kparam(text, cpl_path, command, pparams, kparams) else: - self.log.debug('Cannot complete unknown token %s.' \ + self.log.debug("Cannot complete unknown token %s." % current_token) return completions @@ -844,8 +847,8 @@ class ConfigShell(object): (keyword, sep, value) = value.partition('=') kparams[keyword] = value - self.log.debug("Parse gave path='%s' command='%s' " % (path, command) \ - + "pparams=%s " % str(pparams) \ + self.log.debug("Parse gave path='%s' command='%s' " % (path, command) + + "pparams=%s " % str(pparams) + "kparams=%s" % str(kparams)) return (result_trees, path, command, pparams, kparams) @@ -981,3 +984,11 @@ class ConfigShell(object): self.run_interactive() finally: readline.set_completer(old_completer) + + def attach_root_node(self, root_node): + ''' + @param root_node: The root ConfigNode object + @type root_node: ConfigNode + ''' + self._current_node = root_node + self._root_node = root_node |