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

fix: Mock guppy decorator during sphinx builds #622

Merged
merged 3 commits into from
Nov 5, 2024
Merged
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
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
Loading