<div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div dir="ltr"><div><div><div><div>Hi Steve,<br><br></div>Here are a couple of code snippets. I hope it's not too verbose :)<br><br></div>Setting property on the __doc__ works on the instance, but not on the class:<br><pre style="background-color:rgb(255,255,255);color:rgb(0,0,0);font-family:"DejaVu Sans Mono";font-size:10.5pt"><span style="color:rgb(0,0,128);font-weight:bold">class </span>A:<br>    <span style="color:rgb(0,0,178)">@property<br></span><span style="color:rgb(0,0,178)">    </span><span style="color:rgb(0,0,128);font-weight:bold">def </span><span style="color:rgb(178,0,178)">__doc__</span>(<span style="color:rgb(148,85,141)">self</span>):<br>        <span style="color:rgb(0,0,128);font-weight:bold">return </span><span style="color:rgb(0,128,128);font-weight:bold">"Works only on the instance"<br></span><span style="color:rgb(0,128,128);font-weight:bold"><br></span>a = A()<br><span style="color:rgb(0,0,128)">print</span>(<span style="color:rgb(0,128,128);font-weight:bold">'A.__doc__ is {}'</span>.format(A.<span style="color:rgb(178,0,178)">__doc__</span>))<br><span style="color:rgb(128,128,128);font-style:italic"># A.__doc__ is <property object at 0x7fa48af86e58><br></span><span style="color:rgb(128,128,128);font-style:italic"><br></span><span style="color:rgb(0,0,128)">print</span>(<span style="color:rgb(0,128,128);font-weight:bold">'a.__doc__ is {}'</span>.format(a.<span style="color:rgb(178,0,178)">__doc__</span>))<br><span style="color:rgb(128,128,128);font-style:italic"># a.__doc__ is Works only on the instance<br></span><span style="color:rgb(128,128,128);font-style:italic"><br></span><span style="color:rgb(0,0,128)">help</span>(A)<br><span style="color:rgb(128,128,128);font-style:italic"># Help on class A in module __main__:<br></span><span style="color:rgb(128,128,128);font-style:italic">#<br></span><span style="color:rgb(128,128,128);font-style:italic"># class A(builtins.object)<br></span><span style="color:rgb(128,128,128);font-style:italic">#  |  Data descriptors defined here:<br></span><span style="color:rgb(128,128,128);font-style:italic">#  |<br></span><span style="color:rgb(128,128,128);font-style:italic">#  |  __dict__<br></span><span style="color:rgb(128,128,128);font-style:italic">#  |      dictionary for instance variables (if defined)<br></span><span style="color:rgb(128,128,128);font-style:italic">#  |<br></span><span style="color:rgb(128,128,128);font-style:italic">#  |  __weakref__<br></span><span style="color:rgb(128,128,128);font-style:italic">#  |      list of weak references to the object (if defined)</span></pre><br></div><div>However, you can fix this with a separate class that follows the property protocol:<font size="2"><br></font><pre style="background-color:rgb(255,255,255);color:rgb(0,0,0);font-family:"DejaVu Sans Mono""><font size="2">some_var = <span style="color:rgb(0,128,128);font-weight:bold">"oi"<br></span><span style="color:rgb(0,128,128);font-weight:bold"><br></span><span style="color:rgb(0,0,128);font-weight:bold">class </span>DocProperty:<br>    <span style="color:rgb(0,0,128);font-weight:bold">def </span><span style="color:rgb(178,0,178)">__get__</span>(<span style="color:rgb(148,85,141)">self</span>, instance, owner):<br>        <span style="color:rgb(0,0,128);font-weight:bold">return </span><span style="color:rgb(0,128,128);font-weight:bold">"Dynamic class doc: {}"</span>.format(some_var)<br><br><span style="color:rgb(0,0,128);font-weight:bold">class </span>A:<br>    <span style="color:rgb(128,128,128);font-style:italic">"""Static doc"""<br></span><span style="color:rgb(128,128,128);font-style:italic">    </span><span style="color:rgb(178,0,178)">__doc__ </span>= DocProperty()<br><br><br><span style="color:rgb(0,0,128)">print</span>(A.<span style="color:rgb(178,0,178)">__doc__</span>)<br><span style="color:rgb(128,128,128);font-style:italic"># Dynamic class doc: oi<br></span><span style="color:rgb(128,128,128);font-style:italic"><br></span>some_var = <span style="color:rgb(0,128,128);font-weight:bold">"noi"<br></span><span style="color:rgb(0,128,128);font-weight:bold"><br></span><span style="color:rgb(0,0,128)">print</span>(A.<span style="color:rgb(178,0,178)">__doc__</span>)<br><span style="color:rgb(128,128,128);font-style:italic"># Dynamic class doc: noi<br></span><span style="color:rgb(128,128,128);font-style:italic"><br></span><span style="color:rgb(0,0,128)">help</span>(A)<br><span style="color:rgb(128,128,128);font-style:italic"># Help on class A in module __main__:<br></span><span style="color:rgb(128,128,128);font-style:italic"># <br></span><span style="color:rgb(128,128,128);font-style:italic"># class A(builtins.object)<br></span><span style="color:rgb(128,128,128);font-style:italic">#  |  Dynamic class doc: noi<br></span><span style="color:rgb(128,128,128);font-style:italic">#  |  <br></span><span style="color:rgb(128,128,128);font-style:italic">#  |  Data descriptors defined here:<br></span><span style="color:rgb(128,128,128);font-style:italic">#  |  <br></span><span style="color:rgb(128,128,128);font-style:italic">#  |  __dict__<br></span><span style="color:rgb(128,128,128);font-style:italic">#  |      dictionary for instance variables (if defined)<br></span><span style="color:rgb(128,128,128);font-style:italic">#  |  <br></span><span style="color:rgb(128,128,128);font-style:italic">#  |  __weakref__<br></span><span style="color:rgb(128,128,128);font-style:italic">#  |      list of weak references to the object (if defined)</span></font></pre><br></div><div>This worked well when I decorate a <i>class.</i> Consider now the case where we have to decorate a <i>function</i>.<br><br>I could decorate with a callable class, but __doc__ now does not work and help() becomes misleading (the function signature is lost and the help says "Help on Wrapper"):<font size="2"><br></font><pre style="background-color:rgb(255,255,255);color:rgb(0,0,0);font-family:"DejaVu Sans Mono";font-size:10.5pt"><font size="2"><span style="color:rgb(0,0,128);font-weight:bold">import </span>functools<br><br><br><span style="color:rgb(0,0,128);font-weight:bold">class </span>DocProperty:<br>    <span style="color:rgb(0,0,128);font-weight:bold">def </span><span style="color:rgb(178,0,178)">__init__</span>(<span style="color:rgb(148,85,141)">self</span>, func):<br>        <span style="color:rgb(148,85,141)">self</span>.func = func<br><br>    <span style="color:rgb(0,0,128);font-weight:bold">def </span><span style="color:rgb(178,0,178)">__get__</span>(<span style="color:rgb(148,85,141)">self</span>, instance, owner):<br>        <span style="color:rgb(0,0,128);font-weight:bold">return </span><span style="color:rgb(0,128,128);font-weight:bold">"Func doc is: {!r} and now my part..."</span>.format(<span style="color:rgb(148,85,141)">self</span>.func.<span style="color:rgb(178,0,178)">__doc__</span>)<br><br><br><span style="color:rgb(0,0,128);font-weight:bold">def </span>decorate(func):<br>    <span style="color:rgb(0,0,128);font-weight:bold">class </span>Wrapper:<br>        <span style="color:rgb(178,0,178)">__doc__ </span>= DocProperty(func)<br><br>        <span style="color:rgb(0,0,128);font-weight:bold">def </span><span style="color:rgb(178,0,178)">__call__</span>(<span style="color:rgb(148,85,141)">self</span>, *args, **kwargs):<br>            <span style="color:rgb(0,0,128)">print</span>(<span style="color:rgb(0,128,128);font-weight:bold">"Before the call"</span>)<br>            <span style="color:rgb(0,0,128);font-weight:bold">return </span>func(*args, **kwargs)<br><br>    wrapper = Wrapper()<br><br>    functools.update_wrapper(<span style="color:rgb(102,0,153)">wrapper</span>=wrapper, <span style="color:rgb(102,0,153)">wrapped</span>=func)<br><br>    wrapper.<span style="color:rgb(178,0,178)">__doc__ </span>= DocProperty(func)<br><br>    <span style="color:rgb(0,0,128);font-weight:bold">return </span>wrapper<br><br><br><span style="color:rgb(0,0,178)">@decorate<br></span><span style="color:rgb(0,0,128);font-weight:bold">def </span>some_func(<span style="color:rgb(128,128,128)">x</span>):<br>    <span style="color:rgb(128,128,128);font-style:italic">"""Original doc."""<br></span><span style="color:rgb(128,128,128);font-style:italic">    </span><span style="color:rgb(0,0,128)">print</span>(<span style="color:rgb(0,128,128);font-weight:bold">"Hi from some_func"</span>)<br><br><br><span style="color:rgb(0,0,128)">print</span>(<span style="color:rgb(0,128,128);font-weight:bold">'some_func.__doc__ is {}'</span>.format(some_func.<span style="color:rgb(178,0,178)">__doc__</span>))<br><span style="color:rgb(128,128,128);font-style:italic"># some_func.__doc__ is <__main__.DocProperty object at 0x7fd134a1d668><br></span><span style="color:rgb(128,128,128);font-style:italic"><br></span><span style="color:rgb(0,0,128)">help</span>(some_func)<br><span style="color:rgb(128,128,128);font-style:italic"># Help on Wrapper in module __main__ object:<br></span><span style="color:rgb(128,128,128);font-style:italic"># <br></span><span style="color:rgb(128,128,128);font-style:italic"># some_func = class Wrapper(builtins.object)<br></span><span style="color:rgb(128,128,128);font-style:italic">#  |  Func doc is: 'Original doc.' and now my part...<br></span><span style="color:rgb(128,128,128);font-style:italic">#  |  <br></span><span style="color:rgb(128,128,128);font-style:italic">#  |  Methods defined here:<br></span><span style="color:rgb(128,128,128);font-style:italic">#  |  <br></span><span style="color:rgb(128,128,128);font-style:italic">#  |  __call__(self, *args, **kwargs)<br></span><span style="color:rgb(128,128,128);font-style:italic">#  |  <br></span><span style="color:rgb(128,128,128);font-style:italic">#  |  ----------------------------------------------------------------------<br></span><span style="color:rgb(128,128,128);font-style:italic">#  |  Data descriptors defined here:<br></span><span style="color:rgb(128,128,128);font-style:italic">#  |  <br></span><span style="color:rgb(128,128,128);font-style:italic">#  |  __dict__<br></span><span style="color:rgb(128,128,128);font-style:italic">#  |      dictionary for instance variables (if defined)<br></span><span style="color:rgb(128,128,128);font-style:italic">#  |  <br></span><span style="color:rgb(128,128,128);font-style:italic">#  |  __weakref__<br></span></font><span style="color:rgb(128,128,128);font-style:italic"><font size="2">#  |      list of weak references to the object (if defined)</font><br></span></pre><br></div><div>I tried to decorate the function with a DocProperty, but then hit the wall. Both __doc__ is not "getted" nor does help() works:<br><pre style="background-color:rgb(255,255,255);color:rgb(0,0,0);font-family:"DejaVu Sans Mono";font-size:10.5pt"><font size="2"><span style="color:rgb(0,0,128);font-weight:bold">import </span>functools<br><br><span style="color:rgb(0,0,128);font-weight:bold">class </span>DocProperty:<br>    <span style="color:rgb(0,0,128);font-weight:bold">def </span><span style="color:rgb(178,0,178)">__init__</span>(<span style="color:rgb(148,85,141)">self</span>, func):<br>        <span style="color:rgb(148,85,141)">self</span>.func = func<br><br>    <span style="color:rgb(0,0,128);font-weight:bold">def </span><span style="color:rgb(178,0,178)">__get__</span>(<span style="color:rgb(148,85,141)">self</span>, instance, owner):<br>        <span style="color:rgb(0,0,128);font-weight:bold">return </span><span style="color:rgb(0,128,128);font-weight:bold">"Func doc is: {!r} and now my part..."</span>.format(<span style="color:rgb(148,85,141)">self</span>.func.<span style="color:rgb(178,0,178)">__doc__</span>)<br><br><span style="color:rgb(0,0,128);font-weight:bold">def </span>decorate(func):<br>    <span style="color:rgb(0,0,128);font-weight:bold">def </span>wrapper(*args, **kwargs):<br>        <span style="color:rgb(0,0,128)">print</span>(<span style="color:rgb(0,128,128);font-weight:bold">"Before the call"</span>)<br>        <span style="color:rgb(0,0,128);font-weight:bold">return </span>func(*args, **kwargs)<br><br>    functools.update_wrapper(<span style="color:rgb(102,0,153)">wrapper</span>=wrapper, <span style="color:rgb(102,0,153)">wrapped</span>=func)<br><br>    wrapper.<span style="color:rgb(178,0,178)">__doc__ </span>= DocProperty(func)<br><br>    <span style="color:rgb(0,0,128);font-weight:bold">return </span>wrapper<br><br><span style="color:rgb(0,0,178)">@decorate<br></span><span style="color:rgb(0,0,128);font-weight:bold">def </span>some_func(<span style="color:rgb(128,128,128)">x</span>):<br>    <span style="color:rgb(128,128,128);font-style:italic">"""Original doc."""<br></span><span style="color:rgb(128,128,128);font-style:italic">    </span><span style="color:rgb(0,0,128)">print</span>(<span style="color:rgb(0,128,128);font-weight:bold">"Hi from some_func"</span>)<br><br>some_func(<span style="color:rgb(102,0,153)">x</span>=<span style="color:rgb(0,0,255)">3</span>)<br><span style="color:rgb(128,128,128);font-style:italic"># Before the call<br></span><span style="color:rgb(128,128,128);font-style:italic"># Hi from some_func<br></span><span style="color:rgb(128,128,128);font-style:italic"><br></span><span style="color:rgb(0,0,128)">print</span>(<span style="color:rgb(0,128,128);font-weight:bold">'some_func.__doc__ is {}'</span>.format(some_func.<span style="color:rgb(178,0,178)">__doc__</span>))<br><span style="color:rgb(128,128,128);font-style:italic"># some_func.__doc__ is <__main__.DocProperty object at 0x7f0551eea438><br></span><span style="color:rgb(128,128,128);font-style:italic"><br></span><span style="color:rgb(0,0,128)">help</span>(some_func)<br><span style="color:rgb(128,128,128);font-style:italic"># Help on function some_func in module __main__:<br></span><span style="color:rgb(128,128,128);font-style:italic"># <br></span></font><span style="color:rgb(128,128,128);font-style:italic"><font size="2"># some_func(x)</font><br></span></pre>I just couldn't figure out how to make the __doc__ attribute of a function a getter. Is this a bug or am I making a mistake? If it's my mistake, is there any other way how to dynamically __doc__ a function or am I forced to set __doc__ of a function at the decoration time?<br></div><div><br></div><div>(If you wonder about the use case: I'd like to dynamically generate the docstrings when functions are decorated with contracts from icontract library. Condition functions need to be parsed and re-formatted, so this is something that should be done on-demand, when the user either wants to see the help() or when the sphinx documentation is automatically generated. The docs should not inflict computational overhead during the decoration since normal course of program operation does not need pretty-printed contracts.)<br></div><div><br></div><div>Thanks for any pointers! Please let me know if you'd like me to compress one or the other example or explain something in more detail.<br></div><div><br></div>Cheers,<br></div>Marko<br></div></div></div></div></div></div><br><div class="gmail_quote"><div dir="ltr">On Sun, 7 Oct 2018 at 11:07, Steven D'Aprano <<a href="mailto:steve@pearwood.info" target="_blank">steve@pearwood.info</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">On Sun, Oct 07, 2018 at 09:25:13AM +0200, Marko Ristin-Kaufmann wrote:<br>
> Hi,<br>
> I'm working on decorators that have a dynamic __doc__ property computed at<br>
> runtime and not at decoration time.<br>
<br>
You mean like this?<br>
<br>
py> class X:<br>
...     @property<br>
...     def __doc__(self):<br>
...         return "NOBODY expects the %s Inquisition!" % self.nationality<br>
...     def __init__(self, nationality):<br>
...         self.nationality = nationality<br>
...<br>
py> a = X("Spanish")<br>
py> b = X("Belgian")<br>
py> a.__doc__<br>
'NOBODY expects the Spanish Inquisition!'<br>
py> b.__doc__<br>
'NOBODY expects the Belgian Inquisition!'<br>
<br>
<br>
The only downside is that help(a) doesn't do the right thing. I believe <br>
that counts as a bug in help().<br>
<br>
<br>
<br>
<br>
> The decorator must return the wrapper as a function and can not return a<br>
> callable object with __call__ since the "self" argument would not be<br>
> properly passed through the decorator.<br>
<br>
Sorry, I don't get this. Can you demonstrate? Code speaks much louder <br>
than words.<br>
<br>
<br>
<br>
-- <br>
Steve<br>
_______________________________________________<br>
Python-ideas mailing list<br>
<a href="mailto:Python-ideas@python.org" target="_blank">Python-ideas@python.org</a><br>
<a href="https://mail.python.org/mailman/listinfo/python-ideas" rel="noreferrer" target="_blank">https://mail.python.org/mailman/listinfo/python-ideas</a><br>
Code of Conduct: <a href="http://python.org/psf/codeofconduct/" rel="noreferrer" target="_blank">http://python.org/psf/codeofconduct/</a><br>
</blockquote></div>