Using a decorator to *remove* parameters from a call

Michel Albert exhuma at gmail.com
Mon Apr 13 08:33:16 EDT 2009


On Apr 13, 12:52 pm, Jon Clements <jon... at googlemail.com> wrote:
> On 13 Apr, 11:11, Michel Albert <exh... at gmail.com> wrote:
>
>
>
> > A small foreword: This might look like a cherrypy-oriented post, and
> > should therefore go to the cherrypy group, but if you read to the end,
> > you'll see it's a more basic python problem, with cherrypy only as an
> > example. ;)
>
> > From the decorator PEP (318) I get it that you can /add/ parameters to
> > a call. Say you do something like this:
>
> > @mydeco( foo="bar" )
> > def myfunc( hello ):
> >    print foo, foo
>
> > However, this is not what I would like to do. I would like to take
> > away one or more attributes using a decorator. My goal is to
> > centralize the logic associated with that parameter. In my particular
> > example, I am writing a cherrypy application (more specifically,
> > turbogears1) and I would like all controller method to accept a "lang"
> > and a "skin" attribute. As a simple, but hands-on example, imagine
> > code like this (http://python.pastebin.com/f25f2429b):
>
> > class MyController(Controller):
>
> >     @expose()
> >     def index(self, skin="default", lang="en"):
> >         set_skin( skin )
> >         set_language( lang )
> >         return "Hello skinned world"
>
> >     @expose()
> >     def foo(self, skin="default", lang="en"):
> >         set_skin( skin )
> >         set_language( lang )
> >         return "bar"
>
> > This becomes cumbersome however for a large application with many
> > controllers and methods. Always adding the parameters to the methods
> > and function calls into the method bodies looks way to repetitive for
> > my taste.
>
> > Currently I solve this by using a cherrypy filter which removes those
> > parameters from the HTTP-Request and puts them into the session
> > object. This looked wrong to me though right from the start. Also, the
> > cherrypy docs advise against the use of "filters" for application
> > logic. But this looks pretty much like that to me.... somewhat. Worse
> > yet, filters are not supported in Turbogears2 any longer. Which makes
> > porting my app not straight-forward, as I have to port my filters and
> > write WSGI middleware instead.
>
> > I would prefer a syntax like this (http://python.pastebin.com/
> > f462bc29c)
>
> > class MyController(Controller):
>
> >     @expose()
> >     @skinnable
> >     @translatable
> >     def index(self):
> >         return "Hello skinned world"
>
> >     @expose()
> >     @skinnable
> >     @translatable
> >     def foo(self):
> >         return "bar"
>
> > This, to me has several advantages: The logic is "encapsulated" in the
> > application code itself. I do not need to rely on framework specific
> > features (be it cherrypy or wsgi). This would make the application
> > more self-contained and hence more future proof.
>
> > But right here lies my problem. If you are not familiar with CherryPy,
> > let me digress for a tiny bit to give you the necessary backgroud:
>
> > Inside a controller, an "exposed" method is reachable via HTTP
> > requests. It's possible to force request parameters. If that is the
> > case (as in my case it is), then the call will only be successful, if
> > all parameters received a vaule and no unknown parameters have been
> > passed. Assume the following signature:
>
> > def index(self)
>
> > This will successfully return an HTTP Response when the client
> > requested the resource on the URL "/index". If the client adds a query
> > string, say "/index?lang=en" the call will *fail* as this parameter is
> > unkown. For this to work the signature must read:
>
> > def index(self, lang)
>
> > or
>
> > def index(self, lang="en")
>
> > Right.... end of digression, back to topic:
>
> Haven't finished coffee yet, plus it's a bank holiday! [/excuse]
>
> Can't that be changed to def index(self, **kwdargs) -- then your
> decorators can operate something like set_language(kwdargs.get('lang',
> 'en')) and then delete the key....

Sure. It could work like that. But that, in my case, would mean that
anyone could pass any parameters into that method. That should not
make any problems, but I prefer enforcing only the parameters I expect
(and /only/ those) to be passed into the method. If only for my own
sanity ;)

> Anyway, back to more coffee!

enjoy!

> > My ultimate question is: Is it possible to write a decorator that
> > removes a parameter from a call and return a function without that
> > parameter? Something along the lines (http://python.pastebin.com/
> > f257877cd):
>
> > def translatable(f):
> >     "Note that this code is only an example and will not work!"
> >     lang = f.__get_parameter_value__("lang")
> >     f.__remove_parameter__("lang")
> >     do_something_with_lang(lang)
> >     return f
>
> > I am aware, that this depends on *when* the decorator call is
> > executed. If its called whenever the decorated function is called,
> > then there should be some way to handle this. If it's called at
> > "compile-time", then I suppose it's not going to be possible.
>
> > A usage scenario would be this:
>
> > # --- definition ---------------
> > @translatable
> > def index(self):
> >    return _("Hello", lang)
>
> > # --- call ----------------------
> > obj.index( lang="de")
> > obj.index()
>
> > As you can see, the method definition does not contain the "lang"
> > parameter in the signature, but I want to be able to (optionally) set
> > it during a call.
>
> > I hope I made my ideas clear enough. I would be very positively
> > surprised if this worked. It would make my application code much
> > easier to read, understand and follow. The filters currently do a lot
> > of magic "behind-the-scenes" and if somebody else needs to jump in and
> > code on that application they will surely ask themselves "where the
> > ****** is that ***** lang variable set?" :) And I'd like to make it as
> > easy as possible for everyone :)
>
> > Or, maybe you even have a better solution in mind? I'm open for
> > suggestions.
>
>




More information about the Python-list mailing list