Posted by ben
Tue, 04 Jul 2006 19:55:33 GMT
It’s been a busy month for Pylons, with lots of changes for the big internal API change in 0.9. The great news for those making Pylons apps right now with 0.8.2 is how few backwards compatibility issues there are. Most of the big changes take place under the hood and compatibility objects are present to mitigate the massive breakage that I’ve seen happen in other framework upgrades.
This is going to remain a big focus on future development in Pylons too, and the API in 0.9 is solid enough that I don’t see anything but minor tweaks in the future (1.0 and beyond). Pylons based apps will be easy to maintain and upgrade thanks primarily to WSGI.
We’ve taken some cues from the Django project that we believe make for a cleaner request-cycle. In Pylons 0.9, controllers are expected to return a response object, and for convenience a method is included that renders a template to a new response object and returns it (This will look very familiar to Djangonauts).
The command was integrated with a slightly more enhanced version of Buffet along with the Beaker Session/Caching middleware. The end-result is a powerful command that not only can render templates in any Template-Plugin compatible template language, but the rendered result can also be cached. It’s a great way to utilize template languages which might not be all that quick by themselves.
Sample controller in Pylons 0.9:
from myproj.lib.base import *
class UserController(BaseController):
def show(self, id):
# Cache based on id in the URL, for 30 seconds
return render_response('/user/show.myt', cache_key=id, cache_expire=30)
def index(self):
# Just for fun, use Kid to render the index
return render_response('kid', 'user.index', cache_expire=15)
Being able to easily cache any template in any template language makes it very easy to sprinkle in caching when you need to handle massive loads yet stay dynamic.
Another new feature present in the latest Routes and Pylons is resource mappings, which automatically generate routes for you with HTTP method restrictions. This makes it easy to setup controllers and their actions for specific HTTP verbs (aka, REST-ful URL’s and web services). The implementation of this feature was directly inspired by the Simply Restful Rails plug-in that was also demo’d by David Hansson in his Resources on Rails talk. Being able to discriminate valid routes based on HTTP method was brought up in the past and I’m happy to have seen an implementation that solves the issues I originally had with the idea.
The one feature still in development is to easily discern the content type requested, which was also inspired by Rails. Josh Triplett has written some code that deals with the ugly task of parsing the HTTP Accept headers, and I’m working on adding it into Paste for easy re-use by all. Combined with Routes, it’ll provide a clean and easy way to setup web applications that can serve multiple forms of content from a single action.
Conferences
Pylons was represented at EuroPython by the other lead developer of Pylons, James Gardner. For those still trying to grasp WSGI, he gave a talk on WSGI and middleware that looks like it was quite interesting (I wasn’t there unfortunately.)
Other talks involving Pylons at EuroPython:
I’ll be at OSCON for those interested in chatting about Pylons, Python, or Python web frameworks later this month.
Pylons 0.9 Release
We’re currently wrapping up new features in 0.9, to make sure the resource mapping capability is present before a feature-freeze for release. Hopefully the release will be out within the next 2 weeks, if you haven’t checked out Pylons before I’d highly suggest taking a look at 0.9!
Posted in Code, Python, Routes
Posted by ben
Wed, 05 Apr 2006 04:16:11 GMT
Ah yes, time for another release. This time its Routes 1.3.1, nothing too big to see here, thus the minor version increase. However if you were using Routes with the prefix option its highly recommended that you upgrade as there were 2 bugs specifically when used with that option that have been fixed.
Also, for those that want all their URL’s to end in a /, there’s now a append_slash option that will ensure URL’s are generated exactly how you want them to be.
Enjoy!
Posted in Python, Routes
Posted by ben
Fri, 17 Feb 2006 21:17:27 GMT
I got Routes 1.2 out the door today, it’s a fairly important update for 2 reasons:
- A bug crept in with 1.1 using the default controller directory scanner. The scanner wasn’t properly retaining the directory prefix which causes mismatches when using controllers underneath a sub-directory.
- url_for can (and should) be used for all your URL needs, including static files
The second one is pretty important if you’re at all interested in creating portable web applications that can be used along-side other applications. While some frameworks provide generation of URL’s that lead to other web pages, hard-linking the other ‘static’ content will cause problems if you try and use the application under a different mount point.
Instead of using a url like /css/source.css it should instead be generated with url_for('/css/source.css'). This way Routes can ensure that if you’re running under a WSGI environment and there’s a SCRIPT_NAME present (this indicates the applications location), it will be pre-pended to your absolute URL’s. When used like this, additional keyword arguments passed in will be used as query args on the URL making url_for a handy way to create URL’s that are properly URL encoded.
Another useful feature that made it into 1.2 allows you to alias URL’s you might want to use throughout your web application, I call these static named routes. An example can be found in the named routes section of the Routes Manual.
Enjoy!
Posted in Code, Python, Routes
Posted by ben
Fri, 13 Jan 2006 22:21:01 GMT
I’ve released Routes 1.1 today after extending the unit tests for the new Groupings syntax and updating the docs.
The new syntax is quite powerful and will hopefully make everyone using Routes rather happy. While I’m not about to encourage anyone to use
URL’s with .html at the end, there’s plenty of times when you want extensions to mingle with dynamic parts. You can also get some useful abilities like being able to pull out the extension like so:
map.connect('archives/:category/:(section).:(format)', controller='archive', action='by_extension')
This makes it easy to toggle the response depending on the extension, and the regexp business is handled for you.
Integration Enhancements
An additional feature, suggested by a Routes user was to make integration easier in WSGI environments. Earlier, at the beginning of each request you would have to populate the Routes config with the host, protocol, and match result. Now, merely passing the WSGI environ to the Routes config object will run a match, and populate those attributes for you.
The Routes Mapper now can take a function that when called returns a list of valid controllers. If you want to use the directory scanner Routes comes with, all you need to do is pass in the directory you’d like to scan and Routes will scan it for you.
These two new integration improvements make it rather simple to integrate Routes, here’s a basic
WSGI app showing this off:
#myapp.py
from routes import *
map = Mapper(directory='/my/directory/of/controllers')
map.connect(':controller/:action/:id')
map.connect('home', '', controller='home', action='splash')
class WSGIApp(object):
def __init__(self, mapper=map):
self.mapper = mapper
def __call__(self, environ, start_response):
config = request_config()
config.mapper = self.mapper
config.environ = environ
if not config.mapper_dict:
start_response("404 Not Found", [("content-type","text/html")])
return ["No match"]
else:
start_response("200 OK", [("content-type","text/html")])
return ["Match with the following dict: %s" % str(config.mapper_dict)]
That’s it! If you’re not using WSGI, there’s been no backwards breakage so the old style of setting up all the attributes of the config will work fine as well.
So, now I have to figure out if there’s anything else Routes should possibly have… or is the only space for improvement at this point further optimization and perhaps usability improvements?
Posted in Code, Python, Routes
Posted by ben
Tue, 03 Jan 2006 17:35:43 GMT
The major feature I was waiting on for Routes 1.1 is for the most part done, mainly adding more unit tests for the new syntax now. As I mentioned previously when announcing Routes 1.0, this feature is the one quite a few people have been waiting for. The ability to split the route path somewhere other than on a /.
Here’s what a few Routes using the new feature look like:
map.connect(':category/:(page).html', controller='stories', action='view')
map.connect('feeds/:(section).:(extension)', controller='feeds', action='formatter')
map.connect('archives/:(year):(month):(day).html', controller='archives', action='view',
requirements=dict(year=r'\d{4}',month=r'\d{2}',day=r'\d{2}'))
The new section dividers, :(something) can be used side-by-side as the last example above shows, however in such cases each path part must have a rigid regexp requirement placed on it to ensure proper collection of the variables. Typically you will have some characters in between each dynamic part so this issue doesn’t arise.
I’ve retained full backward compatibility with the old syntax as well, if you don’t designate the dynamic part using the () modifier it will fall back to looking for the next / instead. So far, all the existing (and extensive) unit tests are passing, in addition to new tests I’ve been adding.
Routes generates a full regular expression for
URL matching based off the route path you give it. This makes it a great way to setup
URL routing with the power of regexp’s yet avoiding the hassle of writing a large complex regexp yourself. The other very powerful ability you gain is that Routes can turn the keywords back into the
URL, like so:
>>> url_for(controller='archives', action='view', year=2004, month=10, day=12)
'/archives/20041012.html'
>>> url_for(controller='feeds',action='formatter',section='computers',extension='xml')
'/feeds/computers.xml'
If you’re interested in giving it a spin, it can be checked out and installed easily from the svn repository using setuptools:
sudo easy_install -U http://routes.groovie.org/svn/branches/newsplit
Feedback / Suggestions / Bug Reports greatly appreciated.
Posted in Code, Python, Routes