Show
Ignore:
Timestamp:
05/20/07 10:08:20 (2 years ago)
Author:
athomas
Message:

cly:

  • Added a new video1.py.
  • Fixed cly.all so it works.
  • Alias can now target multiple nodes: Alias('/foo/*')
  • Fixed a couple of bugs in cly.extra, including making integrate

actually work.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • cly/trunk/doc/video1.py

    r425 r428  
    1 # To demonstrate how easy it is to use CLY, I'm going to implement an 
    2 # interactive BeautifulSoup shell
     1# To demonstrate how easy it is to create interactive shells with CLY, I'm 
     2# going to write a simple interface to BeautifulSoup
    33 
     4import sys 
    45import re 
    5 import sys 
     6from cly.all import * 
    67from urllib2 import urlopen 
    7 from cly import * 
    8 from cly.types import * 
    9 from cly.interactive import Interact 
    108from BeautifulSoup import BeautifulSoup 
    119 
    12 # That should be enough imports... 
     10# I'll start with three basic commands: 
     11
     12#  fetch <uri> 
     13#  find tag <tag> 
     14#  find text <regex> 
     15
     16# And there we have it. Three commands built up in a very short amount of 
     17# time. Enjoy. 
    1318 
    14 # The grammar defines the syntax. For this demo we're going to implement 
    15 # two commands: 
    16 
    17 #   fetch <uri> 
    18 #   find tag <tag> 
    19 #   find text <regex> 
    20 #  
    21 # Just the stubs for now... 
    22  
     19# For the purposes of this demonstration we'll use a global to store the 
     20# current page. You could just as easily use a class member, and all callbacks 
     21# could also be methods. 
    2322page = None 
    2423 
    25 # Write our quit callback function (could just as easily be a method 
    26 def quit(): 
    27     sys.exit(0) 
    28  
    29  
    30 # Any "Variable" subclass (in this case URI) is automatically converted into 
    31 # parameters passed to Action callbacks. 
    3224def fetch(uri): 
    3325    global page 
    34  
    3526    content = urlopen(uri).read() 
    3627    page = BeautifulSoup(content) 
     28    # Whoops 
    3729 
    38     Interact.prompt = '%s> ' % uri 
    39  
    40  
    41 def find_tags(tag): 
    42     # We'll implement this first... 
    43     for tag in page(tag): 
    44         print tag 
    45     # That easy? Yep. BeautifulSoup rocks. 
    46  
     30def find_tag(tag): 
     31    for t in page(tag): 
     32        print t 
    4733 
    4834def find_text(text): 
    49     # Next... 
    50     for text in page(text=re.compile(text)): 
    51         print text 
     35    for t in page(text=re.compile(text)): 
     36        print t 
    5237 
    53 # So, with 94 lines, we've implemented the beginnings of a BeautifulSoup 
    54 # shell. Enjoy CLY. 
    55  
    56 # Check if everything is still working...it worked, but being able to  
    57 # specify more than just a single word for the "find text" command would be 
    58 # good. We'll override the pattern... 
    59  
     38# First we define the grammar. Now we'll fill it out a bit more. Each Node 
     39# defines a token in the grammar.  
     40
     41# We've used a couple of new node types here. The first is a Variable, which 
     42# is mapped to the arguments in the final Action callback. An Action is a 
     43# terminal node in the syntax, and defines what function to call to perform 
     44# the action. 
    6045grammar = Grammar( 
    61     # Let's fetch a web page. We'll need a global to store the content... 
    62     fetch=Node('Fetch a web page')( 
    63         # URI is a builtin variable type 
    64         uri=URI('URI of page to fetch')( 
    65             Action('Fetch page', fetch), 
    66         ) 
     46    fetch=Node('Fetch page to interrogate')( 
     47        # What's going on? This is due to the fact that the default pattern 
     48        # used to match tokens is a basic word match... To override this we 
     49        # can pass pattern=r'...' to the Variable constructor... 
     50        # We've been a little too liberal with our pattern...Fortunately there 
     51        # is a built in node that matches a URI: cly.types.URI 
     52        uri=URI('URI of page')( 
     53            Action('Fetch page', fetch) 
     54        ), 
    6755    ), 
    68  
    69     # Ideally the find command would only be visible if we have a valid 
    70     # object fetched. There are two ways to achieve that, by passing a 
    71     # callable to Node, or subclassing Node. We'll try the first. 
    72     find=Node('Find objects within the current page', 
    73               valid=lambda context: page)( 
     56    # Obviously we don't want the "find" command to be used if we don't have 
     57    # a valid page... Fortunately, the 'valid()' method can be overridden to 
     58    # achieve this: 
     59    find=Node('Find objects in page', valid=lambda context: page)( 
    7460        tag=Node('Find tags')( 
    75             tag=Variable('Tag to find')( 
    76                 Action('Find tags', find_tags), 
     61            tag=Variable('Tag to search for')( 
     62                Action('Find tags', find_tag) 
    7763            ) 
    7864        ), 
    79         # We'll flesh this out... 
    8065        text=Node('Find text')( 
    81             text=Variable('Text to find', pattern=r'.*')( 
    82                 Action('Find text', find_text), 
    83             ), 
     66            # Need to be a bit more permissive with our pattern... 
     67            # Displaying <text> for the user is nice, but not explicit enough 
     68            # about what the grammar actually expects, ie. a regex. We can 
     69            # override the node help to achieve this. 
     70            text=Variable('Text to search for', pattern=r'.+')( 
     71                Action('Find text', find_text) 
     72            ) 
    8473        ), 
    85     ), 
    86  
    87     # First we'll fill out quit, because it's easy. Separate it from the 
    88     # other commands visually. 
    89     quit=Node('Quit', group=99)( 
    90         Action('Quit', quit), 
    9174    ), 
    9275) 
    9376 
    94 # Now we need to start the interactive shell... 
    95 interact = Interact(grammar, 'soupsh', prompt='> ') 
    96 interact.interact_loop() 
     77# All non-keyword arguments passed to a node "call" are treated as anonymous 
     78# nodes. eg. Node('Foo')(Node('Bar'), Node('Baz')) 
    9779 
    98 # excellent. 
     80# Next, we interact with the user. Nice, though we want to change the 
     81# application and prompt... The application is, by default, used to build 
     82# a prompt, if not provided, and to store the history 
     83# (~/.<application>_history). 
     84quickstart(grammar, application='soupsh', prompt='> ')