From 9f7d0574b53e9e1a82c78b5a07479100c7df7ea2 Mon Sep 17 00:00:00 2001 From: Russell Martin Date: Wed, 12 Jun 2024 21:39:16 -0400 Subject: [PATCH 1/4] Custom type hint doc formatting for `TypeAlias`, `TypeVar` --- core/pyproject.toml | 1 + core/src/toga/types.py | 4 ++++ docs/conf.py | 21 +++++++++++++++++++++ 3 files changed, 26 insertions(+) diff --git a/core/pyproject.toml b/core/pyproject.toml index 5eebb2668b..8bcd246f57 100644 --- a/core/pyproject.toml +++ b/core/pyproject.toml @@ -89,6 +89,7 @@ docs = [ "sphinx == 8.0.2", "sphinx_tabs == 3.4.5", "sphinx-autobuild == 2024.9.3", + "sphinx-autodoc-typehints == 2.1.1", "sphinx-csv-filter == 0.4.2", "sphinx-copybutton == 0.5.2", "sphinx-toolbox == 3.8.0", diff --git a/core/src/toga/types.py b/core/src/toga/types.py index b50553cb1f..bccea5e7b5 100644 --- a/core/src/toga/types.py +++ b/core/src/toga/types.py @@ -13,6 +13,10 @@ PositionT: TypeAlias = toga.Position | tuple[int, int] SizeT: TypeAlias = toga.Size | tuple[int, int] +else: + # sphinx-autodoc-typehints errors when these are gated with TYPE_CHECKING... + PositionT = None + SizeT = None class LatLng(NamedTuple): diff --git a/docs/conf.py b/docs/conf.py index 35a409a998..cccfff9f33 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -32,6 +32,7 @@ "sphinx_tabs.tabs", "crate.sphinx.csv", "sphinx_copybutton", + "sphinx_autodoc_typehints", "sphinx_toolbox.more_autodoc.autonamedtuple", "sphinx_toolbox.more_autodoc.autoprotocol", "sphinx.ext.intersphinx", @@ -377,3 +378,23 @@ def autodoc_process_signature( # If this is True, todolist produce output without file path and line, The default is False. # todo_link_only = False + + +# Disable WARNING: cannot cache unpickable configuration value: 'typehints_formatter' +suppress_warnings = ["config.cache"] + +always_use_bars_union = True + +from pathlib import Path # noqa: E402 +from typing import Union # noqa: E402 + +import toga # noqa: E402 + + +def typehints_formatter(annotation, config): + # print(f"({type(annotation)}) {str(annotation)=}") + if annotation == Union[str, Path, toga.Icon, None]: + return "``IconContentT``" + elif annotation == toga.widgets.base.StyleT: + return "``StyleT``" + return None From b9cb59271fa8607a4480c684e8fbf027dde0b84b Mon Sep 17 00:00:00 2001 From: Russell Martin Date: Wed, 12 Jun 2024 22:15:26 -0400 Subject: [PATCH 2/4] Link `IconContentT` type hint to its c:type doc :tada: --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index cccfff9f33..9a55170759 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -394,7 +394,7 @@ def autodoc_process_signature( def typehints_formatter(annotation, config): # print(f"({type(annotation)}) {str(annotation)=}") if annotation == Union[str, Path, toga.Icon, None]: - return "``IconContentT``" + return ":any:`IconContentT `" elif annotation == toga.widgets.base.StyleT: return "``StyleT``" return None From 7ff98dd1e481f77929c8c8f1e0190c4872f63e48 Mon Sep 17 00:00:00 2001 From: Russell Martin Date: Mon, 17 Jun 2024 17:49:13 -0400 Subject: [PATCH 3/4] Experiment with removing `TypeAlias` --- core/pyproject.toml | 1 + core/src/toga/app.py | 1 + core/src/toga/command.py | 1 + core/src/toga/icons.py | 11 +---- core/src/toga/images.py | 25 +++++------ core/src/toga/types.py | 21 ++-------- core/src/toga/widgets/numberinput.py | 11 +---- core/src/toga/widgets/optioncontainer.py | 26 +++++------- core/src/toga/widgets/splitcontainer.py | 11 +---- core/src/toga/window.py | 1 + docs/conf.py | 53 ++++++++++++++++++++---- 11 files changed, 78 insertions(+), 84 deletions(-) diff --git a/core/pyproject.toml b/core/pyproject.toml index 8bcd246f57..a0da819293 100644 --- a/core/pyproject.toml +++ b/core/pyproject.toml @@ -94,6 +94,7 @@ docs = [ "sphinx-copybutton == 0.5.2", "sphinx-toolbox == 3.8.0", "sphinxcontrib-spelling == 8.0.0", + "typing-extensions", ] [project.urls] diff --git a/core/src/toga/app.py b/core/src/toga/app.py index 24a15b6b91..2298eac011 100644 --- a/core/src/toga/app.py +++ b/core/src/toga/app.py @@ -29,6 +29,7 @@ from toga.dialogs import Dialog from toga.icons import IconContentT + # Make sure deprecation warnings are shown by default warnings.filterwarnings("default", category=DeprecationWarning) diff --git a/core/src/toga/command.py b/core/src/toga/command.py index 744ff2df55..3167cdca8c 100644 --- a/core/src/toga/command.py +++ b/core/src/toga/command.py @@ -9,6 +9,7 @@ from toga.platform import get_platform_factory if TYPE_CHECKING: + import toga.icons from toga.app import App from toga.icons import IconContentT diff --git a/core/src/toga/icons.py b/core/src/toga/icons.py index 8d45474eb7..99c39b2d43 100644 --- a/core/src/toga/icons.py +++ b/core/src/toga/icons.py @@ -1,21 +1,14 @@ from __future__ import annotations -import sys import warnings from collections.abc import Callable, Iterable from pathlib import Path -from typing import TYPE_CHECKING +from typing import TypeAlias, Union import toga from toga.platform import get_platform_factory -if TYPE_CHECKING: - if sys.version_info < (3, 10): - from typing_extensions import TypeAlias - else: - from typing import TypeAlias - - IconContentT: TypeAlias = str | Path | toga.Icon +IconContentT: TypeAlias = Union[str, Path, "toga.Icon"] class cachedicon: diff --git a/core/src/toga/images.py b/core/src/toga/images.py index 57471e96c3..55d90da903 100644 --- a/core/src/toga/images.py +++ b/core/src/toga/images.py @@ -6,7 +6,7 @@ import warnings from functools import lru_cache from pathlib import Path -from typing import TYPE_CHECKING, Any, Protocol, TypeVar +from typing import Any, Protocol, TypeVar, Union from warnings import warn import toga @@ -22,23 +22,18 @@ # Make sure deprecation warnings are shown by default warnings.filterwarnings("default", category=DeprecationWarning) -if TYPE_CHECKING: - if sys.version_info < (3, 10): - from typing_extensions import TypeAlias - else: - from typing import TypeAlias +# Define the types that can be used as Image content +PathLikeT = Union[str, os.PathLike] +BytesLikeT = Union[bytes, bytearray, memoryview] +ImageLikeT = Any - # Define a type variable for generics where an Image type is required. - ImageT = TypeVar("ImageT") +ImageContentT = Union[PathLikeT, BytesLikeT, ImageLikeT] - # Define the types that can be used as Image content - PathLikeT: TypeAlias = str | os.PathLike - BytesLikeT: TypeAlias = bytes | bytearray | memoryview - ImageLikeT: TypeAlias = Any - ImageContentT: TypeAlias = PathLikeT | BytesLikeT | ImageLikeT +# Define a type variable for generics where an Image type is required. +ImageT = TypeVar("ImageT") - # Define a type variable representing an image of an externally defined type. - ExternalImageT = TypeVar("ExternalImageT") +# Define a type variable representing an image of an externally defined type. +ExternalImageT = TypeVar("ExternalImageT") class ImageConverter(Protocol): diff --git a/core/src/toga/types.py b/core/src/toga/types.py index bccea5e7b5..f8c17f20f7 100644 --- a/core/src/toga/types.py +++ b/core/src/toga/types.py @@ -1,22 +1,9 @@ from __future__ import annotations -import sys -from typing import TYPE_CHECKING, NamedTuple - -import toga - -if TYPE_CHECKING: - if sys.version_info < (3, 10): - from typing_extensions import TypeAlias - else: - from typing import TypeAlias - - PositionT: TypeAlias = toga.Position | tuple[int, int] - SizeT: TypeAlias = toga.Size | tuple[int, int] -else: - # sphinx-autodoc-typehints errors when these are gated with TYPE_CHECKING... - PositionT = None - SizeT = None +from typing import NamedTuple, Tuple, Union + +PositionT = Union["toga.Position", Tuple[int, int]] +SizeT = Union["toga.Size", Tuple[int, int]] class LatLng(NamedTuple): diff --git a/core/src/toga/widgets/numberinput.py b/core/src/toga/widgets/numberinput.py index 8a9907fe67..5947a115e9 100644 --- a/core/src/toga/widgets/numberinput.py +++ b/core/src/toga/widgets/numberinput.py @@ -1,23 +1,16 @@ from __future__ import annotations import re -import sys import warnings from decimal import ROUND_HALF_UP, Decimal, InvalidOperation -from typing import TYPE_CHECKING, Any, Protocol, Union +from typing import Any, Protocol, Union import toga from toga.handlers import wrapped_handler from .base import StyleT, Widget -if TYPE_CHECKING: - if sys.version_info < (3, 10): - from typing_extensions import TypeAlias - else: - from typing import TypeAlias - - NumberInputT: TypeAlias = Union[Decimal, int, float, str] +NumberInputT = Union[Decimal, int, float, str] # Implementation notes # ==================== diff --git a/core/src/toga/widgets/optioncontainer.py b/core/src/toga/widgets/optioncontainer.py index 8716ebb7c6..c13c28ad38 100644 --- a/core/src/toga/widgets/optioncontainer.py +++ b/core/src/toga/widgets/optioncontainer.py @@ -1,29 +1,15 @@ from __future__ import annotations -import sys from collections.abc import Iterable -from typing import TYPE_CHECKING, Any, Protocol, overload +from typing import Any, Protocol, Tuple, Union, overload import toga from toga.handlers import wrapped_handler +from toga.icons import Icon, IconContentT from toga.platform import get_platform_factory from .base import StyleT, Widget -if TYPE_CHECKING: - if sys.version_info < (3, 10): - from typing_extensions import TypeAlias - else: - from typing import TypeAlias - from toga.icons import IconContentT - - OptionContainerContentT: TypeAlias = ( - tuple[str, Widget] - | tuple[str, Widget, IconContentT | None] - | tuple[str, Widget, IconContentT | None, bool] - | toga.OptionItem - ) - class OnSelectHandler(Protocol): def __call__(self, widget: OptionContainer, /, **kwargs: Any) -> None: @@ -375,6 +361,14 @@ def insert( item._add_as_option(index, self.interface) +OptionContainerContentT = Union[ + OptionItem, + Tuple[str, Widget], + Tuple[str, Widget, Union[IconContentT, None]], + Tuple[str, Widget, Union[IconContentT, None], bool], +] + + class OptionContainer(Widget): def __init__( self, diff --git a/core/src/toga/widgets/splitcontainer.py b/core/src/toga/widgets/splitcontainer.py index 5d76f2a24a..91c5cef402 100644 --- a/core/src/toga/widgets/splitcontainer.py +++ b/core/src/toga/widgets/splitcontainer.py @@ -1,8 +1,7 @@ from __future__ import annotations -import sys from collections.abc import Sequence -from typing import TYPE_CHECKING +from typing import Tuple, Union from toga.app import App from toga.constants import Direction @@ -10,13 +9,7 @@ from .base import StyleT, Widget -if TYPE_CHECKING: - if sys.version_info < (3, 10): - from typing_extensions import TypeAlias - else: - from typing import TypeAlias - - SplitContainerContentT: TypeAlias = Widget | tuple[Widget, float] | None +SplitContainerContentT = Union[Widget, Tuple[Widget, float], None] class SplitContainer(Widget): diff --git a/core/src/toga/window.py b/core/src/toga/window.py index dbf1f40d75..5c1ce7d498 100644 --- a/core/src/toga/window.py +++ b/core/src/toga/window.py @@ -15,6 +15,7 @@ from toga.types import Position, Size if TYPE_CHECKING: + import toga.icons from toga.app import App from toga.images import ImageT from toga.screens import Screen diff --git a/docs/conf.py b/docs/conf.py index 9a55170759..9000f927f8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -144,6 +144,8 @@ def setup(app): def autodoc_process_signature( app, what, name, obj, options, signature, return_annotation ): + # print(f"{obj=} {signature=}") + if what == "class": # Travertino classes are not part of the public API. bases = [ @@ -160,7 +162,7 @@ def autodoc_process_signature( linkcheck_ignore = [ # GitHub generates anchors in javascript r"https://github.com/.*#", - # References to Github issues/pulls should all be safe. + # References to GitHub issues/pulls should all be safe. r"^https://github.com/beeware/toga/issues/\d+$", r"^https://github.com/beeware/toga/pull/\d+$", ] @@ -385,16 +387,49 @@ def autodoc_process_signature( always_use_bars_union = True -from pathlib import Path # noqa: E402 +from collections.abc import Iterable from typing import Union # noqa: E402 import toga # noqa: E402 +from toga.icons import IconContentT +from toga.widgets.optioncontainer import OptionContainerContentT +from toga.widgets.splitcontainer import SplitContainerContentT + + +def typehints_formatter(annotation, config) -> str: + """Format override for sphinx_autodoc_typehints. + For TypeAliases, this overrides the default behavior of inserting the entire + definition of the TypeAlias in to the documentation for the argument. -def typehints_formatter(annotation, config): - # print(f"({type(annotation)}) {str(annotation)=}") - if annotation == Union[str, Path, toga.Icon, None]: - return ":any:`IconContentT `" - elif annotation == toga.widgets.base.StyleT: - return "``StyleT``" - return None + For TypeVars, this avoids the opaque `TypeVar("NAME", ..., bound="CONSTRAINTS")` + formatting and instead links the name of the TypeVar to its documentation. + + Instead, the user-friendly name of the type is linked to its documentation. + + :param annotation: the resolved annotation for the object + :param config: configuration for Sphinx + :returns: The ReStructured Text for the type or None for the default formatting + """ + if "OptionItem" in str(annotation): + print(f"{OptionContainerContentT=}") + print(f"{annotation=}") + return { + IconContentT: ":any:`IconContentT `", + Union[IconContentT, None]: ":any:`IconContentT `", + toga.images.ImageContentT: ":any:`ImageContentT `", + Union[toga.images.ImageContentT, None]: ":any:`ImageContentT `", + toga.images.ImageT: "``ImageT``", + toga.images.ExternalImageT: "``ExternalImageT``", + OptionContainerContentT: "``OptionContainerContentT``", + Iterable[OptionContainerContentT]: "``OptionContainerContentT``", + Union[Iterable[OptionContainerContentT], None]: "``OptionContainerContentT``", + Union[OptionContainerContentT, None]: "``OptionContainerContentT``", + SplitContainerContentT: ":any:`SplitContainerContentT `", + Union[ + SplitContainerContentT, None + ]: ":any:`SplitContainerContentT `", + toga.widgets.base.StyleT: "``StyleT``", + toga.types.PositionT: "``PositionT``", + toga.types.SizeT: "``SizeT``", + }.get(annotation, None) From e4806dfa2a45c0afa701d14e9c179f79aaf77e29 Mon Sep 17 00:00:00 2001 From: Russell Martin Date: Wed, 18 Sep 2024 15:53:24 -0400 Subject: [PATCH 4/4] Revert "Experiment with removing `TypeAlias`" This reverts commit 13a555652a40dff5a09159fe754fe2afcf632c74. --- core/pyproject.toml | 1 - core/src/toga/app.py | 1 - core/src/toga/command.py | 1 - core/src/toga/icons.py | 11 ++++- core/src/toga/images.py | 25 +++++++----- core/src/toga/types.py | 21 ++++++++-- core/src/toga/widgets/numberinput.py | 11 ++++- core/src/toga/widgets/optioncontainer.py | 26 +++++++----- core/src/toga/widgets/splitcontainer.py | 11 ++++- core/src/toga/window.py | 1 - docs/conf.py | 51 +++++------------------- 11 files changed, 86 insertions(+), 74 deletions(-) diff --git a/core/pyproject.toml b/core/pyproject.toml index a0da819293..8bcd246f57 100644 --- a/core/pyproject.toml +++ b/core/pyproject.toml @@ -94,7 +94,6 @@ docs = [ "sphinx-copybutton == 0.5.2", "sphinx-toolbox == 3.8.0", "sphinxcontrib-spelling == 8.0.0", - "typing-extensions", ] [project.urls] diff --git a/core/src/toga/app.py b/core/src/toga/app.py index 2298eac011..24a15b6b91 100644 --- a/core/src/toga/app.py +++ b/core/src/toga/app.py @@ -29,7 +29,6 @@ from toga.dialogs import Dialog from toga.icons import IconContentT - # Make sure deprecation warnings are shown by default warnings.filterwarnings("default", category=DeprecationWarning) diff --git a/core/src/toga/command.py b/core/src/toga/command.py index 3167cdca8c..744ff2df55 100644 --- a/core/src/toga/command.py +++ b/core/src/toga/command.py @@ -9,7 +9,6 @@ from toga.platform import get_platform_factory if TYPE_CHECKING: - import toga.icons from toga.app import App from toga.icons import IconContentT diff --git a/core/src/toga/icons.py b/core/src/toga/icons.py index 99c39b2d43..8d45474eb7 100644 --- a/core/src/toga/icons.py +++ b/core/src/toga/icons.py @@ -1,14 +1,21 @@ from __future__ import annotations +import sys import warnings from collections.abc import Callable, Iterable from pathlib import Path -from typing import TypeAlias, Union +from typing import TYPE_CHECKING import toga from toga.platform import get_platform_factory -IconContentT: TypeAlias = Union[str, Path, "toga.Icon"] +if TYPE_CHECKING: + if sys.version_info < (3, 10): + from typing_extensions import TypeAlias + else: + from typing import TypeAlias + + IconContentT: TypeAlias = str | Path | toga.Icon class cachedicon: diff --git a/core/src/toga/images.py b/core/src/toga/images.py index 55d90da903..57471e96c3 100644 --- a/core/src/toga/images.py +++ b/core/src/toga/images.py @@ -6,7 +6,7 @@ import warnings from functools import lru_cache from pathlib import Path -from typing import Any, Protocol, TypeVar, Union +from typing import TYPE_CHECKING, Any, Protocol, TypeVar from warnings import warn import toga @@ -22,18 +22,23 @@ # Make sure deprecation warnings are shown by default warnings.filterwarnings("default", category=DeprecationWarning) -# Define the types that can be used as Image content -PathLikeT = Union[str, os.PathLike] -BytesLikeT = Union[bytes, bytearray, memoryview] -ImageLikeT = Any +if TYPE_CHECKING: + if sys.version_info < (3, 10): + from typing_extensions import TypeAlias + else: + from typing import TypeAlias -ImageContentT = Union[PathLikeT, BytesLikeT, ImageLikeT] + # Define a type variable for generics where an Image type is required. + ImageT = TypeVar("ImageT") -# Define a type variable for generics where an Image type is required. -ImageT = TypeVar("ImageT") + # Define the types that can be used as Image content + PathLikeT: TypeAlias = str | os.PathLike + BytesLikeT: TypeAlias = bytes | bytearray | memoryview + ImageLikeT: TypeAlias = Any + ImageContentT: TypeAlias = PathLikeT | BytesLikeT | ImageLikeT -# Define a type variable representing an image of an externally defined type. -ExternalImageT = TypeVar("ExternalImageT") + # Define a type variable representing an image of an externally defined type. + ExternalImageT = TypeVar("ExternalImageT") class ImageConverter(Protocol): diff --git a/core/src/toga/types.py b/core/src/toga/types.py index f8c17f20f7..bccea5e7b5 100644 --- a/core/src/toga/types.py +++ b/core/src/toga/types.py @@ -1,9 +1,22 @@ from __future__ import annotations -from typing import NamedTuple, Tuple, Union - -PositionT = Union["toga.Position", Tuple[int, int]] -SizeT = Union["toga.Size", Tuple[int, int]] +import sys +from typing import TYPE_CHECKING, NamedTuple + +import toga + +if TYPE_CHECKING: + if sys.version_info < (3, 10): + from typing_extensions import TypeAlias + else: + from typing import TypeAlias + + PositionT: TypeAlias = toga.Position | tuple[int, int] + SizeT: TypeAlias = toga.Size | tuple[int, int] +else: + # sphinx-autodoc-typehints errors when these are gated with TYPE_CHECKING... + PositionT = None + SizeT = None class LatLng(NamedTuple): diff --git a/core/src/toga/widgets/numberinput.py b/core/src/toga/widgets/numberinput.py index 5947a115e9..8a9907fe67 100644 --- a/core/src/toga/widgets/numberinput.py +++ b/core/src/toga/widgets/numberinput.py @@ -1,16 +1,23 @@ from __future__ import annotations import re +import sys import warnings from decimal import ROUND_HALF_UP, Decimal, InvalidOperation -from typing import Any, Protocol, Union +from typing import TYPE_CHECKING, Any, Protocol, Union import toga from toga.handlers import wrapped_handler from .base import StyleT, Widget -NumberInputT = Union[Decimal, int, float, str] +if TYPE_CHECKING: + if sys.version_info < (3, 10): + from typing_extensions import TypeAlias + else: + from typing import TypeAlias + + NumberInputT: TypeAlias = Union[Decimal, int, float, str] # Implementation notes # ==================== diff --git a/core/src/toga/widgets/optioncontainer.py b/core/src/toga/widgets/optioncontainer.py index c13c28ad38..8716ebb7c6 100644 --- a/core/src/toga/widgets/optioncontainer.py +++ b/core/src/toga/widgets/optioncontainer.py @@ -1,15 +1,29 @@ from __future__ import annotations +import sys from collections.abc import Iterable -from typing import Any, Protocol, Tuple, Union, overload +from typing import TYPE_CHECKING, Any, Protocol, overload import toga from toga.handlers import wrapped_handler -from toga.icons import Icon, IconContentT from toga.platform import get_platform_factory from .base import StyleT, Widget +if TYPE_CHECKING: + if sys.version_info < (3, 10): + from typing_extensions import TypeAlias + else: + from typing import TypeAlias + from toga.icons import IconContentT + + OptionContainerContentT: TypeAlias = ( + tuple[str, Widget] + | tuple[str, Widget, IconContentT | None] + | tuple[str, Widget, IconContentT | None, bool] + | toga.OptionItem + ) + class OnSelectHandler(Protocol): def __call__(self, widget: OptionContainer, /, **kwargs: Any) -> None: @@ -361,14 +375,6 @@ def insert( item._add_as_option(index, self.interface) -OptionContainerContentT = Union[ - OptionItem, - Tuple[str, Widget], - Tuple[str, Widget, Union[IconContentT, None]], - Tuple[str, Widget, Union[IconContentT, None], bool], -] - - class OptionContainer(Widget): def __init__( self, diff --git a/core/src/toga/widgets/splitcontainer.py b/core/src/toga/widgets/splitcontainer.py index 91c5cef402..5d76f2a24a 100644 --- a/core/src/toga/widgets/splitcontainer.py +++ b/core/src/toga/widgets/splitcontainer.py @@ -1,7 +1,8 @@ from __future__ import annotations +import sys from collections.abc import Sequence -from typing import Tuple, Union +from typing import TYPE_CHECKING from toga.app import App from toga.constants import Direction @@ -9,7 +10,13 @@ from .base import StyleT, Widget -SplitContainerContentT = Union[Widget, Tuple[Widget, float], None] +if TYPE_CHECKING: + if sys.version_info < (3, 10): + from typing_extensions import TypeAlias + else: + from typing import TypeAlias + + SplitContainerContentT: TypeAlias = Widget | tuple[Widget, float] | None class SplitContainer(Widget): diff --git a/core/src/toga/window.py b/core/src/toga/window.py index 5c1ce7d498..dbf1f40d75 100644 --- a/core/src/toga/window.py +++ b/core/src/toga/window.py @@ -15,7 +15,6 @@ from toga.types import Position, Size if TYPE_CHECKING: - import toga.icons from toga.app import App from toga.images import ImageT from toga.screens import Screen diff --git a/docs/conf.py b/docs/conf.py index 9000f927f8..e544a0846a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -144,8 +144,6 @@ def setup(app): def autodoc_process_signature( app, what, name, obj, options, signature, return_annotation ): - # print(f"{obj=} {signature=}") - if what == "class": # Travertino classes are not part of the public API. bases = [ @@ -387,49 +385,22 @@ def autodoc_process_signature( always_use_bars_union = True -from collections.abc import Iterable -from typing import Union # noqa: E402 +from collections.abc import Iterable # noqa: E402 +from pathlib import Path # noqa: E402 +from typing import Optional, TypeVar, Union # noqa: E402 import toga # noqa: E402 -from toga.icons import IconContentT -from toga.widgets.optioncontainer import OptionContainerContentT -from toga.widgets.splitcontainer import SplitContainerContentT - - -def typehints_formatter(annotation, config) -> str: - """Format override for sphinx_autodoc_typehints. - For TypeAliases, this overrides the default behavior of inserting the entire - definition of the TypeAlias in to the documentation for the argument. +md_none = "" - For TypeVars, this avoids the opaque `TypeVar("NAME", ..., bound="CONSTRAINTS")` - formatting and instead links the name of the TypeVar to its documentation. - Instead, the user-friendly name of the type is linked to its documentation. +def typehints_formatter(annotation, config): + print(f"({type(annotation)}) {str(annotation)=}") - :param annotation: the resolved annotation for the object - :param config: configuration for Sphinx - :returns: The ReStructured Text for the type or None for the default formatting - """ - if "OptionItem" in str(annotation): - print(f"{OptionContainerContentT=}") - print(f"{annotation=}") return { - IconContentT: ":any:`IconContentT `", - Union[IconContentT, None]: ":any:`IconContentT `", - toga.images.ImageContentT: ":any:`ImageContentT `", - Union[toga.images.ImageContentT, None]: ":any:`ImageContentT `", - toga.images.ImageT: "``ImageT``", - toga.images.ExternalImageT: "``ExternalImageT``", - OptionContainerContentT: "``OptionContainerContentT``", - Iterable[OptionContainerContentT]: "``OptionContainerContentT``", - Union[Iterable[OptionContainerContentT], None]: "``OptionContainerContentT``", - Union[OptionContainerContentT, None]: "``OptionContainerContentT``", - SplitContainerContentT: ":any:`SplitContainerContentT `", - Union[ - SplitContainerContentT, None - ]: ":any:`SplitContainerContentT `", - toga.widgets.base.StyleT: "``StyleT``", - toga.types.PositionT: "``PositionT``", - toga.types.SizeT: "``SizeT``", + Union[str, Path, toga.Icon, None]: ":any:`IconContentT `", + toga.widgets.base.StyleT: ":any:`StyleT `", + Optional[toga.widgets.base.StyleT]: ":any:`StyleT ` | :any:`None`", + TypeVar("SourceT", bound=toga.sources.Source): "asdf", + Union[TypeVar("SourceT", bound=toga.sources.Source), Iterable, None]: "xcvb", }.get(annotation, None)