Skip to content

Commit

Permalink
This moves the call to post_convert to immediately following to_ir
Browse files Browse the repository at this point in the history
This uses a decorator to call post_convert after each call to `to_ir` so that it
can't be forgotten. In order to allow subclasses to restrict the type of
`to_ir`, we use a decorator (I originally tried a similar approach as with
`type.render_name` but it doesn't work with the subclass type restrictions).
Typing the decorator took a bit of trial and error, but the key thing is to say
that the decorator doesn't change the type of the function.
  • Loading branch information
hoodmane committed Oct 2, 2023
1 parent 5d9b3fe commit f87e05f
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 3 deletions.
2 changes: 0 additions & 2 deletions sphinx_js/analyzer_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ def search_node_modules(cmdname: str, cmdpath: str, dir: str | Path) -> str:
# search for local install
for base in parent_dirs:
typedoc = base / "node_modules" / cmdpath
print(base, typedoc)

if typedoc.is_file():
return str(typedoc.resolve())

Expand Down
38 changes: 37 additions & 1 deletion sphinx_js/typedoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import typing
from collections.abc import Iterable, Iterator, Sequence
from errno import ENOENT
from functools import cache
from functools import cache, wraps
from inspect import isclass
from json import load
from operator import attrgetter
Expand All @@ -28,6 +28,31 @@
MIN_TYPEDOC_VERSION = (0, 25, 0)


T = typing.TypeVar("T")
P = typing.ParamSpec("P")


def post_convert(f: typing.Callable[P, T]) -> typing.Callable[P, T]:
"""Wrap to_ir with a call to post_convert if it returned a result.
I treid to be more specific about the type of the decorator but it caused
mypy to change the type of the decorated function. This signature ensures
that the inferred type of the decorated method is identical to the type of
the original method and then uses a couple of carefully placed casts.
"""

@wraps(f)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
result = f(*args, **kwargs)
converted, *_ = typing.cast(tuple[ir.TopLevel | None, ...], result)
if converted:
self, converter = typing.cast(tuple[Node | Signature, Converter], args)
converter._post_convert(converter, self, converted)
return result

return wrapper


@cache
def typedoc_version_info(typedoc: str) -> tuple[tuple[int, ...], tuple[int, ...]]:
result = subprocess.run(
Expand Down Expand Up @@ -494,6 +519,7 @@ def _top_level_properties(self) -> TopLevelPropertiesDict:
exported_from=ir.Pathname(self.filepath),
)

@post_convert
def to_ir(
self, converter: Converter
) -> tuple[ir.TopLevel | None, Sequence["Node"]]:
Expand Down Expand Up @@ -528,6 +554,7 @@ def comment(self) -> Comment:
return self.setSignature.comment
return self.comment_

@post_convert
def to_ir(self, converter: Converter) -> tuple[ir.Attribute, Sequence["Node"]]:
if self.getSignature:
# There's no signature to speak of for a getter: only a return type.
Expand Down Expand Up @@ -578,6 +605,7 @@ def comment(self) -> Comment:
def _path_segments(self, base_dir: str) -> list[str]:
return [self.name]

@post_convert
def to_ir(
self, converter: Converter
) -> tuple[ir.Function | None, Sequence["Node"]]:
Expand Down Expand Up @@ -675,6 +703,7 @@ def _constructor_and_members(
class Class(ClassOrInterface):
kindString: Literal["Class"]

@post_convert
def to_ir(self, converter: Converter) -> tuple[ir.Class | None, Sequence["Node"]]:
constructor, members = self._constructor_and_members(converter)
result = ir.Class(
Expand All @@ -694,6 +723,7 @@ def to_ir(self, converter: Converter) -> tuple[ir.Class | None, Sequence["Node"]
class Interface(ClassOrInterface):
kindString: Literal["Interface"]

@post_convert
def to_ir(self, converter: Converter) -> tuple[ir.Interface, Sequence["Node"]]:
_, members = self._constructor_and_members(converter)
result = ir.Interface(
Expand All @@ -718,6 +748,7 @@ def children_with_ids(self) -> Iterator["IndexType"]:
if isinstance(self.type, ReflectionType):
yield self.type.declaration

@post_convert
def to_ir(
self, converter: Converter
) -> tuple[ir.Attribute | ir.Function | None, Sequence["Node"]]:
Expand Down Expand Up @@ -793,6 +824,7 @@ def render(self, converter: Converter) -> Iterator[str | ir.TypeXRef]:
yield "; "
yield "}"

@post_convert
def to_ir(
self, converter: Converter
) -> tuple[ir.Function | None, Sequence["Node"]]:
Expand Down Expand Up @@ -1024,9 +1056,13 @@ def inner(param: Param) -> Iterator[str | ir.TypeXRef]:
else:
yield ir.TypeXRefIntrinsic("void")

# Don't wrap this in @post_convert since it'll always be covered by the
# owner of the signature.
def to_ir(
self, converter: Converter
) -> tuple[ir.Function | None, Sequence["Node"]]:
# TODO: The following shouldn't happen in to_ir since it mutates the
# Node
SYMBOL_PREFIX = "[Symbol\u2024"
if self.name.startswith("[") and not self.name.startswith(SYMBOL_PREFIX):
# a symbol.
Expand Down

0 comments on commit f87e05f

Please sign in to comment.