On Thu, Dec 9, 2021 at 12:12 AM Paul Bryan <pbr...@anode.ca> wrote:

> On Thu, 2021-12-09 at 12:32 +1100, Steven D'Aprano wrote:
>
> On Wed, Dec 08, 2021 at 09:45:35AM -0800, Paul Bryan wrote:
>
> I propose there is already a viable option in typing.Annotated.
> Example:
>
> @dataclass
> class InventoryItem:
>     """Class for keeping track of an item in inventory."""
>
>     name: Annotated[str, "Short name of the item."]
>     unit_price: Annotated[float, "Price per unit in dollar."]
>     ...
>
>
> Oh nice! Guido's time machine strikes again.
>
> So dataclasses could recognise Annotated and populate `__attrdoc__`.
>
>
> Indeed.
>
> Should all classes do the same? How about top level module variables?
>
>
> If we started with dataclass, it could be performed in the dataclass
> decorator and make_dataclass functions. I think this would be low-hanging
> fruit compared to classes and modules.
>

Again: better than nothing. But I'd really like to see this for all classes.

I suggest that if a proposal like this is accepted, the help() output for
classes and modules should have a new section added containing the member
docstring information; it essentially would insert the member information
to the end of the main docstring WHEN DISPLAYED (not suggested attaching it
permanently as part of the docstring). This new section would appear
directly beneath the module/class docstring when using help. Additionally,
other help()-ilke tools (such as the ipython ? operator) should be
encouraged to concatenate the member docstring information directly below
the class/module docstring when printing out help-like information.

To illustrate how things currently stand, here's a current, sort of
minimally complicated class definition (class B) with some annotations:

In [1]: from typing import Annotated

In [2]: class B:
   ...:     """Docstring for the B class."""
   ...:
   ...:     a: Annotated[int, "here is what i'd call a medium length
docstring"]
   ...:     b: Annotated[str, "short docstring"]
   ...:

....and here is the help(B) output that i get (Windows command line):

In [3]: help(B)
Help on class B in module __main__:

class B(builtins.object)
 |  Docstring for the B class.
 |
 |  Data descriptors defined here:
 |
 |  __dict__
 |      dictionary for instance variables (if defined)
 |
 |  __weakref__
 |      list of weak references to the object (if defined)
 |
 |  ----------------------------------------------------------------------
 |  Data and other attributes defined here:
 |
 |  __annotations__ = {'a': typing.Annotated[int, "here is what i'd call
a...

The member annotations aren't very discoverable this way... you can't even
see all of the first one. If it is agreed that Annotated should be The
Blessed Way to annotate members, this needs to be improved.

Here is the same thing for a dataclass (class C); here you CAN at least see
the whole of each docstring, but they are on one line together and it's not
all that great, and bits of the Annotated objects appear in this help
output at least 5 times by my count (maybe this can't be helped since it's
part of the Annotated object):

In [4]: from dataclasses import dataclass

In [5]: @dataclass
   ...: class C:
   ...:     """Docstring for the C class."""
   ...:
   ...:     a: Annotated[int, "here is what i'd call a medium length
docstring"]
   ...:     b: Annotated[str, "short docstring"]
   ...:

In [6]: help(C)
Help on class C in module __main__:

class C(builtins.object)
 |  C(a: typing.Annotated[int, "here is what i'd call a medium length
docstring"], b: typing.Annotated[str, 'short docstring']) -> None
 |
 |  Docstring for the C class.
 |
 |  Methods defined here:
 |
 |  __eq__(self, other)
 |
 |  __init__(self, a: typing.Annotated[int, "here is what i'd call a medium
length docstring"], b: typing.Annotated[str, 'short docstring']) -> None
 |
 |  __repr__(self)
 |
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |
 |  __dict__
 |      dictionary for instance variables (if defined)
 |
 |  __weakref__
 |      list of weak references to the object (if defined)
 |
 |  ----------------------------------------------------------------------
 |  Data and other attributes defined here:
 |
 |  __annotations__ = {'a': typing.Annotated[int, "here is what i'd call
a...
 |
 |  __dataclass_fields__ = {'a':
Field(name='a',type=typing.Annotated[int,...
 |
 |  __dataclass_params__ =
_DataclassParams(init=True,repr=True,eq=True,or...
 |
 |  __hash__ = None

Here is the ipython ? output for the dataclass, by the way-- it is FAR
better! But this is only so nice because dataclasses adds the typing
information to the __init__ signature. For regular classes (like class B
above), we would not get this nice output when using ?.

In [7]: C?
Init signature:
C(
    a: typing.Annotated[int, "here is what i'd call a medium length
docstring"],
    b: typing.Annotated[str, 'short docstring'],
) -> None
Docstring:      Docstring for the C class.
Type:           type
Subclasses:

---
Ricky.

"I've never met a Kentucky man who wasn't either thinking about going home
or actually going home." - Happy Chandler
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/TXTZV6SQB3BV32QFIIQSUJKML3AQHC4K/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to