Skip to content

Commit a79be6c

Browse files
authored
Call post_convert immediately following to_ir (#111)
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.
1 parent 5d9b3fe commit a79be6c

File tree

2 files changed

+37
-3
lines changed

2 files changed

+37
-3
lines changed

sphinx_js/analyzer_utils.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ def search_node_modules(cmdname: str, cmdpath: str, dir: str | Path) -> str:
2828
# search for local install
2929
for base in parent_dirs:
3030
typedoc = base / "node_modules" / cmdpath
31-
print(base, typedoc)
32-
3331
if typedoc.is_file():
3432
return str(typedoc.resolve())
3533

sphinx_js/typedoc.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import typing
88
from collections.abc import Iterable, Iterator, Sequence
99
from errno import ENOENT
10-
from functools import cache
10+
from functools import cache, wraps
1111
from inspect import isclass
1212
from json import load
1313
from operator import attrgetter
@@ -28,6 +28,31 @@
2828
MIN_TYPEDOC_VERSION = (0, 25, 0)
2929

3030

31+
T = typing.TypeVar("T")
32+
P = typing.ParamSpec("P")
33+
34+
35+
def post_convert(f: typing.Callable[P, T]) -> typing.Callable[P, T]:
36+
"""Wrap to_ir with a call to post_convert if it returned a result.
37+
38+
I treid to be more specific about the type of the decorator but it caused
39+
mypy to change the type of the decorated function. This signature ensures
40+
that the inferred type of the decorated method is identical to the type of
41+
the original method and then uses a couple of carefully placed casts.
42+
"""
43+
44+
@wraps(f)
45+
def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
46+
result = f(*args, **kwargs)
47+
converted, *_ = typing.cast(tuple[ir.TopLevel | None, ...], result)
48+
if converted:
49+
self, converter = typing.cast(tuple[Node | Signature, Converter], args)
50+
converter._post_convert(converter, self, converted)
51+
return result
52+
53+
return wrapper
54+
55+
3156
@cache
3257
def typedoc_version_info(typedoc: str) -> tuple[tuple[int, ...], tuple[int, ...]]:
3358
result = subprocess.run(
@@ -494,6 +519,7 @@ def _top_level_properties(self) -> TopLevelPropertiesDict:
494519
exported_from=ir.Pathname(self.filepath),
495520
)
496521

522+
@post_convert
497523
def to_ir(
498524
self, converter: Converter
499525
) -> tuple[ir.TopLevel | None, Sequence["Node"]]:
@@ -528,6 +554,7 @@ def comment(self) -> Comment:
528554
return self.setSignature.comment
529555
return self.comment_
530556

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

608+
@post_convert
581609
def to_ir(
582610
self, converter: Converter
583611
) -> tuple[ir.Function | None, Sequence["Node"]]:
@@ -675,6 +703,7 @@ def _constructor_and_members(
675703
class Class(ClassOrInterface):
676704
kindString: Literal["Class"]
677705

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

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

751+
@post_convert
721752
def to_ir(
722753
self, converter: Converter
723754
) -> tuple[ir.Attribute | ir.Function | None, Sequence["Node"]]:
@@ -793,6 +824,7 @@ def render(self, converter: Converter) -> Iterator[str | ir.TypeXRef]:
793824
yield "; "
794825
yield "}"
795826

827+
@post_convert
796828
def to_ir(
797829
self, converter: Converter
798830
) -> tuple[ir.Function | None, Sequence["Node"]]:
@@ -1024,9 +1056,13 @@ def inner(param: Param) -> Iterator[str | ir.TypeXRef]:
10241056
else:
10251057
yield ir.TypeXRefIntrinsic("void")
10261058

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

0 commit comments

Comments
 (0)