----- Original Message -----

> From: Ian Kelly <ian.g.ke...@gmail.com>
> To: Python <python-list@python.org>
> Cc: 
> Sent: Saturday, January 10, 2015 7:30 AM
> Subject: Re: class-based class decorator
> 
> On Fri, Jan 9, 2015 at 2:26 PM, Albert-Jan Roskam
> 
> <fo...@yahoo.com.dmarc.invalid> wrote:
>> 
>> 
>>  Hi,
>> 
>>  I am trying to write a class decorator that checks whether deprecated 
> parameters with non-default
>> 
>>  arguments are used. More complete code is here: 

> http://pastebin.com/ZqnMis6M.

Oops, the period was included in the url. It was 
http://pastebin.com/ZqnMis6M

>> In the code below,>>  how should I modify __call__ such that f.bar(old="oh 
>> no") prints 
> "hello world"?
>>  I thought it would be a fun way to play with the inspect module, but my 
> head is numb now and I am thirsty for a beer!
> 
> You can use inspect.getargspec to look up the default argument values
> for the target function. You can use inspect.getcallargs to map your
> *args and **kwargs to a dictionary of argument names and values. Since
> the target function will be the unbound __init__ method, you'll also
> want to pass in a dummy value like None for the self argument. Then
> you just look up each of the deprecated names in the result and flag
> any where the value doesn't match the default.
> 
> If you were using Python 3.3+, then I would recommend using
> inspect.signature instead, which is a lot more powerful. But your code
> appears to be Python 2, so we work with what we've got.


Hi Ian, Chris,

Thanks for your replies. I changed it into a regular decorator (not a class 
decorator). It would have been even nicer if I only needed to specify it once 
per class, but, well, in my case this hardly matters. The code below works as 
intended. One problem (not specific to the code): the decorator destroys the 
signature: it always becomes "*args, **kwargs"). This is annoying with help(), 
but what worries me most is that my Sphinx documentation is also affected. The 
information about the defaults gets lost (the parameters are decribed in the 
docstring, but not the defaults). That kind of sucks. Is there a builtin way 
around this (in other words: I am aware of this package: 
https://pypi.python.org/pypi/decorator). I am hoping to get the code working on 
Python 2.7 and 3.3 and up.

import functools
import inspect
import warnings

warnings.simplefilter("always")

class check_deprecated_args(object):

    def __init__(self, deprecated_params, msg=None):
        self.deprecated_params = deprecated_params
        self.msg = msg

    def __call__(self, func):
        @functools.wraps(func)
        def inner(*args, **kwargs):
            argspec = inspect.getargspec(func)
            default_signature = dict(zip(argspec.args[1:], argspec.defaults))
            callargs = inspect.getcallargs(func, *args, **kwargs)
            deprecated_calls = [(p, a) for p, a in callargs.items() if 
                                 p in self.deprecated_params and 
                                 a != default_signature[p]]
            for (param, arg) in deprecated_calls:
                msg = "you're using obsolete parameters in %s: [%s:%s]" 
                msg = msg % (func.__name__, param, arg)
                msg = msg + " " + self.msg if self.msg else msg 
                warnings.warn(msg, DeprecationWarning, stacklevel=2)
            return func(*args, **kwargs)
        functools.update_wrapper(inner, func)
        return inner

if __name__ == "__main__":
    class Foo(object):

        @check_deprecated_args(["old", "older"], "use 'brand_new' param 
instead")
        def __init__(self, old="old", older="ancient"):
            print "hello"

        @check_deprecated_args(deprecated_params=["old", "older"])
        def bar(self, old="default"):
            print "world"    

    f = Foo(old="old", older="dino era")
    f.bar("gnarly")

    help(f)  # now the signature is *args, **kwargs, which makes my Sphinx 
documentation less readable!

Best wishes,
Albert-Jan
-- 
https://mail.python.org/mailman/listinfo/python-list

Reply via email to