Care to provide some technical discourse here? Aside from losing the
neat and evocative @decorator syntax, the above is simple and overt.
What part of "Ew ew ew ew" was not technical enough for you? Would it
help if I add a few extra "ew"s?
*wink*
But seriously, I don't like doubling the number of names in the namespace
just for the sake of white-box testing. That means you have to somehow
document each and every one of the private functions that they aren't for
using, just for testing.
For the avoidance of doubt, I understand you flagged them as private. But
even for private use within the module, they're not supposed to be used.
They are only for testing. So now you have to have a naming convention
and/or documentation to ensure that they aren't used internally.
If there really is a good use-case for using both the decorated and
undecorated version of the function (whether internally, or as part of
the public API), then I'm completely with you. We don't have to use
decorator syntax if we have good reason to keep the wrapped and unwrapped
functions separate, just bind them to separate names.
I also like Terry Reedy's suggestion of having the decorator
automatically add the unwrapped function to the wrapped function as an
attribute:
def decorate(func):
@functools.wraps(func)
def inner(arg):
blah blah
inner._unwrapped = func # make it public if you prefer
return inner
which makes it all nice and clean and above board. (I seem to recall a
proposal to have functools.wraps do this automatically...)
I would much rather do something like this:
def undecorate(f):
"""Return the undecorated inner function from function f.""" return
f.func_closure[0].cell_contents
Whereas this feels like black magic. Is this portable to any decorated
function? If so, I'd have hoped it was in the stdlib. If not: black
magic.
Not every one-line function needs to be in the std lib
To go into the std lib, it would need to be a tad more bullet-proof. For
instance, it should have better error checking for the case where
func_closure is None, rather than just raise the cryptic error message:
TypeError: 'NoneType' object is not subscriptable
If would also need to deal with arbitrary closures, where item 0 is not
necessarily a function, or where there might be multiple functions, or no
functions at all.
But for purely internal use within a test suite, we can afford to be a
little more ad hoc and just deal with those cases when and if they occur.
Since we're white-box testing, we presumably know which functions are
decorated and which ones aren't (we can read the source code!), and will
only call undecorate on those which are decorated.
All lovely, provided you can convince me that undecorate() is robust.
(And if you can, I'll certainly be filing it away in my funcutils module
for later use.)
It needs error handling. It assumes that the closure only references a
single object, namely the function being wrapped. In that sense, it's not
ready for production as a public utility function. But as a private
function for use only in testing, under controlled conditions, I think it
is robust enough, it works in CPython 2.5 through 2.7 and IronPython 2.6.
In Python 3.x, you have to change func_closure to __closure__, but
otherwise it works in 3.2 and 3.3. Jython 2.5 seems to choke on the
decorator syntax:
File "<stdin>", line 1
@decorate
^
SyntaxError: mismatched input '<EOF>' expecting CLASS
which surely is a bug in Jython. If I apply the decorator manually, it
works.