Re: functools.wraps behaviour

2014-09-16 Thread ISE Development
Chris Angelico wrote:

> On Tue, Sep 16, 2014 at 9:15 AM, ISE Development 
> wrote:
>> @functools.wraps(func)
>> def wrapper(self):
>> func(self)
>> return wrapper
>>
>> try:
>> k.method(1)
>> except Exception as e:
>> print('exception:',e)
>>
>> The output (Python 3.3) is:
>>
>> Klass.method: 
>> k.method > 0x7f2d7c4570d0>>
>> exception: wrapper() takes 1 positional argument but 2 were given
>>
>> The first two lines are as expected, using the name of the decorated
>> function. However, the exception uses the name of the decorating wrapper
>> function.
> 
> In your wrapper, you're swallowing all arguments. That means you're
> consciously rejecting them, and passing none on. If you want a wrapper
> to let the wrapped function decide about arguments, try this:
> 
> def decorator(func):
> @functools.wraps(func)
> def wrapper(self, *args, **kwargs):
> func(self, args, kwargs)
> return wrapper
> 
> With that change, the error message reports that it's method() that's
> rejecting the args.
> 
> So yes, I'd say this is a feature; you can either let the wrapped
> function make the decision, or you can have the wrapping function deal
> with args.
> 
> ChrisA

Very good point. Hadn't thought about it that way - it makes sense now.

Thanks.

-- isedev
-- 
https://mail.python.org/mailman/listinfo/python-list


functools.wraps behaviour

2014-09-15 Thread ISE Development
The purpose of 'functools.wraps' is to make a decorated function look like 
the original function, i.e. such that the __name__, __module__, __doc__ 
attributes are the same as the wrapped function.

However, I've noticed inconsistent behaviour.

Given the following:

import functools

def decorator(func):
@functools.wraps(func)
def wrapper(self):
func(self)
return wrapper

class Klass:
@decorator
def method(self):
raise Exception('boom!')

print('Klass.method:',Klass.method)

k = Klass()
print('k.method',k.method)

try:
k.method(1)
except Exception as e:
print('exception:',e)

The output (Python 3.3) is:

Klass.method: 
k.method >
exception: wrapper() takes 1 positional argument but 2 were given

The first two lines are as expected, using the name of the decorated 
function. However, the exception uses the name of the decorating wrapper 
function.

Is this a bug in functools? Or is this a language feature? If so, is there a 
valid argument to change this behaviour?

-- isedev
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: __qualname__ in python 3.3

2014-09-07 Thread ISE Development
Antoine Pitrou wrote:
> Hi,
> 
> ISE Development  gmail.com> writes:
>> 'code' object 'function' object
>>   
>> co_name: test __qualname__: test
>> co_name: T__qualname__: T
>> co_name: method   __qualname__: test..T.method
>> 
>> The second call corresponds to the class definition and not the call to
>> the constructor (which is in fact a call to 'object.__init__', a C
>> function hence not traced as a 'call' event - I checked this by
>> disassembling the code object).
> 
> There's nothing wrong here. That's just the way things are implemented
> internally. This may change in the future without prior notice, so
> you shouldn't rely on it.
> 
> If you want to dig more, you have to look at how the hidden function ("T")
> works:
> 
>>>> def f():
> ...   class T: pass
> ...
>>>> f.__code__.co_consts
> (None, ", line 2>, 'T')
>>>> dis.dis(f.__code__.co_consts[1])
>   2   0 LOAD_NAME0 (__name__)
>   3 STORE_NAME   1 (__module__)
>   6 LOAD_CONST   0 ('f..T')
>   9 STORE_NAME   2 (__qualname__)
>  12 LOAD_CONST   1 (None)
>  15 RETURN_VALUE
> 
> Regards
> 
> Antoine.

Ok, I accept it's implementation specific. That's fair enough.

Yet wouldn't it make more sense to have the 'T' function '__qualname__' 
attribute refer to the defining context, i.e. in the present case, 
'test..T' (much along the lines of the actual method qualified 
names, e.g. 'test..T.__init__', and identical to the qualified name 
of the actual object if returned by the defining function - see Peter Otten 
reply)?

The rationale is that a properly qualified name is easier to interpret than 
the current unqualified one. To properly identify the 'T' function if a 
'class T' is defined in more than enclosing function requires some 
additional complex (and hence error prone) logic as things stand: one has to 
examine the previous stack frame.

Effectively, I am not saying the current behaviour is wrong, simply that it 
is inconsistent and could be improved. In that context, is it worth an 
enhancement request?

-- isedev
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: __qualname__ in python 3.3

2014-09-06 Thread ISE Development
Peter Otten wrote:

> ISE Development wrote:
> 
>> Hi,
>> 
>> When a class is defined within a function, the class generation
>> function's '__qualname__' attrbute is not qualified a name.
>> 
>> For instance:
>> 
>> def test():
>> class T:
>> def method(self):
>> pass
>> t = T()
>> t.method()
>> 
>> When tracing a call to 'test()' using 'sys.settrace()', extracting the
>> 'code' object from the frames of 'call' events and matching it to a
>> 'function' object (using 'gc.get_referrers()') results in the following:
>> 
>> 'code' object 'function' object
>>   
>> co_name: test __qualname__: test
>> co_name: T__qualname__: T
>> co_name: method   __qualname__: test..T.method
>> 
>> The second call corresponds to the class definition and not the call to
>> the constructor (which is in fact a call to 'object.__init__', a C
>> function hence not traced as a 'call' event - I checked this by
>> disassembling the code object).
>> 
>> I would expect the second call's '__qualname__' to be 'test..T'.
>> Can this be considered a bug? If so, I'll open one.
> 
> I don't understand what you are doing, so I tried to reproduce the
> unqualified class name in 3.4 with the simpler approach of returning T:
> 
> Python 3.4.0 (default, Apr 11 2014, 13:05:11)
> [GCC 4.8.2] on linux
> Type "help", "copyright", "credits" or "license" for more information.
>>>> def test():
> ... class T:
> ... def method(self): pass
> ... return T
> ...
>>>> T = test()
>>>> T.__qualname__
> 'test..T'
>>>> T.method.__qualname__
> 'test..T.method'
> 
> If you do it that way with 3.3 (I don't have it handy) do you still see
> T instead of test..T?

Python 3.3 behaves in the same way in that case.

This following shows the behaviour I am referring to:

import gc
import sys
import inspect

def global_trace(frame,event,arg):
if event == 'call':
code = frame.f_code
funcs = [obj for obj in gc.get_referrers(code)
 if inspect.isfunction(obj)]
if len(funcs) == 1:
f = funcs[0]
print(f.__qualname__)

def test():
class C:
def method(self):
self
c = C()
c.method()

sys.settrace(global_trace)
try:
test()
finally:
sys.settrace(None)

which produces:

test
C
test..C.method

-- 
https://mail.python.org/mailman/listinfo/python-list


__qualname__ in python 3.3

2014-09-06 Thread ISE Development
Hi,

When a class is defined within a function, the class generation function's 
'__qualname__' attrbute is not qualified a name.

For instance:

def test():
class T:
def method(self):
pass
t = T()
t.method()

When tracing a call to 'test()' using 'sys.settrace()', extracting the 
'code' object from the frames of 'call' events and matching it to a 
'function' object (using 'gc.get_referrers()') results in the following:

'code' object 'function' object
  
co_name: test __qualname__: test
co_name: T__qualname__: T
co_name: method   __qualname__: test..T.method

The second call corresponds to the class definition and not the call to the 
constructor (which is in fact a call to 'object.__init__', a C function 
hence not traced as a 'call' event - I checked this by disassembling the 
code object).

I would expect the second call's '__qualname__' to be 'test..T'. Can 
this be considered a bug? If so, I'll open one.

-- isedev
-- 
https://mail.python.org/mailman/listinfo/python-list


Multiple ways to access attributes

2013-02-10 Thread ISE Development
Is it considered acceptable practice (e.g. not confusing, not 
surprising or not Pythonic) to allow multiple ways to access 
the same attributes?

For example, supposing I am providing access to external 
devices, that these parameters may vary slightly between 
devices (e.g. different models, etc...) and that the device 
may be queried for parameter meta-data (such as name, data 
type, data value constraints), would the following API be 
acceptable (in the sense alluded to above)? Or would it be 
generally considered a distortion of Python idealogy (e.g. 
like PERL's 'more than one way to do it' approach)?

class option
  -> represents a single option
  -> two attributes: info  (the parameter meta-data)
 value (the parameter getsetter)  

class options:
  -> represents the device parameter interface
  -> provides the following API:

iter(options)
  -> iterable through all parameter meta-data

options[0]
  -> access parameter 0 meta-data
  -> key is integer

options['foo']
  -> access parameter 'foo' (returns an 'option' object)
  -> key is basestring
  -> useful when processing the parameters generically
 
options.foo
  -> same as options['foo']
  -> useful for well-known, often used parameters 
 (really just short-hand for the user)

options.keys()
  -> iterator through option names (a specific meta-data
 field)

options.values()
  -> iterator through option values

options.items()
  -> iterator through (name,value) tuples


-- isedev
-- 
http://mail.python.org/mailman/listinfo/python-list