Changeset 356
- Timestamp:
- 11/12/06 18:52:14 (2 years ago)
- Files:
-
- cly/trunk/cly/__init__.py (modified) (15 diffs)
- cly/trunk/cly/interactive.py (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
cly/trunk/cly/__init__.py
r355 r356 10 10 Node Alias Action Validator Grammar 11 11 12 Help Context Parser12 Help LazyHelp HelpParser Context Parser 13 13 14 14 static_candidates … … 19 19 class NonMatchingNode(Error): pass 20 20 class InvalidType(Error): pass 21 class InvalidHelp(Error): pass 21 22 class ReadOnlyAlias(Error): pass 22 23 class InvalidNodePath(Error): pass 24 class InvalidAnonymousNode(Error): pass 23 25 class ParseError(Error): 24 26 """ Report a parse error. Output is formatted using string templates, … … 84 86 traversals = 1 85 87 86 def __init__(self, doc, pattern=None, name=None, separator=None, **options):88 def __init__(self, help, pattern=None, name=None, separator=None, **options): 87 89 """Construct a new CLY grammar node. 88 90 89 `doc` can be either a help string, a tuple of (command, help), or a 90 lambda returning either of these, for specifying the command text to 91 match. If command is not provided, the Node name is used. 91 `help` can be either a help string, or a callable returning an iterable 92 of (key, help) pairs. 92 93 93 94 `pattern` is a regular expression for matching user input.""" 94 95 self._children = {} 95 self.doc = doc 96 if isinstance(help, basestring): 97 self.help = LazyHelp(self, help) 98 elif callable(help): 99 self.help = help 100 else: 101 raise InvalidHelp('help must be a callable or a string') 96 102 if pattern is not None: 97 103 self.pattern = pattern … … 107 113 self.name = name 108 114 self.parent = None 115 self.__anonymous_children = 0 109 116 self(**options) 110 117 … … 119 126 name = property(lambda self: self._name, _set_name) 120 127 121 def __call__(self, * *options):128 def __call__(self, *anonymous, **options): 122 129 """ Update or add options and child nodes. 123 130 … … 128 135 <Node:/top/subnode> 129 136 """ 137 for node in anonymous: 138 if not isinstance(node, Node): 139 raise InvalidAnonymousNode('Anonymous node is not a Node object') 140 # TODO Convert help to name instead of __anonymous_<n> 141 node.name = '__anonymous_%i' % self.__anonymous_children 142 node.parent = self 143 self._children[node.name] = node 144 self.__anonymous_children += 1 145 130 146 for k, v in options.iteritems(): 131 147 if isinstance(v, Node): 148 if k.endswith('_'): 149 k = k[:-1] 132 150 v.name = k 133 151 v.parent = self … … 209 227 context.advance(len(match.group())) 210 228 211 def predicate(self, context):212 return True213 214 229 def visible(self, context): 215 230 """ Should this node be visible? """ … … 233 248 def path(self): 234 249 """ The full grammar path to this node. Path components are separated 235 by a f ull stop.250 by a forward slash. 236 251 237 252 >>> grammar = Grammar(one=Node('One'), two=Node('Two')) … … 247 262 return '/' + '/'.join(names) 248 263 249 def help(self, context):250 """ Return help for this Node as an iterable of tuples in the form251 (<key>, <help>). If <key> begins with '<' the item matches multiple252 items.253 254 >>> grammar = Grammar(one=Node('One'), two=Node('Two'))255 >>> list(grammar.find('one').help(None))256 [('one', 'One')]257 """258 doc = self.doc259 while callable(doc):260 doc = doc(context)261 if isinstance(doc, (tuple, list)):262 yield doc263 else:264 if self.name == self.pattern:265 yield (self.name, doc)266 else:267 yield ('<%s>' % self.name, doc)268 269 264 def candidates(self, context, text): 270 """ Return an iterable of completion candidates for the given text. 265 """ Return an iterable of completion candidates for the given text. The 266 default is to use the content of self.help(). 271 267 272 268 >>> grammar = Grammar(one=Node('One'), two=Node('Two')) … … 364 360 with_context = False 365 361 366 def __init__(self, doc, callback, *args, **kwargs): 367 Node.__init__(self, doc, callback=callback, *args, **kwargs) 362 def __init__(self, help, callback=None, *args, **kwargs): 363 if isinstance(help, basestring): 364 help_string = help 365 help = lambda ctx: (('<eol>', help_string),) 366 Node.__init__(self, help, callback=callback, *args, **kwargs) 368 367 369 368 def help(self, context): … … 430 429 431 430 class Help(object): 431 """ A callable object representing help for a Node. Must return an iterable 432 of pairs in the form (key, help). """ 433 def __init__(self, doc): 434 self.doc = doc 435 436 def __call__(self, context): 437 for n, h in self.doc: 438 yield (n, h) 439 440 @staticmethod 441 def pairs(*pairs): 442 """ Create a Help object from an iterable of (name, help) pairs. """ 443 return Help([(n, h) for n, h in pairs]) 444 445 @staticmethod 446 def pair(name, help): 447 """ Create a Help object from a single (name, help) pair. """ 448 return Help([(name, help)]) 449 450 451 class LazyHelp(Help): 452 """ Lazily generate help from a Node. 453 454 If the Node does not have a custom pattern, the help will be in the form 455 (name, text), otherwise it will be in the form (<name>, text). 456 457 """ 458 def __init__(self, node, text): 459 self.node = node 460 self.text = text 461 462 def __call__(self, context): 463 if self.node.name == self.node.pattern: 464 yield (self.node.name, self.text) 465 else: 466 yield ('<%s>' % self.node.name, self.text) 467 468 469 470 class HelpParser(object): 432 471 """ Extract the help for children of the specified Node. """ 433 472 … … 450 489 451 490 >>> context = Context(None, None) 452 >>> help = Help (context, Grammar(one=Node('One'),491 >>> help = HelpParser(context, Grammar(one=Node('One'), 453 492 ... two=Node(('<two>', 'Two'), group=2))) 454 493 >>> list(help) … … 464 503 >>> import sys 465 504 >>> context = Context(None, None) 466 >>> help = Help (context, Grammar(one=Node('One'),505 >>> help = HelpParser(context, Grammar(one=Node('One'), 467 506 ... two=Node(('<two>', 'Two'), group=2))) 468 507 >>> help.format(sys.stdout) … … 525 564 526 565 def help(self): 527 """ Return a Help object describing the last successfully parsed566 """ Return a HelpParser object describing the last successfully parsed 528 567 node. """ 529 return Help (self, self.last_node)568 return HelpParser(self, self.last_node) 530 569 531 570 def traverse(self, node): … … 592 631 593 632 for subnode in node.next(context): 594 if not subnode. predicate(context):633 if not subnode.valid(context): 595 634 continue 596 635 submatch = subnode.match(context) cly/trunk/cly/interactive.py
r355 r356 15 15 class Interact(object): 16 16 """CLY interaction through readline. Due to readline limitations, only one 17 Interact object can be active within an application. """ 17 Interact object can be active within an application. 18 19 Some useful variables are stored in the Interact class, *not* the instance: 20 `prompt`, `user_context`, `history_file`, `history_length`. 21 """ 18 22 __cli_inject_text = '' 19 23 __completion_candidates = [] … … 45 49 46 50 readline.parse_and_bind("tab: complete") 47 readline.set_completer_delims( '`~!#$%^&*()=+[{]}\|;\'",<>? \t')51 readline.set_completer_delims(self.completion_delimiters) 48 52 readline.set_completer(Interact.__cli_completion) 49 53 readline.set_startup_hook(Interact.__cli_injector) … … 53 57 54 58 55 def interact_once(self, default_text='' ):59 def interact_once(self, default_text='', callback=None): 56 60 """ Input one command from the user and return the result of the 57 executed command. """ 61 executed command. `callback` is called with the Interact object before 62 each line is displayed. """ 58 63 Interact.__cli_inject_text = default_text 59 64 … … 83 88 pass 84 89 finally: 85 try: 86 readline.write_history_file(self.history_file) 87 except: 88 pass 90 self.write_history() 89 91 92 def write_history(self): 93 """ Write command line history out. """ 94 try: 95 readline.write_history_file(self.history_file) 96 except: 97 pass 98 90 99 @staticmethod 91 100 def __dump_traceback(exception):
