Skip to content

Instantly share code, notes, and snippets.

@pawamoy
Last active August 29, 2023 14:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pawamoy/a12cb4a5f66d913519070b52b2a9d54b to your computer and use it in GitHub Desktop.
Save pawamoy/a12cb4a5f66d913519070b52b2a9d54b to your computer and use it in GitHub Desktop.
Documentation Metadata
# This gist shows how we could get rid of docstrings micro-syntax
# like Google-style and Numpydoc-style pseudo-standards,
# using an enhanced version of PEP 727.
# The goal is to replace the following sections:
# Attributes
# Functions/Methods
# Classes
# Modules
# Deprecated
# Examples
# Parameters
# Other Parameters
# Raises
# Warns
# Yields
# Receives
# Returns
from typing import (
Annotated,
Generator,
Iterator,
NotRequired,
TypedDict,
Unpack,
doc,
deprecated,
name, # does not exist yet
raises, # does not exist yet
warns, # does not exist yet
)
# Documenting module/class attributes, replacing Attributes sections:
FOO: Annotated[str, doc("Description.")]
# The benefit is that the documentation is also available at runtime,
# while a docstring below the attribute is not, and requires static analysis.
### ALREADY POSSIBLE.
# Documenting functions, methods, classes and modules is done as previously:
# with functions/methods/classes/modules docstrings (summary and body, using whatever markup).
# Documentation generators can then auto-summarize existing objects.
### ALREADY POSSIBLE.
# Documenting deprecations, replacing Deprecated sections:
BAR: Annotated[int, deprecated("Deprecated since v2.")]
# For functions, maybe add the information to the return value annotation:
def bar() -> Annotated[None, deprecated("Deprecated since v2.")]:
...
# For parameters:
def bar(param: Annotated[int, deprecated("Deprecated since v2.")] = None):
...
### NOT YET POSSIBLE: PEP 727 + 702
# Documenting examples, replacing Examples sections:
# nothing specific here, we can have some nice things
# like (Markdown) extensions that parse pycon code blocks
# directly, without requiring indentation or fences.
# Or maybe no extensions at all, and just indent pycon code:
# it's correctly parsed, and readable as is.
### ALREADY POSSIBLE.
# Documenting parameters, replacing Parameters sections:
def foo(
bar: Annotated[str, doc("Description.")] = "default",
):
...
### ALREADY POSSIBLE.
# Documenting other parameters (keyword arguments), replacing Other Parameters sections:
class FooFooKwargs(TypedDict, total=False):
bar: Annotated[NotRequired[str], doc("Description.")]
baz: Annotated[NotRequired[str], doc("Description.")]
def foofoo(
**kwargs: Unpack[FooFooKwargs],
):
...
# THIS IS RATHER CONVOLUTED. Hoping for something easier in the future...
### ALREADY POSSIBLE.
# Documenting exceptions, replacing Raises sections,
# maybe add the information to the return value annotation:
def bar() -> Annotated[
None,
raises(
ValueError("When something goes wrong."),
TypeError("When something goes even wronger."),
)
]:
...
### NOT YET POSSIBLE
# Documenting warnings, replacing Warns sections,
# maybe add the information to the return value annotation:
def bar() -> Annotated[
None,
warns(
FutureWarning("Hello users."),
DeprecationWarning("Hello developers."),
)
]:
...
### NOT YET POSSIBLE
# Documenting yielded and received values, replacing Yields and Receives sections:
def bar() -> Generator[
Annotated[int, doc("Yielded integers.")],
Annotated[int, doc("Received integers.")],
Annotated[int, doc("Final returned value.")],
]:
...
# Same thing with Iterator instead of Generator:
def bar() -> Iterator[Annotated[int, doc("Yielded integers.")]]:
...
### ALREADY POSSIBLE.
# Advanced use-case: documenting multiple yielded/received/returned values:
def bar() -> Generator[
tuple[
Annotated[int, name("python"), doc("First element of the yielded value.")],
Annotated[float, name("cobra"), doc("Second element of the yielded value.")],
],
tuple[
Annotated[int, name("beep"), doc("First element of the received value.")],
Annotated[float, name("boop"), doc("Second element of the received value.")],
],
tuple[
Annotated[int, name("super"), doc("First element of the returned value.")],
Annotated[float, name("hyper"), doc("Second element of the returned value.")],
],
]:
...
# That works with Iterator too, and regular functions.
### ALREADY POSSIBLE (without specifying names).
# Other considerations: attributes `deprecated` and `name`
# of typing could instead be keyword arguments of the `doc` function:
def bar(
param: Annotated[str, doc("Old parameter.", deprecated="Deprecated since v2.")],
) -> Iterator[Annotated[int, doc("Yielded integers.", name="cute_int")]]:
...
### NOT YET POSSIBLE: PEP 727 + 702
# Admonitions like Note, Warning, etc., can then simply be
# written in docstrings using the chosen markup features.
### ALREADY POSSIBLE.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment