From ffb8f98869748ad2351597ce7325ff91b4b934e8 Mon Sep 17 00:00:00 2001 From: Julien Palard Date: Thu, 3 Feb 2022 11:14:19 +0100 Subject: [PATCH 1/5] bpo-42238: [doc] moving from rstlint.py to sphinx-lint. --- Doc/Makefile | 5 +- Doc/make.bat | 12 +- Doc/requirements.txt | 2 + Doc/tools/rstlint.py | 403 ------------------------------------------- 4 files changed, 16 insertions(+), 406 deletions(-) delete mode 100755 Doc/tools/rstlint.py diff --git a/Doc/Makefile b/Doc/Makefile index e60aa3427fea24..61a7ce0d0981f0 100644 --- a/Doc/Makefile +++ b/Doc/Makefile @@ -7,6 +7,7 @@ PYTHON = python3 VENVDIR = ./venv SPHINXBUILD = PATH=$(VENVDIR)/bin:$$PATH sphinx-build +SPHINXLINT = PATH=$(VENVDIR)/bin:$$PATH sphinx-lint BLURB = PATH=$(VENVDIR)/bin:$$PATH blurb PAPER = SOURCES = @@ -214,8 +215,8 @@ dist: rm dist/python-$(DISTVERSION)-docs-texinfo.tar check: - $(PYTHON) tools/rstlint.py -i tools -i $(VENVDIR) -i README.rst - $(PYTHON) tools/rstlint.py ../Misc/NEWS.d/next/ + $(SPHINXLINT) -i tools -i $(VENVDIR) -i README.rst + $(SPHINXLINT) ../Misc/NEWS.d/next/ serve: $(PYTHON) ../Tools/scripts/serve.py build/html $(SERVE_PORT) diff --git a/Doc/make.bat b/Doc/make.bat index 7fde0636427713..9eaaa46806829f 100644 --- a/Doc/make.bat +++ b/Doc/make.bat @@ -36,6 +36,16 @@ if not defined BLURB ( set BLURB=%PYTHON% -m blurb ) +if not defined SPHINXLINT ( + %PYTHON% -c "import sphinxlint" > nul 2> nul + if errorlevel 1 ( + echo Installing sphinx-lint with %PYTHON% + %PYTHON% -m pip install sphinx-lint + if errorlevel 1 exit /B + ) + set SPHINXLINT=%PYTHON% -m sphinxlint +) + if "%1" NEQ "htmlhelp" goto :skiphhcsearch if exist "%HTMLHELP%" goto :skiphhcsearch @@ -168,7 +178,7 @@ if EXIST "%BUILDDIR%\html\index.html" ( goto end :check -cmd /S /C "%PYTHON% tools\rstlint.py -i tools" +cmd /S /C "%SPHINXLINT% -i tools" goto end :serve diff --git a/Doc/requirements.txt b/Doc/requirements.txt index 0331a8dbebc46c..687f2ee1862e7a 100644 --- a/Doc/requirements.txt +++ b/Doc/requirements.txt @@ -7,6 +7,8 @@ sphinx==4.2.0 blurb +sphinx-lint + # The theme used by the documentation is stored separately, so we need # to install that as well. python-docs-theme>=2022.1 diff --git a/Doc/tools/rstlint.py b/Doc/tools/rstlint.py deleted file mode 100755 index 33cbaadfce944e..00000000000000 --- a/Doc/tools/rstlint.py +++ /dev/null @@ -1,403 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -# Check for stylistic and formal issues in .rst and .py -# files included in the documentation. -# -# 01/2009, Georg Brandl - -# TODO: - wrong versions in versionadded/changed -# - wrong markup after versionchanged directive - -import os -import re -import sys -import getopt -from string import ascii_letters -from os.path import join, splitext, abspath, exists -from collections import defaultdict - -directives = [ - # standard docutils ones - 'admonition', 'attention', 'caution', 'class', 'compound', 'container', - 'contents', 'csv-table', 'danger', 'date', 'default-role', 'epigraph', - 'error', 'figure', 'footer', 'header', 'highlights', 'hint', 'image', - 'important', 'include', 'line-block', 'list-table', 'meta', 'note', - 'parsed-literal', 'pull-quote', 'raw', 'replace', - 'restructuredtext-test-directive', 'role', 'rubric', 'sectnum', 'sidebar', - 'table', 'target-notes', 'tip', 'title', 'topic', 'unicode', 'warning', - # Sphinx and Python docs custom ones - 'acks', 'attribute', 'autoattribute', 'autoclass', 'autodata', - 'autoexception', 'autofunction', 'automethod', 'automodule', - 'availability', 'centered', 'cfunction', 'class', 'classmethod', 'cmacro', - 'cmdoption', 'cmember', 'code-block', 'confval', 'cssclass', 'ctype', - 'currentmodule', 'cvar', 'data', 'decorator', 'decoratormethod', - 'deprecated-removed', 'deprecated(?!-removed)', 'describe', 'directive', - 'doctest', 'envvar', 'event', 'exception', 'function', 'glossary', - 'highlight', 'highlightlang', 'impl-detail', 'index', 'literalinclude', - 'method', 'miscnews', 'module', 'moduleauthor', 'opcode', 'pdbcommand', - 'productionlist', 'program', 'role', 'sectionauthor', 'seealso', - 'sourcecode', 'staticmethod', 'tabularcolumns', 'testcode', 'testoutput', - 'testsetup', 'toctree', 'todo', 'todolist', 'versionadded', - 'versionchanged' -] - -roles = [ - "(? 81: - # don't complain about tables, links and function signatures - if line.lstrip()[0] not in '+|' and \ - 'http://' not in line and \ - not line.lstrip().startswith(('.. function', - '.. method', - '.. cfunction')): - yield lno+1, "line too long" - - -@checker('.html', severity=2, falsepositives=True) -def check_leaked_markup(fn, lines): - """Check HTML files for leaked reST markup; this only works if - the HTML files have been built. - """ - for lno, line in enumerate(lines): - if leaked_markup_re.search(line): - yield lno+1, 'possibly leaked markup: %r' % line - - -def hide_literal_blocks(lines): - """Tool to remove literal blocks from given lines. - - It yields empty lines in place of blocks, so line numbers are - still meaningful. - """ - in_block = False - for line in lines: - if line.endswith("::\n"): - in_block = True - elif in_block: - if line == "\n" or line.startswith(" "): - line = "\n" - else: - in_block = False - yield line - - -def type_of_explicit_markup(line): - if re.match(fr'\.\. {all_directives}::', line): - return 'directive' - if re.match(r'\.\. \[[0-9]+\] ', line): - return 'footnote' - if re.match(r'\.\. \[[^\]]+\] ', line): - return 'citation' - if re.match(r'\.\. _.*[^_]: ', line): - return 'target' - if re.match(r'\.\. \|[^\|]*\| ', line): - return 'substitution_definition' - return 'comment' - - -def hide_comments(lines): - """Tool to remove comments from given lines. - - It yields empty lines in place of comments, so line numbers are - still meaningful. - """ - in_multiline_comment = False - for line in lines: - if line == "..\n": - in_multiline_comment = True - elif in_multiline_comment: - if line == "\n" or line.startswith(" "): - line = "\n" - else: - in_multiline_comment = False - if line.startswith(".. ") and type_of_explicit_markup(line) == 'comment': - line = "\n" - yield line - - - -@checker(".rst", severity=2) -def check_missing_surrogate_space_on_plural(fn, lines): - r"""Check for missing 'backslash-space' between a code sample a letter. - - Good: ``Point``\ s - Bad: ``Point``s - """ - in_code_sample = False - check_next_one = False - for lno, line in enumerate(hide_comments(hide_literal_blocks(lines))): - tokens = line.split("``") - for token_no, token in enumerate(tokens): - if check_next_one: - if token[0] in ascii_letters: - yield lno + 1, f"Missing backslash-space between code sample and {token!r}." - check_next_one = False - if token_no == len(tokens) - 1: - continue - if in_code_sample: - check_next_one = True - in_code_sample = not in_code_sample - -def main(argv): - usage = '''\ -Usage: %s [-v] [-f] [-s sev] [-i path]* [path] - -Options: -v verbose (print all checked file names) - -f enable checkers that yield many false positives - -s sev only show problems with severity >= sev - -i path ignore subdir or file path -''' % argv[0] - try: - gopts, args = getopt.getopt(argv[1:], 'vfs:i:') - except getopt.GetoptError: - print(usage) - return 2 - - verbose = False - severity = 1 - ignore = [] - falsepos = False - for opt, val in gopts: - if opt == '-v': - verbose = True - elif opt == '-f': - falsepos = True - elif opt == '-s': - severity = int(val) - elif opt == '-i': - ignore.append(abspath(val)) - - if len(args) == 0: - path = '.' - elif len(args) == 1: - path = args[0] - else: - print(usage) - return 2 - - if not exists(path): - print('Error: path %s does not exist' % path) - return 2 - - count = defaultdict(int) - - for root, dirs, files in os.walk(path): - # ignore subdirs in ignore list - if abspath(root) in ignore: - del dirs[:] - continue - - for fn in files: - fn = join(root, fn) - if fn[:2] == './': - fn = fn[2:] - - # ignore files in ignore list - if abspath(fn) in ignore: - continue - - ext = splitext(fn)[1] - checkerlist = checkers.get(ext, None) - if not checkerlist: - continue - - if verbose: - print('Checking %s...' % fn) - - try: - with open(fn, 'r', encoding='utf-8') as f: - lines = list(f) - except (IOError, OSError) as err: - print('%s: cannot open: %s' % (fn, err)) - count[4] += 1 - continue - - for checker in checkerlist: - if checker.falsepositives and not falsepos: - continue - csev = checker.severity - if csev >= severity: - for lno, msg in checker(fn, lines): - print('[%d] %s:%d: %s' % (csev, fn, lno, msg)) - count[csev] += 1 - if verbose: - print() - if not count: - if severity > 1: - print('No problems with severity >= %d found.' % severity) - else: - print('No problems found.') - else: - for severity in sorted(count): - number = count[severity] - print('%d problem%s with severity %d found.' % - (number, number > 1 and 's' or '', severity)) - return int(bool(count)) - - -if __name__ == '__main__': - sys.exit(main(sys.argv)) From 22da852f0cabe5a5ea4603a8b8ebfd1da58078c5 Mon Sep 17 00:00:00 2001 From: Julien Palard Date: Thu, 3 Feb 2022 11:25:29 +0100 Subject: [PATCH 2/5] Add news. --- .../next/Documentation/2022-02-03-11-24-59.bpo-42238.yJcMa8.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Documentation/2022-02-03-11-24-59.bpo-42238.yJcMa8.rst diff --git a/Misc/NEWS.d/next/Documentation/2022-02-03-11-24-59.bpo-42238.yJcMa8.rst b/Misc/NEWS.d/next/Documentation/2022-02-03-11-24-59.bpo-42238.yJcMa8.rst new file mode 100644 index 00000000000000..a8dffff3fcf289 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2022-02-03-11-24-59.bpo-42238.yJcMa8.rst @@ -0,0 +1,2 @@ +``Doc/tools/rstlint.py`` has moved to its own repository and is now packaged +on PyPI as ``sphinx-lint``. From a9bc65fe19987f4928344f3d907348c9022d6588 Mon Sep 17 00:00:00 2001 From: Julien Palard Date: Thu, 3 Feb 2022 13:14:28 +0100 Subject: [PATCH 3/5] Pin a bit, just in case we want to release breaking changes. --- Doc/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/requirements.txt b/Doc/requirements.txt index 687f2ee1862e7a..3b28495d4b4d04 100644 --- a/Doc/requirements.txt +++ b/Doc/requirements.txt @@ -7,7 +7,7 @@ sphinx==4.2.0 blurb -sphinx-lint +sphinx-lint<1 # The theme used by the documentation is stored separately, so we need # to install that as well. From ee7f4d9fac03b697ab625420de903afdc8ecb50c Mon Sep 17 00:00:00 2001 From: Julien Palard Date: Mon, 7 Feb 2022 11:00:32 +0100 Subject: [PATCH 4/5] Don't drop the file that soon, notify about its move instead. --- Doc/tools/rstlint.py | 408 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 408 insertions(+) create mode 100644 Doc/tools/rstlint.py diff --git a/Doc/tools/rstlint.py b/Doc/tools/rstlint.py new file mode 100644 index 00000000000000..d1c53dcb1a698e --- /dev/null +++ b/Doc/tools/rstlint.py @@ -0,0 +1,408 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Check for stylistic and formal issues in .rst and .py +# files included in the documentation. +# +# 01/2009, Georg Brandl + +# TODO: - wrong versions in versionadded/changed +# - wrong markup after versionchanged directive + +import os +import re +import sys +import getopt +from string import ascii_letters +from os.path import join, splitext, abspath, exists +from collections import defaultdict + +directives = [ + # standard docutils ones + 'admonition', 'attention', 'caution', 'class', 'compound', 'container', + 'contents', 'csv-table', 'danger', 'date', 'default-role', 'epigraph', + 'error', 'figure', 'footer', 'header', 'highlights', 'hint', 'image', + 'important', 'include', 'line-block', 'list-table', 'meta', 'note', + 'parsed-literal', 'pull-quote', 'raw', 'replace', + 'restructuredtext-test-directive', 'role', 'rubric', 'sectnum', 'sidebar', + 'table', 'target-notes', 'tip', 'title', 'topic', 'unicode', 'warning', + # Sphinx and Python docs custom ones + 'acks', 'attribute', 'autoattribute', 'autoclass', 'autodata', + 'autoexception', 'autofunction', 'automethod', 'automodule', + 'availability', 'centered', 'cfunction', 'class', 'classmethod', 'cmacro', + 'cmdoption', 'cmember', 'code-block', 'confval', 'cssclass', 'ctype', + 'currentmodule', 'cvar', 'data', 'decorator', 'decoratormethod', + 'deprecated-removed', 'deprecated(?!-removed)', 'describe', 'directive', + 'doctest', 'envvar', 'event', 'exception', 'function', 'glossary', + 'highlight', 'highlightlang', 'impl-detail', 'index', 'literalinclude', + 'method', 'miscnews', 'module', 'moduleauthor', 'opcode', 'pdbcommand', + 'productionlist', 'program', 'role', 'sectionauthor', 'seealso', + 'sourcecode', 'staticmethod', 'tabularcolumns', 'testcode', 'testoutput', + 'testsetup', 'toctree', 'todo', 'todolist', 'versionadded', + 'versionchanged' +] + +roles = [ + "(? 81: + # don't complain about tables, links and function signatures + if line.lstrip()[0] not in '+|' and \ + 'http://' not in line and \ + not line.lstrip().startswith(('.. function', + '.. method', + '.. cfunction')): + yield lno+1, "line too long" + + +@checker('.html', severity=2, falsepositives=True) +def check_leaked_markup(fn, lines): + """Check HTML files for leaked reST markup; this only works if + the HTML files have been built. + """ + for lno, line in enumerate(lines): + if leaked_markup_re.search(line): + yield lno+1, 'possibly leaked markup: %r' % line + + +def hide_literal_blocks(lines): + """Tool to remove literal blocks from given lines. + + It yields empty lines in place of blocks, so line numbers are + still meaningful. + """ + in_block = False + for line in lines: + if line.endswith("::\n"): + in_block = True + elif in_block: + if line == "\n" or line.startswith(" "): + line = "\n" + else: + in_block = False + yield line + + +def type_of_explicit_markup(line): + if re.match(fr'\.\. {all_directives}::', line): + return 'directive' + if re.match(r'\.\. \[[0-9]+\] ', line): + return 'footnote' + if re.match(r'\.\. \[[^\]]+\] ', line): + return 'citation' + if re.match(r'\.\. _.*[^_]: ', line): + return 'target' + if re.match(r'\.\. \|[^\|]*\| ', line): + return 'substitution_definition' + return 'comment' + + +def hide_comments(lines): + """Tool to remove comments from given lines. + + It yields empty lines in place of comments, so line numbers are + still meaningful. + """ + in_multiline_comment = False + for line in lines: + if line == "..\n": + in_multiline_comment = True + elif in_multiline_comment: + if line == "\n" or line.startswith(" "): + line = "\n" + else: + in_multiline_comment = False + if line.startswith(".. ") and type_of_explicit_markup(line) == 'comment': + line = "\n" + yield line + + + +@checker(".rst", severity=2) +def check_missing_surrogate_space_on_plural(fn, lines): + r"""Check for missing 'backslash-space' between a code sample a letter. + + Good: ``Point``\ s + Bad: ``Point``s + """ + in_code_sample = False + check_next_one = False + for lno, line in enumerate(hide_comments(hide_literal_blocks(lines))): + tokens = line.split("``") + for token_no, token in enumerate(tokens): + if check_next_one: + if token[0] in ascii_letters: + yield lno + 1, f"Missing backslash-space between code sample and {token!r}." + check_next_one = False + if token_no == len(tokens) - 1: + continue + if in_code_sample: + check_next_one = True + in_code_sample = not in_code_sample + +def main(argv): + usage = '''\ +Usage: %s [-v] [-f] [-s sev] [-i path]* [path] + +Options: -v verbose (print all checked file names) + -f enable checkers that yield many false positives + -s sev only show problems with severity >= sev + -i path ignore subdir or file path +''' % argv[0] + try: + gopts, args = getopt.getopt(argv[1:], 'vfs:i:') + except getopt.GetoptError: + print(usage) + return 2 + + verbose = False + severity = 1 + ignore = [] + falsepos = False + for opt, val in gopts: + if opt == '-v': + verbose = True + elif opt == '-f': + falsepos = True + elif opt == '-s': + severity = int(val) + elif opt == '-i': + ignore.append(abspath(val)) + + if len(args) == 0: + path = '.' + elif len(args) == 1: + path = args[0] + else: + print(usage) + return 2 + + if not exists(path): + print('Error: path %s does not exist' % path) + return 2 + + count = defaultdict(int) + + print("""⚠ rstlint.py is no longer maintained here and will be removed +⚠ in a future release. +⚠ Please use https://pypi.org/p/sphinx-lint instead. +""") + + for root, dirs, files in os.walk(path): + # ignore subdirs in ignore list + if abspath(root) in ignore: + del dirs[:] + continue + + for fn in files: + fn = join(root, fn) + if fn[:2] == './': + fn = fn[2:] + + # ignore files in ignore list + if abspath(fn) in ignore: + continue + + ext = splitext(fn)[1] + checkerlist = checkers.get(ext, None) + if not checkerlist: + continue + + if verbose: + print('Checking %s...' % fn) + + try: + with open(fn, 'r', encoding='utf-8') as f: + lines = list(f) + except (IOError, OSError) as err: + print('%s: cannot open: %s' % (fn, err)) + count[4] += 1 + continue + + for checker in checkerlist: + if checker.falsepositives and not falsepos: + continue + csev = checker.severity + if csev >= severity: + for lno, msg in checker(fn, lines): + print('[%d] %s:%d: %s' % (csev, fn, lno, msg)) + count[csev] += 1 + if verbose: + print() + if not count: + if severity > 1: + print('No problems with severity >= %d found.' % severity) + else: + print('No problems found.') + else: + for severity in sorted(count): + number = count[severity] + print('%d problem%s with severity %d found.' % + (number, number > 1 and 's' or '', severity)) + return int(bool(count)) + + +if __name__ == '__main__': + sys.exit(main(sys.argv)) From 9b02019543a32745411b09d38f0529f7d699dae9 Mon Sep 17 00:00:00 2001 From: Julien Palard Date: Mon, 7 Feb 2022 11:06:39 +0100 Subject: [PATCH 5/5] FIX: Missing newline at end of file. --- .../next/Library/2022-02-06-08-54-03.bpo-46655.DiLzYv.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2022-02-06-08-54-03.bpo-46655.DiLzYv.rst b/Misc/NEWS.d/next/Library/2022-02-06-08-54-03.bpo-46655.DiLzYv.rst index 4f0de9519a00e4..183e064b8308ef 100644 --- a/Misc/NEWS.d/next/Library/2022-02-06-08-54-03.bpo-46655.DiLzYv.rst +++ b/Misc/NEWS.d/next/Library/2022-02-06-08-54-03.bpo-46655.DiLzYv.rst @@ -1 +1 @@ -In :func:`typing.get_type_hints`, support evaluating bare stringified ``TypeAlias`` annotations. Patch by Gregory Beauregard. \ No newline at end of file +In :func:`typing.get_type_hints`, support evaluating bare stringified ``TypeAlias`` annotations. Patch by Gregory Beauregard. pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy