My question is: What is happening here between jupyter and functools.wraps that produces the strangest of effects described below? How should I write my decorator "correctly" (besides the solution I show at the end) to avoid such effects?
The double_up_as_factory decorator below is meant to transform other decorator functions so that they can be used both as parameter-less decorators (e.g. @decorator) or "factories" (e.g. @decorator(...)).
from functools import partial, wraps
def _double_up_as_factory(wrapped=None, *args, __decorator_func=None, **kwargs):
if wrapped is None: # then we want a factory
return partial(__decorator_func, **kwargs)
else:
return __decorator_func(wrapped, *args, **kwargs)
def double_up_as_factory(decorator_func):
return wraps(decorator_func)(
partial(_double_up_as_factory, __decorator_func=decorator_func)
)
The above is a stripped down version of my actual function, but it works. Below is the minimum to produce the error, but not representative of an actual use (I can post that, but don't think it's necessary).
from i2.deco import double_up_as_factory
def foo(f=None, *, x=1):
return f(x)
# This creates no problem (as long as the block below is commented out)
bar = double_up_as_factory(foo)
# Using double_up_as_factory with @ creates the problem
@double_up_as_factory
def same_foo(f=None, *, x):
return f(x)
To test, in a notebook, do:
from PACKAGE import MODULE_WHERE_YOU_PUT_THE_ABOVE_CODE as m
m.[TRY_TAB_COMPLETING]
The first time I (after a kernel restart) I get the expected auto-suggestions, but the second time I try, I get nothing.
This problem
- only happens in notebooks (browser-based jupyter access with Mac Chrome); not in VSCode or PyCharm,
- and only if
double_up_as_factoryis used the@-way, - and only on the second tab-completion.
And the plot thickens: If I copy the code for wraps from functools within the module I'm defining double_up_as_factory in:
from functools import WRAPPER_ASSIGNMENTS, WRAPPER_UPDATES, update_wrapper
def wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES):
return partial(update_wrapper, wrapped=wrapped,
assigned=assigned, updated=updated)
the problem disappears.
I'm stumped as to why such minor differences would affect jupyter's tab-completion abilities!