📜 ⬆️ ⬇️

How to use decorators and documentation in Python

(This material is for novice users.)

Decorators in Python conflict with the help documentation function — if the decorator is used, the output of the help function changes. To see, we write a simple decorator:

  1. def deco(fn): def z( * args, ** kwargs): return fn( * args, ** kwargs) return z
  2. def deco(fn): def z( * args, ** kwargs): return fn( * args, ** kwargs) return z
  3. def deco(fn): def z( * args, ** kwargs): return fn( * args, ** kwargs) return z
  4. def deco(fn): def z( * args, ** kwargs): return fn( * args, ** kwargs) return z


')
And 2 functions, with and without decorator:

  1. def x (c, d):
  2. "" "This is X" ""
  3. pass
  4. @deco
  5. def y (a, b = 20 ):
  6. "" "This is Y" ""
  7. pass



Now we check:

 Help on function x:

 >>> help (x)
 x (c, d)
     This is X

 >>> help (y)
 Help on function z:

 z (* args, ** kwargs)


Instead of y, we see the description of the internal function of the decorator.

If you make your module and want to use it again after a while, then the internal documentation will be very useful - to learn how to call a specific function, you will not need to read the source code of the module. But if decorators are needed, you will have to look for some solution ...

Functions have properties func_doc, func_name, which, in principle, can be set by force:

  1. def deco (fn):
  2. def z ( * args, ** kwargs):
  3. return fn ( * args, ** kwargs)
  4. z.func_doc = fn.func_doc
  5. z.func_name = fn.func_name
  6. return z
  7. @deco
  8. def y (a, b = 20 ):
  9. "" "This is Y" ""
  10. pass



  >>> help (y)
 Help on function y:

 y (* args, ** kwargs) # a should be (a, b = 20)
     This is Y </ code>


But the argument list still remains from the internal function. ( Response to the kmike reader remark: the decorator functools.wraps does the same with the same result )

There are 2 solutions: “clean” in the form of a module and “dirty” in the form of a hack from Alex Martelli :

Decorator module



  1. from decorator import decorator
  2. @decorator
  3. def deco (fn):
  4. def z ( * args, ** kwargs):
  5. return fn ( * args, ** kwargs)
  6. return z
  7. @deco
  8. def y (a, b = 20 ):
  9. pass



  >>> help (y)
 Help on function y:

 y (a, b = 20)


Hack alex martelli


The essence of the hack is to write all the functions to be decorated into the lookaside array, and the function inspect.getargspec (which provides information about the function for help) replaces the other, which produces the data recorded in lookaside, or calls the original getargspec.

  1. import functools, inspect
  2. realgas = inspect. getargspec
  3. lookaside = dict ()
  4. def fakegas (f):
  5. if f in lookaside:
  6. return lookaside [f]
  7. return realgas (f)
  8. inspect .getargspec = fakegas
  9. def deco (fn):
  10. @ functools.wraps (fn)
  11. def z ( * args, ** kwargs):
  12. return fn ( * args, ** kwargs)
  13. lookaside [z] = realgas (fn) # note that
  14. return z
  15. @deco
  16. def x (a, b = 23 ):
  17. "" "This is X" ""
  18. return a + b



  help (x)

 Help on function x in module __main__:

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

Source: https://habr.com/ru/post/74838/


All Articles