[Python-3000] pep 3124 plans (original) (raw)
Jonathan LaCour jonathan-lists at cleverdevil.org
Thu Jul 19 22:00:53 CEST 2007
- Previous message: [Python-3000] pep 3124 plans
- Next message: [Python-3000] pep 3124 plans
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Guido van Rossum wrote:
FWIW, I think the Turbogears use you're thinking of is jsonify, a GF for converting arbitrary Python data into JSON (JavaScript Object Notation). But I'm not aware of it using any of the advanced features -- it seems to be using just the basic facility of overloading on a single argument type, which could be done with my own "overloading" example (see the Python subversion sandbox). At least that's what I got from skimming the docs: http://docs.turbogears.org/1.0/JsonifyDecorator . That article claims that TurboGears uses RuleDispatch extensively. I'd love to hear from them about how they use the advanced features.
There are several places in TurboGears that we use generic functions:
TurboJSON
TurboGears controllers work by returning dictionaries, which are then passed to template engines to generate and render responses. TurboJSON is a Buffet-compatible template plugin that jsonifies data that is returned from a TurboGears controller. The jsonify function is a generic function that is used to perform the serialization, and is commonly extended to provide custom JSON serialization in a cross-cutting way:
# TurboGears Controller
class PeopleController(controllers.Controller):
@expose('json')
def person(self, person_id):
person = Person.get(person_id)
return dict(person=person)
# generic function for JSONifying Person objects
@jsonify.when('isinstance(obj, Person)')
def jsonify_person(obj):
return dict(
name=person.name,
age=person.age,
birthdate=person.birthdate.strftime('%Y-%M-%D')
)
I use this feature heavily, and find it to be easy to understand once you get used to the concept of generic functions.
Of course, we don't restrict @jsonify.when() to isinstance checking. I've seen production code which checks the value of an object before jsonifying it, or which checks and attribute on the object to determine how it should be rendered in the JSON. For example if one of our users has a bunch of different contacts in a contact object, but she wants different JSON for contacts who are also leads, she can use predicate dispatch in the @jsonify.when decorator to do that...
Picking a Template Engine
TurboGears supports a variety of templating engines in a cross-framework way using a standard API called Buffet. TurboGears controllers can specify different templating engines and different templates for a controller method if they so desire, and we use generic functions to implement this on the backend so that you can regester multiple template options for rendering the same controller method.
class Root(controllers.RootController):
@expose(template='mako:path.to.mako.template.html')
def get_mako(self):
return dict(...)
@expose("actionflow.templates.tasks")
@expose("cheetah:actionflow.templates.tasktext",
accept_format="text/plain")
@expose("kid:actionflow.templates.taskfeed,
accept_format="rss")
@expose("json", accept_format = "text/javascript",
as_format="json")
def task(self):
return dict(...)
Rule dispatch gets used to check what format is requested (either in the headers, or explicitly via a tg_format parameter) and calls the correct rendering function in the correct way to turn the dict that's returned into what the client asked for. We're going to be improving this and making it even more powerful in TurboGears 2.0.
Validation and Error Handling
TurboGears has a built-in framework for validating parameters that are passed in over HTTP. This integrates with an underlying widget system which can be used to generate forms, called ToscaWidgets, that you can use to validate against. You can find good documentation and examples here: http://docs.turbogears.org/1.0/ErrorHandling
Here is an example:
import turbogears from turbogears import controllers, expose, validate, redirect from turbogears import exception_handler
class Root(controllers.RootController): def vh(self, tg_exceptions=None): return dict( handling_value=True, exception=str(tg_exceptions) )
def ih(self, tg_exceptions=None):
return dict(
handling_index=True,
exception=str(tg_exceptions)
)
@expose()
@exception_handler(vh, "isinstance(tg_exceptions, ValueError)")
@exception_handler(ih, "isinstance(tg_exceptions, IndexError)")
def exceptional(self, number=2):
number = int(number)
if number < 42:
raise IndexError("Number too Low!")
if number == 42:
raise IndexError("Wise guy, eh?")
if number > 100:
raise Exception("This number is exceptionally high!")
return dict(result="No errors!")
Lots of users are currently making use of this functionality in TurboGears, and it seems to be fairly well received. And again, you can use predicate dispatch to regoster different error_handlers for different kinds of errors.
I for one, as a committer on TurboGears, would absolutely love to see a good, solid generic function capability integrated into the standard library, and find PEP 3124 to completely cover my needs. There are certainly things in the PEP that I do not have a use for, but nothing in the PEP seems to be much of a stretch to me.
Just my 2 cents (or maybe 50 cents...)
-- Jonathan LaCour http://cleverdevil.org
- Previous message: [Python-3000] pep 3124 plans
- Next message: [Python-3000] pep 3124 plans
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]