This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Decorator class with optional arguments and a __call__ method gets not called when there are no arguments
Type: behavior Stage:
Components: Interpreter Core Versions: Python 3.2
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: benjamin.peterson, carsten.klein, rhettinger, santoso.wijaya
Priority: normal Keywords:

Created on 2011-04-06 21:46 by carsten.klein, last changed 2022-04-11 14:57 by admin. This issue is now closed.

Messages (8)
msg133171 - (view) Author: Carsten Klein (carsten.klein) Date: 2011-04-06 21:46
Scenario:

class deco(object):
  def __init__(self, optional = False):
    self._optional = optional

  def __call__(self, decorated):
    decorated.optional = self._optional
    return decorated

@deco
class y(object):
  pass

will fail decorating the class since y is passed in as the first parameter to deco.__init__, and deco.__call__ will never be called.

@deco(optional = True)
class y(object):
  pass

will succeed.

I wonder why there is a distinction between decorator class w/ arguments and decorator class w/o arguments?

Guessing that one would like to have a decorator class decorating another class and also acting as a kind of proxy by implementing a __call__ method, this could also be achieved by further indirection, provided that it will not break existing code.

A working alternative would be a decorator function like this:

def deco(_decorated = None, optional = False):
  def _wrapped(decorated):
    decorated.optional = optional
    return decorated
  if _decorated is not None:
    return _wrapped(decorated)
  return _wrapped

Is there a chance that the behavior of the decorator class will be fixed in a future release?

Expected behavior for the decorator class would be:

if formal parameter list has optional parameters and actual parameter list is empty and there are no formal mandatory parameters:
   if decorator class is callable:
      deco = decorator class ()
      decor.__call__(decorated)
   else:
      fall back to old behaviour, requiring the decorator class __init__ method to have one mandatory parameter
else:
  deco = decorator class(actual parameters...)
  deco.__call__(decorated)

TIA
msg133172 - (view) Author: Carsten Klein (carsten.klein) Date: 2011-04-06 21:47
will fail decorating the class since y...

actually means that instead of an instance of class y, an instance of the decorator class will be returned, with y being lost along the way...
msg133175 - (view) Author: Santoso Wijaya (santoso.wijaya) * Date: 2011-04-06 22:18
I wonder if this is a valid use-case to begin with. The semantic of a decorator, as I understand it, is very straightforward in that this:

    @foo
    @bar
    class A:
        pass

is equivalent to this:

    class A:
        pass
    A = foo(bar(A))

In your example, the equivalent statement is:

    y = deco(y)

Which gives you the somewhat surprising result, but it is as the language is designed.
msg133177 - (view) Author: Carsten Klein (carsten.klein) Date: 2011-04-06 22:31
I think it is, actually, considering

    @foo
    @bar
    class A:
        pass

with foo and bar being both decorator classes, the chained call

foo(bar(A))

will return and instance of foo instead of A


With decorator classes you need to actually do this:

foo()(bar()(A))

which will give you the "required" result, an instance of class A.
msg133178 - (view) Author: Benjamin Peterson (benjamin.peterson) * (Python committer) Date: 2011-04-06 22:34
This is expected; it's the same with functions. Just do @deco().
msg133179 - (view) Author: Carsten Klein (carsten.klein) Date: 2011-04-06 22:38
Ok, looks like a valid work around to me.

However, IMO it is not the same as with decorator functions.
These will be called and will return the correct result,
whereas the decorator class will instead return an instance
of self instead of being called when no parameters are given.
msg133180 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2011-04-06 22:41
I concur with Benjamin.
msg133181 - (view) Author: Benjamin Peterson (benjamin.peterson) * (Python committer) Date: 2011-04-06 22:47
2011/4/6 Carsten Klein <report@bugs.python.org>:
>
> Carsten Klein <carsten.klein@axn-software.de> added the comment:
>
> Ok, looks like a valid work around to me.

It's not a workaround. It's the way decorators work.
History
Date User Action Args
2022-04-11 14:57:15adminsetgithub: 55997
2011-04-06 22:47:43benjamin.petersonsetmessages: + msg133181
2011-04-06 22:41:31rhettingersetnosy: + rhettinger
messages: + msg133180
2011-04-06 22:38:14carsten.kleinsetmessages: + msg133179
2011-04-06 22:34:16benjamin.petersonsetstatus: open -> closed

nosy: + benjamin.peterson
messages: + msg133178

resolution: not a bug
2011-04-06 22:31:48carsten.kleinsetmessages: + msg133177
2011-04-06 22:18:11santoso.wijayasetnosy: + santoso.wijaya
messages: + msg133175
2011-04-06 21:47:57carsten.kleinsetmessages: + msg133172
2011-04-06 21:46:54carsten.kleincreate