1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
from __future__ import annotations
from typing import Protocol, runtime_checkable
from abcreg import AnnotatedProtocolMeta # type: ignore[import-untyped]
_DEFAULT_METHODS = ("link_name", "subpage_html", "summary_value_suffix_html")
def _install_default_methods(interface: type, impl: type) -> None:
"""Give implementations the protocol's optional methods before signature checks."""
if getattr(impl, "_is_protocol", False):
return
skip = set(interface.__mro__)
for name in _DEFAULT_METHODS:
if any(name in base.__dict__ for base in impl.__mro__ if base not in skip):
continue
for base in interface.__mro__:
default = base.__dict__.get(name)
if callable(default):
setattr(impl, name, default)
break
class _SubpageMeta(AnnotatedProtocolMeta):
def __init__(
cls,
name: str,
bases: tuple[type, ...],
namespace: dict[str, object],
**kwargs: object,
) -> None:
if not getattr(cls, "_is_protocol", False):
for base in bases:
if isinstance(base, _SubpageMeta) and getattr(base, "_is_protocol", False):
_install_default_methods(base, cls)
super().__init__(name, bases, namespace, **kwargs)
def register(cls, subclass: type) -> type:
_install_default_methods(cls, subclass)
return super().register(subclass)
@runtime_checkable
class Subpage(Protocol, metaclass=_SubpageMeta):
"""
Subpage: A per-project subpage provided by a plugin.
Subclass this protocol, or register a virtual subclass with ``@Subpage.register``.
"""
def subpage_name(self) -> str: ...
def link_name(self, project: str) -> str | None:
return None
def subpage_html(self, project: str) -> str | None:
return None
def summary_value_suffix_html(self, project: str, project_enc: str) -> str | None:
"""Optional HTML appended after the subpage \"view\" link on the project summary table."""
return None