- CLY "FAQ"
- CLY from the end-user's perspective
- Developing with CLY
- Q: How can I print pretty colours?
- Q: Can nodes be "optional"?
- Q: How can I customise tab completion?
- Q: Is there a convenient way to print tables of data?
- Q: How can I use reserverd words like class as node names?
- Q: Is it possible to use something other than the node name for the …
- Q: Can I group similar commands together visually?
- Q: How do I define what text a Node matches?
- Q: How can I add my own Node "types"?
- XML Syntax
CLY "FAQ"
And by FAQ, of course, I mean questions I suspect people will want answers to.
CLY from the end-user's perspective
Q: ? is the contextual help key, how can I enter a literal ??
Press ^V (Ctrl-V) followed by ?.
Q: How do I use completion?
Press <Tab>.
Developing with CLY
Q: How can I print pretty colours?
CLY includes the console module from pycrash. This provides a simple way of printing the 8 colours of basic terminals, as well as bold and underline.
from cly.console import cprint cprint('CLY supports ^Bbold^B and ^Uunderlined^U text as well as ^B^1C^2O^3L^4O^5U^6R^7S^N!')
Documentation is here.
Q: Can nodes be "optional"?
Yes and no. There is no explicit "optional" node but you can achieve the same effect using a node with an alias back to a parent:
def add_host(hostname, ip, comment=''): ... add=Node('Add a host')( hostname=Variable('Host name')( ip=IP('IP address')( action=Action('Add host', add_host), comment=Variable('Comment', pattern=r'.+')( Alias('../../action'), ) ) ), )
This is a grammar for this command:
add <hostname> <ip> [<comment>]
Q: How can I customise tab completion?
Override the Node.candidates() method to return all possible candidates and optionally override Node.match() to ensure the users input matches one of your candidates (or set the class variable Node.match_candidates=True). An example is the LocalFile variable in cly/trunk/cly/variables.py
Q: Is there a convenient way to print tables of data?
Why yes, yes there is. cly.console.print_table() is the answer.
Q: How can I use reserverd words like class as node names?
Add a trailing underscore to the name. It will be stripped by CLY and used as the name. eg. class_=Node().
Q: Is it possible to use something other than the node name for the Variable name?
Yes. Pass var_name=<name> to the Variable constructor to override the default use of the node name.
Q: Can I group similar commands together visually?
Sure can. Pass group=<int> to all nodes of the same group or use the convenience node cly.Group(group=<int>)(<children ...>).
In this example both child, another_child and yet_another_child will be in group 10:
Grammar( Group(group=10)( child=Node('Child node'), another_child=Node('Child node'), ), yet_another_child=Node('Child node', group=10), )
Note that the Group node can be used to set any attributes on all children, not just group.
Q: How do I define what text a Node matches?
The quick answer is to pass pattern=<regex> to the Node constructor:
Node('Match any text', pattern=r'.+')
You can also override the separator regex, which matches the text separating one node from the next.
The long answer is that you can override Node.match(context) and return a regex match object, but I wouldn't recommend it.
Q: How can I add my own Node "types"?
Subclass Variable and overload the parse() method. Here's an example of a 2D coordinate type in the form (<float>, <float>):
class Coordinate(Variable): pattern = r'\(\s*(%s)\s*,\s*(%s)\s*\)' % (Float.pattern, Float.pattern) def parse(self, context, match): return float(match.group(1)), float(match.group(2))
The parse() method is called by Variable.select() when the node is selected during matching. select() inserts the return value of parse() and inserts it into the context's vars member. select() can also be overridden to provide even more customised behaviour:
class KeyValue(Variable): """Parse <key>=<value> pairs and pass them to the final callback as a dictionary.""" pattern = r'(\w+?)\s*=\s*%s' % String.pattern def selected(self, context, match): context.vars.setdefault(self.var_name, {})[match.group(1)] = \ match.group(match.lastindex).decode('string_escape')
XML Syntax
Q: What is the XML syntax for a node class?
CLY node classes become XML elements. Strict XML specifies that elements and attributes must be lower case therefore the syntax for a node class, for example 'IP', should be converted to lower case first.
The "eval:" XML namespace may be used to cause attributes to be evaluated as python code instead of read as strings.
The following two grammars are equivalent:
grammar = Grammar( add=Node('Add a host to the hosts file')( ipaddr=IP('The IPv4 address of the host you wish to add')( host=Hostname('The hostname you wish to add', pattern=r'[\w@._-]+')( comment=Variable('A comment for this entry', var_name='comment', pattern=r'.+')( Action('', hosts_file.add), ) ), ), ), )
<grammar xmlns="http://swapoff.org/cly/xml" xmlns:eval="http://swapoff.org/cly/xml/eval">
<node name="add" help="Add a host to the hosts file">
<ip name="ipaddr" help="The IPv4 address of the host you wish to add">
<hostname name="host" help="The hostname you wish to add" eval:pattern="r'[\w@._-]+'">
<variable name="comment" help="A comment for this entry" var_name="comment" eval:pattern="r'.+'">
<action exec="hosts_file.add" />
</variable>
</hostname>
</ip>
</node>
</grammar>
