Skip to content

Commit e6fec11

Browse files
committed
Fix and narrow repr.py typing
First of many stabs at eliminating Any and # type: ignore from the codebase. Here we eliminated several different type ignores; the remaining # type: ignore is just a mypy limitation. I also narrowed the typing of RichReprResult from Iterable[Any|...]] to Iterable[object] since after inspection of the code this is what's really required. There's one particularly nasty cast that's needed. It's definitely a mypy bug that it's not throwing an error for us here. See: python/mypy#1178 and I've opened a similar issue for pyright. There's an open PR for mypy that would resolve this, but at least until this is fixed we need to be casting. In order to correctly type the auto_repr function, I introduced a SupportsRichRepr protocol; we could also have made this runtimecheckable and replaced a hasattr check in the code with it, but it seemed unnecessary so I opted not to.
1 parent eb673d1 commit e6fec11

File tree

1 file changed

+45
-25
lines changed

1 file changed

+45
-25
lines changed

rich/repr.py

+45-25
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,79 @@
11
from functools import partial
22
import inspect
3+
import sys
34

45
from typing import (
5-
Any,
66
Callable,
77
Iterable,
88
List,
99
Optional,
10-
overload,
11-
Union,
1210
Tuple,
1311
Type,
1412
TypeVar,
13+
Union,
14+
cast,
15+
overload,
1516
)
1617

18+
if sys.version_info >= (3, 8):
19+
from typing import Protocol
20+
else:
21+
from typing_extensions import Protocol # pragma: no cover
22+
1723

1824
T = TypeVar("T")
1925

2026

21-
Result = Iterable[Union[Any, Tuple[Any], Tuple[str, Any], Tuple[str, Any, Any]]]
27+
Result = Iterable[
28+
Union[object, Tuple[object], Tuple[str, object], Tuple[str, object, object]]
29+
]
2230
RichReprResult = Result
2331

2432

2533
class ReprError(Exception):
2634
"""An error occurred when attempting to build a repr."""
2735

2836

37+
class SupportsRichRepr(Protocol):
38+
def __rich_repr__(self) -> Result: # pragma: no cover
39+
...
40+
41+
2942
@overload
30-
def auto(cls: Optional[T]) -> T:
43+
def auto(cls: Optional[Type[T]]) -> Type[T]:
3144
...
3245

3346

3447
@overload
35-
def auto(*, angular: bool = False) -> Callable[[T], T]:
48+
def auto(*, angular: bool) -> Callable[[Type[T]], Type[T]]:
3649
...
3750

3851

3952
def auto(
40-
cls: Optional[T] = None, *, angular: Optional[bool] = None
41-
) -> Union[T, Callable[[T], T]]:
53+
cls: Optional[Type[T]] = None, *, angular: Optional[bool] = None
54+
) -> Union[Type[T], Callable[[Type[T]], Type[T]]]:
4255
"""Class decorator to create __repr__ from __rich_repr__"""
4356

4457
def do_replace(cls: Type[T], angular: Optional[bool] = None) -> Type[T]:
45-
def auto_repr(self: Type[T]) -> str:
58+
def auto_repr(self: SupportsRichRepr) -> str:
4659
"""Create repr string from __rich_repr__"""
4760
repr_str: List[str] = []
4861
append = repr_str.append
4962

50-
angular = getattr(self.__rich_repr__, "angular", False) # type: ignore
51-
for arg in self.__rich_repr__(): # type: ignore
63+
angular = getattr(self.__rich_repr__, "angular", False)
64+
for arg in self.__rich_repr__():
5265
if isinstance(arg, tuple):
5366
if len(arg) == 1:
5467
append(repr(arg[0]))
5568
else:
56-
key, value, *default = arg
69+
key, value, *default = cast(
70+
Union[
71+
Tuple[object, ...],
72+
Tuple[str, object],
73+
Tuple[str, object, object],
74+
],
75+
arg,
76+
)
5777
if key is None:
5878
append(repr(value))
5979
else:
@@ -67,10 +87,10 @@ def auto_repr(self: Type[T]) -> str:
6787
else:
6888
return f"{self.__class__.__name__}({', '.join(repr_str)})"
6989

70-
def auto_rich_repr(self: Type[T]) -> Result:
71-
"""Auto generate __rich_rep__ from signature of __init__"""
90+
def auto_rich_repr(self: T) -> Result:
91+
"""Auto generate __rich_repr__ from signature of __init__"""
7292
try:
73-
signature = inspect.signature(self.__init__) ## type: ignore
93+
signature = inspect.signature(self.__init__) # type: ignore[misc]
7494
for name, param in signature.parameters.items():
7595
if param.kind == param.POSITIONAL_ONLY:
7696
yield getattr(self, name)
@@ -89,33 +109,33 @@ def auto_rich_repr(self: Type[T]) -> Result:
89109

90110
if not hasattr(cls, "__rich_repr__"):
91111
auto_rich_repr.__doc__ = "Build a rich repr"
92-
cls.__rich_repr__ = auto_rich_repr # type: ignore
112+
setattr(cls, "__rich_repr__", auto_rich_repr)
93113

94114
auto_repr.__doc__ = "Return repr(self)"
95-
cls.__repr__ = auto_repr # type: ignore
115+
setattr(cls, "__repr__", auto_repr)
96116
if angular is not None:
97-
cls.__rich_repr__.angular = angular # type: ignore
117+
setattr(cast(SupportsRichRepr, cls).__rich_repr__, "angular", angular)
98118
return cls
99119

100120
if cls is None:
101-
return partial(do_replace, angular=angular) # type: ignore
121+
return partial(do_replace, angular=angular)
102122
else:
103-
return do_replace(cls, angular=angular) # type: ignore
123+
return do_replace(cls, angular=angular)
104124

105125

106126
@overload
107-
def rich_repr(cls: Optional[T]) -> T:
127+
def rich_repr(cls: Optional[Type[T]]) -> Type[T]:
108128
...
109129

110130

111131
@overload
112-
def rich_repr(*, angular: bool = False) -> Callable[[T], T]:
132+
def rich_repr(*, angular: bool = False) -> Callable[[Type[T]], Type[T]]:
113133
...
114134

115135

116136
def rich_repr(
117-
cls: Optional[T] = None, *, angular: bool = False
118-
) -> Union[T, Callable[[T], T]]:
137+
cls: Optional[Type[T]] = None, *, angular: bool = False
138+
) -> Union[Type[T], Callable[[Type[T]], Type[T]]]:
119139
if cls is None:
120140
return auto(angular=angular)
121141
else:
@@ -143,7 +163,7 @@ def __rich_repr__(self) -> Result:
143163
console.print(foo, width=30)
144164

145165
console.rule("Angular repr")
146-
Foo.__rich_repr__.angular = True # type: ignore
166+
setattr(Foo.__rich_repr__, "angular", True)
147167

148168
console.print(foo)
149169

0 commit comments

Comments
 (0)