Changeset 428 for cly/trunk/doc/video1.py
- Timestamp:
- 05/20/07 10:08:20 (2 years ago)
- Files:
-
- cly/trunk/doc/video1.py (modified) (1 diff)
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 an2 # 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. 3 3 4 import sys 4 5 import re 5 import sys 6 from cly.all import * 6 7 from urllib2 import urlopen 7 from cly import *8 from cly.types import *9 from cly.interactive import Interact10 8 from BeautifulSoup import BeautifulSoup 11 9 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. 13 18 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. 23 22 page = None 24 23 25 # Write our quit callback function (could just as easily be a method26 def quit():27 sys.exit(0)28 29 30 # Any "Variable" subclass (in this case URI) is automatically converted into31 # parameters passed to Action callbacks.32 24 def fetch(uri): 33 25 global page 34 35 26 content = urlopen(uri).read() 36 27 page = BeautifulSoup(content) 28 # Whoops 37 29 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 30 def find_tag(tag): 31 for t in page(tag): 32 print t 47 33 48 34 def 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 52 37 53 # So, with 94 lines, we've implemented the beginnings of a BeautifulSoup54 # shell. Enjoy CLY.55 56 # Check if everything is still working...it worked, but being able to57 # specify more than just a single word for the "find text" command would be58 # 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. 60 45 grammar = 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 ), 67 55 ), 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)( 74 60 tag=Node('Find tags')( 75 tag=Variable('Tag to find')(76 Action('Find tags', find_tag s),61 tag=Variable('Tag to search for')( 62 Action('Find tags', find_tag) 77 63 ) 78 64 ), 79 # We'll flesh this out...80 65 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 ) 84 73 ), 85 ),86 87 # First we'll fill out quit, because it's easy. Separate it from the88 # other commands visually.89 quit=Node('Quit', group=99)(90 Action('Quit', quit),91 74 ), 92 75 ) 93 76 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')) 97 79 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). 84 quickstart(grammar, application='soupsh', prompt='> ')
