Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: make _griffe_compat a submodule that generates import stubs #393

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
from griffe import GriffeLoader
from griffe import ModulesCollection, LinesCollection

import _griffe.models as dataclasses
import _griffe.docstrings.models as docstrings
import _griffe.expressions as expressions
from . import dataclasses
from . import docstrings
from . import expressions

from griffe import Parser, parse, parse_numpy
from griffe import AliasResolutionError
Expand Down
71 changes: 71 additions & 0 deletions quartodoc/_griffe_compat/_generate_stubs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
"""Generate the submodules which import parts of griffe (e.g. dataclasses.py)

This process balances having to import specific objects from griffe, against
importing everything via the top-level griffe module. It does this by generating
modules for objects commonly used together in quartodoc.

* dataclasses: represent python objects
* docstrings: represent python docstrings
* expressions: represent annotation expressions

Run using: python -m quartodoc._griffe_compat._generate_stubs
"""

import ast
import black
import copy
import griffe
import inspect
from pathlib import Path


def fetch_submodule(ast_body, submodule: str):
ast_imports = [
obj
for obj in ast_body
if isinstance(obj, ast.ImportFrom) and obj.module == submodule
]

if len(ast_imports) > 1:
raise Exception(f"Found {len(ast_imports)} imports for {submodule}")
elif not ast_imports:
raise Exception(f"Could not find import for {submodule}")

return ast_imports[0]


def code_for_imports(mod_code: str, submodules: list[str]) -> str:
res = []
mod = ast.parse(mod_code)
for submod in submodules:
expr = fetch_submodule(mod.body, submod)
expr.module = "griffe"
new_expr = copy.copy(expr)
new_expr.module = "griffe"
res.append(ast.unparse(new_expr))

return black.format_str(
"# flake8: noqa\n\n" + "\n".join(res), mode=black.FileMode()
)


def generate_griffe_stub(out_path: Path, mod, submodules: list[str]):
res = code_for_imports(inspect.getsource(mod), submodules)
out_path.write_text(res)


MAPPINGS = {
"dataclasses": [
"_griffe.models",
"_griffe.mixins",
"_griffe.enumerations",
],
"docstrings": ["_griffe.docstrings.models"],
"expressions": ["_griffe.expressions"],
}

if __name__ == "__main__":
for out_name, submodules in MAPPINGS.items():
generate_griffe_stub(
Path(__file__).parent / f"{out_name}.py", griffe, submodules
)
31 changes: 31 additions & 0 deletions quartodoc/_griffe_compat/dataclasses.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# flake8: noqa

from griffe import (
Alias,
Attribute,
Class,
Decorator,
Docstring,
Function,
Module,
Object,
Parameter,
Parameters,
)
from griffe import (
DelMembersMixin,
GetMembersMixin,
ObjectAliasMixin,
SerializationMixin,
SetMembersMixin,
)
from griffe import (
BreakageKind,
DocstringSectionKind,
ExplanationStyle,
Kind,
LogLevel,
ObjectKind,
ParameterKind,
Parser,
)
34 changes: 34 additions & 0 deletions quartodoc/_griffe_compat/docstrings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# flake8: noqa

from griffe import (
DocstringAdmonition,
DocstringAttribute,
DocstringClass,
DocstringDeprecated,
DocstringElement,
DocstringFunction,
DocstringModule,
DocstringNamedElement,
DocstringParameter,
DocstringRaise,
DocstringReceive,
DocstringReturn,
DocstringSection,
DocstringSectionAdmonition,
DocstringSectionAttributes,
DocstringSectionClasses,
DocstringSectionDeprecated,
DocstringSectionExamples,
DocstringSectionFunctions,
DocstringSectionModules,
DocstringSectionOtherParameters,
DocstringSectionParameters,
DocstringSectionRaises,
DocstringSectionReceives,
DocstringSectionReturns,
DocstringSectionText,
DocstringSectionWarns,
DocstringSectionYields,
DocstringWarn,
DocstringYield,
)
44 changes: 44 additions & 0 deletions quartodoc/_griffe_compat/expressions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# flake8: noqa

from griffe import (
Expr,
ExprAttribute,
ExprBinOp,
ExprBoolOp,
ExprCall,
ExprCompare,
ExprComprehension,
ExprConstant,
ExprDict,
ExprDictComp,
ExprExtSlice,
ExprFormatted,
ExprGeneratorExp,
ExprIfExp,
ExprJoinedStr,
ExprKeyword,
ExprLambda,
ExprList,
ExprListComp,
ExprName,
ExprNamedExpr,
ExprParameter,
ExprSet,
ExprSetComp,
ExprSlice,
ExprSubscript,
ExprTuple,
ExprUnaryOp,
ExprVarKeyword,
ExprVarPositional,
ExprYield,
ExprYieldFrom,
get_annotation,
get_base_class,
get_condition,
get_expression,
safe_get_annotation,
safe_get_base_class,
safe_get_condition,
safe_get_expression,
)
3 changes: 2 additions & 1 deletion quartodoc/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from ._griffe_compat import docstrings as ds
from ._griffe_compat import dataclasses as dc
from ._griffe_compat import AliasResolutionError

from enum import Enum
from dataclasses import dataclass
Expand Down Expand Up @@ -210,7 +211,7 @@ def fields(el: dc.Object):
def fields(el: dc.ObjectAliasMixin):
try:
return fields(el.target)
except dc.AliasResolutionError:
except AliasResolutionError:
warnings.warn(
f"Could not resolve Alias target `{el.target_path}`."
" This often occurs because the module was not loaded (e.g. it is a"
Expand Down
Loading