DanCardin / cappa

Declarative CLI argument parser
Apache License 2.0
135 stars 8 forks source link

suggestion: Support PEP 727's `typing.Doc` (draft) #9

Closed pawamoy closed 1 year ago

pawamoy commented 1 year ago

PEP 727 proposes a new way to document attributes (module/class/instance attributes) and function/method parameters. Examples from the proposal:

from typing import Annotated, Doc

class User:
    name: Annotated[str, Doc("The user's name")]
    age: Annotated[int, Doc("The user's age")]

I think it would play well with Cappa's use of Annotated. It would help avoiding duplication of the help text / description into the attribute docstring (for API docs generation purposes).

Without support:

from dataclasses import dataclass
from typing import Annotated, Doc

import cappa

@dataclass
class User:
    # duplication of help text and `typing.Doc` string
    name: Annotated[str, cappa.Arg(..., help="The user's name."), Doc("The user's name.")]
    age: Annotated[int, cappa.Arg(..., help="The user's age."), Doc("The user's age.")]

With support:

from dataclasses import dataclass
from typing import Annotated, Doc

import cappa

@dataclass
class User:
    # Cappa populates `help` or `description` using `typing.Doc`'s value
    name: Annotated[str, cappa.Arg(...), Doc("The user's name.")]
    age: Annotated[int, cappa.Arg(...), Doc("The user's age.")]

Alternatively, Cappa could support populating help/description using the attribute docstring:

from dataclasses import dataclass
from typing import Annotated, Doc

import cappa

@dataclass
class User:
    # Cappa populates `help` or `description` using attribute docstrings
    name: Annotated[str, cappa.Arg(...)]
    """The user's name."""
    age: Annotated[int, cappa.Arg(...)]
    """"The user's age."""

(by the way I thought this was supported, but after some tests it seems it isn't? maybe it fetches descriptions from an Attributes section in the class docstring instead?)

...but I'm kinda attracted to PEP 727 and Cappa's use of annotations :smiling_face_with_three_hearts:

DanCardin commented 1 year ago

I'm with you re typing.Doc. What's actually interesting, is that it's already implemented in typing_extensions since 4.8.0. I dont know that I'm willing to specifically raise the minimum version requirement just for that unless/until the pep was accepted, but I can probably add support for it regardless.


So "attribute docstrings" dont show up in the AST, so it's impractical to support something like

    name: Annotated[str, cappa.Arg(...)]
    """The user's name."""

What you can do today is define your attributes with either numpy or google docstring style formats on the class itself, and the arguments' descriptions will be used as fallbacks for any args without explicit help:

class User:
    """...

    Arguments:
        name: The user's name
        age: The user's age
pawamoy commented 1 year ago

That's great to hear!

Yeah you don't have to raise your lower bound on typing-extensions if you don't plan on using it yourself (in Cappa's source). Users can specify >=4.8 themselves.


The docstring does appear in the AST, only not as a subnode of the assignment but as a node after it. It's possible to match pairs of (assignment, docstring) in the AST with a bit of logic. I do it myself in Griffe 🙂 See how I declare and use ast_next: https://github.com/search?q=repo%3Amkdocstrings%2Fgriffe%20ast_next&type=code


Thanks for the confirmation on using an Attributes section in the class docstring 👍


Let me know if I can be of any help in implementing one of these features, I'll happily try to submit PRs!

DanCardin commented 1 year ago

So support for what's available as of typing_extensions 4.8 should be in 0.8.0, fwiw.

The docstring does appear in the AST,

ahhh right. i misspoke. I meant it wont end up anywhere obviously detectable like __doc__. I suppose it makes sense that it'd end up in the AST.

Let me know if I can be of any help in implementing one of these features

If you were interested in attempting to pull out attribute "docstrings", it's definitely something I probably wont personally implement (any time soon anyway), but would accept a PR implementing if that was more valuable to you than class-level docstring

pawamoy commented 1 year ago

Wow, awesome, thanks for the super fast implementation and release!

To me, attribute docstrings (below the assignment) are interesting for the following reasons:

One less nice thing is that they are not available at runtime, as you mentioned.

Anyway, what I'm saying is that I'm interested in bringing support for attribute docstrings in Cappa, so I might try and send a PR sometime soon. Not sure though, because now that you have added support for PEP 727 Doc I'll probably use just that :smile:

Thanks again!