Skip to content

Commit d9ede38

Browse files
committed
Add ruff config
1 parent 3daa5c1 commit d9ede38

File tree

5 files changed

+126
-57
lines changed

5 files changed

+126
-57
lines changed

pyproject.toml

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,76 @@ build-backend = "setuptools.build_meta"
5555
dev = [
5656
"sphinx>=8.1.3",
5757
]
58+
59+
60+
[tool.ruff]
61+
# Exclude a variety of commonly ignored directories.
62+
exclude = [
63+
".bzr",
64+
".direnv",
65+
".eggs",
66+
".git",
67+
".git-rewrite",
68+
".hg",
69+
".ipynb_checkpoints",
70+
".mypy_cache",
71+
".nox",
72+
".pants.d",
73+
".pyenv",
74+
".pytest_cache",
75+
".pytype",
76+
".ruff_cache",
77+
".svn",
78+
".tox",
79+
".venv",
80+
".vscode",
81+
"__pypackages__",
82+
"_build",
83+
"buck-out",
84+
"build",
85+
"dist",
86+
"node_modules",
87+
"site-packages",
88+
"venv",
89+
]
90+
91+
# Same as Black.
92+
line-length = 88
93+
indent-width = 4
94+
95+
target-version = "py310"
96+
97+
98+
[tool.ruff.lint]
99+
select = ["E", # pycodestyle
100+
"F", # pyflakes
101+
"PL", # pylint
102+
"UP", # refurb
103+
"R", # pylint-related
104+
"D", # docstring
105+
"PT", # pytest
106+
"B", # bugbear
107+
"D212", # summary of multi-line docstrings should start at first line
108+
"D417", # undocumented-param
109+
"I", # isort
110+
]
111+
ignore = [
112+
"PLR2004", # magic value used in comparison
113+
"PLR0911", # too many return statements
114+
"PLR0912", # too many branches
115+
"PLR0915", # too many statements
116+
"D100", # missing docstring in public module
117+
"D101", # missing docstring in public class
118+
"D102", # missing docstring in public method
119+
"D103", # missing docstring in public function
120+
"D104", # missing docstring in public package
121+
"D105", # missing docstring in magic method
122+
"D107", # missing docstring in __init__
123+
"D401", # imperative mood
124+
]
125+
126+
[tool.ruff.lint.pydocstyle]
127+
convention = "numpy"
128+
129+
[tool.ruff.lint.isort]
130+
force-single-line = true

src/pytest_sphinx.py

Lines changed: 36 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,24 @@
1-
"""
1+
"""Sphinx pytest plugin.
2+
23
http://www.sphinx-doc.org/en/stable/ext/doctest.html
3-
https://github.com/sphinx-doc/sphinx/blob/master/sphinx/ext/doctest.py
4+
https://github.com/sphinx-doc/sphinx/blob/master/sphinx/ext/doctest.py.
45
56
* TODO
67
** CLEANUP: use the sphinx directive parser from the sphinx project
78
"""
89

10+
from __future__ import annotations
11+
912
import doctest
1013
import enum
1114
import re
1215
import sys
1316
import textwrap
1417
import traceback
18+
from collections.abc import Iterator
1519
from pathlib import Path
1620
from typing import TYPE_CHECKING
1721
from typing import Any
18-
from typing import Dict
19-
from typing import Iterator
20-
from typing import List
21-
from typing import Optional
22-
from typing import Tuple
23-
from typing import Union
2422

2523
import _pytest.doctest
2624
import pytest
@@ -60,12 +58,12 @@ class SphinxDoctestDirectives(enum.Enum):
6058

6159

6260
def pytest_collect_file(
63-
file_path: Path, parent: Union[Session, Package]
64-
) -> Optional[Union["SphinxDoctestModule", "SphinxDoctestTextfile"]]:
61+
file_path: Path, parent: Session | Package
62+
) -> SphinxDoctestModule | SphinxDoctestTextfile | None:
6563
config = parent.config
6664
if file_path.suffix == ".py":
6765
if config.option.doctestmodules:
68-
mod: Union["SphinxDoctestModule", "SphinxDoctestTextfile"] = (
66+
mod: SphinxDoctestModule | SphinxDoctestTextfile = (
6967
SphinxDoctestModule.from_parent(parent, path=file_path)
7068
)
7169
return mod
@@ -74,10 +72,10 @@ def pytest_collect_file(
7472
return None
7573

7674

77-
GlobDict = Dict[str, Any]
75+
GlobDict = dict[str, Any]
7876

7977

80-
def _is_doctest(config: Config, path: Path, parent: Union[Session, Package]) -> bool:
78+
def _is_doctest(config: Config, path: Path, parent: Session | Package) -> bool:
8179
if path.suffix in (".txt", ".rst") and parent.session.isinitpath(path):
8280
return True
8381
globs = config.getoption("doctestglob") or ["test*.txt"]
@@ -108,7 +106,7 @@ def _is_doctest(config: Config, path: Path, parent: Union[Session, Package]) ->
108106

109107
def _split_into_body_and_options(
110108
section_content: str,
111-
) -> Tuple[str, Optional[str], Dict[int, bool]]:
109+
) -> tuple[str, str | None, dict[int, bool]]:
112110
"""Parse the the full content of a directive and split it.
113111
114112
It is split into a string, where the options (:options:, :hide: and
@@ -184,8 +182,8 @@ def _split_into_body_and_options(
184182

185183

186184
def _get_next_textoutputsections(
187-
sections: List["Section"], index: int
188-
) -> Iterator["Section"]:
185+
sections: list[Section], index: int
186+
) -> Iterator[Section]:
189187
"""Yield successive TESTOUTPUT sections."""
190188
for j in range(index, len(sections)):
191189
section = sections[j]
@@ -195,7 +193,7 @@ def _get_next_textoutputsections(
195193
break
196194

197195

198-
SectionGroups = Optional[List[str]]
196+
SectionGroups = list[str] | None
199197

200198

201199
class Section:
@@ -221,7 +219,7 @@ def __init__(
221219
self.options = options
222220

223221

224-
def get_sections(docstring: str) -> List[Union[Any, Section]]:
222+
def get_sections(docstring: str) -> list[Any | Section]:
225223
lines = textwrap.dedent(docstring).splitlines()
226224
sections = []
227225

@@ -271,11 +269,11 @@ def add_match(
271269

272270

273271
def docstring2examples(
274-
docstring: str, globs: Optional[GlobDict] = None
275-
) -> List[Union[Any, doctest.Example]]:
276-
"""
277-
Parse all sphinx test directives in the docstring and create a
278-
list of examples.
272+
docstring: str, globs: GlobDict | None = None
273+
) -> list[Any | doctest.Example]:
274+
"""Parse all sphinx test directives in the docstring.
275+
276+
This function also creates a list of examples that are returned.
279277
"""
280278
# TODO subclass doctest.DocTestParser instead?
281279

@@ -285,11 +283,11 @@ def docstring2examples(
285283
sections = get_sections(docstring)
286284

287285
def get_testoutput_section_data(
288-
section: "Section",
289-
) -> Tuple[str, Dict[int, bool], int, Optional[Any]]:
286+
section: Section,
287+
) -> tuple[str, dict[int, bool], int, Any | None]:
290288
want = section.body
291289
exc_msg = None
292-
options: Dict[int, bool] = {}
290+
options: dict[int, bool] = {}
293291

294292
if section.skipif_expr and eval(section.skipif_expr, globs):
295293
want = ""
@@ -344,20 +342,19 @@ def get_testoutput_section_data(
344342

345343

346344
class SphinxDocTestRunner(doctest.DebugRunner):
347-
"""
348-
overwrite doctest.DocTestRunner.__run, since it uses 'single' for the
349-
`compile` function instead of 'exec'.
345+
"""Overwrite `doctest.DocTestRunner.__run`.
346+
347+
since it uses 'single' for the `compile` function instead of 'exec'.
350348
"""
351349

352-
_checker: "doctest.OutputChecker"
353-
_fakeout: "_SpoofOut"
354-
debugger: "pdb.Pdb"
350+
_checker: doctest.OutputChecker
351+
_fakeout: _SpoofOut
352+
debugger: pdb.Pdb
355353

356354
def _DocTestRunner__run(
357-
self, test: doctest.DocTest, compileflags: int, out: "_Out"
355+
self, test: doctest.DocTest, compileflags: int, out: _Out
358356
) -> doctest.TestResults:
359-
"""
360-
Run the examples in `test`.
357+
"""Run the examples in `test`.
361358
362359
Write the outcome of each example with one of the
363360
`DocTestRunner.report_*` methods, using the writer function
@@ -409,7 +406,7 @@ def _DocTestRunner__run(
409406
# Use a special filename for compile(), so we can retrieve
410407
# the source code during interactive debugging (see
411408
# __patched_linecache_getlines).
412-
filename = "<doctest %s[%d]>" % (test.name, examplenum)
409+
filename = f"<doctest {test.name}[{examplenum}]>"
413410

414411
# Run the example in the given context (globs), and record
415412
# any exception that gets raised. (But don't intercept
@@ -484,7 +481,7 @@ def _DocTestRunner__run(
484481
)
485482
failures += 1
486483
else:
487-
assert False, ("unknown outcome", outcome)
484+
raise AssertionError(("unknown outcome", outcome))
488485

489486
if failures and self.optionflags & doctest.FAIL_FAST:
490487
break
@@ -504,7 +501,7 @@ class SphinxDocTestParser:
504501
def get_doctest(
505502
self,
506503
docstring: str,
507-
globs: Dict[str, Any],
504+
globs: dict[str, Any],
508505
name: str,
509506
filename: str,
510507
lineno: int,
@@ -563,7 +560,7 @@ def collect(self) -> Iterator[_pytest.doctest.DoctestItem]:
563560
)
564561
except ImportError:
565562
if self.config.getvalue("doctest_ignore_import_errors"):
566-
pytest.skip("unable to import module %r" % self.path)
563+
pytest.skip(f"unable to import module {self.path!r}")
567564
else:
568565
raise
569566
optionflags = _pytest.doctest.get_optionflags(self.config) # type:ignore

tests/test_doc2test.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,17 @@
1010

1111
@pytest.mark.parametrize("in_between_content", ["", "\nsome text\nmore text"])
1212
def test_simple(in_between_content: str) -> None:
13-
doc = """
13+
doc = f"""
1414
.. testcode::
1515
1616
import pprint
1717
pprint.pprint({{'3': 4, '5': 6}})
18-
{}
18+
{in_between_content}
1919
.. testoutput::
2020
2121
{{'3': 4,
2222
'5': 6}}
23-
""".format(in_between_content)
23+
"""
2424

2525
examples = docstring2examples(doc)
2626
assert len(examples) == 1

tests/test_options.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,12 @@ def test_options_and_text() -> None:
8787
@pytest.mark.parametrize("with_options", [True, False])
8888
def test_skipif_and_text(expr: str, with_options: bool) -> None:
8989
want = textwrap.dedent(
90-
"""
91-
:skipif: {}
90+
f"""
91+
:skipif: {expr}
9292
9393
abcedf
9494
abcedf
95-
""".format(expr)
95+
"""
9696
)
9797
if with_options:
9898
want = "\n:options: +NORMALIZE_WHITESPACE" + want

tests/test_sphinx_doctest.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@
55
import subprocess
66
import sys
77
import textwrap
8+
from collections.abc import Iterator
89
from pathlib import Path
9-
from typing import Iterator
10-
from typing import Union
1110

1211
import pytest
1312
from _pytest._py.path import LocalPath
@@ -58,7 +57,7 @@ def __call__(
5857
if sphinxopts:
5958
cmd.append(sphinxopts)
6059

61-
def to_str(subprocess_output: Union[str, bytes]) -> str:
60+
def to_str(subprocess_output: str | bytes) -> str:
6261
if isinstance(subprocess_output, bytes):
6362
output_str = "\n".join(subprocess_output.decode().splitlines())
6463
else:
@@ -187,16 +186,16 @@ def test_doctest_multiple(
187186
def test_skipif_true(
188187
self, testdir: Testdir, sphinx_tester: SphinxDoctestRunner, testcode: str
189188
) -> None:
190-
code = """
189+
code = f"""
191190
.. testcode::
192191
193-
{}
192+
{testcode}
194193
195194
.. testoutput::
196195
:skipif: True
197196
198197
NOT EVALUATED
199-
""".format(testcode)
198+
"""
200199

201200
raise_in_testcode = testcode != "pass"
202201
sphinx_output = sphinx_tester(code, must_raise=raise_in_testcode)
@@ -218,16 +217,16 @@ def test_skipif_true(
218217
def test_skipif_false(
219218
self, testdir: Testdir, sphinx_tester: SphinxDoctestRunner, testcode: str
220219
) -> None:
221-
code = """
220+
code = f"""
222221
.. testcode::
223222
224-
{}
223+
{testcode}
225224
226225
.. testoutput::
227226
:skipif: False
228227
229228
EVALUATED
230-
""".format(testcode)
229+
"""
231230

232231
expected_failure = "EVALUATED" not in testcode
233232

@@ -287,17 +286,17 @@ def test_skipif_multiple_testoutput(
287286
def test_skipif_true_in_testcode(
288287
self, testdir: Testdir, sphinx_tester: SphinxDoctestRunner, testcode: str
289288
) -> None:
290-
code = """
289+
code = f"""
291290
.. testcode::
292291
:skipif: True
293292
294-
{}
293+
{testcode}
295294
296295
.. testoutput::
297296
:skipif: False
298297
299298
NOT EVALUATED
300-
""".format(testcode)
299+
"""
301300

302301
sphinx_output = sphinx_tester(code, must_raise=False)
303302
assert "0 tests" in sphinx_output

0 commit comments

Comments
 (0)