Skip to content

Commit

Permalink
feat: Add Google-style docstring parser
Browse files Browse the repository at this point in the history
  • Loading branch information
pawamoy committed Sep 25, 2021
1 parent 00de148 commit cdefccc
Show file tree
Hide file tree
Showing 10 changed files with 780 additions and 10 deletions.
8 changes: 7 additions & 1 deletion config/flake8.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[flake8]
exclude = fixtures,site
exclude = fixtures,site,numpy.py,rst.py
max-line-length = 132
docstring-convention = google
ban-relative-imports = true
Expand Down Expand Up @@ -64,6 +64,8 @@ ignore =
WPS210
# too many arguments
WPS211
# too many return statements
WPS212
# too many expressions
WPS213
# too many methods
Expand All @@ -78,6 +80,10 @@ ignore =
WPS226
# too many public instance attributes
WPS230
# module too complex
WPS232
# too many imports
WPS235
# too complex f-string
WPS237
# too cumbersome, asks to write class A(object)
Expand Down
48 changes: 43 additions & 5 deletions src/griffe/dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
from pathlib import Path
from typing import Any

from griffe.docstrings.dataclasses import DocstringSection
from griffe.docstrings.parsers import Parser, parse # noqa: WPS347

ParameterKind = inspect._ParameterKind # noqa: WPS437


Expand All @@ -33,11 +36,11 @@ def __init__(self, lineno: int | None, endlineno: int | None) -> None:
self.lineno: int | None = lineno
self.endlineno: int | None = endlineno

def as_dict(self, full=False) -> dict[str, Any]:
def as_dict(self, **kwargs) -> dict[str, Any]:
"""Return this decorator's data as a dictionary.
Arguments:
full: Whether to return full info, or just base info.
**kwargs: Additional serialization options.
Returns:
A dictionary.
Expand All @@ -55,34 +58,69 @@ class Docstring:
value: The actual documentation string, cleaned up.
lineno: The starting line number.
endlineno: The ending line number.
parent: The parent object on which this docstring is attached.
"""

def __init__(self, value: str, lineno: int | None, endlineno: int | None) -> None:
def __init__(
self,
value: str,
lineno: int | None,
endlineno: int | None,
parent: Module | Class | Function | Data | None = None,
) -> None:
"""Initialize the docstring.
Arguments:
value: The docstring value.
lineno: The starting line number.
endlineno: The ending line number.
parent: The parent object on which this docstring is attached.
"""
self.value: str = inspect.cleandoc(value)
self.lineno: int | None = lineno
self.endlineno: int | None = endlineno
self.parent: Module | Class | Function | Data | None = parent

def as_dict(self, full=False) -> dict[str, Any]:
@cached_property
def parsed(self) -> list[DocstringSection]:
"""Return the docstring, parsed into structured data.
Returns:
The parsed docstring.
"""
return self.parse()

def parse(self, docstring_parser: Parser = Parser.google, **options) -> list[DocstringSection]:
"""Parse the docstring into structured data.
Arguments:
docstring_parser: The docstring parser to use.
**options: Additional docstring parsing options.
Returns:
The parsed docstring.
"""
return parse(self, docstring_parser, **options)

def as_dict(self, full: bool = False, docstring_parser: Parser = Parser.google, **kwargs) -> dict[str, Any]:
"""Return this docstring's data as a dictionary.
Arguments:
full: Whether to return full info, or just base info.
docstring_parser: The docstring docstring_parser to parse the docstring with.
**kwargs: Additional serialization or docstring parsing options.
Returns:
A dictionary.
"""
return {
base: dict[str, Any] = {
"value": self.value,
"lineno": self.lineno,
"endlineno": self.endlineno,
}
if full:
base["parsed"] = self.parse(docstring_parser, **kwargs)
return base


class Argument:
Expand Down
1 change: 1 addition & 0 deletions src/griffe/docstrings/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""This module exposes objects related to docstrings."""
137 changes: 137 additions & 0 deletions src/griffe/docstrings/dataclasses.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
"""This module contains the dataclasses related to docstrings."""

from __future__ import annotations

import enum
from typing import Any


class DocstringSectionKind(enum.Enum):
"""The possible section kinds."""

text = "text"
arguments = "arguments"
raises = "raises"
returns = "returns"
yields = "yields"
examples = "examples"
attributes = "attributes"
keyword_arguments = "keyword arguments"


class DocstringSection:
"""Placeholder."""

def __init__(self, kind: DocstringSectionKind, value: Any) -> None:
"""Initialize the section.
Arguments:
kind: The section kind.
value: The section value.
"""
self.kind: DocstringSectionKind = kind
self.value: Any = value

def as_dict(self, **kwargs) -> dict[str, Any]:
"""Return this section's data as a dictionary.
Arguments:
**kwargs: Additional serialization options.
Returns:
A dictionary.
"""
if hasattr(self.value, "as_dict"):
serialized_value = self.value.as_dict(**kwargs)
else:
serialized_value = self.value
return {"kind": self.kind.value, "value": serialized_value}


class DocstringElement:
"""This base class represents annotated, nameless elements.
Attributes:
annotation: The element annotation, if any.
description: The element description.
"""

def __init__(self, annotation: str | None, description: str) -> None:
"""Initialize the element.
Arguments:
annotation: The element annotation, if any.
description: The element description.
"""
self.annotation: str | None = annotation
self.description: str = description

def as_dict(self, **kwargs) -> dict[str, Any]:
"""Return this element's data as a dictionary.
Arguments:
**kwargs: Additional serialization options.
Returns:
A dictionary.
"""
return {
"annotation": self.annotation,
"description": self.description,
}


class DocstringException(DocstringElement):
"""This class represents a documented exception."""


class DocstringReturn(DocstringElement):
"""This class represents a documented return value."""


class DocstringYield(DocstringElement):
"""This class represents a documented yield value."""


class DocstringNamedElement(DocstringElement):
"""This base class represents annotated, named elements.
Attributes:
name: The element name.
value: The element value, as a string, if any.
"""

def __init__(self, name: str, annotation: str | None, description: str, value: str | None = None) -> None:
"""Initialize the element.
Arguments:
name: The element name.
annotation: The element annotation, if any.
description: The element description.
value: The element value, as a string.
"""
super().__init__(annotation, description)
self.name: str = name
self.value: str | None = value

def as_dict(self, **kwargs) -> dict[str, Any]:
"""Return this element's data as a dictionary.
Arguments:
**kwargs: Additional serialization options.
Returns:
A dictionary.
"""
base = {"name": self.name, **super().as_dict(**kwargs)}
if self.value is not None:
base["value"] = self.value
return base


class DocstringArgument(DocstringNamedElement):
"""This class represent a documented function argument."""


class DocstringAttribute(DocstringNamedElement):
"""This class represents a documented module/class attribute."""
Loading

0 comments on commit cdefccc

Please sign in to comment.