Skip to content

Commit

Permalink
fix: Mock guppy decorator during sphinx builds (#622)
Browse files Browse the repository at this point in the history
Fixes #583

The problem was that sphinx autodoc got confused by `@guppy` decorated
functions. Now, we mock the decorator when we detect that sphinx is
running.
  • Loading branch information
mark-koch authored Nov 5, 2024
1 parent 45a460f commit 1cccc04
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 3 deletions.
56 changes: 53 additions & 3 deletions guppylang/decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from dataclasses import dataclass, field
from pathlib import Path
from types import ModuleType
from typing import Any, TypeVar, overload
from typing import Any, TypeVar, cast, overload

from hugr import ops
from hugr import tys as ht
Expand Down Expand Up @@ -41,6 +41,7 @@
PyFunc,
find_guppy_module_in_py_module,
get_calling_frame,
sphinx_running,
)
from guppylang.tys.subst import Inst
from guppylang.tys.ty import NumericType
Expand Down Expand Up @@ -429,7 +430,7 @@ def get_module(
return module

def compile_module(self, id: ModuleIdentifier | None = None) -> ModulePointer:
"""Compiles the local module into a Hugr."""
"""Compiles the xlocal module into a Hugr."""
module = self.get_module(id)
if not module:
err = (
Expand Down Expand Up @@ -461,7 +462,56 @@ def registered_modules(self) -> KeysView[ModuleIdentifier]:
return self._modules.keys()


guppy = _Guppy()
class _GuppyDummy:
"""A dummy class with the same interface as `@guppy` that is used during sphinx
builds to mock the decorator.
"""

def __call__(self, arg: PyFunc | GuppyModule) -> Any:
if isinstance(arg, GuppyModule):
return lambda f: f
return arg

def init_module(self, *args: Any, **kwargs: Any) -> None:
pass

def extend_type(self, *args: Any, **kwargs: Any) -> Any:
return lambda cls: cls

def type(self, *args: Any, **kwargs: Any) -> Any:
return lambda cls: cls

def struct(self, *args: Any, **kwargs: Any) -> Any:
return lambda cls: cls

def type_var(self, *args: Any, **kwargs: Any) -> Any:
return lambda cls: cls

def nat_var(self, *args: Any, **kwargs: Any) -> Any:
return lambda cls: cls

def custom(self, *args: Any, **kwargs: Any) -> Any:
return lambda f: f

def hugr_op(self, *args: Any, **kwargs: Any) -> Any:
return lambda f: f

def declare(self, arg: PyFunc | GuppyModule) -> Any:
if isinstance(arg, GuppyModule):
return lambda f: f
return arg

def constant(self, *args: Any, **kwargs: Any) -> Any:
return None

def extern(self, *args: Any, **kwargs: Any) -> Any:
return None

def load(self, *args: Any, **kwargs: Any) -> None:
pass


guppy = cast(_Guppy, _GuppyDummy()) if sphinx_running() else _Guppy()


def _parse_expr_string(ty_str: str, parse_err: str) -> ast.expr:
Expand Down
22 changes: 22 additions & 0 deletions guppylang/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ def load(
Keyword args may be used to specify alias names for the imports.
"""
# Note that we shouldn't evaluate imports during a sphinx build since the @guppy
# decorator is mocked in that case
if sphinx_running():
return

modules: set[GuppyModule] = set()
defs: dict[DefId, CheckedDef] = {}
names: dict[str, DefId] = {}
Expand Down Expand Up @@ -158,6 +163,11 @@ def load(

def load_all(self, mod: GuppyModule | ModuleType) -> None:
"""Imports all public members of a module."""
# Note that we shouldn't evaluate imports during a sphinx build since the @guppy
# decorator is mocked in that case
if sphinx_running():
return

if isinstance(mod, GuppyModule):
mod.check()
self.load(
Expand Down Expand Up @@ -422,3 +432,15 @@ def get_calling_frame() -> FrameType | None:
return frame
frame = frame.f_back
return None


def sphinx_running() -> bool:
"""Checks if this module was imported during a sphinx build."""
# This is the most general solution available at the moment.
# See: https://github.com/sphinx-doc/sphinx/issues/9805
try:
import sphinx # type: ignore[import-untyped, import-not-found, unused-ignore]

return hasattr(sphinx, "application")
except ImportError:
return False

0 comments on commit 1cccc04

Please sign in to comment.