Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drop Python 3.7 and Docutils 0.17 #10470

Merged
merged 4 commits into from Sep 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 4 additions & 6 deletions .github/workflows/main.yml
Expand Up @@ -7,20 +7,18 @@ permissions:

jobs:
ubuntu:
runs-on: ubuntu-18.04
runs-on: ubuntu-20.04
name: Python ${{ matrix.python }} (${{ matrix.docutils }})
strategy:
fail-fast: false
matrix:
include:
- python: "3.7"
docutils: du15
- python: "3.8"
docutils: du16
docutils: du18
- python: "3.9"
docutils: du17
docutils: du19
- python: "3.10"
docutils: du18
docutils: du19
- python: "3.10"
docutils: du19
- python: "3.11-dev"
Expand Down
4 changes: 3 additions & 1 deletion CHANGES
Expand Up @@ -4,7 +4,9 @@ Release 6.0.0 (in development)
Dependencies
------------

* Drop python 3.6 support
* #10468: Drop Python 3.6 support
* #10470: Drop Python 3.7, Docutils 0.14, Docutils 0.15, Docutils 0.16, and
Docutils 0.17 support. Patch by Adam Turner

Incompatible changes
--------------------
Expand Down
16 changes: 9 additions & 7 deletions doc/internals/release-process.rst
Expand Up @@ -113,13 +113,15 @@ April 2023 and shipping Python 3.8.

This is a summary table with the current policy:

========== ========= ======
Date Ubuntu Python
========== ========= ======
April 2023 20.04 LTS 3.8+
---------- --------- ------
April 2025 22.04 LTS 3.10+
========== ========= ======
========== ========= ====== ======
Date Ubuntu Python Sphinx
========== ========= ====== ======
April 2021 18.04 LTS 3.6+ 4, 5
---------- --------- ------ ------
April 2023 20.04 LTS 3.8+ 6, 7
---------- --------- ------ ------
April 2025 22.04 LTS 3.10+ 8
========== ========= ====== ======

Release procedures
------------------
Expand Down
2 changes: 1 addition & 1 deletion doc/usage/installation.rst
Expand Up @@ -12,7 +12,7 @@ Installing Sphinx
Overview
--------

Sphinx is written in `Python`__ and supports Python 3.7+. It builds upon the
Sphinx is written in `Python`__ and supports Python 3.8+. It builds upon the
shoulders of many third-party libraries such as `Docutils`__ and `Jinja`__,
which are installed when Sphinx is installed.

Expand Down
9 changes: 3 additions & 6 deletions pyproject.toml
Expand Up @@ -13,7 +13,7 @@ urls.Download = "https://pypi.org/project/Sphinx/"
urls.Homepage = "https://www.sphinx-doc.org/"
urls."Issue tracker" = "https://github.com/sphinx-doc/sphinx/issues"
license.text = "BSD"
requires-python = ">=3.7"
requires-python = ">=3.8"

# Classifiers list: https://pypi.org/classifiers/
classifiers = [
Expand All @@ -30,7 +30,6 @@ classifiers = [
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
Expand Down Expand Up @@ -64,7 +63,7 @@ dependencies = [
"sphinxcontrib-qthelp",
"Jinja2>=3.0",
"Pygments>=2.12",
"docutils>=0.14,<0.20",
"docutils>=0.18,<0.20",
"snowballstemmer>=2.0",
"babel>=2.9",
"alabaster>=0.7,<0.8",
Expand All @@ -89,13 +88,11 @@ lint = [
"mypy>=0.981",
"sphinx-lint",
"docutils-stubs",
"types-typed-ast",
"types-requests",
]
test = [
"pytest>=4.6",
"html5lib",
"typed_ast; python_version < '3.8'",
"cython",
]

Expand Down Expand Up @@ -144,7 +141,7 @@ disallow_incomplete_defs = true
follow_imports = "skip"
ignore_missing_imports = true
no_implicit_optional = true
python_version = "3.7"
python_version = "3.8"
show_column_numbers = true
show_error_codes = true
show_error_context = true
Expand Down
44 changes: 14 additions & 30 deletions sphinx/addnodes.py
Expand Up @@ -2,19 +2,24 @@

from typing import TYPE_CHECKING, Any, Dict, List, Optional, Sequence

import docutils
from docutils import nodes
from docutils.nodes import Element

from sphinx.deprecation import RemovedInSphinx70Warning, deprecated_alias

if TYPE_CHECKING:
from sphinx.application import Sphinx

try:
from docutils.nodes import meta as docutils_meta # type: ignore
except ImportError:
# docutils-0.17 or older
from docutils.parsers.rst.directives.html import MetaBody
docutils_meta = MetaBody.meta
deprecated_alias('sphinx.addnodes',
{
'meta': nodes.meta, # type: ignore
'docutils_meta': nodes.meta, # type: ignore
},
RemovedInSphinx70Warning,
{
'meta': 'docutils.nodes.meta',
'docutils_meta': 'docutils.nodes.meta',
})


class document(nodes.document):
Expand All @@ -29,18 +34,7 @@ class document(nodes.document):

def set_id(self, node: Element, msgnode: Optional[Element] = None,
suggested_prefix: str = '') -> str:
if docutils.__version_info__ >= (0, 16):
ret = super().set_id(node, msgnode, suggested_prefix) # type: ignore
else:
ret = super().set_id(node, msgnode)

if docutils.__version_info__ < (0, 17):
# register other node IDs forcedly
for node_id in node['ids']:
if node_id not in self.ids:
self.ids[node_id] = node

return ret
return super().set_id(node, msgnode, suggested_prefix) # type: ignore


class translatable(nodes.Node):
Expand Down Expand Up @@ -198,7 +192,7 @@ class desc_inline(_desc_classes_injector, nodes.Inline, nodes.TextElement):
classes = ['sig', 'sig-inline']

def __init__(self, domain: str, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs)
super().__init__(*args, **kwargs, domain=domain)
self['classes'].append(domain)


Expand Down Expand Up @@ -435,13 +429,6 @@ class tabular_col_spec(nodes.Element):
"""Node for specifying tabular columns, used for LaTeX output."""


class meta(nodes.Special, nodes.PreBibliographic, nodes.Element):
"""Node for meta directive -- same as docutils' standard meta node,
but pickleable.
"""
rawcontent = None


# inline nodes

class pending_xref(nodes.Inline, nodes.Element):
Expand Down Expand Up @@ -568,9 +555,6 @@ def setup(app: "Sphinx") -> Dict[str, Any]:
app.add_node(literal_strong)
app.add_node(manpage)

if docutils.__version_info__ < (0, 18):
app.add_node(meta)

return {
'version': 'builtin',
'parallel_read_safe': True,
Expand Down
2 changes: 1 addition & 1 deletion sphinx/directives/other.py
Expand Up @@ -77,7 +77,7 @@ def run(self) -> List[Node]:
return ret

def parse_content(self, toctree: addnodes.toctree) -> List[Node]:
generated_docnames = frozenset(self.env.domains['std'].initial_data['labels'].keys())
generated_docnames = frozenset(self.env.domains['std']._virtual_doc_names)
suffixes = self.config.source_suffix

# glob target documents
Expand Down
29 changes: 2 additions & 27 deletions sphinx/directives/patches.py
@@ -1,14 +1,14 @@
import os
from os import path
from typing import TYPE_CHECKING, Any, Dict, List, Sequence, cast
from typing import TYPE_CHECKING, Any, Dict, List, cast

from docutils import nodes
from docutils.nodes import Node, make_id
from docutils.parsers.rst import directives
from docutils.parsers.rst.directives import images, tables
from docutils.parsers.rst.directives.misc import Meta # type: ignore[attr-defined]
from docutils.parsers.rst.roles import set_classes

from sphinx import addnodes
from sphinx.directives import optional_int
from sphinx.domains.math import MathDomain
from sphinx.locale import __
Expand All @@ -18,12 +18,6 @@
from sphinx.util.osutil import SEP, os_path, relpath
from sphinx.util.typing import OptionSpec

try:
from docutils.parsers.rst.directives.misc import Meta as MetaBase # type: ignore
except ImportError:
# docutils-0.17 or older
from docutils.parsers.rst.directives.html import Meta as MetaBase

if TYPE_CHECKING:
from sphinx.application import Sphinx

Expand Down Expand Up @@ -57,25 +51,6 @@ def run(self) -> List[Node]:
return [figure_node]


class Meta(MetaBase, SphinxDirective):
def run(self) -> Sequence[Node]:
result = super().run()
for node in result:
# for docutils-0.17 or older. Since docutils-0.18, patching is no longer needed
# because it uses picklable node; ``docutils.nodes.meta``.
if (isinstance(node, nodes.pending) and
isinstance(node.details['nodes'][0], addnodes.docutils_meta)):
meta = node.details['nodes'][0]
meta.source = self.env.doc2path(self.env.docname)
meta.line = self.lineno
meta.rawcontent = meta['content']

# docutils' meta nodes aren't picklable because the class is nested
meta.__class__ = addnodes.meta

return result


class CSVTable(tables.CSVTable):
"""The csv-table directive which searches a CSV file from Sphinx project's source
directory when an absolute path is given via :file: option.
Expand Down
17 changes: 2 additions & 15 deletions sphinx/domains/python.py
@@ -1,9 +1,9 @@
"""The Python domain."""

import ast
import builtins
import inspect
import re
import sys
import typing
from inspect import Parameter
from typing import Any, Dict, Iterable, Iterator, List, NamedTuple, Optional, Tuple, Type, cast
Expand All @@ -21,7 +21,6 @@
from sphinx.domains import Domain, Index, IndexEntry, ObjType
from sphinx.environment import BuildEnvironment
from sphinx.locale import _, __
from sphinx.pycode.ast import ast
from sphinx.pycode.ast import parse as ast_parse
from sphinx.roles import XRefRole
from sphinx.util import logging
Expand Down Expand Up @@ -138,7 +137,7 @@ def unparse(node: ast.AST) -> List[Node]:
return [addnodes.desc_sig_space(),
addnodes.desc_sig_punctuation('', '|'),
addnodes.desc_sig_space()]
elif isinstance(node, ast.Constant): # type: ignore
elif isinstance(node, ast.Constant):
if node.value is Ellipsis:
return [addnodes.desc_sig_punctuation('', "...")]
elif isinstance(node.value, bool):
Expand Down Expand Up @@ -204,18 +203,6 @@ def unparse(node: ast.AST) -> List[Node]:

return result
else:
if sys.version_info < (3, 8):
if isinstance(node, ast.Bytes):
return [addnodes.desc_sig_literal_string('', repr(node.s))]
elif isinstance(node, ast.Ellipsis):
return [addnodes.desc_sig_punctuation('', "...")]
elif isinstance(node, ast.NameConstant):
return [nodes.Text(node.value)]
elif isinstance(node, ast.Num):
return [addnodes.desc_sig_literal_string('', repr(node.n))]
elif isinstance(node, ast.Str):
return [addnodes.desc_sig_literal_string('', repr(node.s))]

raise SyntaxError # unsupported syntax

try:
Expand Down
18 changes: 9 additions & 9 deletions sphinx/domains/std.py
@@ -1,10 +1,9 @@
"""The standard domain."""

import re
import sys
from copy import copy
from typing import (TYPE_CHECKING, Any, Callable, Dict, Iterable, Iterator, List, Optional,
Tuple, Type, Union, cast)
from typing import (TYPE_CHECKING, Any, Callable, Dict, Final, Iterable, Iterator, List,
Optional, Tuple, Type, Union, cast)

from docutils import nodes
from docutils.nodes import Element, Node, system_message
Expand All @@ -29,11 +28,6 @@

logger = logging.getLogger(__name__)

if sys.version_info[:2] >= (3, 8):
from typing import Final
else:
Final = Any

# RE for option descriptions
option_desc_re = re.compile(r'((?:/|--|-|\+)?[^\s=]+)(=?\s*.*)')
# RE for grammar tokens
Expand Down Expand Up @@ -589,7 +583,7 @@ class StandardDomain(Domain):
'doc': XRefRole(warn_dangling=True, innernodeclass=nodes.inline),
}

initial_data: Final = {
initial_data: Final = { # type: ignore[misc]
'progoptions': {}, # (program, name) -> docname, labelid
'objects': {}, # (type, name) -> docname, labelid
'labels': { # labelname -> docname, labelid, sectionname
Expand All @@ -604,6 +598,12 @@ class StandardDomain(Domain):
},
}

_virtual_doc_names: Dict[str, Tuple[str, str]] = { # labelname -> docname, sectionname
'genindex': ('genindex', _('Index')),
'modindex': ('py-modindex', _('Module Index')),
'search': ('search', _('Search Page')),
}

dangling_warnings = {
'term': 'term not in glossary: %(target)r',
'numref': 'undefined label: %(target)r',
Expand Down
3 changes: 0 additions & 3 deletions sphinx/environment/__init__.py
Expand Up @@ -9,7 +9,6 @@
from typing import (TYPE_CHECKING, Any, Callable, Dict, Generator, Iterator, List, Optional,
Set, Tuple, Union)

import docutils
from docutils import nodes
from docutils.nodes import Node

Expand Down Expand Up @@ -52,8 +51,6 @@
'file_insertion_enabled': True,
'smartquotes_locales': [],
}
if docutils.__version_info__[:2] <= (0, 17):
default_settings['embed_images'] = False

# This is increased every time an environment attribute is added
# or changed to properly invalidate pickle files.
Expand Down
4 changes: 2 additions & 2 deletions sphinx/environment/adapters/toctree.py
Expand Up @@ -54,7 +54,7 @@ def resolve(self, docname: str, builder: "Builder", toctree: addnodes.toctree,
"""
if toctree.get('hidden', False) and not includehidden:
return None
generated_docnames: Dict[str, Tuple[str, str, str]] = self.env.domains['std'].initial_data['labels'].copy() # NoQA: E501
generated_docnames: Dict[str, Tuple[str, str]] = self.env.domains['std']._virtual_doc_names.copy() # NoQA: E501

# For reading the following two helper function, it is useful to keep
# in mind the node structure of a toctree (using HTML-like node names
Expand Down Expand Up @@ -141,7 +141,7 @@ def _entries_from_toctree(toctreenode: addnodes.toctree, parents: List[str],
# don't show subitems
toc = nodes.bullet_list('', item)
elif ref in generated_docnames:
docname, _, sectionname = generated_docnames[ref]
docname, sectionname = generated_docnames[ref]
if not title:
title = sectionname
reference = nodes.reference('', title, internal=True,
Expand Down