mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-05 18:58:53 -05:00
Add deprecated-removed directive to Sphinx (#4912)
* Add `deprecated-removed` directive to Sphinx * Add equivalent function to internal utils
This commit is contained in:
parent
76bb65912e
commit
0144cbf88b
129
docs/_ext/deprecated_removed.py
Normal file
129
docs/_ext/deprecated_removed.py
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
"""
|
||||||
|
A Sphinx extension adding a ``deprecated-removed`` directive that works
|
||||||
|
similarly to CPython's directive with the same name.
|
||||||
|
|
||||||
|
The key difference is that instead of passing the version of planned removal,
|
||||||
|
the writer must provide the minimum amount of days that must pass
|
||||||
|
since the date of the release it was deprecated in.
|
||||||
|
|
||||||
|
Due to lack of a concrete release schedule for Red, this ensures that
|
||||||
|
we give enough time to people affected by the changes no matter
|
||||||
|
when the releases actually happen.
|
||||||
|
|
||||||
|
`DeprecatedRemoved` class is heavily based on
|
||||||
|
`sphinx.domains.changeset.VersionChange` class that is available at:
|
||||||
|
https://github.com/sphinx-doc/sphinx/blob/0949735210abaa05b6448e531984f159403053f4/sphinx/domains/changeset.py
|
||||||
|
|
||||||
|
Copyright 2007-2020 by the Sphinx team, see AUTHORS:
|
||||||
|
https://github.com/sphinx-doc/sphinx/blob/82f495fed386c798735adf675f867b95d61ee0e1/AUTHORS
|
||||||
|
|
||||||
|
The original copy was distributed under BSD License and this derivative work
|
||||||
|
is distributed under GNU GPL Version 3.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import multiprocessing
|
||||||
|
import subprocess
|
||||||
|
from typing import Any, Dict, List, Optional
|
||||||
|
|
||||||
|
from docutils import nodes
|
||||||
|
from sphinx import addnodes
|
||||||
|
from sphinx.application import Sphinx
|
||||||
|
from sphinx.util.docutils import SphinxDirective
|
||||||
|
|
||||||
|
|
||||||
|
class TagDateCache:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self._tags: Dict[str, datetime.date] = {}
|
||||||
|
|
||||||
|
def _populate_tags(self) -> None:
|
||||||
|
with _LOCK:
|
||||||
|
if self._tags:
|
||||||
|
return
|
||||||
|
out = subprocess.check_output(
|
||||||
|
("git", "tag", "-l", "--format", "%(creatordate:raw)\t%(refname:short)"),
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
lines = out.splitlines(False)
|
||||||
|
for line in lines:
|
||||||
|
creator_date, tag_name = line.split("\t", maxsplit=1)
|
||||||
|
timestamp = int(creator_date.split(" ", maxsplit=1)[0])
|
||||||
|
self._tags[tag_name] = datetime.datetime.fromtimestamp(
|
||||||
|
timestamp, tz=datetime.timezone.utc
|
||||||
|
).date()
|
||||||
|
|
||||||
|
def get_tag_date(self, tag_name: str) -> Optional[datetime.date]:
|
||||||
|
self._populate_tags()
|
||||||
|
return self._tags.get(tag_name)
|
||||||
|
|
||||||
|
|
||||||
|
_LOCK = multiprocessing.Manager().Lock()
|
||||||
|
_TAGS = TagDateCache()
|
||||||
|
|
||||||
|
|
||||||
|
class DeprecatedRemoved(SphinxDirective):
|
||||||
|
has_content = True
|
||||||
|
required_arguments = 2
|
||||||
|
optional_arguments = 1
|
||||||
|
final_argument_whitespace = True
|
||||||
|
|
||||||
|
def run(self) -> List[nodes.Node]:
|
||||||
|
# Some Sphinx stuff
|
||||||
|
node = addnodes.versionmodified()
|
||||||
|
node.document = self.state.document
|
||||||
|
self.set_source_info(node)
|
||||||
|
node["type"] = self.name
|
||||||
|
node["version"] = tuple(self.arguments)
|
||||||
|
if len(self.arguments) == 3:
|
||||||
|
inodes, messages = self.state.inline_text(self.arguments[2], self.lineno + 1)
|
||||||
|
para = nodes.paragraph(self.arguments[2], "", *inodes, translatable=False)
|
||||||
|
self.set_source_info(para)
|
||||||
|
node.append(para)
|
||||||
|
else:
|
||||||
|
messages = []
|
||||||
|
|
||||||
|
# Text generation
|
||||||
|
deprecation_version = self.arguments[0]
|
||||||
|
minimum_days = int(self.arguments[1])
|
||||||
|
tag_date = _TAGS.get_tag_date(deprecation_version)
|
||||||
|
text = (
|
||||||
|
f"Will be deprecated in version {deprecation_version},"
|
||||||
|
" and removed in the first minor version that gets released"
|
||||||
|
f" after {minimum_days} days since deprecation"
|
||||||
|
if tag_date is None
|
||||||
|
else f"Deprecated since version {deprecation_version},"
|
||||||
|
" will be removed in the first minor version that gets released"
|
||||||
|
f" after {tag_date + datetime.timedelta(days=minimum_days)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# More Sphinx stuff
|
||||||
|
if self.content:
|
||||||
|
self.state.nested_parse(self.content, self.content_offset, node)
|
||||||
|
classes = ["versionmodified"]
|
||||||
|
if len(node):
|
||||||
|
if isinstance(node[0], nodes.paragraph) and node[0].rawsource:
|
||||||
|
content = nodes.inline(node[0].rawsource, translatable=True)
|
||||||
|
content.source = node[0].source
|
||||||
|
content.line = node[0].line
|
||||||
|
content += node[0].children
|
||||||
|
node[0].replace_self(nodes.paragraph("", "", content, translatable=False))
|
||||||
|
|
||||||
|
node[0].insert(0, nodes.inline("", f"{text}: ", classes=classes))
|
||||||
|
else:
|
||||||
|
para = nodes.paragraph(
|
||||||
|
"", "", nodes.inline("", f"{text}.", classes=classes), translatable=False
|
||||||
|
)
|
||||||
|
node.append(para)
|
||||||
|
|
||||||
|
ret = [node]
|
||||||
|
ret += messages
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def setup(app: Sphinx) -> Dict[str, Any]:
|
||||||
|
app.add_directive("deprecated-removed", DeprecatedRemoved)
|
||||||
|
return {
|
||||||
|
"version": "1.0",
|
||||||
|
"parallel_read_safe": True,
|
||||||
|
}
|
||||||
@ -21,6 +21,7 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
sys.path.insert(0, os.path.abspath(".."))
|
sys.path.insert(0, os.path.abspath(".."))
|
||||||
|
sys.path.insert(0, os.path.abspath("_ext"))
|
||||||
|
|
||||||
os.environ["BUILDING_DOCS"] = "1"
|
os.environ["BUILDING_DOCS"] = "1"
|
||||||
|
|
||||||
@ -42,6 +43,7 @@ extensions = [
|
|||||||
"sphinx.ext.napoleon",
|
"sphinx.ext.napoleon",
|
||||||
"sphinx.ext.doctest",
|
"sphinx.ext.doctest",
|
||||||
"sphinxcontrib_trio",
|
"sphinxcontrib_trio",
|
||||||
|
"deprecated_removed",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import os
|
|||||||
import re
|
import re
|
||||||
import shutil
|
import shutil
|
||||||
import tarfile
|
import tarfile
|
||||||
|
import warnings
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import (
|
from typing import (
|
||||||
@ -46,6 +47,7 @@ __all__ = (
|
|||||||
"send_to_owners_with_prefix_replaced",
|
"send_to_owners_with_prefix_replaced",
|
||||||
"expected_version",
|
"expected_version",
|
||||||
"fetch_latest_red_version_info",
|
"fetch_latest_red_version_info",
|
||||||
|
"deprecated_removed",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -317,3 +319,19 @@ async def fetch_latest_red_version_info() -> Tuple[Optional[VersionInfo], Option
|
|||||||
required_python = data["info"]["requires_python"]
|
required_python = data["info"]["requires_python"]
|
||||||
|
|
||||||
return release, required_python
|
return release, required_python
|
||||||
|
|
||||||
|
|
||||||
|
def deprecated_removed(
|
||||||
|
deprecation_target: str,
|
||||||
|
deprecation_version: str,
|
||||||
|
minimum_days: int,
|
||||||
|
message: str = "",
|
||||||
|
stacklevel: int = 1,
|
||||||
|
) -> None:
|
||||||
|
warnings.warn(
|
||||||
|
f"{deprecation_target} is deprecated since version {deprecation_version}"
|
||||||
|
" and will be removed in the first minor version that gets released"
|
||||||
|
f" after {minimum_days} days since deprecation. {message}",
|
||||||
|
DeprecationWarning,
|
||||||
|
stacklevel=stacklevel + 1,
|
||||||
|
)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user