These days, there's a lot going on right now in the Twisted community as well as here at Divmod. It's really quite exciting :-) But, in an effort to not spam the world, I'll try to group things together when possible.
First off is Twisted news: in case you haven't noticed yet, Twisted Labs now has a group blog here. We're making an effort to share more of our conversations and vision with the wider community. We'll also be posting news items there, which means (at long last!) we now have an RSS feed for the latest Twisty Happenings. To just read the news items (if that's your preference), you can visit this page (it's a "news" tag filter). What's more, if you want an RSS feed that just covers the news, you can use this one, which is also being maintained. As always, our News Archives page continues its non-feed-having, wiki-ish existence.
Next up is the Divmod Sprint at PyCon 2008: we're having one! I posted an entry on our company blog, so please check it out and follow the links to join our sprint, if you are so inclined.
More soon!
Thursday, February 21, 2008
Wednesday, February 20, 2008
zope.testbrowser: Automating the Web
Speaking of little known python modules, if you haven't used zope.testbrowser, please raise your hand. Okay, all of you that raised your hand and have ever wanted to automate web forms, check a web site's functionality, or perform screen-scraping related tasks, pull up a chair. This won't take long, because zope.testbrowser's API rocks so hard and is so easy to use.
I first started using zope.testbrowser sometime before consulting for Zenoss. However, when I implemented Zenoss' "Synthetic Transactions" (I believe this was originally part of their open source offering, but has since been moved into the Enterprise Edition), I used it again and profusely. It provided me with a wonderful Python API (a mechanize wrapper and then some), one so intuitive that I could use not only without thinking, but for which I didn't even have to read the docs. This solution proved to be friendly to programmers, but we were really targeting non-programming network administrators and IT managers with the Zenoss plugin, so I completed the plugin using Twill and TestGen4Web (whose python runner I rewrote and to which I added support for zope.testbrowser).
I used it again at Zenoss later when building the community software, enabling users to set their Mailman preferences from their Plone settings. Most recently, I've been using it to publish news articles to various sites from a single file used by a script.
Here are a couple of standard use cases with sample code:
Visit a page and follow some links
That gives us the actual link object... but what if we want to follow that link?
Simple enough :-)
Check for content
Let's continue on the Google link trail, and check for content:
Sign into a site
Signing into a site requires filling in form information and submitting that data.
A couple things to note here:
Now that we're logged in, let's change some out-dated information in my profile (my old blog's link has been there for too long):
Now that we saved it, let's check our results:
Excellent; just what we expected to see.
These are all really simple examples, but they should be helpful in providing some insight and inspiring you to use it :-) Working with radio buttons is a little more complex in that you have to get the containing object first and use getControl on that object in order to get the selection you want. However, if you've worked with HTML more than any sane person should have to (I think that exact amount is "any") , this will all be quite expected.
So, who's up for making an async version to work with Twisted? ;-)
A tangential caveat: I made a comment above about the zope.testbrowser API rocking, and that needs some clarification. As far as I am concerned, there are two major ways in which API's can be graded:
I first started using zope.testbrowser sometime before consulting for Zenoss. However, when I implemented Zenoss' "Synthetic Transactions" (I believe this was originally part of their open source offering, but has since been moved into the Enterprise Edition), I used it again and profusely. It provided me with a wonderful Python API (a mechanize wrapper and then some), one so intuitive that I could use not only without thinking, but for which I didn't even have to read the docs. This solution proved to be friendly to programmers, but we were really targeting non-programming network administrators and IT managers with the Zenoss plugin, so I completed the plugin using Twill and TestGen4Web (whose python runner I rewrote and to which I added support for zope.testbrowser).
I used it again at Zenoss later when building the community software, enabling users to set their Mailman preferences from their Plone settings. Most recently, I've been using it to publish news articles to various sites from a single file used by a script.
Here are a couple of standard use cases with sample code:
Visit a page and follow some links
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
>>> from zope.testbrowser.browser import Browser | |
>>> b = Browser('http://google.com') | |
>>> b.title | |
'Google' | |
>>> b.getLink('About Google') | |
<Link text='About Google' url='http://www.google.com/intl/en/about.html'> |
That gives us the actual link object... but what if we want to follow that link?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
>>> b.getLink('About Google').click() | |
>>> b.title | |
'About Google' |
Check for content
Let's continue on the Google link trail, and check for content:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
>>> b.getLink('Investor Relations').click() | |
>>> b.title | |
'Google Investor Relations' | |
>>> b.getLink('Guidelines').click() | |
>>> 'Corporate Governance' in b.contents | |
True |
Sign into a site
Signing into a site requires filling in form information and submitting that data.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
>>> b = Browser('https://launchpad.net/+login') | |
>>> b.title | |
'Log in or register with Launchpad' | |
>>> b.getControl(name='loginpage_email', index=0).value = username | |
>>> b.getControl(name='loginpage_password').value = passwd | |
>>> b.getControl(name='loginpage_submit_login').click() | |
>>> b.title | |
'Launchpad' | |
>>> 'Logged in as' in b.contents | |
True |
A couple things to note here:
- you need to look at the HTML so that you know what the form elements are named;
- there are two form elements on the page that are named loginpage_email, and we want the first one; and
- I omitted the part of the code where I pulled my credentials from the file system.
Now that we're logged in, let's change some out-dated information in my profile (my old blog's link has been there for too long):
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
>>> b.getLink('Duncan McGreggor').click() | |
>>> b.title | |
'Duncan McGreggor in Launchpad' | |
>>> b.getLink('http://www.advogato.org/person/oubiwann/') | |
<Link text='http://www.advogato.org/person/oubiwann/' url='http://www.advogato.org/person/oubiwann/'> | |
>>> b.getLink('Change details').click() | |
>>> b.getControl(name='field.homepage_content').value = 'blog: http://oubiwann.blogspot.com' | |
>>> b.getControl(name='field.actions.save').click() |
Now that we saved it, let's check our results:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
>>> b.getLink('http://www.advogato.org/person/oubiwann/') | |
Traceback (most recent call last): | |
[snip] | |
mechanize._mechanize.LinkNotFoundError | |
>>> b.getLink('http://oubiwann.blogspot.com') | |
<Link text='http://oubiwann.blogspot.com' url='http://oubiwann.blogspot.com'> |
Excellent; just what we expected to see.
These are all really simple examples, but they should be helpful in providing some insight and inspiring you to use it :-) Working with radio buttons is a little more complex in that you have to get the containing object first and use getControl on that object in order to get the selection you want. However, if you've worked with HTML more than any sane person should have to (I think that exact amount is "any") , this will all be quite expected.
So, who's up for making an async version to work with Twisted? ;-)
A tangential caveat: I made a comment above about the zope.testbrowser API rocking, and that needs some clarification. As far as I am concerned, there are two major ways in which API's can be graded:
- how easy is the API to use, how well is it documented, how much of the problem domain does it cover? (developer as user)
- how elegantly/efficiently was the solution implemented (developer as contributor)
Tuesday, February 19, 2008
PyCon 2008 Registration Discounts Expire Tomorrow!
Hey all, in case you're like me and wait for the last minute to do everything, you need to hop on the good foot and do the bad thing: pay now. Or do the worse thing: pay later, and pay more.
Currently, the rates are as follows:
Don't wait any longer... :-)
Currently, the rates are as follows:
- hobbyist registration: $220
- corporate registration: $400
- single room: $99 (maybe all rooms are the same price?)
- hobbyist registration: $300
- corporate registration: $500
- single room: dunno... I've heard rumors that you might be able to get something for $160/night
Don't wait any longer... :-)
Sunday, February 17, 2008
Twisted's filepath Module
Glyph recently blogged about some of the buried treasure in Twisted, the filepath module in particular. Since working at Divmod, I've made use of this module quite a bit, and thought I'd share some of the features that I've found most useful (and intuitively nice to use).
Assuming you've got Twisted installed, let's fire up a python interpreter and import the FilePath class and instantiate it with /tmp and test some basic operations:
Isn't that nice? I love not having to import and use the os.path.exists function; it's a method on the object representing a file or a path, as it should be. I also enjoy the convenience FilePath offers when it comes to creating paths:
We had to call restat() so that the object would check the file system again (since we just made some changes). Now, for some files:
And if you don't believe that, we can switch to shell:
Reading and writing operations are the same as usual:
But you can use shortcuts like this, too:
Let's get the path and recheck the file size, just to make sure:
Or, again from shell:
Hmm... for some reason, I really like this file and want to keep it. What do I do?
That's it! Nice, eh? Of course, we're careful, so we check to make sure it happened:
To see more, check out the source, or at least do dir(FilePath) to see what other goodies this class has to offer, and enjoy :-)
Assuming you've got Twisted installed, let's fire up a python interpreter and import the FilePath class and instantiate it with /tmp and test some basic operations:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
>>> from twisted.python.filepath import FilePath | |
>>> tmp = FilePath('/tmp') | |
>>> tmp.exists() | |
True | |
>>> bogus = tmp.child('bogus') | |
>>> bogus.exists() | |
False |
Isn't that nice? I love not having to import and use the os.path.exists function; it's a method on the object representing a file or a path, as it should be. I also enjoy the convenience FilePath offers when it comes to creating paths:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
>>> mydir = tmp.child('notbogus').child('anotherdir').child('mydir') | |
>>> mydir.exists() | |
False | |
>>> mydir.makedirs() | |
>>> mydir.restat() | |
>>> mydir.exists() | |
True |
We had to call restat() so that the object would check the file system again (since we just made some changes). Now, for some files:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
>>> for filename in ['test1.txt', 'test2.txt', 'test3.tst']: | |
... mydir.child(filename).touch() | |
... | |
>>> mydir.listdir() | |
['test1.txt', 'test2.txt', 'test3.tst'] |
And if you don't believe that, we can switch to shell:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
lorien:oubiwann 20:51:58 $ ls -al /tmp/notbogus/anotherdir/mydir/ | |
total 0 | |
drwxr-xr-x 2 oubiwann wheel 170 Feb 17 20:51 . | |
drwxr-xr-x 3 oubiwann wheel 102 Feb 17 20:46 .. | |
-rw-r--r-- 1 oubiwann wheel 0 Feb 17 20:51 test1.txt | |
-rw-r--r-- 1 oubiwann wheel 0 Feb 17 20:51 test2.txt | |
-rw-r--r-- 1 oubiwann wheel 0 Feb 17 20:51 test3.tst |
Reading and writing operations are the same as usual:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
>>> myfile = mydir.child(filename) | |
>>> myfile.isdir() | |
False | |
>>> myfile.islink() | |
False | |
>>> myfile.isfile() | |
True | |
>>> fh = myfile.open('w+') | |
>>> fh.write('do the usual thing') | |
>>> fh.close() | |
>>> myfile.getsize() | |
18L | |
>>> myfile.open().read() | |
'do the usual thing' |
But you can use shortcuts like this, too:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
>>> myfile.getContent() | |
'do the usual thing' | |
>>> myfile.setContent('and now for something completely different...') | |
>>> myfile.getContent() | |
'and now for something completely different...' |
Let's get the path and recheck the file size, just to make sure:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
>>> myfile.path | |
'/tmp/notbogus/anotherdir/mydir/test3.tst' | |
>>> myfile.restat() | |
>>> myfile.getsize() | |
45L |
Or, again from shell:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
lorien:oubiwann 20:52:06 $ ls -al /tmp/notbogus/anotherdir/mydir/ | |
total 8 | |
drwxr-xr-x 2 oubiwann wheel 170 Feb 17 20:58 . | |
drwxr-xr-x 3 oubiwann wheel 102 Feb 17 20:46 .. | |
-rw-r--r-- 1 oubiwann wheel 0 Feb 17 20:51 test1.txt | |
-rw-r--r-- 1 oubiwann wheel 0 Feb 17 20:51 test2.txt | |
-rw-r--r-- 1 oubiwann wheel 45 Feb 17 20:58 test3.tst |
Hmm... for some reason, I really like this file and want to keep it. What do I do?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
>>> docs = FilePath('/Users/oubiwann/Documents') | |
>>> myfile.moveTo(docs.child('special_file.txt')) |
That's it! Nice, eh? Of course, we're careful, so we check to make sure it happened:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
>>> myfile.exists() | |
False | |
>>> newfile = docs.child('special_file.txt') | |
>>> newfile.exists() | |
True | |
>>> newfile.path | |
'/Users/oubiwann/Documents/special_file.txt' | |
>>> newfile.getsize() | |
45L | |
>>> newfile.getContent() | |
'and now for somethibg completely different...' |
To see more, check out the source, or at least do dir(FilePath) to see what other goodies this class has to offer, and enjoy :-)
Wednesday, February 13, 2008
Twisted Presence at PyCon 2008
A quick post to let folks know that there is a planned Twisted presence at PyCon this year (last year's was a happy coincidence). We're already coordinating travel and have some BoF ideas up our collective sleeves that are sure to intrigue if not titillate and please.
Twisted brothers and sisters: be there or... well, you'll just miss out on all the goodness that is the unstoppable force of Twisted :-)
Twisted brothers and sisters: be there or... well, you'll just miss out on all the goodness that is the unstoppable force of Twisted :-)
Subscribe to:
Posts (Atom)