decorator to fetch arguments from global objects

Wolfgang Maier wolfgang.maier at biologie.uni-freiburg.de
Tue Jun 18 10:01:58 EDT 2013


andrea crotti <andrea.crotti.0 <at> gmail.com> writes:

> 
> 2013/6/18 Wolfgang Maier <wolfgang.maier <at> biologie.uni-freiburg.de>
> 
> 
> andrea crotti <andrea.crotti.0 <at> gmail.com> writes:
> >
> >
> > Using a CouchDB server we have a different database object potentially for
> every request.
> >
> > We already set that db in the request object to make it easy to pass it
> around form our django app, however it would be nice if I could set it once
> in the API and automatically fetch it from there.
> >
> > Basically I have something like
> >
> > class Entity:
> >      def save_doc(db)
> >         ...
> >
> > I would like basically to decorate this function in such a way that:
> > - if I pass a db object use it
> > - if I don't pass it in try to fetch it from a global object
> > - if both don't exist raise an exception
> >
> > Now it kinda of works already with the decorator below.
> > The problem is that the argument is positional so I end up maybe passing
> it twice.
> > So I have to enforce that 'db' if there is passed as first argument..
> >
> > It would be a lot easier removing the db from the arguments but then it
> would look too magic and I didn't want to change the signature.. any other
> advice?
> >
> > def with_optional_db(func):
> >     """Decorator that sets the database to the global current one if
> >     not passed in or if passed in and None
> >     """
> >      <at> wraps(func)>     def _with_optional_db(*args, **kwargs):
> >         func_args = func.func_code.co_varnames
> >         db = None
> >         # if it's defined in the first elements it needs to be
> >         # assigned to *args, otherwise to kwargs
> >         if 'db' in func_args:
> >             assert 'db' == func_args[0], "Needs to be the first defined"
> >         else:
> >             db = kwargs.get('db', None)
> >
> >         if db is None:
> >             kwargs['db'] = get_current_db()
> >
> >         assert kwargs['db'] is not None, "Can't have a not defined database"
> >         ret = func(*args, **kwargs)
> >         return ret
> >
> >     return _with_optional_db
> >
> I'm not sure, whether your code would work. I get the logic for the db in
> kwargs case, but why are you checking whether db is in func_args? Isn't the
> real question whether it's in args ?? In general, I don't understand why you
> want to use .func_code.co_varnames here. You know how you defined your
> function (or rather method):class Entity:
>     def save_doc(db):
>         ...
> Maybe I misunderstood the problem?Wolfgang
> --http://mail.python.org/mailman/listinfo/python-list
> 
> Well the point is that I could allow someone to not use "db" as argument
of the function if he only wants to use the global db object..
> 
> Or at least I want to check that it's the first argument and not in
another position, just as a sanity check.
> 
> 
> 
> I might drop some magic and make it a bit simpler though, even the default
argument DEFAULT_DB could be actually good, and I would not even need the
decorator at that point..
> 
> 
Ok, if you really just that you want to have an optional argument then
providing a default argument for db would certainly be the standard and most
readable solution. Of course, if you don't want to change your method and
you would like to keep db as first argument that's not an option (except if
you make all other arguments optional too). Maybe it would help if you could
provide the full parameter list for your method.
Still, from your description I think your current code is wrong. Have you
tried it without passing in db as a keyword argument? I guess in that case
your code just checks that the first parameter in your save_doc method is
called db, which it always is, then executes your kwargs['db'] =
get_current_db() assignment, then it will try to pass db twice (once as
positional argument and once in kwargs) and still fail.
The problem is that with .func_code.co_varnames you aren't checking whether
db is passed as first argument, but whether your method defines it as the
first parameter. To see what's getting passed you have to check args!
Best,
Wolfgang




More information about the Python-list mailing list