GH-121970: Extract `misc_news` into a new extension (#129577)

This commit is contained in:
Adam Turner 2025-02-02 16:17:02 +00:00 committed by GitHub
parent 0612a89ffc
commit ae4788809d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 85 additions and 50 deletions

View File

@ -28,6 +28,7 @@ extensions = [
'changes',
'glossary_search',
'lexers',
'misc_news',
'pydoc_topics',
'pyspecific',
'sphinx.ext.coverage',

View File

@ -127,16 +127,14 @@ goto end
:build
if not exist "%BUILDDIR%" mkdir "%BUILDDIR%"
rem PY_MISC_NEWS_DIR is also used by our Sphinx extension in tools/extensions/pyspecific.py
if not defined PY_MISC_NEWS_DIR set PY_MISC_NEWS_DIR=%BUILDDIR%\%1
if not exist "%PY_MISC_NEWS_DIR%" mkdir "%PY_MISC_NEWS_DIR%"
if not exist build mkdir build
if exist ..\Misc\NEWS (
echo.Copying Misc\NEWS to %PY_MISC_NEWS_DIR%\NEWS
copy ..\Misc\NEWS "%PY_MISC_NEWS_DIR%\NEWS" > nul
echo.Copying existing Misc\NEWS file to Doc\build\NEWS
copy ..\Misc\NEWS build\NEWS > nul
) else if exist ..\Misc\NEWS.D (
if defined BLURB (
echo.Merging Misc/NEWS with %BLURB%
%BLURB% merge -f "%PY_MISC_NEWS_DIR%\NEWS"
%BLURB% merge -f build\NEWS
) else (
echo.No Misc/NEWS file and Blurb is not available.
exit /B 1

View File

@ -0,0 +1,75 @@
"""Support for including Misc/NEWS."""
from __future__ import annotations
import re
from pathlib import Path
from typing import TYPE_CHECKING
from docutils import nodes
from sphinx.locale import _ as sphinx_gettext
from sphinx.util.docutils import SphinxDirective
if TYPE_CHECKING:
from typing import Final
from docutils.nodes import Node
from sphinx.application import Sphinx
from sphinx.util.typing import ExtensionMetadata
BLURB_HEADER = """\
+++++++++++
Python News
+++++++++++
"""
bpo_issue_re: Final[re.Pattern[str]] = re.compile(
"(?:issue #|bpo-)([0-9]+)", re.ASCII
)
gh_issue_re: Final[re.Pattern[str]] = re.compile(
"gh-(?:issue-)?([0-9]+)", re.ASCII | re.IGNORECASE
)
whatsnew_re: Final[re.Pattern[str]] = re.compile(
r"^what's new in (.*?)\??$", re.ASCII | re.IGNORECASE | re.MULTILINE
)
class MiscNews(SphinxDirective):
has_content = False
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = False
option_spec = {}
def run(self) -> list[Node]:
# Get content of NEWS file
source, _ = self.get_source_info()
news_file = Path(source).resolve().parent / self.arguments[0]
self.env.note_dependency(news_file)
try:
news_text = news_file.read_text(encoding="utf-8")
except (OSError, UnicodeError):
text = sphinx_gettext("The NEWS file is not available.")
return [nodes.strong(text, text)]
# remove first 3 lines as they are the main heading
news_text = news_text.removeprefix(BLURB_HEADER)
news_text = bpo_issue_re.sub(r":issue:`\1`", news_text)
# Fallback handling for GitHub issues
news_text = gh_issue_re.sub(r":gh:`\1`", news_text)
news_text = whatsnew_re.sub(r"\1", news_text)
self.state_machine.insert_input(news_text.splitlines(), str(news_file))
return []
def setup(app: Sphinx) -> ExtensionMetadata:
app.add_directive("miscnews", MiscNews)
return {
"version": "1.0",
"parallel_read_safe": True,
"parallel_write_safe": True,
}

View File

@ -141,46 +141,6 @@ class PyAbstractMethod(PyMethod):
return PyMethod.run(self)
# Support for including Misc/NEWS
issue_re = re.compile('(?:[Ii]ssue #|bpo-)([0-9]+)', re.I)
gh_issue_re = re.compile('(?:gh-issue-|gh-)([0-9]+)', re.I)
whatsnew_re = re.compile(r"(?im)^what's new in (.*?)\??$")
class MiscNews(SphinxDirective):
has_content = False
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = False
option_spec = {}
def run(self):
fname = self.arguments[0]
source = self.state_machine.input_lines.source(
self.lineno - self.state_machine.input_offset - 1)
source_dir = getenv('PY_MISC_NEWS_DIR')
if not source_dir:
source_dir = path.dirname(path.abspath(source))
fpath = path.join(source_dir, fname)
self.env.note_dependency(path.abspath(fpath))
try:
with io.open(fpath, encoding='utf-8') as fp:
content = fp.read()
except Exception:
text = 'The NEWS file is not available.'
node = nodes.strong(text, text)
return [node]
content = issue_re.sub(r':issue:`\1`', content)
# Fallback handling for the GitHub issue
content = gh_issue_re.sub(r':gh:`\1`', content)
content = whatsnew_re.sub(r'\1', content)
# remove first 3 lines as they are the main heading
lines = ['.. default-role:: obj', ''] + content.splitlines()[3:]
self.state_machine.insert_input(lines, fname)
return []
# Support for documenting Opcodes
opcode_sig_re = re.compile(r'(\w+(?:\+\d)?)(?:\s*\((.*)\))?')
@ -268,6 +228,5 @@ def setup(app):
app.add_directive_to_domain('py', 'awaitablefunction', PyAwaitableFunction)
app.add_directive_to_domain('py', 'awaitablemethod', PyAwaitableMethod)
app.add_directive_to_domain('py', 'abstractmethod', PyAbstractMethod)
app.add_directive('miscnews', MiscNews)
app.connect('env-check-consistency', patch_pairindextypes)
return {'version': '1.0', 'parallel_read_safe': True}

View File

@ -1,5 +1,7 @@
.. _changelog:
.. default-role:: py:obj
+++++++++
Changelog
+++++++++

View File

@ -1146,7 +1146,7 @@ after a commit.
.. section: Library
A new version of typing.py from https://github.com/python/typing:
Collection (only for 3.6) (Issue #27598). Add FrozenSet to __all__
Collection (only for 3.6) (issue #27598). Add FrozenSet to __all__
(upstream #261). Fix crash in _get_type_vars() (upstream #259). Remove the
dict constraint in ForwardRef._eval_type (upstream #252).

View File

@ -177,7 +177,7 @@ Support keyword arguments to zlib.decompress(). Patch by Xiang Zhang.
.. section: Library
Prevent segfault after interpreter re-initialization due to ref count
problem introduced in code for Issue #27038 in 3.6.0a3. Patch by Xiang
problem introduced in code for issue #27038 in 3.6.0a3. Patch by Xiang
Zhang.
..

View File

@ -1137,7 +1137,7 @@ chunked transfer-encoding.
.. section: Library
A new version of typing.py from https://github.com/python/typing: -
Collection (only for 3.6) (Issue #27598) - Add FrozenSet to __all__
Collection (only for 3.6) (issue #27598) - Add FrozenSet to __all__
(upstream #261) - fix crash in _get_type_vars() (upstream #259) - Remove the
dict constraint in ForwardRef._eval_type (upstream #252)