Skip to content

Commit

Permalink
Fix tests for Sphinx 8.1, remove Python 3.9 support (#178)
Browse files Browse the repository at this point in the history
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Philipp A. <[email protected]>
  • Loading branch information
pre-commit-ci[bot] and flying-sheep authored Oct 22, 2024
1 parent a94d488 commit 16857cd
Show file tree
Hide file tree
Showing 14 changed files with 116 additions and 85 deletions.
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

0 comments on commit 16857cd

Please sign in to comment.