0

When I apply my own decorator (the details are not important, but see below) or other decorators such as cached, from cachetools, my Sphinx-generated docs do not show argument names in their signatures.

For example, the documentation for

@cached()
def some_func(argA, argB=None):
    ...

@require_unicode('third')
def another_func(first, second=None, third=None):
...

will read, generically

some_func(*args, **kwargs)

another_func(*args, **kwargs)

rather than, informatively, as

some_func(argA, argB=None)

another_func(first, second=None, third=None)

How can I remedy this so that my argument names appear in my Sphinx documentation? I understand that this is a known issue, and, since I know the names of the decorators I use, I've thought of just making them into no-ops, in my conf.py but can't figure out how do to that.

For example, something like this seems promising, but I'm at a loss how to get it working. I can put it before the my definition above, but can't see how to get it working for cached.


My decorator, for reference. Note that at least this generates docs (which it wouldn't if I didn't use wraps):

from functools import wraps

def require_unicode(*given_arg_names):
    def check_types(_func_):
        @wraps(_func_)
        def modified(*args, **kwargs):
            arg_names = list(_func_.func_code.co_varnames[:_func_.func_code.co_argcount])
            if len(given_arg_names) == 0:
                raise TypeError('No arguments provided to require_unicode decorator.')
                #unicode_arg_names = arg_names
            else:
                unicode_arg_names = given_arg_names
            for unicode_arg_name in unicode_arg_names:
                try:
                    arg_index = arg_names.index(unicode_arg_name)
                    if len(args) > arg_index:
                        arg = args[arg_index]
                    elif unicode_arg_name in kwargs:
                        arg = kwargs[unicode_arg_name]
                    else:
                    if not isinstance(arg, unicode):
                        raise TypeError("Parameter '{}' should be Unicode".format(unicode_arg_name))
                except ValueError:
                    raise NameError(unicode_arg_name)
            return _func_(*args, **kwargs)
        return modified
    return check_types
Community
  • 1
  • 1
orome
  • 45,163
  • 57
  • 202
  • 418
  • I don't think you can fix that, because the signature *is* now the generic decorator signature. Are you not documenting the parameters too? – jonrsharpe Nov 28 '15 at 23:11
  • Something like [this](http://stackoverflow.com/a/28371786/656912) seems promising, but I'm at a loss how to get it working. I can put it before the my definition above. but can't see how to get it working for `cached`. – orome Nov 28 '15 at 23:11
  • You'll have to see if `cached` is using `wraps` and monkey-patch it there, too. – jonrsharpe Nov 28 '15 at 23:12
  • @jonrsharpe: It's generic only if I let things get that far. If I can intercept the decorators somehow, when documentation is being generated, I can fix the problem (see previous comment). – orome Nov 28 '15 at 23:12
  • @jonrsharpe: That's the question. Where's "there"? – orome Nov 28 '15 at 23:13
  • Then could you edit the question, such that that's what it says? – jonrsharpe Nov 28 '15 at 23:13

1 Answers1

2

Looking at the source code for cached, it doesn't use functools.wraps, so your current monkey patching won't succeed there. It uses either functools.update_wrapper or its own version, aliased to _update_wrapper, so you'll need to patch out that. If you're using any other libraries with their own ways of implementing wrapping, you'll need to investigate each one accordingly.

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
  • Yes, just saw the same thing. Can you help with (a) what the patch to `cached` should look like and (b) where to place my patch for it (so that it affects only document builds). I can get the patch for my decorator working by putting it before its definition (and conditionally executing with a flag I set in `conf.py`). – orome Nov 28 '15 at 23:20
  • @raxacoricofallapatorius why don't you do it as the linked question does, patching `wraps` out in `conf.py` so that you don't have to let that documentation implementation detail bleed into your actual code? `cachetools._update_wrapper` can then be patched trivially with e.g. `lambda wrapper, func: func` to no-op the wrapping. Or patch Sphinx to use `__wrapped__`, as the second answer suggests. – jonrsharpe Nov 28 '15 at 23:24
  • Actually, that part of the answer doesn't work. Putting the patch right before my definition works, but putting it in `conf.py` has no effect. – orome Nov 28 '15 at 23:34
  • So where I need help is figuring out where it goes. Your suggestion to patch Sphinx to use `__wrapped__` may also work (it might even be better, though I have less idea how to pursue that!). – orome Nov 28 '15 at 23:42
  • @raxacoricofallapatorius I think you need to spend more time doing research and working on this to narrow down exactly what your question is. – jonrsharpe Nov 29 '15 at 08:30