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

Fix tests for Sphinx 8.1, remove Python 3.9 support #178

Merged
merged 8 commits into from
Oct 22, 2024
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12"]
python-version: ["3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v4
with:
Expand All @@ -31,11 +31,11 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: pip
- uses: hynek/setup-cached-uv@v2
with:
cache-dependency-path: pyproject.toml
- name: dependencies
run: |
pip install --upgrade pip wheel
pip install .[test,typehints,myst] coverage-rich 'anyconfig[toml] >=0.14'
run: uv pip install --system .[test,typehints,myst] coverage-rich 'anyconfig[toml] >=0.14'
- name: tests
run: coverage run -m pytest --verbose --color=yes
- name: show coverage
Expand Down
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ repos:
hooks:
- id: trailing-whitespace
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.9
rev: v0.7.0
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
Expand All @@ -17,7 +17,7 @@ repos:
- [email protected]
- [email protected]
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.11.2
rev: v1.12.1
hooks:
- id: mypy
additional_dependencies:
Expand Down
4 changes: 2 additions & 2 deletions .readthedocs.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
version: 2
build:
os: ubuntu-22.04
os: ubuntu-24.04
tools:
python: "3.11"
python: "3.12"
python:
install:
- method: pip
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,9 @@ classifiers = [
'Framework :: Sphinx :: Extension',
'Typing :: Typed',
]
requires-python = '>=3.9'
requires-python = '>=3.10'
dependencies = [
'sphinx>=7.0',
'get-annotations; python_version < "3.10"',
]

[project.optional-dependencies]
Expand All @@ -30,6 +29,7 @@ test = [
'coverage',
'legacy-api-wrap',
'defusedxml', # sphinx[test] would also pull in cython
'sphinx>=8.1.0' # https://github.com/sphinx-doc/sphinx/pull/12743
]
doc = [
'scanpydoc[typehints,myst,theme]',
Expand Down
9 changes: 1 addition & 8 deletions src/scanpydoc/elegant_typehints/_formatting.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from __future__ import annotations

import sys
import inspect
from types import GenericAlias
from typing import TYPE_CHECKING, Any, cast, get_args, get_origin
Expand All @@ -15,12 +14,6 @@
from sphinx.config import Config


if sys.version_info >= (3, 10):
from types import UnionType
else: # pragma: no cover
UnionType = None


def typehints_formatter(annotation: type[Any], config: Config) -> str | None:
"""Generate reStructuredText containing links to the types.

Expand All @@ -43,7 +36,7 @@ def typehints_formatter(annotation: type[Any], config: Config) -> str | None:

tilde = "" if config.typehints_fully_qualified else "~"

if isinstance(annotation, (GenericAlias, _GenericAlias)):
if isinstance(annotation, GenericAlias | _GenericAlias):
args = get_args(annotation)
annotation = cast(type[Any], get_origin(annotation))
else:
Expand Down
11 changes: 3 additions & 8 deletions src/scanpydoc/elegant_typehints/_return_tuple.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from __future__ import annotations

import re
import sys
import inspect
from types import UnionType
from typing import TYPE_CHECKING, Union, get_args, get_origin, get_type_hints
from typing import Tuple as t_Tuple # noqa: UP035
from logging import getLogger
Expand All @@ -19,12 +19,7 @@
from sphinx.ext.autodoc import Options


if sys.version_info > (3, 10):
from types import UnionType

UNION_TYPES = {Union, UnionType}
else: # pragma: no cover
UNION_TYPES = {Union}
UNION_TYPES = {Union, UnionType}


__all__ = ["process_docstring", "_parse_returns_section", "setup"]
Expand Down Expand Up @@ -77,7 +72,7 @@ def process_docstring( # noqa: PLR0913

idxs_ret_names = _get_idxs_ret_names(lines)
if len(idxs_ret_names) == len(ret_types):
for l, rt in zip(idxs_ret_names, ret_types):
for l, rt in zip(idxs_ret_names, ret_types, strict=False):
typ = format_annotation(rt, app.config)
if (line := lines[l]).lstrip() in {":returns: :", ":return: :", ":"}:
transformed = f"{line[:-1]}{typ}"
Expand Down
7 changes: 2 additions & 5 deletions src/scanpydoc/rtd_github_links/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,7 @@ def _infer_vars(config: Config) -> tuple[str, PurePosixPath]:


def _get_annotations(obj: _SourceObjectType) -> dict[str, Any]:
if sys.version_info > (3, 10):
from inspect import get_annotations
else: # pragma: no cover
from get_annotations import get_annotations
from inspect import get_annotations

try:
return get_annotations(obj) # type: ignore[no-any-return,arg-type,unused-ignore]
Expand Down Expand Up @@ -159,7 +156,7 @@ def _get_obj_module(qualname: str) -> tuple[Any, ModuleType]:
raise e from None
if isinstance(thing, ModuleType): # pragma: no cover
mod = thing
elif is_dataclass(obj) or isinstance(thing, (GenericAlias, _GenericAlias)):
elif is_dataclass(obj) or isinstance(thing, GenericAlias | _GenericAlias):
obj = thing
else:
obj = thing
Expand Down
24 changes: 24 additions & 0 deletions src/scanpydoc/testing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"""Testing utilities."""

from __future__ import annotations

from typing import TYPE_CHECKING, Protocol


if TYPE_CHECKING:
from typing import Any

from sphinx.testing.util import SphinxTestApp


class MakeApp(Protocol):
"""Create a SphinxTestApp instance."""

def __call__( # noqa: D102
self,
builder: str = "html",
/,
*,
exception_on_warning: bool = False,
**conf: Any, # noqa: ANN401
) -> SphinxTestApp: ...
25 changes: 19 additions & 6 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,29 @@
from pathlib import Path
from collections.abc import Callable, Generator

from sphinx.application import Sphinx
from sphinx.testing.util import SphinxTestApp

from scanpydoc.testing import MakeApp


@pytest.fixture
def make_app_setup(
make_app: Callable[..., Sphinx], tmp_path: Path
) -> Callable[..., Sphinx]:
def make_app_setup(builder: str = "html", /, **conf: Any) -> Sphinx: # noqa: ANN401
def make_app_setup(make_app: type[SphinxTestApp], tmp_path: Path) -> MakeApp:
def make_app_setup(
builder: str = "html",
/,
*,
exception_on_warning: bool = False,
**conf: Any, # noqa: ANN401
) -> SphinxTestApp:
(tmp_path / "conf.py").write_text("")
return make_app(buildername=builder, srcdir=tmp_path, confoverrides=conf)
conf.setdefault("suppress_warnings", []).append("app.add_node")
return make_app(
buildername=builder,
srcdir=tmp_path,
confoverrides=conf,
warningiserror=exception_on_warning,
exception_on_warning=exception_on_warning,
)

return make_app_setup

Expand Down
6 changes: 3 additions & 3 deletions tests/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@


if TYPE_CHECKING:
from collections.abc import Callable

import pytest
from sphinx.application import Sphinx

from scanpydoc.testing import MakeApp


DEPRECATED = frozenset({"scanpydoc.autosummary_generate_imported"})


def test_all_get_installed(
monkeypatch: pytest.MonkeyPatch, make_app_setup: Callable[..., Sphinx]
monkeypatch: pytest.MonkeyPatch, make_app_setup: MakeApp
) -> None:
setups_seen: set[str] = set()
setups_called: dict[str, Sphinx] = {}
Expand Down
13 changes: 7 additions & 6 deletions tests/test_definition_list_typed_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@


if TYPE_CHECKING:
from collections.abc import Callable

from sphinx.application import Sphinx

from scanpydoc.testing import MakeApp


@pytest.fixture
def app(make_app_setup: Callable[..., Sphinx]) -> Sphinx:
def app(make_app_setup: MakeApp) -> Sphinx:
app = make_app_setup()
app.setup_extension("scanpydoc.definition_list_typed_field")
return app
Expand All @@ -41,7 +41,7 @@ def app(make_app_setup: Callable[..., Sphinx]) -> Sphinx:
"""


def test_apps_separate(app: Sphinx, make_app_setup: Callable[..., Sphinx]) -> None:
def test_apps_separate(app: Sphinx, make_app_setup: MakeApp) -> None:
app_no_setup = make_app_setup()
assert app is not app_no_setup
assert "scanpydoc.definition_list_typed_field" in app.extensions
Expand Down Expand Up @@ -97,11 +97,12 @@ def test_convert_params(
assert isinstance(cyr := term[2], nodes.classifier)
assert len(cyr) == len(conv_types), cyr.children
assert all(
isinstance(cyr_part, conv_type) for cyr_part, conv_type in zip(cyr, conv_types)
isinstance(cyr_part, conv_type)
for cyr_part, conv_type in zip(cyr, conv_types, strict=True)
)


def test_load_error(make_app_setup: Callable[..., Sphinx]) -> None:
def test_load_error(make_app_setup: MakeApp) -> None:
with pytest.raises(RuntimeError, match=r"Please load sphinx\.ext\.napoleon before"):
make_app_setup(
extensions=["scanpydoc.definition_list_typed_field", "sphinx.ext.napoleon"]
Expand Down
29 changes: 10 additions & 19 deletions tests/test_elegant_typehints.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,9 @@
from __future__ import annotations

import re
import sys
import inspect
from io import StringIO
from typing import (
TYPE_CHECKING,
Any,
Union,
AnyStr,
NoReturn,
Optional,
cast,
get_origin,
)
from typing import TYPE_CHECKING, Any, AnyStr, NoReturn, cast, get_origin
from pathlib import Path
from operator import attrgetter
from collections.abc import Mapping, Callable
Expand All @@ -31,6 +21,8 @@

from sphinx.application import Sphinx

from scanpydoc.testing import MakeApp

class ProcessDoc(Protocol): # noqa: D101
def __call__( # noqa: D102
self, fn: Callable[..., Any], *, run_napoleon: bool = False
Expand Down Expand Up @@ -60,7 +52,7 @@ class Gen(Generic[T]): pass


@pytest.fixture
def app(make_app_setup: Callable[..., Sphinx]) -> Sphinx:
def app(make_app_setup: MakeApp) -> Sphinx:
return make_app_setup(
master_doc="index",
extensions=[
Expand Down Expand Up @@ -256,8 +248,8 @@ def fn_test(m: object) -> None: # pragma: no cover
AnyStr,
NoReturn,
Callable[[int], None],
Union[int, str],
Union[int, str, None],
int | str,
int | str | None,
],
ids=lambda p: str(p).replace("typing.", ""),
)
Expand All @@ -274,7 +266,6 @@ def test_typing_classes(app: Sphinx, annotation: type) -> None:
assert output is None or output.startswith(f":py:data:`typing.{name}")


@pytest.mark.skipif(sys.version_info < (3, 10), reason="requires Python 3.10+")
def test_union_type(app: Sphinx) -> None:
union = eval("int | str") # noqa: S307
assert typehints_formatter(union, app.config) is None
Expand Down Expand Up @@ -389,15 +380,15 @@ class B:
("return_ann", "foo_rendered"),
[
pytest.param(tuple[str, int], ":py:class:`str`", id="tuple"),
pytest.param(Optional[tuple[str, int]], ":py:class:`str`", id="tuple | None"),
pytest.param(tuple[str, int] | None, ":py:class:`str`", id="tuple | None"),
pytest.param(
tuple[Mapping[str, float], int],
r":py:class:`~collections.abc.Mapping`\ \["
":py:class:`str`, :py:class:`float`"
"]",
id="complex",
),
pytest.param(Optional[int], None, id="int | None"),
pytest.param(int | None, None, id="int | None"),
],
)
def test_return_tuple(
Expand Down Expand Up @@ -472,14 +463,14 @@ def fn() -> tuple[int, str]: # pragma: no cover
assert res[2].startswith(":rtype: :sphinx_autodoc_typehints_type:")


def test_load_without_sat(make_app_setup: Callable[..., Sphinx]) -> None:
def test_load_without_sat(make_app_setup: MakeApp) -> None:
make_app_setup(
master_doc="index",
extensions=["sphinx.ext.autodoc", "scanpydoc.elegant_typehints"],
)


def test_load_error(make_app_setup: Callable[..., Sphinx]) -> None:
def test_load_error(make_app_setup: MakeApp) -> None:
with pytest.raises(
RuntimeError,
match=r"`scanpydoc.elegant_typehints` requires `sphinx.ext.autodoc`",
Expand Down
Loading