Как сохранить строки справки одинаковыми при применении декораторов? - PullRequest
2 голосов
/ 02 ноября 2009

Как сохранить строки справки в функциях, которые будут видны после применения декоратора?

Сейчас строка документа (частично) заменена внутренней функцией декоратора.

def deco(fn):
    def x(*args, **kwargs):
        return fn(*args, **kwargs)
    x.func_doc = fn.func_doc
    x.func_name = fn.func_name
    return x

@deco
def y(a, b):
    """This is Y"""
    pass

def z(c, d):
    """This is Z"""
    pass

help(y) # 1
help(z) # 2

В функции Y обязательные аргументы не отображаются в справке. Пользователь может предположить, что он принимает любые аргументы, а на самом деле это не так.

y(*args, **kwargs) <= y(a, b) is desired
    This is Y

z(c, d)
    This is Z

Я часто использую help() и dir(), поскольку это быстрее, чем руководства в формате pdf, и я хочу создавать надежные строки документов для моей библиотеки и инструментов, но это является препятствием.

Ответы [ 2 ]

5 голосов
/ 03 ноября 2009

дает взглянуть модулю decorator . я верю, что это именно то, что вы хотите.

In [1]: from decorator import decorator
In [2]: @decorator
   ...: def say_hello(f, *args, **kwargs):
   ...:     print "Hello!"
   ...:     return f(*args, **kwargs)
   ...: 
In [3]: @say_hello
   ...: def double(x):
   ...:     return 2*x
   ...: 

и информация говорит "двойной (х)" в нем.

1 голос
/ 03 ноября 2009

То, что вы запрашиваете, очень трудно сделать «должным образом», потому что help получает сигнатуру функции от inspect.getargspec, которая, в свою очередь, получает ее от самоанализа, который нельзя обмануть напрямую - сделать это «правильно» означало бы создание нового функционального объекта на лету (вместо простой функции-оболочки) с правильными именами и числами аргументов (и значениями по умолчанию). Другими словами, требуется чрезвычайно сложный, продвинутый взлом черного байт-кода.

Я думаю, что это может быть проще сделать с помощью обезьяньего патча (никогда не приятная перспектива, но иногда единственный способ выполнить задачи по настройке, которые в противном случае настолько сложны, что оказывается почти невозможным, например, тот, который вам нужен) - заменить real inspect.getargspec с вашей собственной функцией-аналогом, которая использует таблицу обратного просмотра (отображение генерируемых вами функций-оболочек в аргументы-аргументы упакованных функций и в противном случае делегирование в реальную вещь).

import functools
import inspect

realgas = inspect.getargspec

lookaside = dict()

def fakegas(f):
    if f in lookaside:
        return lookaside[f]
    return realgas(f)

inspect.getargspec = fakegas

def deco(fn):
    @functools.wraps(fn)
    def x(*args, **kwargs):
        return fn(*args, **kwargs)
    lookaside[x] = realgas(fn)
    return x

@deco
def x(a, b=23):
  """Some doc for x."""
  return a + b

help(x)

При необходимости печатается:

Help on function x in module __main__:

x(a, b=23)
    Some doc for x.
(END)
...