Skip to content

Commit 686e315

Browse files
authored
Merge 2412a64 into 2dcffbe
2 parents 2dcffbe + 2412a64 commit 686e315

14 files changed

+296
-176
lines changed

.github/workflows/python_ci.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@ jobs:
2727
fail-fast: False
2828
matrix:
2929
config:
30-
- {python-version: "3.6", testenvs: "py36,build", experimental: False}
31-
- {python-version: "3.7", testenvs: "py37,build", experimental: False}
32-
- {python-version: "3.8", testenvs: "py38,build", experimental: False}
33-
- {python-version: "3.9", testenvs: "py39,build", experimental: False}
34-
- {python-version: "3.10", testenvs: "py310-dev,build", experimental: True}
35-
- {python-version: "pypy-3.6", testenvs: "pypy36,build", experimental: False}
36-
- {python-version: "pypy-3.7", testenvs: "pypy37,build", experimental: True}
30+
- {python-version: "3.6", testenvs: "py36-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3},build", experimental: False}
31+
- {python-version: "3.7", testenvs: "py37-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3},build", experimental: False}
32+
- {python-version: "3.8", testenvs: "py38-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3},build", experimental: False}
33+
- {python-version: "3.9", testenvs: "py39-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3},build", experimental: False}
34+
- {python-version: "3.10", testenvs: "py310-dev-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3},build", experimental: True}
35+
- {python-version: "pypy-3.6", testenvs: "pypy36-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3},build", experimental: False}
36+
- {python-version: "pypy-3.7", testenvs: "pypy37-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3},build", experimental: True}
3737

3838
steps:
3939
- name: Checkout 🛎️

.github/workflows/python_ci_linux.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,13 @@ jobs:
2828
fail-fast: False
2929
matrix:
3030
config:
31-
- {python-version: "3.6", testenvs: "py36,build", experimental: False}
32-
- {python-version: "3.7", testenvs: "py37,build", experimental: False}
33-
- {python-version: "3.8", testenvs: "py38,build", experimental: False}
34-
- {python-version: "3.9", testenvs: "py39,build", experimental: False}
35-
- {python-version: "3.10", testenvs: "py310-dev,build", experimental: True}
36-
- {python-version: "pypy-3.6", testenvs: "pypy36,build", experimental: False}
37-
- {python-version: "pypy-3.7", testenvs: "pypy37,build", experimental: True}
31+
- {python-version: "3.6", testenvs: "py36-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3},build", experimental: False}
32+
- {python-version: "3.7", testenvs: "py37-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3},build", experimental: False}
33+
- {python-version: "3.8", testenvs: "py38-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3},build", experimental: False}
34+
- {python-version: "3.9", testenvs: "py39-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3},build", experimental: False}
35+
- {python-version: "3.10", testenvs: "py310-dev-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3},build", experimental: True}
36+
- {python-version: "pypy-3.6", testenvs: "pypy36-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3},build", experimental: False}
37+
- {python-version: "pypy-3.7", testenvs: "pypy37-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3},build", experimental: True}
3838

3939
steps:
4040
- name: Checkout 🛎️

.github/workflows/python_ci_macos.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@ jobs:
2727
fail-fast: False
2828
matrix:
2929
config:
30-
- {python-version: "3.6", testenvs: "py36,build", experimental: False}
31-
- {python-version: "3.7", testenvs: "py37,build", experimental: False}
32-
- {python-version: "3.8", testenvs: "py38,build", experimental: False}
33-
- {python-version: "3.9", testenvs: "py39,build", experimental: False}
34-
- {python-version: "3.10", testenvs: "py310-dev,build", experimental: True}
35-
- {python-version: "pypy-3.6", testenvs: "pypy36,build", experimental: False}
36-
- {python-version: "pypy-3.7", testenvs: "pypy37,build", experimental: True}
30+
- {python-version: "3.6", testenvs: "py36-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3},build", experimental: False}
31+
- {python-version: "3.7", testenvs: "py37-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3},build", experimental: False}
32+
- {python-version: "3.8", testenvs: "py38-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3},build", experimental: False}
33+
- {python-version: "3.9", testenvs: "py39-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3},build", experimental: False}
34+
- {python-version: "3.10", testenvs: "py310-dev-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3},build", experimental: True}
35+
- {python-version: "pypy-3.6", testenvs: "pypy36-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3},build", experimental: False}
36+
- {python-version: "pypy-3.7", testenvs: "pypy37-sphinx{3.2,3.3,3.4,3.5,4.0,4.1,4.2,4.3},build", experimental: True}
3737

3838
steps:
3939
- name: Checkout 🛎️

__pkginfo__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,6 @@
1414
__all__ = ["extras_require"]
1515

1616
extras_require = {
17-
"sphinx": ["sphinx<3.5.0,>=3.0.3", "sphinx-toolbox>=1.2.0"],
18-
"all": ["sphinx<3.5.0,>=3.0.3", "sphinx-toolbox>=1.2.0"]
17+
"sphinx": ["sphinx<4.4,>=3.2.0", "sphinx-toolbox>=1.2.0"],
18+
"all": ["sphinx<4.4,>=3.2.0", "sphinx-toolbox>=1.2.0"]
1919
}

enum_tools/autoenum.py

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,17 +54,26 @@
5454
# stdlib
5555
from contextlib import suppress
5656
from enum import Enum
57-
from typing import Any, Dict, List, Optional, Tuple
57+
from typing import Any, Dict, List, Optional, Tuple, get_type_hints
5858

5959
# 3rd party
6060
from docutils.nodes import Element # nodep
6161
from sphinx.application import Sphinx # nodep
6262
from sphinx.domains import ObjType # nodep
6363
from sphinx.domains.python import PyClasslike, PyXRefRole # nodep
6464
from sphinx.environment import BuildEnvironment # nodep
65-
from sphinx.ext.autodoc import ALL, INSTANCEATTR, AttributeDocumenter, ClassDocumenter, Documenter # nodep
65+
from sphinx.ext.autodoc import ( # nodep
66+
ALL,
67+
INSTANCEATTR,
68+
SUPPRESS,
69+
AttributeDocumenter,
70+
ClassDocumenter,
71+
ClassLevelDocumenter,
72+
Documenter
73+
)
6674
from sphinx.locale import _ # nodep
67-
from sphinx.util.inspect import object_description # nodep
75+
from sphinx.util.inspect import memory_address_re, safe_getattr # nodep
76+
from sphinx.util.typing import stringify as stringify_typehint # nodep
6877
from sphinx_toolbox.more_autodoc.typehints import format_annotation # nodep
6978
from sphinx_toolbox.utils import begin_generate # nodep
7079

@@ -321,7 +330,6 @@ def import_object(self, raiseerror: bool = False) -> bool:
321330
.. latex:clearpage::
322331
"""
323332

324-
self._datadescriptor = False
325333
return Documenter.import_object(self, raiseerror=raiseerror)
326334

327335
def generate(
@@ -366,12 +374,40 @@ def add_directive_header(self, sig: str) -> None:
366374
:param sig:
367375
"""
368376

369-
super().add_directive_header(sig)
377+
ClassLevelDocumenter.add_directive_header(self, sig)
378+
sourcename = self.get_sourcename()
379+
if not self.options.annotation:
380+
# obtain type annotation for this attribute
381+
try:
382+
annotations = get_type_hints(self.parent)
383+
except NameError:
384+
# Failed to evaluate ForwardRef (maybe TYPE_CHECKING)
385+
annotations = safe_getattr(self.parent, "__annotations__", {})
386+
except (TypeError, KeyError, AttributeError):
387+
# KeyError = a broken class found (refs: https://github.com/sphinx-doc/sphinx/issues/8084)
388+
# AttributeError is raised on 3.5.2 (fixed by 3.5.3)
389+
annotations = {}
390+
391+
if self.objpath[-1] in annotations:
392+
objrepr = stringify_typehint(annotations.get(self.objpath[-1]))
393+
self.add_line(" :type: " + objrepr, sourcename)
394+
else:
395+
key = ('.'.join(self.objpath[:-1]), self.objpath[-1])
396+
if self.analyzer and key in self.analyzer.annotations:
397+
self.add_line(" :type: " + self.analyzer.annotations[key], sourcename)
398+
399+
elif self.options.annotation is SUPPRESS:
400+
pass
401+
else:
402+
self.add_line(" :annotation: %s" % self.options.annotation, sourcename)
370403

371404
if not self.options.annotation:
372-
with suppress(ValueError):
405+
with suppress(Exception):
373406
if self.object is not INSTANCEATTR:
374-
objrepr = object_description(self.object)
407+
408+
# Workaround for https://github.com/sphinx-doc/sphinx/issues/9272
409+
# which broke Enum displays in 4.1.0
410+
objrepr = memory_address_re.sub('', repr(self.object)).replace('\n', ' ')
375411
self.add_line(f' :value: {objrepr}', self.get_sourcename())
376412

377413

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ Homepage = "https://github.com/domdfcoding/enum_tools"
2525
Documentation = "https://enum_tools.readthedocs.io/en/latest"
2626

2727
[project.optional-dependencies]
28-
sphinx = [ "sphinx<3.5.0,>=3.0.3", "sphinx-toolbox>=1.2.0",]
29-
all = [ "sphinx<3.5.0,>=3.0.3", "sphinx-toolbox>=1.2.0",]
28+
sphinx = [ "sphinx<4.4,>=3.2.0", "sphinx-toolbox>=1.2.0",]
29+
all = [ "sphinx<4.4,>=3.2.0", "sphinx-toolbox>=1.2.0",]
3030

3131
[tool.whey]
3232
base-classifiers = [

repo_helper.yml

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ short_desc: "Tools to expand Python's enum module."
1212
use_whey: true
1313
tox_testenv_extras: all
1414
standalone_contrib_guide: true
15-
min_coverage: 85
15+
min_coverage: 88
1616
docs_fail_on_warning: true
1717

1818
conda_channels:
@@ -51,7 +51,7 @@ keywords:
5151

5252
extras_require:
5353
sphinx:
54-
- sphinx<3.5.0,>=3.0.3
54+
- sphinx<4.4,>=3.2.0
5555
- sphinx-toolbox>=1.2.0
5656

5757
extra_sphinx_extensions:
@@ -76,3 +76,15 @@ sphinx_conf_epilogue:
7676
- enum_tools.autoenum.Type = Type
7777

7878
preserve_custom_theme: true
79+
80+
81+
third_party_version_matrix:
82+
sphinx:
83+
- 3.2
84+
- 3.3
85+
- 3.4
86+
- 3.5
87+
- 4.0
88+
- 4.1
89+
- 4.2
90+
- 4.3

tests/requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ pytest-regressions>=2.0.1
1313
pytest-rerunfailures>=9.0
1414
pytest-timeout>=1.4.2
1515
sphinx>=3.2.1
16-
sphinx-toolbox[testing]>=0.3.0
16+
sphinx-toolbox[testing]>=2.15.2

tests/test_autoenum.py

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99

1010
# 3rd party
1111
import pytest
12-
from bs4 import BeautifulSoup # type: ignore
12+
import sphinx
13+
from bs4 import BeautifulSoup, NavigableString # type: ignore
1314
from pytest_regressions.file_regression import FileRegressionFixture # type: ignore
1415
from sphinx_toolbox.testing import HTMLRegressionFixture
1516

@@ -53,6 +54,33 @@ def test(app):
5354

5455
# pytestmark = pytest.mark.sphinx('html', testroot='root')
5556

57+
return_arrow = " → "
58+
59+
60+
def preprocess_soup(soup: BeautifulSoup):
61+
62+
if sphinx.version_info >= (3, 5): # pragma: no cover
63+
for em in soup.select("em.property"):
64+
child = ''.join(c.string for c in em.contents)
65+
for c in em.children:
66+
c.extract()
67+
em.contents = []
68+
em.insert(0, child)
69+
70+
for dl in soup.select("dl.py.method dt"): # .sig.sig-object.py
71+
if return_arrow in dl.contents:
72+
arrow_idx = dl.contents.index(return_arrow)
73+
dl.contents[arrow_idx] = NavigableString(
74+
dl.contents[arrow_idx] + dl.contents[arrow_idx + 1].contents[0]
75+
)
76+
dl.contents[arrow_idx + 1].extract()
77+
78+
for dt in soup.select("span.pre"):
79+
dt.replace_with_children()
80+
81+
for dt in soup.select("span.sig-return"):
82+
dt.replace_with(NavigableString(dt.get_text()))
83+
5684

5785
@pytest.mark.parametrize(
5886
"page", [
@@ -64,6 +92,8 @@ def test_index(page: BeautifulSoup, html_regression: HTMLRegressionFixture):
6492
title = page.find("h1").contents[0].strip()
6593
assert "autoenum Demo" == title
6694

95+
preprocess_soup(page)
96+
6797
html_regression.check(page, jinja2=True)
6898

6999
# Now test the directive
@@ -81,10 +111,8 @@ def test_index(page: BeautifulSoup, html_regression: HTMLRegressionFixture):
81111
assert class_.find("dt")["id"] == "enum_tools.demo.NoMethods"
82112
assert class_.find("dd").findAll('p')[0].contents[0] == "An enumeration of people without any methods."
83113

84-
assert str(class_.find("dd").findAll('p')[1].contents[0]) == (
85-
'<code class="xref py py-class docutils literal notranslate">'
86-
'<span class="pre">int</span></code>'
87-
)
114+
tag = '<code class="xref py py-class docutils literal notranslate">int</code>'
115+
assert str(class_.find("dd").findAll('p')[1].contents[0]) == tag
88116
assert class_.find("dd").findAll('p')[2].contents[0] == "Valid values are as follows:"
89117

90118
attr_count = 0
@@ -147,6 +175,8 @@ def test_flag(page: BeautifulSoup, html_regression: HTMLRegressionFixture):
147175
title = page.find("h1").contents[0].strip()
148176
assert "autoenum Demo - Flag" == title
149177

178+
preprocess_soup(page)
179+
150180
html_regression.check(page, jinja2=True)
151181

152182
# Now test the directive
@@ -164,10 +194,8 @@ def test_flag(page: BeautifulSoup, html_regression: HTMLRegressionFixture):
164194

165195
assert class_.find("dd").findAll('p')[0].contents[0] == "An enumeration of status codes."
166196

167-
assert str(class_.find("dd").findAll('p')[1].contents[0]) == (
168-
'<code class="xref py py-class docutils literal notranslate">'
169-
'<span class="pre">int</span></code>'
170-
)
197+
tag = '<code class="xref py py-class docutils literal notranslate">int</code>'
198+
assert str(class_.find("dd").findAll('p')[1].contents[0]) == tag
171199
assert class_.find("dd").findAll('p')[2].contents[0] == "Valid values are as follows:"
172200

173201
attr_count = 0
@@ -232,6 +260,8 @@ def test_no_member_doc(page: BeautifulSoup, html_regression: HTMLRegressionFixtu
232260
title = page.find("h1").contents[0].strip()
233261
assert "autoenum Demo - Members without docstrings" == title
234262

263+
preprocess_soup(page)
264+
235265
html_regression.check(page, jinja2=True)
236266

237267
# Now test the directive
@@ -247,10 +277,8 @@ def test_no_member_doc(page: BeautifulSoup, html_regression: HTMLRegressionFixtu
247277
0] == "An enumeration of people without any member docstrings."
248278

249279
if class_count == 0:
250-
assert str(class_.find("dd").findAll('p')[1].contents[0]) == (
251-
'<code class="xref py py-class docutils literal notranslate">'
252-
'<span class="pre">int</span></code>'
253-
)
280+
tag = '<code class="xref py py-class docutils literal notranslate">int</code>'
281+
assert str(class_.find("dd").findAll('p')[1].contents[0]) == tag
254282
assert class_.find("dd").findAll('p')[2].contents[0] == "Valid values are as follows:"
255283
else:
256284
assert class_.find("dd").findAll('p')[1].contents[0] == "Valid values are as follows:"

0 commit comments

Comments
 (0)