summaryrefslogtreecommitdiffstats
path: root/Doc
diff options
context:
space:
mode:
authorJulien Palard <julien@palard.fr>2022-10-11 13:31:33 (GMT)
committerGitHub <noreply@github.com>2022-10-11 13:31:33 (GMT)
commit4067c6d7fe9b0b266367aafa8cde71e2761cb764 (patch)
tree50295f994bd202343b5bcb040a5014aea98a3f09 /Doc
parentad8e297b7297dfdc34fad53b589b31d0920150ab (diff)
downloadcpython-4067c6d7fe9b0b266367aafa8cde71e2761cb764.zip
cpython-4067c6d7fe9b0b266367aafa8cde71e2761cb764.tar.gz
cpython-4067c6d7fe9b0b266367aafa8cde71e2761cb764.tar.bz2
gh-86404: Doc: Drop now unused make suspicious and rstlint. (GH-98179)
They have been replaced by [sphinx-lint](https://github.com/sphinx-contrib/sphinx-lint).
Diffstat (limited to 'Doc')
-rw-r--r--Doc/Makefile15
-rw-r--r--Doc/README.rst3
-rw-r--r--Doc/make.bat2
-rw-r--r--Doc/tools/extensions/pyspecific.py5
-rw-r--r--Doc/tools/extensions/suspicious.py251
-rw-r--r--Doc/tools/rstlint.py408
-rw-r--r--Doc/tools/susp-ignored.csv400
-rw-r--r--Doc/whatsnew/3.12.rst5
8 files changed, 7 insertions, 1082 deletions
diff --git a/Doc/Makefile b/Doc/Makefile
index 5b6a958..f520874 100644
--- a/Doc/Makefile
+++ b/Doc/Makefile
@@ -22,7 +22,7 @@ ALLSPHINXOPTS = -b $(BUILDER) -d build/doctrees $(PAPEROPT_$(PAPER)) -j auto \
$(SPHINXOPTS) $(SPHINXERRORHANDLING) . build/$(BUILDER) $(SOURCES)
.PHONY: help build html htmlhelp latex text texinfo changes linkcheck \
- suspicious coverage doctest pydoc-topics htmlview clean dist check serve \
+ coverage doctest pydoc-topics htmlview clean dist check serve \
autobuild-dev autobuild-stable venv
help:
@@ -42,7 +42,6 @@ help:
@echo " doctest to run doctests in the documentation"
@echo " pydoc-topics to regenerate the pydoc topics file"
@echo " dist to create a \"dist\" directory with archived docs for download"
- @echo " suspicious to check for suspicious markup in output text"
@echo " check to run a check for frequent markup errors"
build:
@@ -110,18 +109,6 @@ linkcheck:
"or in build/$(BUILDER)/output.txt"; \
false; }
-suspicious: BUILDER = suspicious
-suspicious:
- @$(MAKE) build BUILDER=$(BUILDER) || { \
- echo "Suspicious check complete; look for any errors in the above output" \
- "or in build/$(BUILDER)/suspicious.csv. If all issues are false" \
- "positives, append that file to tools/susp-ignored.csv."; \
- false; }
- @echo "⚠ make suspicious is deprecated and will be removed soon."
- @echo "⚠ Use:"
- @echo "⚠ make check"
- @echo "⚠ instead."
-
coverage: BUILDER = coverage
coverage: build
@echo "Coverage finished; see c.txt and python.txt in build/coverage"
diff --git a/Doc/README.rst b/Doc/README.rst
index d67cad7..a3bb5fa 100644
--- a/Doc/README.rst
+++ b/Doc/README.rst
@@ -93,9 +93,6 @@ Available make targets are:
plain text documentation for the labels defined in
``tools/pyspecific.py`` -- pydoc needs these to show topic and keyword help.
-* "suspicious", which checks the parsed markup for text that looks like
- malformed and thus unconverted reST.
-
* "check", which checks for frequent markup errors.
* "serve", which serves the build/html directory on port 8000.
diff --git a/Doc/make.bat b/Doc/make.bat
index 4f0b3c1..87d8359 100644
--- a/Doc/make.bat
+++ b/Doc/make.bat
@@ -109,7 +109,7 @@ echo.always available include:
echo.
echo. Provided by Sphinx:
echo. html, htmlhelp, latex, text
-echo. suspicious, linkcheck, changes, doctest
+echo. linkcheck, changes, doctest
echo. Provided by this script:
echo. clean, check, htmlview
echo.
diff --git a/Doc/tools/extensions/pyspecific.py b/Doc/tools/extensions/pyspecific.py
index 8c3aa47..3b9f744 100644
--- a/Doc/tools/extensions/pyspecific.py
+++ b/Doc/tools/extensions/pyspecific.py
@@ -37,10 +37,6 @@ except ImportError:
from sphinx.domains.python import PyClassmember as PyMethod
from sphinx.domains.python import PyModulelevel as PyFunction
-# Support for checking for suspicious markup
-
-import suspicious
-
ISSUE_URI = 'https://bugs.python.org/issue?@action=redirect&bpo=%s'
GH_ISSUE_URI = 'https://github.com/python/cpython/issues/%s'
@@ -686,7 +682,6 @@ def setup(app):
app.add_directive('audit-event-table', AuditEventListDirective)
app.add_directive('deprecated-removed', DeprecatedRemoved)
app.add_builder(PydocTopicsBuilder)
- app.add_builder(suspicious.CheckSuspiciousMarkupBuilder)
app.add_object_type('opcode', 'opcode', '%s (opcode)', parse_opcode_signature)
app.add_object_type('pdbcommand', 'pdbcmd', '%s (pdb command)', parse_pdb_command)
app.add_object_type('2to3fixer', '2to3fixer', '%s (2to3 fixer)')
diff --git a/Doc/tools/extensions/suspicious.py b/Doc/tools/extensions/suspicious.py
deleted file mode 100644
index 2d581a8..0000000
--- a/Doc/tools/extensions/suspicious.py
+++ /dev/null
@@ -1,251 +0,0 @@
-"""
-Try to detect suspicious constructs, resembling markup
-that has leaked into the final output.
-
-Suspicious lines are reported in a comma-separated-file,
-``suspicious.csv``, located in the output directory.
-
-The file is utf-8 encoded, and each line contains four fields:
-
- * document name (normalized)
- * line number in the source document
- * problematic text
- * complete line showing the problematic text in context
-
-It is common to find many false positives. To avoid reporting them
-again and again, they may be added to the ``ignored.csv`` file
-(located in the configuration directory). The file has the same
-format as ``suspicious.csv`` with a few differences:
-
- - each line defines a rule; if the rule matches, the issue
- is ignored.
- - line number may be empty (that is, nothing between the
- commas: ",,"). In this case, line numbers are ignored (the
- rule matches anywhere in the file).
- - the last field does not have to be a complete line; some
- surrounding text (never more than a line) is enough for
- context.
-
-Rules are processed sequentially. A rule matches when:
-
- * document names are the same
- * problematic texts are the same
- * line numbers are close to each other (5 lines up or down)
- * the rule text is completely contained into the source line
-
-The simplest way to create the ignored.csv file is by copying
-undesired entries from suspicious.csv (possibly trimming the last
-field.)
-
-Copyright 2009 Gabriel A. Genellina
-
-"""
-
-import os
-import re
-import csv
-
-from docutils import nodes
-from sphinx.builders import Builder
-import sphinx.util
-
-detect_all = re.compile(r'''
- ::(?=[^=])| # two :: (but NOT ::=)
- :[a-zA-Z][a-zA-Z0-9]+| # :foo
- `| # ` (seldom used by itself)
- (?<!\.)\.\.[ \t]*\w+: # .. foo: (but NOT ... else:)
- ''', re.VERBOSE).finditer
-
-
-class Rule:
- def __init__(self, docname, lineno, issue, line):
- """A rule for ignoring issues"""
- self.docname = docname # document to which this rule applies
- self.lineno = lineno # line number in the original source;
- # this rule matches only near that.
- # None -> don't care
- self.issue = issue # the markup fragment that triggered this rule
- self.line = line # text of the container element (single line only)
- self.used = False
-
- def __repr__(self):
- return '{0.docname},,{0.issue},{0.line}'.format(self)
-
-
-
-class dialect(csv.excel):
- """Our dialect: uses only linefeed as newline."""
- lineterminator = '\n'
-
-
-class CheckSuspiciousMarkupBuilder(Builder):
- """
- Checks for possibly invalid markup that may leak into the output.
- """
- name = 'suspicious'
- logger = sphinx.util.logging.getLogger("CheckSuspiciousMarkupBuilder")
-
- def init(self):
- # create output file
- self.log_file_name = os.path.join(self.outdir, 'suspicious.csv')
- open(self.log_file_name, 'w').close()
- # load database of previously ignored issues
- self.load_rules(os.path.join(os.path.dirname(__file__), '..',
- 'susp-ignored.csv'))
-
- def get_outdated_docs(self):
- return self.env.found_docs
-
- def get_target_uri(self, docname, typ=None):
- return ''
-
- def prepare_writing(self, docnames):
- pass
-
- def write_doc(self, docname, doctree):
- # set when any issue is encountered in this document
- self.any_issue = False
- self.docname = docname
- visitor = SuspiciousVisitor(doctree, self)
- doctree.walk(visitor)
-
- def finish(self):
- unused_rules = [rule for rule in self.rules if not rule.used]
- if unused_rules:
- self.logger.warning(
- 'Found %s/%s unused rules: %s' % (
- len(unused_rules), len(self.rules),
- '\n'.join(repr(rule) for rule in unused_rules),
- )
- )
- return
-
- def check_issue(self, line, lineno, issue):
- if not self.is_ignored(line, lineno, issue):
- self.report_issue(line, lineno, issue)
-
- def is_ignored(self, line, lineno, issue):
- """Determine whether this issue should be ignored."""
- docname = self.docname
- for rule in self.rules:
- if rule.docname != docname: continue
- if rule.issue != issue: continue
- # Both lines must match *exactly*. This is rather strict,
- # and probably should be improved.
- # Doing fuzzy matches with levenshtein distance could work,
- # but that means bringing other libraries...
- # Ok, relax that requirement: just check if the rule fragment
- # is contained in the document line
- if rule.line not in line: continue
- # Check both line numbers. If they're "near"
- # this rule matches. (lineno=None means "don't care")
- if (rule.lineno is not None) and \
- abs(rule.lineno - lineno) > 5: continue
- # if it came this far, the rule matched
- rule.used = True
- return True
- return False
-
- def report_issue(self, text, lineno, issue):
- self.any_issue = True
- self.write_log_entry(lineno, issue, text)
- self.logger.warning('[%s:%d] "%s" found in "%-.120s"' %
- (self.docname, lineno, issue, text))
- self.app.statuscode = 1
-
- def write_log_entry(self, lineno, issue, text):
- f = open(self.log_file_name, 'a')
- writer = csv.writer(f, dialect)
- writer.writerow([self.docname, lineno, issue, text.strip()])
- f.close()
-
- def load_rules(self, filename):
- """Load database of previously ignored issues.
-
- A csv file, with exactly the same format as suspicious.csv
- Fields: document name (normalized), line number, issue, surrounding text
- """
- self.logger.info("loading ignore rules... ", nonl=1)
- self.rules = rules = []
- try:
- f = open(filename, 'r')
- except IOError:
- return
- for i, row in enumerate(csv.reader(f)):
- if len(row) != 4:
- raise ValueError(
- "wrong format in %s, line %d: %s" % (filename, i+1, row))
- docname, lineno, issue, text = row
- if lineno:
- lineno = int(lineno)
- else:
- lineno = None
- rule = Rule(docname, lineno, issue, text)
- rules.append(rule)
- f.close()
- self.logger.info('done, %d rules loaded' % len(self.rules))
-
-
-def get_lineno(node):
- """Obtain line number information for a node."""
- lineno = None
- while lineno is None and node:
- node = node.parent
- lineno = node.line
- return lineno
-
-
-def extract_line(text, index):
- """text may be a multiline string; extract
- only the line containing the given character index.
-
- >>> extract_line("abc\ndefgh\ni", 6)
- >>> 'defgh'
- >>> for i in (0, 2, 3, 4, 10):
- ... print extract_line("abc\ndefgh\ni", i)
- abc
- abc
- abc
- defgh
- defgh
- i
- """
- p = text.rfind('\n', 0, index) + 1
- q = text.find('\n', index)
- if q < 0:
- q = len(text)
- return text[p:q]
-
-
-class SuspiciousVisitor(nodes.GenericNodeVisitor):
-
- lastlineno = 0
-
- def __init__(self, document, builder):
- nodes.GenericNodeVisitor.__init__(self, document)
- self.builder = builder
-
- def default_visit(self, node):
- if isinstance(node, (nodes.Text, nodes.image)): # direct text containers
- text = node.astext()
- # lineno seems to go backwards sometimes (?)
- self.lastlineno = lineno = max(get_lineno(node) or 0, self.lastlineno)
- seen = set() # don't report the same issue more than only once per line
- for match in detect_all(text):
- issue = match.group()
- line = extract_line(text, match.start())
- if (issue, line) not in seen:
- self.builder.check_issue(line, lineno, issue)
- seen.add((issue, line))
-
- unknown_visit = default_visit
-
- def visit_document(self, node):
- self.lastlineno = 0
-
- def visit_comment(self, node):
- # ignore comments -- too much false positives.
- # (although doing this could miss some errors;
- # there were two sections "commented-out" by mistake
- # in the Python docs that would not be caught)
- raise nodes.SkipNode
diff --git a/Doc/tools/rstlint.py b/Doc/tools/rstlint.py
deleted file mode 100644
index 4ea68ef..0000000
--- a/Doc/tools/rstlint.py
+++ /dev/null
@@ -1,408 +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 = [
- "(?<!py):class:",
- "(?<!:c|py):func:",
- "(?<!py):meth:",
- "(?<!:py):mod:",
- ":exc:",
- ":issue:",
- ":attr:",
- ":c:func:",
- ":ref:",
- ":const:",
- ":term:",
- "(?<!:c|py):data:",
- ":keyword:",
- ":file:",
- ":pep:",
- ":c:type:",
- ":c:member:",
- ":option:",
- ":rfc:",
- ":envvar:",
- ":c:data:",
- ":source:",
- ":mailheader:",
- ":program:",
- ":c:macro:",
- ":dfn:",
- ":kbd:",
- ":command:",
- ":mimetype:",
- ":opcode:",
- ":manpage:",
- ":py:data:",
- ":RFC:",
- ":pdbcmd:",
- ":abbr:",
- ":samp:",
- ":token:",
- ":PEP:",
- ":sup:",
- ":py:class:",
- ":menuselection:",
- ":doc:",
- ":sub:",
- ":py:meth:",
- ":newsgroup:",
- ":code:",
- ":py:func:",
- ":makevar:",
- ":guilabel:",
- ":title-reference:",
- ":py:mod:",
- ":download:",
- ":2to3fixer:",
-]
-
-all_directives = "(" + "|".join(directives) + ")"
-all_roles = "(" + "|".join(roles) + ")"
-
-# Find comments that looks like a directive, like:
-# .. versionchanged 3.6
-# or
-# .. versionchanged: 3.6
-# as it should be:
-# .. versionchanged:: 3.6
-seems_directive_re = re.compile(r"(?<!\.)\.\. %s([^a-z:]|:(?!:))" % all_directives)
-
-# Find directive prefixed with three dots instead of two, like:
-# ... versionchanged:: 3.6
-# instead of:
-# .. versionchanged:: 3.6
-three_dot_directive_re = re.compile(r"\.\.\. %s::" % all_directives)
-
-# Find role used with double backticks instead of simple backticks like:
-# :const:``None``
-# instead of:
-# :const:`None`
-double_backtick_role = re.compile(r"(?<!``)%s``" % all_roles)
-
-
-# Find role used with no backticks instead of simple backticks like:
-# :const:None
-# instead of:
-# :const:`None`
-role_with_no_backticks = re.compile(r"%s[^` ]" % all_roles)
-
-# Find role glued with another word like:
-# the:c:func:`PyThreadState_LeaveTracing` function.
-# instead of:
-# the :c:func:`PyThreadState_LeaveTracing` function.
-role_glued_with_word = re.compile(r"[a-zA-Z]%s" % all_roles)
-
-default_role_re = re.compile(r"(^| )`\w([^`]*?\w)?`($| )")
-leaked_markup_re = re.compile(r"[a-z]::\s|`|\.\.\s*\w+:")
-
-
-checkers = {}
-
-checker_props = {'severity': 1, 'falsepositives': False}
-
-
-def checker(*suffixes, **kwds):
- """Decorator to register a function as a checker."""
- def deco(func):
- for suffix in suffixes:
- checkers.setdefault(suffix, []).append(func)
- for prop in checker_props:
- setattr(func, prop, kwds.get(prop, checker_props[prop]))
- return func
- return deco
-
-
-@checker('.py', severity=4)
-def check_syntax(fn, lines):
- """Check Python examples for valid syntax."""
- code = ''.join(lines)
- if '\r' in code:
- if os.name != 'nt':
- yield 0, '\\r in code file'
- code = code.replace('\r', '')
- try:
- compile(code, fn, 'exec')
- except SyntaxError as err:
- yield err.lineno, 'not compilable: %s' % err
-
-
-@checker('.rst', severity=2)
-def check_suspicious_constructs(fn, lines):
- """Check for suspicious reST constructs."""
- inprod = False
- for lno, line in enumerate(lines, start=1):
- if seems_directive_re.search(line):
- yield lno, "comment seems to be intended as a directive"
- if three_dot_directive_re.search(line):
- yield lno, "directive should start with two dots, not three."
- if double_backtick_role.search(line):
- yield lno, "role use a single backtick, double backtick found."
- if role_with_no_backticks.search(line):
- yield lno, "role use a single backtick, no backtick found."
- if role_glued_with_word.search(line):
- yield lno, "missing space before role"
- if ".. productionlist::" in line:
- inprod = True
- elif not inprod and default_role_re.search(line):
- yield lno, "default role used"
- elif inprod and not line.strip():
- inprod = False
-
-
-@checker('.py', '.rst')
-def check_whitespace(fn, lines):
- """Check for whitespace and line length issues."""
- for lno, line in enumerate(lines):
- if '\r' in line:
- yield lno+1, '\\r in line'
- if '\t' in line:
- yield lno+1, 'OMG TABS!!!1'
- if line[:-1].rstrip(' \t') != line[:-1]:
- yield lno+1, 'trailing whitespace'
-
-
-@checker('.rst', severity=0)
-def check_line_length(fn, lines):
- """Check for line length; this checker is not run by default."""
- for lno, line in enumerate(lines):
- if len(line) > 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))
diff --git a/Doc/tools/susp-ignored.csv b/Doc/tools/susp-ignored.csv
deleted file mode 100644
index 0dba074..0000000
--- a/Doc/tools/susp-ignored.csv
+++ /dev/null
@@ -1,400 +0,0 @@
-c-api/arg,,:ref,"PyArg_ParseTuple(args, ""O|O:ref"", &object, &callback)"
-c-api/list,,:high,list[low:high]
-c-api/sequence,,:i2,del o[i1:i2]
-c-api/sequence,,:i2,o[i1:i2]
-c-api/tuple,,:high,p[low:high]
-c-api/unicode,,:end,str[start:end]
-c-api/unicode,,:start,unicode[start:start+length]
-distutils/examples,,`,This is the description of the ``foobar`` package.
-distutils/setupscript,,::,
-extending/embedding,,:numargs,"if(!PyArg_ParseTuple(args, "":numargs""))"
-extending/extending,,:myfunction,"PyArg_ParseTuple(args, ""D:myfunction"", &c);"
-extending/extending,,:set,"if (PyArg_ParseTuple(args, ""O:set_callback"", &temp)) {"
-extending/newtypes,,:call,"if (!PyArg_ParseTuple(args, ""sss:call"", &arg1, &arg2, &arg3)) {"
-faq/programming,,:chr,">=4.0) or 1+f(xc,yc,x*x-y*y+xc,2.0*x*y+yc,k-1,f):f(xc,yc,x,y,k,f):chr("
-faq/programming,,:reduce,"print((lambda Ru,Ro,Iu,Io,IM,Sx,Sy:reduce(lambda x,y:x+y,map(lambda y,"
-faq/programming,,:reduce,"Sx=Sx,Sy=Sy:reduce(lambda x,y:x+y,map(lambda x,xc=Ru,yc=yc,Ru=Ru,Ro=Ro,"
-faq/windows,,:d48eceb,"Python 3.6.4 (v3.6.4:d48eceb, Dec 19 2017, 06:04:45) [MSC v.1900 32 bit (Intel)] on win32"
-howto/curses,,:black,"colors when it activates color mode. They are: 0:black, 1:red,"
-howto/curses,,:red,"colors when it activates color mode. They are: 0:black, 1:red,"
-howto/curses,,:green,"2:green, 3:yellow, 4:blue, 5:magenta, 6:cyan, and 7:white. The"
-howto/curses,,:yellow,"2:green, 3:yellow, 4:blue, 5:magenta, 6:cyan, and 7:white. The"
-howto/curses,,:blue,"2:green, 3:yellow, 4:blue, 5:magenta, 6:cyan, and 7:white. The"
-howto/curses,,:magenta,"2:green, 3:yellow, 4:blue, 5:magenta, 6:cyan, and 7:white. The"
-howto/curses,,:cyan,"2:green, 3:yellow, 4:blue, 5:magenta, 6:cyan, and 7:white. The"
-howto/curses,,:white,"2:green, 3:yellow, 4:blue, 5:magenta, 6:cyan, and 7:white. The"
-howto/descriptor,,:root,"INFO:root"
-howto/descriptor,,:Updating,"root:Updating"
-howto/descriptor,,:Accessing,"root:Accessing"
-howto/instrumentation,,::,python$target:::function-entry
-howto/instrumentation,,:function,python$target:::function-entry
-howto/instrumentation,,::,python$target:::function-return
-howto/instrumentation,,:function,python$target:::function-return
-howto/instrumentation,,:call,156641360502280 function-entry:call_stack.py:start:23
-howto/instrumentation,,:start,156641360502280 function-entry:call_stack.py:start:23
-howto/instrumentation,,:function,156641360518804 function-entry: call_stack.py:function_1:1
-howto/instrumentation,,:function,156641360532797 function-entry: call_stack.py:function_3:9
-howto/instrumentation,,:function,156641360546807 function-return: call_stack.py:function_3:10
-howto/instrumentation,,:function,156641360563367 function-return: call_stack.py:function_1:2
-howto/instrumentation,,:function,156641360578365 function-entry: call_stack.py:function_2:5
-howto/instrumentation,,:function,156641360591757 function-entry: call_stack.py:function_1:1
-howto/instrumentation,,:function,156641360605556 function-entry: call_stack.py:function_3:9
-howto/instrumentation,,:function,156641360617482 function-return: call_stack.py:function_3:10
-howto/instrumentation,,:function,156641360629814 function-return: call_stack.py:function_1:2
-howto/instrumentation,,:function,156641360642285 function-return: call_stack.py:function_2:6
-howto/instrumentation,,:function,156641360656770 function-entry: call_stack.py:function_3:9
-howto/instrumentation,,:function,156641360669707 function-return: call_stack.py:function_3:10
-howto/instrumentation,,:function,156641360687853 function-entry: call_stack.py:function_4:13
-howto/instrumentation,,:function,156641360700719 function-return: call_stack.py:function_4:14
-howto/instrumentation,,:function,156641360719640 function-entry: call_stack.py:function_5:18
-howto/instrumentation,,:function,156641360732567 function-return: call_stack.py:function_5:21
-howto/instrumentation,,:call,156641360747370 function-return:call_stack.py:start:28
-howto/instrumentation,,:start,156641360747370 function-return:call_stack.py:start:28
-howto/ipaddress,,:DB8,>>> ipaddress.ip_address('2001:DB8::1')
-howto/ipaddress,,::,>>> ipaddress.ip_address('2001:DB8::1')
-howto/ipaddress,,:db8,IPv6Address('2001:db8::1')
-howto/ipaddress,,::,IPv6Address('2001:db8::1')
-howto/ipaddress,,::,IPv6Address('::1')
-howto/ipaddress,,:db8,>>> ipaddress.ip_network('2001:db8::0/96')
-howto/ipaddress,,::,>>> ipaddress.ip_network('2001:db8::0/96')
-howto/ipaddress,,:db8,IPv6Network('2001:db8::/96')
-howto/ipaddress,,::,IPv6Network('2001:db8::/96')
-howto/ipaddress,,:db8,IPv6Network('2001:db8::/128')
-howto/ipaddress,,::,IPv6Network('2001:db8::/128')
-howto/ipaddress,,:db8,IPv6Interface('2001:db8::1/96')
-howto/ipaddress,,::,IPv6Interface('2001:db8::1/96')
-howto/ipaddress,,:db8,>>> addr6 = ipaddress.ip_address('2001:db8::1')
-howto/ipaddress,,::,>>> addr6 = ipaddress.ip_address('2001:db8::1')
-howto/ipaddress,,:db8,>>> host6 = ipaddress.ip_interface('2001:db8::1/96')
-howto/ipaddress,,::,>>> host6 = ipaddress.ip_interface('2001:db8::1/96')
-howto/ipaddress,,:db8,>>> net6 = ipaddress.ip_network('2001:db8::0/96')
-howto/ipaddress,,::,>>> net6 = ipaddress.ip_network('2001:db8::0/96')
-howto/ipaddress,,:ffff,IPv6Address('ffff:ffff:ffff:ffff:ffff:ffff::')
-howto/ipaddress,,::,IPv6Address('ffff:ffff:ffff:ffff:ffff:ffff::')
-howto/ipaddress,,::,IPv6Address('::ffff:ffff')
-howto/ipaddress,,:ffff,IPv6Address('::ffff:ffff')
-howto/ipaddress,,:db8,'2001:db8::/96'
-howto/ipaddress,,::,'2001:db8::/96'
-howto/ipaddress,,:db8,>>> ipaddress.ip_interface('2001:db8::1/96')
-howto/ipaddress,,::,>>> ipaddress.ip_interface('2001:db8::1/96')
-howto/ipaddress,,:db8,'2001:db8::1'
-howto/ipaddress,,::,'2001:db8::1'
-howto/ipaddress,,:db8,IPv6Address('2001:db8::ffff:ffff')
-howto/ipaddress,,::,IPv6Address('2001:db8::ffff:ffff')
-howto/ipaddress,,:ffff,IPv6Address('2001:db8::ffff:ffff')
-howto/logging,,:And,"WARNING:And this, too"
-howto/logging,,:And,"WARNING:root:And this, too"
-howto/logging,,:And,"ERROR:root:And non-ASCII stuff, too, like "
-howto/logging,,:Doing,INFO:root:Doing something
-howto/logging,,:Finished,INFO:root:Finished
-howto/logging,,:logger,severity:logger name:message
-howto/logging,,:Look,WARNING:root:Look before you leap!
-howto/logging,,:message,severity:logger name:message
-howto/logging,,:root,DEBUG:root:This message should go to the log file
-howto/logging,,:root,INFO:root:Doing something
-howto/logging,,:root,INFO:root:Finished
-howto/logging,,:root,INFO:root:So should this
-howto/logging,,:root,"ERROR:root:And non-ASCII stuff, too, like "
-howto/logging,,:root,INFO:root:Started
-howto/logging,,:root,"WARNING:root:And this, too"
-howto/logging,,:root,WARNING:root:Look before you leap!
-howto/logging,,:root,WARNING:root:Watch out!
-howto/logging,,:So,INFO:root:So should this
-howto/logging,,:So,INFO:So should this
-howto/logging,,:Started,INFO:root:Started
-howto/logging,,:This,DEBUG:root:This message should go to the log file
-howto/logging,,:This,DEBUG:This message should appear on the console
-howto/logging,,:Watch,WARNING:root:Watch out!
-howto/pyporting,,::,Programming Language :: Python :: 2
-howto/pyporting,,::,Programming Language :: Python :: 3
-howto/regex,,::,
-howto/regex,,:foo,(?:foo)
-howto/urllib2,,:password,"""joe:password@example.com"""
-library/__main__,,`,
-library/ast,,:upper,lower:upper
-library/ast,,:step,lower:upper:step
-library/audioop,,:ipos,"# factor = audioop.findfactor(in_test[ipos*2:ipos*2+len(out_test)],"
-library/configparser,,:home,my_dir: ${Common:home_dir}/twosheds
-library/configparser,,:option,${section:option}
-library/configparser,,:path,python_dir: ${Frameworks:path}/Python/Versions/${Frameworks:Python}
-library/configparser,,:Python,python_dir: ${Frameworks:path}/Python/Versions/${Frameworks:Python}
-library/configparser,,:system,path: ${Common:system_dir}/Library/Frameworks/
-library/datetime,,:MM,
-library/datetime,,:SS,
-library/decimal,,:optional,"trailneg:optional trailing minus indicator"
-library/difflib,,:ahi,a[alo:ahi]
-library/difflib,,:bhi,b[blo:bhi]
-library/difflib,,:i1,
-library/difflib,,:i2,
-library/difflib,,:j2,
-library/doctest,,`,``factorial`` from the ``example`` module:
-library/doctest,,`,The ``example`` module
-library/doctest,,`,Using ``factorial``
-library/exceptions,,:err,err.object[err.start:err.end]
-library/functions,,:step,a[start:stop:step]
-library/functions,,:stop,"a[start:stop, i]"
-library/functions,,:stop,a[start:stop:step]
-library/hashlib,,:LEAF,"h00 = blake2b(buf[0:LEAF_SIZE], fanout=FANOUT, depth=DEPTH,"
-library/http.client,,:port,host:port
-library/http.cookies,,`,!#$%&'*+-.^_`|~:
-library/imaplib,,:MM,"""DD-Mmm-YYYY HH:MM:SS"
-library/imaplib,,:SS,"""DD-Mmm-YYYY HH:MM:SS"
-library/inspect,,:int,">>> def foo(a, *, b:int, **kwargs):"
-library/inspect,,:int,"'(a, *, b:int, **kwargs)'"
-library/inspect,,:int,'b:int'
-library/ipaddress,,:db8,>>> ipaddress.ip_address('2001:db8::')
-library/ipaddress,,::,>>> ipaddress.ip_address('2001:db8::')
-library/ipaddress,,:db8,IPv6Address('2001:db8::')
-library/ipaddress,,::,IPv6Address('2001:db8::')
-library/ipaddress,,:db8,>>> ipaddress.IPv6Address('2001:db8::1000')
-library/ipaddress,,::,>>> ipaddress.IPv6Address('2001:db8::1000')
-library/ipaddress,,:db8,'2001:db8::1000'
-library/ipaddress,,::,'2001:db8::1000'
-library/ipaddress,,:db8,">>> f'{ipaddress.IPv6Address(""2001:db8::1000""):s}'"
-library/ipaddress,,::,">>> f'{ipaddress.IPv6Address(""2001:db8::1000""):s}'"
-library/ipaddress,,::,IPv6Address('ff02::5678%1')
-library/ipaddress,,::,fe80::1234
-library/ipaddress,,:db8,">>> ipaddress.ip_address(""2001:db8::1"").reverse_pointer"
-library/ipaddress,,::,">>> ipaddress.ip_address(""2001:db8::1"").reverse_pointer"
-library/ipaddress,,::,"""::abc:7:def"""
-library/ipaddress,,:def,"""::abc:7:def"""
-library/ipaddress,,::,::FFFF/96
-library/ipaddress,,::,2002::/16
-library/ipaddress,,::,2001::/32
-library/ipaddress,,::,>>> str(ipaddress.IPv6Address('::1'))
-library/ipaddress,,::,'::1'
-library/ipaddress,,:ff00,ffff:ff00::
-library/ipaddress,,:db00,2001:db00::0/24
-library/ipaddress,,::,2001:db00::0/24
-library/ipaddress,,:db00,2001:db00::0/ffff:ff00::
-library/ipaddress,,::,2001:db00::0/ffff:ff00::
-library/itertools,,:step,elements from seq[start:stop:step]
-library/itertools,,::,kernel = tuple(kernel)[::-1]
-library/itertools,,:stop,elements from seq[start:stop:step]
-library/logging.handlers,,:port,host:port
-library/logging,,:root,WARNING:root:Watch out!
-library/logging,,:Watch,WARNING:root:Watch out!
-library/mmap,,:i2,obj[i1:i2]
-library/multiprocessing,,`,# Add more tasks using `put()`
-library/multiprocessing,,:queue,">>> QueueManager.register('get_queue', callable=lambda:queue)"
-library/multiprocessing,,`,# register the Foo class; make `f()` and `g()` accessible via proxy
-library/multiprocessing,,`,# register the Foo class; make `g()` and `_h()` accessible via proxy
-library/multiprocessing,,`,# register the generator function baz; use `GeneratorProxy` to make proxies
-library/nntplib,,:bytes,:bytes
-library/nntplib,,:lines,:lines
-library/optparse,,:len,"del parser.rargs[:len(value)]"
-library/os.path,,:foo,c:foo
-library/pathlib,,:bar,">>> PureWindowsPath('c:/Windows', 'd:bar')"
-library/pathlib,,:bar,PureWindowsPath('d:bar')
-library/pathlib,,:Program,>>> PureWindowsPath('c:Program Files/').root
-library/pathlib,,:Program,>>> PureWindowsPath('c:Program Files/').anchor
-library/pdb,,:lineno,filename:lineno
-library/pickle,,:memory,"conn = sqlite3.connect("":memory:"")"
-library/posix,,`,"CFLAGS=""`getconf LFS_CFLAGS`"" OPT=""-g -O2 $CFLAGS"""
-library/pprint,,::,"'Programming Language :: Python :: 2.6',"
-library/pprint,,::,"'Programming Language :: Python :: 2.7',"
-library/pprint,,::,"'classifiers': ['Development Status :: 3 - Alpha',"
-library/pprint,,::,"'Intended Audience :: Developers',"
-library/pprint,,::,"'License :: OSI Approved :: MIT License',"
-library/pprint,,::,"'Programming Language :: Python :: 2',"
-library/pprint,,::,"'Programming Language :: Python :: 3',"
-library/pprint,,::,"'Programming Language :: Python :: 3.2',"
-library/pprint,,::,"'Programming Language :: Python :: 3.3',"
-library/pprint,,::,"'Programming Language :: Python :: 3.4',"
-library/pprint,,::,"'Topic :: Software Development :: Build Tools'],"
-library/profile,,:lineno,filename:lineno(function)
-library/pyexpat,,:elem1,<py:elem1 />
-library/pyexpat,,:py,"xmlns:py = ""http://www.python.org/ns/"">"
-library/random,,:len,new_diff = mean(combined[:len(drug)]) - mean(combined[len(drug):])
-library/readline,,:bind,"python:bind -v"
-library/readline,,:bind,"python:bind ^I rl_complete"
-library/smtplib,,:port,method must support that as well as a regular host:port
-library/socket,,::,'5aef:2b::8'
-library/socket,,:can,"return (can_id, can_dlc, data[:can_dlc])"
-library/socket,,:len,fds.frombytes(cmsg_data[:len(cmsg_data) - (len(cmsg_data) % fds.itemsize)])
-library/sqlite3,,:year,"cur.execute(""select * from lang where first_appeared=:year"", {""year"": 1972})"
-library/sqlite3,,:memory,
-library/sqlite3,,:template,"con = sqlite3.connect(""file:template.db?mode=ro"", uri=True)"
-library/sqlite3,,:nosuchdb,"con = sqlite3.connect(""file:nosuchdb.db?mode=rw"", uri=True)"
-library/sqlite3,,:mem1,"con1 = sqlite3.connect(""file:mem1?mode=memory&cache=shared"", uri=True)"
-library/sqlite3,,:mem1,"con2 = sqlite3.connect(""file:mem1?mode=memory&cache=shared"", uri=True)"
-library/ssl,,:My,"Organizational Unit Name (eg, section) []:My Group"
-library/ssl,,:My,"Organization Name (eg, company) [Internet Widgits Pty Ltd]:My Organization, Inc."
-library/ssl,,:myserver,"Common Name (eg, YOUR name) []:myserver.mygroup.myorganization.com"
-library/ssl,,:MyState,State or Province Name (full name) [Some-State]:MyState
-library/ssl,,:ops,Email Address []:ops@myserver.mygroup.myorganization.com
-library/ssl,,:Some,"Locality Name (eg, city) []:Some City"
-library/ssl,,:US,Country Name (2 letter code) [AU]:US
-library/stdtypes,,:end,s[start:end]
-library/stdtypes,,::,>>> hash(v[::-2]) == hash(b'abcefg'[::-2])
-library/stdtypes,,:len,s[len(s):len(s)]
-library/stdtypes,,::,>>> y = m[::2]
-library/stdtypes,,::,>>> z = y[::-2]
-library/string,,`,"!""#$%&'()*+,-./:;<=>?@[\]^_`{|}~"
-library/tarfile,,:bz2,
-library/tarfile,,:compression,filemode[:compression]
-library/tarfile,,:gz,
-library/tarfile,,:xz,'a:xz'
-library/tarfile,,:xz,'r:xz'
-library/tarfile,,:xz,'w:xz'
-library/time,,:mm,
-library/time,,:ss,
-library/tracemalloc,,:limit,"for index, stat in enumerate(top_stats[:limit], 1):"
-library/turtle,,::,Example::
-library/unittest,,:foo,"self.assertEqual(cm.output, ['INFO:foo:first message',"
-library/unittest,,:first,"self.assertEqual(cm.output, ['INFO:foo:first message',"
-library/unittest,,:foo,'ERROR:foo.bar:second message'])
-library/unittest,,:second,'ERROR:foo.bar:second message'])
-library/urllib.request,,:close,Connection:close
-library/urllib.request,,:port,:port
-library/urllib.request,,:lang,"xmlns=""http://www.w3.org/1999/xhtml"" xml:lang=""en"" lang=""en"">\n\n<head>\n"
-library/urllib.request,,:password,"""joe:password@python.org"""
-library/urllib.parse,,:scheme,<URL:scheme://host/path>
-library/urllib.parse,,:scheme,URL:scheme://host/path
-library/uuid,,:uuid,urn:uuid:12345678-1234-5678-1234-567812345678
-library/venv,,:param,":param nodist: If true, setuptools and pip are not installed into the"
-library/venv,,:param,":param progress: If setuptools or pip are installed, the progress of the"
-library/venv,,:param,":param nopip: If true, pip is not installed into the created"
-library/venv,,:param,:param context: The information for the virtual environment
-library/xmlrpc.client,,:nil,ex:nil
-library/xmlrpc.client,,:pass,http://user:pass@host:port/path
-library/xmlrpc.client,,:pass,user:pass
-library/xmlrpc.client,,:port,http://user:pass@host:port/path
-license,,`,"``Software''), to deal in the Software without restriction, including"
-license,,`,"THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,"
-license,,`,* THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
-license,,`,THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-license,,`,* THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
-license,,`,THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
-license,,:zooko,mailto:zooko@zooko.com
-reference/expressions,,:index,x[index:index]
-reference/lexical_analysis,,`,$ ? `
-reference/lexical_analysis,,:fileencoding,# vim:fileencoding=<encoding-name>
-tutorial/datastructures,,:value,It is also possible to delete a key:value
-tutorial/datastructures,,:value,key:value pairs within the braces adds initial key:value pairs
-tutorial/stdlib2,,:config,"logging.warning('Warning:config file %s not found', 'server.conf')"
-tutorial/stdlib2,,:config,WARNING:root:Warning:config file server.conf not found
-tutorial/stdlib2,,:Critical,CRITICAL:root:Critical error -- shutting down
-tutorial/stdlib2,,:Error,ERROR:root:Error occurred
-tutorial/stdlib2,,:root,CRITICAL:root:Critical error -- shutting down
-tutorial/stdlib2,,:root,ERROR:root:Error occurred
-tutorial/stdlib2,,:root,WARNING:root:Warning:config file server.conf not found
-tutorial/stdlib2,,:start,extra = data[start:start+extra_size]
-tutorial/stdlib2,,:start,"fields = struct.unpack('<IIIHH', data[start:start+16])"
-tutorial/stdlib2,,:start,filename = data[start:start+filenamesize]
-tutorial/stdlib2,,:Warning,WARNING:root:Warning:config file server.conf not found
-using/cmdline,,:errorhandler,:errorhandler
-using/cmdline,,:message,action:message:category:module:lineno
-using/cmdline,,:category,action:message:category:module:lineno
-using/cmdline,,:module,action:message:category:module:lineno
-using/cmdline,,:lineno,action:message:category:module:lineno
-using/cmdline,,::,-W ignore::DeprecationWarning
-using/unix,,:Packaging,https://en.opensuse.org/Portal:Packaging
-whatsnew/2.0,,:len,
-whatsnew/2.3,,::,
-whatsnew/2.3,,:config,
-whatsnew/2.3,,:Critical,
-whatsnew/2.3,,:Error,
-whatsnew/2.3,,:Problem,
-whatsnew/2.3,,:root,
-whatsnew/2.3,,:Warning,
-whatsnew/2.4,,::,
-whatsnew/2.4,,:System,
-whatsnew/2.5,,:memory,:memory:
-whatsnew/2.5,,:step,[start:stop:step]
-whatsnew/2.5,,:stop,[start:stop:step]
-whatsnew/2.7,,::,"ParseResult(scheme='http', netloc='[1080::8:800:200C:417A]',"
-whatsnew/2.7,,::,>>> urlparse.urlparse('http://[1080::8:800:200C:417A]/foo')
-whatsnew/2.7,,:Sunday,'2009:4:Sunday'
-whatsnew/2.7,,:Cookie,"export PYTHONWARNINGS=all,error:::Cookie:0"
-whatsnew/2.7,,::,"export PYTHONWARNINGS=all,error:::Cookie:0"
-whatsnew/3.2,,:affe,"netloc='[dead:beef:cafe:5417:affe:8FA3:deaf:feed]',"
-whatsnew/3.2,,:affe,>>> urllib.parse.urlparse('http://[dead:beef:cafe:5417:affe:8FA3:deaf:feed]/foo/')
-whatsnew/3.2,,:beef,"netloc='[dead:beef:cafe:5417:affe:8FA3:deaf:feed]',"
-whatsnew/3.2,,:beef,>>> urllib.parse.urlparse('http://[dead:beef:cafe:5417:affe:8FA3:deaf:feed]/foo/')
-whatsnew/3.2,,:cafe,"netloc='[dead:beef:cafe:5417:affe:8FA3:deaf:feed]',"
-whatsnew/3.2,,:cafe,>>> urllib.parse.urlparse('http://[dead:beef:cafe:5417:affe:8FA3:deaf:feed]/foo/')
-whatsnew/3.2,,:deaf,"netloc='[dead:beef:cafe:5417:affe:8FA3:deaf:feed]',"
-whatsnew/3.2,,:deaf,>>> urllib.parse.urlparse('http://[dead:beef:cafe:5417:affe:8FA3:deaf:feed]/foo/')
-whatsnew/3.2,,:directory,${buildout:directory}/downloads/dist
-whatsnew/3.2,,::,"$ export PYTHONWARNINGS='ignore::RuntimeWarning::,once::UnicodeWarning::'"
-whatsnew/3.2,,:feed,"netloc='[dead:beef:cafe:5417:affe:8FA3:deaf:feed]',"
-whatsnew/3.2,,:feed,>>> urllib.parse.urlparse('http://[dead:beef:cafe:5417:affe:8FA3:deaf:feed]/foo/')
-whatsnew/3.2,,:gz,">>> with tarfile.open(name='myarchive.tar.gz', mode='w:gz') as tf:"
-whatsnew/3.2,,:location,zope9-location = ${zope9:location}
-whatsnew/3.2,,:prefix,zope-conf = ${custom:prefix}/etc/zope.conf
-library/re,,`,!#$%&'*+-.^_`|~:
-library/re,,`,!\#\$%\&'\*\+\-\.\^_`\|\~:
-library/tarfile,,:xz,'x:xz'
-library/warnings,,:message,action:message:category:module:line
-library/warnings,,:category,action:message:category:module:line
-library/warnings,,:module,action:message:category:module:line
-library/warnings,,:line,action:message:category:module:line
-library/warnings,,::,error::ResourceWarning
-library/warnings,,::,default::DeprecationWarning
-library/warnings,,::,default:::mymodule
-library/warnings,,:mymodule,default:::mymodule
-library/warnings,,::,error:::mymodule
-library/warnings,,:mymodule,error:::mymodule
-library/warnings,,::,ignore::DeprecationWarning
-library/warnings,,::,ignore::PendingDeprecationWarning
-library/warnings,,::,ignore::ImportWarning
-library/warnings,,::,ignore::ResourceWarning
-library/xml.etree.elementtree,,:sometag,prefix:sometag
-library/xml.etree.elementtree,,:fictional,"<actors xmlns:fictional=""http://characters.example.com"""
-library/xml.etree.elementtree,,:character,<fictional:character>Lancelot</fictional:character>
-library/xml.etree.elementtree,,:character,<fictional:character>Archie Leach</fictional:character>
-library/xml.etree.elementtree,,:character,<fictional:character>Sir Robin</fictional:character>
-library/xml.etree.elementtree,,:character,<fictional:character>Gunther</fictional:character>
-library/xml.etree.elementtree,,:character,<fictional:character>Commander Clement</fictional:character>
-library/xml.etree.elementtree,,:actor,"for actor in root.findall('real_person:actor', ns):"
-library/xml.etree.elementtree,,:name,"name = actor.find('real_person:name', ns)"
-library/xml.etree.elementtree,,:character,"for char in actor.findall('role:character', ns):"
-library/xml.etree.elementtree,,:xi,<document xmlns:xi="http://www.w3.org/2001/XInclude">
-library/xml.etree.elementtree,,:include, <xi:include href="source.xml" parse="xml" />
-library/xml.etree.elementtree,,:include, Copyright (c) <xi:include href="year.txt" parse="text" />.
-library/zipapp,,:main,"$ python -m zipapp myapp -m ""myapp:main"""
-library/zipapp,,:fn,"pkg.mod:fn"
-library/zipapp,,:callable,"pkg.module:callable"
-library/stdtypes,,::,>>> m[::2].tolist()
-whatsnew/3.5,,:root,'WARNING:root:warning\n'
-whatsnew/3.5,,:warning,'WARNING:root:warning\n'
-whatsnew/3.5,,::,>>> addr6 = ipaddress.IPv6Address('::1')
-whatsnew/3.5,,:root,ERROR:root:exception
-whatsnew/3.5,,:exception,ERROR:root:exception
-whatsnew/changelog,,`,'`'
-whatsnew/changelog,,:end,str[start:end]
-library/binascii,,`,'`'
-library/uu,,`,'`'
-whatsnew/3.7,,`,'`'
-whatsnew/3.7,,::,error::BytesWarning
-whatsnew/changelog,,::,error::BytesWarning
-whatsnew/changelog,,::,default::BytesWarning
-whatsnew/changelog,,::,default::DeprecationWarning
-library/importlib.metadata,,:main,"EntryPoint(name='wheel', value='wheel.cli:main', group='console_scripts')"
-library/importlib.metadata,,`,loading the metadata for packages for the indicated ``context``.
-library/re,,`,"`"
-library/typing,,`,# Type of ``val`` is narrowed to ``str``
-library/typing,,`,"# Else, type of ``val`` is narrowed to ``float``."
-library/typing,,`,# Type of ``val`` is narrowed to ``list[str]``.
-library/typing,,`,# Type of ``val`` remains as ``list[object]``.
-library/tkinter,,::,ttk::frame .frm -padding 10
-library/tkinter,,::,"grid [ttk::label .frm.lbl -text ""Hello World!""] -column 0 -row 0"
-library/tkinter,,::,"grid [ttk::button .frm.btn -text ""Quit"" -command ""destroy .""] -column 1 -row 0"
-library/tkinter,,::,ttk::frame
-library/tkinter,,::,ttk::button
-library/tkinter,,::,ttk::widget
-reference/compound_stmts,,:exc,subclass of :exc:`BaseExceptionGroup`. It is not possible to mix except
-reference/compound_stmts,,`,subclass of :exc:`BaseExceptionGroup`. It is not possible to mix except
-reference/compound_stmts,,:keyword,"and except* in the same :keyword:`try`. :keyword:`break`,"
-reference/compound_stmts,,`,"and except* in the same :keyword:`try`. :keyword:`break`,"
-reference/compound_stmts,,:keyword,:keyword:`continue` and :keyword:`return` cannot appear in an except*
-reference/compound_stmts,,`,:keyword:`continue` and :keyword:`return` cannot appear in an except*
-whatsnew/changelog,,:CON,": os.path.abspath(“C:CON”) is now fixed to return “\.CON”, not"
-whatsnew/changelog,,::,Lib/email/mime/nonmultipart.py::MIMENonMultipart
-library/typing,,`,"assert_type(name, str) # OK, inferred type of `name` is `str`"
-library/typing,,`,# after which we hope the inferred type will be `int`
-whatsnew/changelog,,:company,-V:company/tag
-library/typing,,`,# are located in the `typing_extensions` backports package.
-library/dis,490,:TOS,TOS = TOS2[TOS1:TOS]
-library/dis,497,:TOS,TOS2[TOS1:TOS] = TOS3
diff --git a/Doc/whatsnew/3.12.rst b/Doc/whatsnew/3.12.rst
index f8122ed..ebc4906 100644
--- a/Doc/whatsnew/3.12.rst
+++ b/Doc/whatsnew/3.12.rst
@@ -468,6 +468,11 @@ Removed
* ``importlib.util.set_package`` has been removed.
(Contributed by Brett Cannon in :gh:`65961`.)
+* Removed the ``suspicious`` rule from the documentation Makefile, and
+ removed ``Doc/tools/rstlint.py``, both in favor of `sphinx-lint
+ <https://github.com/sphinx-contrib/sphinx-lint>`_.
+ (Contributed by Julien Palard in :gh:`98179`.)
+
Porting to Python 3.12
======================