Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for paramspec #255

Merged
merged 6 commits into from Sep 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -3,6 +3,7 @@
## 1.20

- Use hatchling instead of setuptools
- Add support for typing.ParamSpec

## 1.19.2

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Expand Up @@ -28,7 +28,7 @@ optional-dependencies.testing = [
"sphobjinv>=2.2.2",
"typing-extensions>=4.3",
]
optional-dependencies.type_comment = ['typed-ast>=1.5.4; python_version < "3.8"']
optional-dependencies.type-comment = ['typed-ast>=1.5.4; python_version < "3.8"']
dynamic = ["version"]
classifiers = [
"Development Status :: 5 - Production/Stable",
Expand Down
8 changes: 4 additions & 4 deletions src/sphinx_autodoc_typehints/__init__.py
Expand Up @@ -26,7 +26,7 @@ def get_annotation_module(annotation: Any) -> str:
if annotation is None:
return "builtins"
is_new_type = sys.version_info >= (3, 10) and isinstance(annotation, NewType)
if is_new_type or isinstance(annotation, TypeVar):
if is_new_type or isinstance(annotation, TypeVar) or type(annotation).__name__ == "ParamSpec":
return "typing"
if hasattr(annotation, "__module__"):
return annotation.__module__ # type: ignore # deduced Any
Expand Down Expand Up @@ -63,8 +63,8 @@ def get_annotation_class_name(annotation: Any, module: str) -> str:
elif getattr(origin, "_name", None): # Required for Union on Python 3.7+
return origin._name # type: ignore # deduced Any

annotation_cls = annotation if inspect.isclass(annotation) else annotation.__class__
return annotation_cls.__qualname__.lstrip("_") # type: ignore # deduced Any
annotation_cls = annotation if inspect.isclass(annotation) else type(annotation)
return annotation_cls.__qualname__.lstrip("_")


def get_annotation_args(annotation: Any, module: str, class_name: str) -> tuple[Any, ...]:
Expand Down Expand Up @@ -153,7 +153,7 @@ def format_annotation(annotation: Any, config: Config) -> str: # noqa: C901 # t
if full_name == "typing.NewType":
args_format = f"\\(``{annotation.__name__}``, {{}})"
role = "class" if sys.version_info >= (3, 10) else "func"
elif full_name == "typing.TypeVar":
elif full_name in {"typing.TypeVar", "typing.ParamSpec"}:
params = {k: getattr(annotation, f"__{k}__") for k in ("bound", "covariant", "contravariant")}
params = {k: v for k, v in params.items() if v}
if "bound" in params:
Expand Down
9 changes: 9 additions & 0 deletions tests/test_sphinx_autodoc_typehints.py
Expand Up @@ -56,6 +56,10 @@
Z = TypeVar("Z", bound="A")
S = TypeVar("S", bound="miss") # type: ignore # miss not defined on purpose # noqa: F821
W = NewType("W", str)
P = typing_extensions.ParamSpec("P")
P_co = typing_extensions.ParamSpec("P_co", covariant=True) # type: ignore
P_contra = typing_extensions.ParamSpec("P_contra", contravariant=True) # type: ignore
P_bound = typing_extensions.ParamSpec("P_bound", bound=str) # type: ignore

# Mypy does not support recursive type aliases, but
# other type checkers do.
Expand Down Expand Up @@ -239,6 +243,11 @@ def test_parse_annotation(annotation: Any, module: str, class_name: str, args: t
(Y, ":py:class:`~typing.TypeVar`\\(``Y``, bound= :py:class:`str`)"),
(Z, ":py:class:`~typing.TypeVar`\\(``Z``, bound= A)"),
(S, ":py:class:`~typing.TypeVar`\\(``S``, bound= miss)"),
# ParamSpec should behave like TypeVar, except for missing constraints
(P, ":py:class:`~typing.ParamSpec`\\(``P``)"),
(P_co, ":py:class:`~typing.ParamSpec`\\(``P_co``, covariant=True)"),
(P_contra, ":py:class:`~typing.ParamSpec`\\(``P_contra``, contravariant=True)"),
(P_bound, ":py:class:`~typing.ParamSpec`\\(``P_bound``, bound= :py:class:`str`)"),
# ## These test for correct internal tuple rendering, even if not all are valid Tuple types
# Zero-length tuple remains
(Tuple[()], ":py:data:`~typing.Tuple`"),
Expand Down
2 changes: 1 addition & 1 deletion tox.ini
Expand Up @@ -22,7 +22,7 @@ setenv =
COVERAGE_FILE = {toxworkdir}{/}.coverage.{envname}
extras =
testing
type_comments
type-comment
commands =
pytest {tty:--color=yes} {posargs: \
--junitxml {toxworkdir}{/}junit.{envname}.xml --cov {envsitepackagesdir}{/}sphinx_autodoc_typehints --cov {toxinidir}{/}tests \
Expand Down