Skip to content

Commit 40855f3

Browse files
GH-121970: Use Ruff to check and format the docs tools (#122018)
Co-authored-by: Alex Waygood <[email protected]>
1 parent 898e90c commit 40855f3

File tree

6 files changed

+157
-52
lines changed

6 files changed

+157
-52
lines changed

.pre-commit-config.yaml

+10-2
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,21 @@ repos:
33
rev: v0.3.4
44
hooks:
55
- id: ruff
6-
name: Run Ruff on Lib/test/
6+
name: Run Ruff (lint) on Doc/
7+
args: [--exit-non-zero-on-fix]
8+
files: ^Doc/
9+
- id: ruff
10+
name: Run Ruff (lint) on Lib/test/
711
args: [--exit-non-zero-on-fix]
812
files: ^Lib/test/
913
- id: ruff
10-
name: Run Ruff on Argument Clinic
14+
name: Run Ruff (lint) on Argument Clinic
1115
args: [--exit-non-zero-on-fix, --config=Tools/clinic/.ruff.toml]
1216
files: ^Tools/clinic/|Lib/test/test_clinic.py
17+
- id: ruff-format
18+
name: Run Ruff (format) on Doc/
19+
args: [--check]
20+
files: ^Doc/
1321

1422
- repo: https://github.com/psf/black-pre-commit-mirror
1523
rev: 24.4.2

Doc/.ruff.toml

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
target-version = "py312" # Align with the version in oldest_supported_sphinx
2+
fix = true
3+
output-format = "full"
4+
line-length = 79
5+
extend-exclude = [
6+
"includes/*",
7+
# Temporary exclusions:
8+
"tools/extensions/c_annotations.py",
9+
"tools/extensions/escape4chm.py",
10+
"tools/extensions/patchlevel.py",
11+
"tools/extensions/pyspecific.py",
12+
]
13+
14+
[lint]
15+
preview = true
16+
select = [
17+
"C4", # flake8-comprehensions
18+
"B", # flake8-bugbear
19+
"E", # pycodestyle
20+
"F", # pyflakes
21+
"FA", # flake8-future-annotations
22+
"FLY", # flynt
23+
"FURB", # refurb
24+
"G", # flake8-logging-format
25+
"I", # isort
26+
"LOG", # flake8-logging
27+
"N", # pep8-naming
28+
"PERF", # perflint
29+
"PGH", # pygrep-hooks
30+
"PT", # flake8-pytest-style
31+
"TCH", # flake8-type-checking
32+
"UP", # pyupgrade
33+
"W", # pycodestyle
34+
]
35+
ignore = [
36+
"E501", # Ignore line length errors (we use auto-formatting)
37+
]
38+
39+
[format]
40+
preview = true
41+
quote-style = "preserve"
42+
docstring-code-format = true
43+
exclude = [
44+
"tools/extensions/lexers/*",
45+
]

Doc/conf.py

+83-35
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import os
1010
import sys
1111
import time
12+
1213
sys.path.append(os.path.abspath('tools/extensions'))
1314
sys.path.append(os.path.abspath('includes'))
1415

@@ -30,13 +31,13 @@
3031

3132
# Skip if downstream redistributors haven't installed them
3233
try:
33-
import notfound.extension
34+
import notfound.extension # noqa: F401
3435
except ImportError:
3536
pass
3637
else:
3738
extensions.append('notfound.extension')
3839
try:
39-
import sphinxext.opengraph
40+
import sphinxext.opengraph # noqa: F401
4041
except ImportError:
4142
pass
4243
else:
@@ -63,7 +64,8 @@
6364

6465
# We look for the Include/patchlevel.h file in the current Python source tree
6566
# and replace the values accordingly.
66-
import patchlevel
67+
import patchlevel # noqa: E402
68+
6769
version, release = patchlevel.get_version_info()
6870

6971
rst_epilog = f"""
@@ -298,7 +300,8 @@
298300

299301
# Disable Docutils smartquotes for several translations
300302
smartquotes_excludes = {
301-
'languages': ['ja', 'fr', 'zh_TW', 'zh_CN'], 'builders': ['man', 'text'],
303+
'languages': ['ja', 'fr', 'zh_TW', 'zh_CN'],
304+
'builders': ['man', 'text'],
302305
}
303306

304307
# Avoid a warning with Sphinx >= 4.0
@@ -319,11 +322,13 @@
319322
'collapsiblesidebar': True,
320323
'issues_url': '/bugs.html',
321324
'license_url': '/license.html',
322-
'root_include_title': False # We use the version switcher instead.
325+
'root_include_title': False, # We use the version switcher instead.
323326
}
324327

325328
if os.getenv("READTHEDOCS"):
326-
html_theme_options["hosted_on"] = '<a href="https://about.readthedocs.com/">Read the Docs</a>'
329+
html_theme_options["hosted_on"] = (
330+
'<a href="https://about.readthedocs.com/">Read the Docs</a>'
331+
)
327332

328333
# Override stylesheet fingerprinting for Windows CHM htmlhelp to fix GH-91207
329334
# https://github.com/python/cpython/issues/91207
@@ -337,17 +342,21 @@
337342

338343
# Deployment preview information
339344
# (See .readthedocs.yml and https://docs.readthedocs.io/en/stable/reference/environment-variables.html)
340-
repository_url = os.getenv("READTHEDOCS_GIT_CLONE_URL")
345+
is_deployment_preview = os.getenv("READTHEDOCS_VERSION_TYPE") == "external"
346+
repository_url = os.getenv("READTHEDOCS_GIT_CLONE_URL", "")
347+
repository_url = repository_url.removesuffix(".git")
341348
html_context = {
342-
"is_deployment_preview": os.getenv("READTHEDOCS_VERSION_TYPE") == "external",
343-
"repository_url": repository_url.removesuffix(".git") if repository_url else None,
349+
"is_deployment_preview": is_deployment_preview,
350+
"repository_url": repository_url or None,
344351
"pr_id": os.getenv("READTHEDOCS_VERSION"),
345352
"enable_analytics": os.getenv("PYTHON_DOCS_ENABLE_ANALYTICS"),
346353
}
347354

348355
# This 'Last updated on:' timestamp is inserted at the bottom of every page.
349356
html_time = int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))
350-
html_last_updated_fmt = time.strftime('%b %d, %Y (%H:%M UTC)', time.gmtime(html_time))
357+
html_last_updated_fmt = time.strftime(
358+
'%b %d, %Y (%H:%M UTC)', time.gmtime(html_time)
359+
)
351360

352361
# Path to find HTML templates.
353362
templates_path = ['tools/templates']
@@ -407,30 +416,70 @@
407416
# (source start file, target name, title, author, document class [howto/manual]).
408417
_stdauthor = 'Guido van Rossum and the Python development team'
409418
latex_documents = [
410-
('c-api/index', 'c-api.tex',
411-
'The Python/C API', _stdauthor, 'manual'),
412-
('extending/index', 'extending.tex',
413-
'Extending and Embedding Python', _stdauthor, 'manual'),
414-
('installing/index', 'installing.tex',
415-
'Installing Python Modules', _stdauthor, 'manual'),
416-
('library/index', 'library.tex',
417-
'The Python Library Reference', _stdauthor, 'manual'),
418-
('reference/index', 'reference.tex',
419-
'The Python Language Reference', _stdauthor, 'manual'),
420-
('tutorial/index', 'tutorial.tex',
421-
'Python Tutorial', _stdauthor, 'manual'),
422-
('using/index', 'using.tex',
423-
'Python Setup and Usage', _stdauthor, 'manual'),
424-
('faq/index', 'faq.tex',
425-
'Python Frequently Asked Questions', _stdauthor, 'manual'),
426-
('whatsnew/' + version, 'whatsnew.tex',
427-
'What\'s New in Python', 'A. M. Kuchling', 'howto'),
419+
('c-api/index', 'c-api.tex', 'The Python/C API', _stdauthor, 'manual'),
420+
(
421+
'extending/index',
422+
'extending.tex',
423+
'Extending and Embedding Python',
424+
_stdauthor,
425+
'manual',
426+
),
427+
(
428+
'installing/index',
429+
'installing.tex',
430+
'Installing Python Modules',
431+
_stdauthor,
432+
'manual',
433+
),
434+
(
435+
'library/index',
436+
'library.tex',
437+
'The Python Library Reference',
438+
_stdauthor,
439+
'manual',
440+
),
441+
(
442+
'reference/index',
443+
'reference.tex',
444+
'The Python Language Reference',
445+
_stdauthor,
446+
'manual',
447+
),
448+
(
449+
'tutorial/index',
450+
'tutorial.tex',
451+
'Python Tutorial',
452+
_stdauthor,
453+
'manual',
454+
),
455+
(
456+
'using/index',
457+
'using.tex',
458+
'Python Setup and Usage',
459+
_stdauthor,
460+
'manual',
461+
),
462+
(
463+
'faq/index',
464+
'faq.tex',
465+
'Python Frequently Asked Questions',
466+
_stdauthor,
467+
'manual',
468+
),
469+
(
470+
'whatsnew/' + version,
471+
'whatsnew.tex',
472+
'What\'s New in Python',
473+
'A. M. Kuchling',
474+
'howto',
475+
),
428476
]
429477
# Collect all HOWTOs individually
430-
latex_documents.extend(('howto/' + fn[:-4], 'howto-' + fn[:-4] + '.tex',
431-
'', _stdauthor, 'howto')
432-
for fn in os.listdir('howto')
433-
if fn.endswith('.rst') and fn != 'index.rst')
478+
latex_documents.extend(
479+
('howto/' + fn[:-4], 'howto-' + fn[:-4] + '.tex', '', _stdauthor, 'howto')
480+
for fn in os.listdir('howto')
481+
if fn.endswith('.rst') and fn != 'index.rst'
482+
)
434483

435484
# Documents to append as an appendix to all manuals.
436485
latex_appendices = ['glossary', 'about', 'license', 'copyright']
@@ -458,8 +507,7 @@
458507
'test($|_)',
459508
]
460509

461-
coverage_ignore_classes = [
462-
]
510+
coverage_ignore_classes = []
463511

464512
# Glob patterns for C source files for C API coverage, relative to this directory.
465513
coverage_c_path = [
@@ -476,7 +524,7 @@
476524
# The coverage checker will ignore all C items whose names match these regexes
477525
# (using re.match) -- the keys must be the same as in coverage_c_regexes.
478526
coverage_ignore_c_items = {
479-
# 'cfunction': [...]
527+
# 'cfunction': [...]
480528
}
481529

482530

Doc/tools/check-warnings.py

+11-11
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"""
33
Check the output of running Sphinx in nit-picky mode (missing references).
44
"""
5+
56
from __future__ import annotations
67

78
import argparse
@@ -206,7 +207,9 @@ def annotate_diff(
206207

207208

208209
def fail_if_regression(
209-
warnings: list[str], files_with_expected_nits: set[str], files_with_nits: set[str]
210+
warnings: list[str],
211+
files_with_expected_nits: set[str],
212+
files_with_nits: set[str],
210213
) -> int:
211214
"""
212215
Ensure some files always pass Sphinx nit-picky mode (no missing references).
@@ -252,17 +255,11 @@ def fail_if_new_news_nit(warnings: list[str], threshold: int) -> int:
252255
"""
253256
Ensure no warnings are found in the NEWS file before a given line number.
254257
"""
255-
news_nits = (
256-
warning
257-
for warning in warnings
258-
if "/build/NEWS:" in warning
259-
)
258+
news_nits = (warning for warning in warnings if "/build/NEWS:" in warning)
260259

261260
# Nits found before the threshold line
262261
new_news_nits = [
263-
nit
264-
for nit in news_nits
265-
if int(nit.split(":")[1]) <= threshold
262+
nit for nit in news_nits if int(nit.split(":")[1]) <= threshold
266263
]
267264

268265
if new_news_nits:
@@ -311,7 +308,8 @@ def main(argv: list[str] | None = None) -> int:
311308
exit_code = 0
312309

313310
wrong_directory_msg = "Must run this script from the repo root"
314-
assert Path("Doc").exists() and Path("Doc").is_dir(), wrong_directory_msg
311+
if not Path("Doc").exists() or not Path("Doc").is_dir():
312+
raise RuntimeError(wrong_directory_msg)
315313

316314
with Path("Doc/sphinx-warnings.txt").open(encoding="UTF-8") as f:
317315
warnings = f.read().splitlines()
@@ -339,7 +337,9 @@ def main(argv: list[str] | None = None) -> int:
339337
)
340338

341339
if args.fail_if_improved:
342-
exit_code += fail_if_improved(files_with_expected_nits, files_with_nits)
340+
exit_code += fail_if_improved(
341+
files_with_expected_nits, files_with_nits
342+
)
343343

344344
if args.fail_if_new_news_nit:
345345
exit_code += fail_if_new_news_nit(warnings, args.fail_if_new_news_nit)

Doc/tools/extensions/glossary_search.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@
1717
logger = logging.getLogger(__name__)
1818

1919

20-
def process_glossary_nodes(app: Sphinx, doctree: nodes.document, _docname: str) -> None:
20+
def process_glossary_nodes(
21+
app: Sphinx,
22+
doctree: nodes.document,
23+
_docname: str,
24+
) -> None:
2125
if app.builder.format != 'html' or app.builder.embedded:
2226
return
2327

@@ -34,15 +38,15 @@ def process_glossary_nodes(app: Sphinx, doctree: nodes.document, _docname: str)
3438
rendered = app.builder.render_partial(definition)
3539
terms[term.lower()] = {
3640
'title': term,
37-
'body': rendered['html_body']
41+
'body': rendered['html_body'],
3842
}
3943

4044

4145
def write_glossary_json(app: Sphinx, _exc: Exception) -> None:
4246
if not getattr(app.env, 'glossary_terms', None):
4347
return
4448

45-
logger.info(f'Writing glossary.json', color='green')
49+
logger.info('Writing glossary.json', color='green')
4650
dest = Path(app.outdir, '_static', 'glossary.json')
4751
dest.parent.mkdir(exist_ok=True)
4852
dest.write_text(json.dumps(app.env.glossary_terms), encoding='utf-8')

Doc/tools/extensions/lexers/asdl_lexer.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class ASDLLexer(RegexLexer):
2828
# Keep in line with ``builtin_types`` from Parser/asdl.py.
2929
# ASDL's 4 builtin types are
3030
# constant, identifier, int, string
31-
('constant|identifier|int|string', Name.Builtin),
31+
("constant|identifier|int|string", Name.Builtin),
3232
(r"attributes", Name.Builtin),
3333
(
3434
_name + _text_ws + "(=)",

0 commit comments

Comments
 (0)