Re: functools.wraps behaviour
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
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
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
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
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
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