Merge branch '3.x' into 8222_novalue

This commit is contained in:
Takeshi KOMIYA 2020-11-17 23:56:42 +09:00
commit 3becd56e0e
27 changed files with 257 additions and 129 deletions

View File

@ -46,6 +46,8 @@ jobs:
if: endsWith(matrix.python, '-dev')
with:
python-version: ${{ matrix.python }}
env:
ACTIONS_ALLOW_UNSECURE_COMMANDS: true
- name: Check Python version
run: python --version
- name: Install graphviz

View File

@ -40,6 +40,8 @@ Bugs fixed
* #4606: autodoc: the location of the warning is incorrect for inherited method
* #8105: autodoc: the signature of class constructor is incorrect if the class
is decorated
* #8434: autodoc: :confval:`autodoc_type_aliases` does not effect to variables
and attributes
Testing
--------

View File

@ -29,9 +29,11 @@ directory = sphinx/locale/
[flake8]
max-line-length = 95
ignore = E116,E241,E251,E741,W504,I101
exclude = .git,.tox,.venv,tests/*,build/*,doc/_build/*,sphinx/search/*,doc/usage/extensions/example*.py
exclude = .git,.tox,.venv,tests/roots/*,build/*,doc/_build/*,sphinx/search/*,doc/usage/extensions/example*.py
application-import-names = sphinx
import-order-style = smarkets
per-file-ignores =
tests/*: E501
[flake8:local-plugins]
extension =

View File

@ -1703,7 +1703,8 @@ class DataDocumenter(ModuleLevelDocumenter):
if not self.options.annotation:
# obtain annotation for this data
try:
annotations = get_type_hints(self.parent)
annotations = get_type_hints(self.parent, None,
self.config.autodoc_type_aliases)
except NameError:
# Failed to evaluate ForwardRef (maybe TYPE_CHECKING)
annotations = safe_getattr(self.parent, '__annotations__', {})
@ -2095,7 +2096,8 @@ class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter):
if not self.options.annotation:
# obtain type annotation for this attribute
try:
annotations = get_type_hints(self.parent)
annotations = get_type_hints(self.parent, None,
self.config.autodoc_type_aliases)
except NameError:
# Failed to evaluate ForwardRef (maybe TYPE_CHECKING)
annotations = safe_getattr(self.parent, '__annotations__', {})
@ -2272,8 +2274,8 @@ class SlotsAttributeDocumenter(AttributeDocumenter):
% self.__class__.__name__,
RemovedInSphinx50Warning, stacklevel=2)
name = self.objpath[-1]
__slots__ = safe_getattr(self.parent, '__slots__', [])
if isinstance(__slots__, dict) and isinstance(__slots__.get(name), str):
__slots__ = inspect.getslots(self.parent)
if __slots__ and isinstance(__slots__.get(name, None), str):
docstring = prepare_docstring(__slots__[name])
return [docstring]
else:

View File

@ -16,7 +16,7 @@ from typing import Any, Callable, Dict, List, Mapping, NamedTuple, Optional, Tup
from sphinx.deprecation import RemovedInSphinx40Warning, deprecated_alias
from sphinx.pycode import ModuleAnalyzer
from sphinx.util import logging
from sphinx.util.inspect import isclass, isenumclass, safe_getattr
from sphinx.util.inspect import getslots, isclass, isenumclass, safe_getattr
if False:
# For type annotation
@ -203,14 +203,15 @@ def get_object_members(subject: Any, objpath: List[str], attrgetter: Callable,
members[name] = Attribute(name, True, value)
# members in __slots__
if isclass(subject) and getattr(subject, '__slots__', None) is not None:
from sphinx.ext.autodoc import SLOTSATTR
try:
__slots__ = getslots(subject)
if __slots__:
from sphinx.ext.autodoc import SLOTSATTR
slots = subject.__slots__
if isinstance(slots, str):
slots = [slots]
for name in slots:
members[name] = Attribute(name, True, SLOTSATTR)
for name in __slots__:
members[name] = Attribute(name, True, SLOTSATTR)
except (TypeError, ValueError):
pass
# other members
for name in dir(subject):

View File

@ -137,6 +137,27 @@ def unwrap_all(obj: Any, *, stop: Callable = None) -> Any:
return obj
def getslots(obj: Any) -> Optional[Dict]:
"""Get __slots__ attribute of the class as dict.
Return None if gienv *obj* does not have __slots__.
"""
if not inspect.isclass(obj):
raise TypeError
__slots__ = safe_getattr(obj, '__slots__', None)
if __slots__ is None:
return None
elif isinstance(__slots__, dict):
return __slots__
elif isinstance(__slots__, str):
return {__slots__: None}
elif isinstance(__slots__, (list, tuple)):
return {e: None for e in __slots__}
else:
raise ValueError
def isenumclass(x: Any) -> bool:
"""Check if the object is subclass of enum."""
return inspect.isclass(x) and issubclass(x, enum.Enum)

View File

@ -1,50 +1,50 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDLbT8rECMOtm7H
6KwFnCiS2kUjZCy/3yJGpIeGoTLXP+N8a1LUUIX3MJ8qu1vsPK6Yz1h/+Xiv73xy
WjwK6Di17q35VyYVYiRjuhIsiH1dwwYfNE/hO3QhLOEtBK7+BTWUQ36K+wRFBf/Y
VynHh7EvKawCWSmaZVyqjj2XS83mp1A8Jt0MTA4A4RyhV4aS16VviSeRf3OcVd69
qtvf3MClrDFRyFnCIE6ajfp1hrZ5wC7Fv5d0A0dKJ2MGYoblUPT/qq+b8jn1nxPv
uSHo4XhjWXq+k05idIl7mLk7wurmgHjmslB0/ZdE4wNa6oANLevF4vxiCGJ6c9gf
8Q56gaUZAgMBAAECggEAXoXguC3DXG7AgvtGE0VARRxOy+ccM/uGfbStlI0KhqIV
HhbwYd8YoIdjLgPo7pgzuKV/xdcxkO6CsM/k3lyRHVhOVnF8LKtxpTUshKzXM94O
1ikEhO+PQmsMJlLqzPW2s7G49vM0RK5I90lpDGGsnvGKD31Gq4s1x7pYPBjpD6cS
LBzbvPgrrfUNgLGeaIyyJDJSyBrSaNbtHTDjtF/i72KSNUEIfRepCm8IQYrvnEYU
E8+fzJNvDz2qF1lA58AkkaDznCLTa9pV1DEFwvXJ3N4nN+CZdsZ9jmrSPQ4uTcJG
bSU9jPrfFvsP7nQJHx66uD5qEy/SWqDfN7pDqsgFkQKBgQD1TWxI0CihfxM/z0mu
V6nkaYBNtuJxllFu1azJpMBAqu/40GW4EtEq6s2lEig/6ct2+bLZcT+u6DVUHeGv
DzA3Wu8mrzIChT7+4c080CMBKX8zcADK1IpeTV5DMR1sGgUV+OqYG7z8Vz+PyfMs
UzR1dCGjcdAcRFVg6nlIeDAlBwKBgQDUTFHvHMyPhzY+jwKFHxMIIvMaZZQ1z7uN
Oks0DHl7yD+4qH/fRWJBS0YtxWXIuA7xnnz1MMWVEJAXrR/CvnrBuyYWYhZnziU4
rWwqEK1o+sTS8QY+5NcQ4Hrzmszx3qtNjBBUwudkmwiCP5b9eyk68Xj2mbdNjhfK
xCIqPc583wKBgHk5KLEXBW1Bwj5/btcUhWXWaUx+e4tMkLOoLrp7i3Kpxut7+Tit
O+bsoHHZ9kAXhrAmF6dzWthR8sC9/6Cmbdp9OsAwRhOOy6Hj7qwF47aYTj8aM5oI
zNRrgZDM/dBFT4wbNbuzwYImj8e8MksOV1dP66u8++5sKpE5bnRMyOYTAoGACkZ3
YL9gF0JQGc8KLC9I2If4hDqOZdxcE4XSxf4kkx0qGGHvbnsJOmfOScDYIFLoRkGJ
gsSNi511m+/BLcfSYTYRrdupgfS0UH30UkTkX8RjamJIDxs8XZC/4rKHYN2KJQK2
d6PHV1M5ojQ5tqMTZ8rwM99Uw+gwtpuvm6PKLrkCgYAxlz2Aw4VWDuHhGOYM3htw
6mdU0kcC9oupQGFbWrw6S8kJhSN3cS2e3ZtIfy841fFvi0zNIxPNW25CiP+aplZ3
EFUTEUpVg7OQP57KBRcztp2N5FZUKSwq/FUOuxJWCxIYe/nsqQ7Xet5cxRaK7vb4
hFJQbfh2LfWCK7O+kpXXtg==
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC9fzHGBPNaZNcN
nL/1nvO2xJR/E64vFua3QfPQQ5HpigjrK/HRUlRGztRKJ+CEjCXNYNfQ4dUcV45o
k5uPH3U1CkAw2d/We+kZnAHkNuw4mRC0ohdzpUByyDOA5WtUWPn9SwhXCVz6fM7e
I52auvzpUE6soVDM3nucnqZDJ3Ua9KgB02FrqX13S76Uq+uf8Q2hpTruO/nBzB4p
6xFwJJ1taXEEWi8swg6HO8/+0x0AeripV6JieNUptEFuV9kLvRz9qGg0CO2f7AdI
jNeFDGrgO7qJ+VxXV9Gnbi6ph4vsUwtJZB3phRGGomdgiRd6PSma81nvTe1z69x/
g+8P091pAgMBAAECggEAIrTABfd0JpMffAPAeJjjJA8+70NIfKFiIiA3Kmalu7Mn
TQMgZ+j/PHS3FtnU2hHc/o+FF2G1KVqz311heUYWrl8xQIE26M6K88DJ6+VPQFJw
Z9TkHK8gbaVTIYFjNfCR4J00atRxLgNb0/2L6QHkPksSDbYB2XPKCfZYlyYL4aKq
dePghFu9ePXhUXooPCqke+kP0b8OmHzPlmJpxbeb8ujiox2+4wYjN8lWPz8xHv8i
IM7V5hAbPIaQfu/joKrRKk+Kk8UqGurkKQ75KLLL+1oaJO/GLTQ4bk5tpRgfWPda
aEBzSPrnqame2CKUWtBughuRWSxdTIMvdXIC/ym1gQKBgQDx6Nyio/L6I5CdlXwC
HAzBCy1mnj70Kj97tQc+A/0z8dD7fCSE/oo8IiEKixcjnaSxHk8VjINF/w17n63W
8neE7pVsuDwxfhiQ9ZRI1WpV0LsFEoTrEWG7Ax8UzbHXCQbNJ9SI0HJRo9UN0f/Z
t+ZT+HNUzdcpCwTvdRVDisbXcQKBgQDIiMz58GFEwdGPXJKEhSyQ3kSQBjeqo0Vl
wMDuDvFEckHl/p1RnDo0lzaq6FivOX84ymvGNdQW14TnQp3A/mkQ5o6k/e1pfAA6
X0Y6tBH/QppVo5sFvOufyn02k48k5pFAjLHH9L9i0dyWqq4V6PgA2uk4qilFxEg/
CJEVfq4ZeQKBgQCZPHKWq9f8T48J42kcRPxnRFdMC63BKQnxqOifhhNcVi+VPjw7
6qlSEiRv80+DBhcPAy4BbnKxYjD+QFX0NL80+5S3u7SVfVS+bnGx+U5UcdYmDmcY
KHiJ6B5GJU4j8tnWFwbwa2ofAPKywHWbSnyicF1OON20aACGVtpTYJM4YQKBgBW4
09NDGZY0FHoeAfT+4/vxR6X+NmtyciL6hSuETNgoNEEwmmPrs1ZdBtvufSTF6qUB
MDlxPT8YK1pNmf78z+63ur3ej6f8eZ3ZEidruANZeJRMO4+cjj1p1rRhuYC6xQMj
+mH5ff27U9SyOlc/PBYDoH212PCouVaym9yjM0KpAoGBALr583slY55ESOthLrfX
1ecoET5xxRm431XbZMnxu0uUvHWNfqoojtmD7laclb9HwkpShPB6PT1egBIvDWWM
bVUuXzJ8gP0tIG3dHgiiUlld3ahOiaMYSU77uLFBRWv5sQqfewLuFvlzHn/2ZSt7
TcipT4f67b18W8iuLJELEs57
-----END PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIDozCCAougAwIBAgIUfuExHknCb9E7HvWowDBr/pvOcx8wDQYJKoZIhvcNAQEL
MIIDuTCCAqGgAwIBAgIUUNvkPwe0W8C2I0+KnLpMaQ+S+vowDQYJKoZIhvcNAQEL
BQAwYTELMAkGA1UEBhMCRlIxETAPBgNVBAgMCEJyZXRhZ25lMQ8wDQYDVQQHDAZS
ZW5uZXMxGjAYBgNVBAoMEVNwaGlueCB0ZXN0IHN1aXRlMRIwEAYDVQQDDAlsb2Nh
bGhvc3QwHhcNMjAxMTE0MTE0MzQ3WhcNMzAxMTEyMTE0MzQ3WjBhMQswCQYDVQQG
bGhvc3QwHhcNMjAxMTE1MTcyNDExWhcNMzAxMTEzMTcyNDExWjBhMQswCQYDVQQG
EwJGUjERMA8GA1UECAwIQnJldGFnbmUxDzANBgNVBAcMBlJlbm5lczEaMBgGA1UE
CgwRU3BoaW54IHRlc3Qgc3VpdGUxEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJ
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAMttPysQIw62bsforAWcKJLaRSNkLL/f
Ikakh4ahMtc/43xrUtRQhfcwnyq7W+w8rpjPWH/5eK/vfHJaPAroOLXurflXJhVi
JGO6EiyIfV3DBh80T+E7dCEs4S0Erv4FNZRDfor7BEUF/9hXKceHsS8prAJZKZpl
XKqOPZdLzeanUDwm3QxMDgDhHKFXhpLXpW+JJ5F/c5xV3r2q29/cwKWsMVHIWcIg
TpqN+nWGtnnALsW/l3QDR0onYwZihuVQ9P+qr5vyOfWfE++5IejheGNZer6TTmJ0
iXuYuTvC6uaAeOayUHT9l0TjA1rqgA0t68Xi/GIIYnpz2B/xDnqBpRkCAwEAAaNT
MFEwHQYDVR0OBBYEFNkkFFS7VnquYF3ncePQNfFzpU+hMB8GA1UdIwQYMBaAFNkk
FFS7VnquYF3ncePQNfFzpU+hMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL
BQADggEBAGgfOaXba4tnb+DORKUWm0WQ5umV8LKDr3BMjh8BZ2m/GWpFoL19o6LB
7S0oPdWnuRSy92SqAODt10e/PhepSpBrRa12g92AgAicgfnEutlY4zB8amCJBN/m
TemAAjMrBwxd6gD8tadu06Ye5ml+eMZ3S2JFPjQQgij//sDv89nl0JP+jOiwbDGn
cPmQSdP+6EH5ng5uKb6FXGKt29g3ZSfAzHS3+eCY0rkQnZWye9ZS2AaBTW/IKd2N
0VcJWXcWLx0XRi5zWVp7TkwGdiZRmB7NxXDbGuHSfq8n42FiKQnyIyNQyTmMeOgw
WHqLRjX6vLio3RwWAWleprLfHq6Jyf8=
KoZIhvcNAQEBBQADggEPADCCAQoCggEBAL1/McYE81pk1w2cv/We87bElH8Tri8W
5rdB89BDkemKCOsr8dFSVEbO1Eon4ISMJc1g19Dh1RxXjmiTm48fdTUKQDDZ39Z7
6RmcAeQ27DiZELSiF3OlQHLIM4Dla1RY+f1LCFcJXPp8zt4jnZq6/OlQTqyhUMze
e5yepkMndRr0qAHTYWupfXdLvpSr65/xDaGlOu47+cHMHinrEXAknW1pcQRaLyzC
Doc7z/7THQB6uKlXomJ41Sm0QW5X2Qu9HP2oaDQI7Z/sB0iM14UMauA7uon5XFdX
0aduLqmHi+xTC0lkHemFEYaiZ2CJF3o9KZrzWe9N7XPr3H+D7w/T3WkCAwEAAaNp
MGcwHQYDVR0OBBYEFN1iHZj88N6eI2FlRzza52xzOU5EMB8GA1UdIwQYMBaAFN1i
HZj88N6eI2FlRzza52xzOU5EMA8GA1UdEwEB/wQFMAMBAf8wFAYDVR0RBA0wC4IJ
bG9jYWxob3N0MA0GCSqGSIb3DQEBCwUAA4IBAQBVUZm1iw7N7uZu/SF3hailxS+1
3KChItWu3ZOIjlmDIkaJ9kWqP2ficUg3tBUx6/UOjHQAwRC4rj87BoSV2mEy+0OX
fyy+ER/BeHYly5v+hpjVojVKeqysk5CKttZM+cOibT2SzLLYf0InNqZRQRJco+nL
QNR0hVo/Lz6Mf1gF2ywf9bXSF3+XECU4K6sVm4QpFbJNm+fHqJBuh1LXHRrcTAsP
LM6PBnd3P5QTcr/G0s/tYMPmero9YHZUO8FMvMVoI2K8k6/duG/EbBaNzriRI1OM
PpZGCWxbJfyApnzc5lGAG4zJnV/wpOyNhKJuW9N1fr2oEwPpJlS3VzrgeKcY
-----END CERTIFICATE-----

View File

@ -4,6 +4,9 @@ from typing import overload
myint = int
#: docstring
variable: myint
def sum(x: myint, y: myint) -> myint:
"""docstring"""
@ -23,3 +26,10 @@ def mult(x: float, y: float) -> float:
def mult(x, y):
"""docstring"""
return x, y
class Foo:
"""docstring"""
#: docstring
attr: myint

View File

@ -20,7 +20,7 @@ from test_build_html import ENV_WARNINGS
from sphinx.builders.latex import default_latex_documents
from sphinx.config import Config
from sphinx.errors import SphinxError, ThemeError
from sphinx.errors import SphinxError
from sphinx.testing.util import strip_escseq
from sphinx.util import docutils
from sphinx.util.osutil import cd, ensuredir
@ -763,7 +763,7 @@ def test_reference_in_caption_and_codeblock_in_footnote(app, status, warning):
assert ('\\caption{This is the figure caption with a footnote to '
'\\sphinxfootnotemark[7].}\\label{\\detokenize{index:id29}}\\end{figure}\n'
'%\n\\begin{footnotetext}[7]\\sphinxAtStartFootnote\n'
'Footnote in caption\n%\n\\end{footnotetext}')in result
'Footnote in caption\n%\n\\end{footnotetext}') in result
assert ('\\sphinxcaption{footnote \\sphinxfootnotemark[8] in '
'caption of normal table}\\label{\\detokenize{index:id30}}') in result
assert ('\\caption{footnote \\sphinxfootnotemark[9] '

View File

@ -10,15 +10,12 @@
import http.server
import json
import os
import re
import textwrap
from unittest import mock
import pytest
import requests
from utils import CERT_FILE, http_server, https_server
from utils import CERT_FILE, http_server, https_server, modify_env
@pytest.mark.sphinx('linkcheck', testroot='linkcheck', freshenv=True)
@ -60,7 +57,7 @@ def test_defaults_json(app):
assert len(rows) == 10
# the output order of the rows is not stable
# due to possible variance in network latency
rowsby = {row["uri"]:row for row in rows}
rowsby = {row["uri"]: row for row in rows}
assert rowsby["https://www.google.com#!bar"] == {
'filename': 'links.txt',
'lineno': 10,
@ -113,6 +110,7 @@ def test_anchors_ignored(app):
# expect all ok when excluding #top
assert not content
@pytest.mark.sphinx('linkcheck', testroot='linkcheck-localserver-anchor', freshenv=True)
def test_raises_for_invalid_status(app):
class InternalServerErrorHandler(http.server.BaseHTTPRequestHandler):
@ -138,6 +136,7 @@ class HeadersDumperHandler(http.server.BaseHTTPRequestHandler):
self.end_headers()
print(self.headers.as_string())
@pytest.mark.sphinx(
'linkcheck', testroot='linkcheck-localserver', freshenv=True,
confoverrides={'linkcheck_auth': [
@ -213,6 +212,7 @@ def test_linkcheck_request_headers_default(app, capsys):
assert "Accepts: application/json\n" not in stdout
assert "X-Secret: open sesami\n" in stdout
def make_redirect_handler(*, support_head):
class RedirectOnceHandler(http.server.BaseHTTPRequestHandler):
def do_HEAD(self):
@ -236,6 +236,7 @@ def make_redirect_handler(*, support_head):
return RedirectOnceHandler
@pytest.mark.sphinx('linkcheck', testroot='linkcheck-localserver', freshenv=True)
def test_follows_redirects_on_HEAD(app, capsys):
with http_server(make_redirect_handler(support_head=True)):
@ -253,6 +254,7 @@ def test_follows_redirects_on_HEAD(app, capsys):
"""
)
@pytest.mark.sphinx('linkcheck', testroot='linkcheck-localserver', freshenv=True)
def test_follows_redirects_on_GET(app, capsys):
with http_server(make_redirect_handler(support_head=False)):
@ -281,6 +283,7 @@ class OKHandler(http.server.BaseHTTPRequestHandler):
self.do_HEAD()
self.wfile.write(b"ok\n")
@pytest.mark.sphinx('linkcheck', testroot='linkcheck-localserver-https', freshenv=True)
def test_invalid_ssl(app):
# Link indicates SSL should be used (https) but the server does not handle it.
@ -295,6 +298,7 @@ def test_invalid_ssl(app):
assert content["uri"] == "https://localhost:7777/"
assert "SSLError" in content["info"]
@pytest.mark.sphinx('linkcheck', testroot='linkcheck-localserver-https', freshenv=True)
def test_connect_to_selfsigned_fails(app):
with https_server(OKHandler):
@ -308,6 +312,7 @@ def test_connect_to_selfsigned_fails(app):
assert content["uri"] == "https://localhost:7777/"
assert "[SSL: CERTIFICATE_VERIFY_FAILED]" in content["info"]
@pytest.mark.sphinx('linkcheck', testroot='linkcheck-localserver-https', freshenv=True)
def test_connect_to_selfsigned_with_tls_verify_false(app):
app.config.tls_verify = False
@ -325,6 +330,7 @@ def test_connect_to_selfsigned_with_tls_verify_false(app):
"info": "",
}
@pytest.mark.sphinx('linkcheck', testroot='linkcheck-localserver-https', freshenv=True)
def test_connect_to_selfsigned_with_tls_cacerts(app):
app.config.tls_cacerts = CERT_FILE
@ -342,10 +348,10 @@ def test_connect_to_selfsigned_with_tls_cacerts(app):
"info": "",
}
@pytest.mark.sphinx('linkcheck', testroot='linkcheck-localserver-https', freshenv=True)
def test_connect_to_selfsigned_with_requests_env_var(app):
os.environ["REQUESTS_CA_BUNDLE"] = CERT_FILE
with https_server(OKHandler):
with modify_env(REQUESTS_CA_BUNDLE=CERT_FILE), https_server(OKHandler):
app.builder.build_all()
with open(app.outdir / 'output.json') as fp:
@ -358,3 +364,21 @@ def test_connect_to_selfsigned_with_requests_env_var(app):
"uri": "https://localhost:7777/",
"info": "",
}
@pytest.mark.sphinx('linkcheck', testroot='linkcheck-localserver-https', freshenv=True)
def test_connect_to_selfsigned_nonexistent_cert_file(app):
app.config.tls_cacerts = "does/not/exist"
with https_server(OKHandler):
app.builder.build_all()
with open(app.outdir / 'output.json') as fp:
content = json.load(fp)
assert content == {
"code": 0,
"status": "broken",
"filename": "index.rst",
"lineno": 1,
"uri": "https://localhost:7777/",
"info": "Could not find a suitable TLS CA certificate bundle, invalid path: does/not/exist",
}

View File

@ -75,12 +75,12 @@ def _check(name, input, idDict, output, key, asTextOutput):
idExpected.append(idExpected[i - 1])
idActual = [None]
for i in range(1, _max_id + 1):
#try:
# try:
id = ast.get_id(version=i)
assert id is not None
idActual.append(id[len(_id_prefix[i]):])
#except NoOldIdError:
# idActual.append(None)
# except NoOldIdError:
# idActual.append(None)
res = [True]
for i in range(1, _max_id + 1):
@ -94,7 +94,7 @@ def _check(name, input, idDict, output, key, asTextOutput):
print("Error in id version %d." % i)
print("result: %s" % idActual[i])
print("expected: %s" % idExpected[i])
#print(rootSymbol.dump(0))
# print(rootSymbol.dump(0))
raise DefinitionError("")
@ -106,7 +106,7 @@ def check(name, input, idDict, output=None, key=None, asTextOutput=None):
if name != 'macro':
# Second, check with semicolon
_check(name, input + ' ;', idDict, output + ';', key,
asTextOutput + ';' if asTextOutput is not None else None)
asTextOutput + ';' if asTextOutput is not None else None)
def test_expressions():
@ -422,7 +422,7 @@ def test_nested_name():
check('function', 'void f(.A.B a)', {1: "f"})
def test_union_definitions():
def test_struct_definitions():
check('struct', '{key}A', {1: 'A'})
@ -482,7 +482,7 @@ def test_attributes():
# style: user-defined paren
check('member', 'paren_attr() int f', {1: 'f'})
check('member', 'paren_attr(a) int f', {1: 'f'})
check('member', 'paren_attr("") int f',{1: 'f'})
check('member', 'paren_attr("") int f', {1: 'f'})
check('member', 'paren_attr(()[{}][]{}) int f', {1: 'f'})
with pytest.raises(DefinitionError):
parse('member', 'paren_attr(() int f')
@ -521,7 +521,7 @@ def test_attributes():
def filter_warnings(warning, file):
lines = warning.getvalue().split("\n");
lines = warning.getvalue().split("\n")
res = [l for l in lines if "domain-c" in l and "{}.rst".format(file) in l and
"WARNING: document isn't included in any toctree" not in l]
print("Filtered warnings for file '{}':".format(file))
@ -602,6 +602,7 @@ def _get_obj(app, queryName):
return (docname, anchor, objectType)
return (queryName, "not", "found")
def test_cfunction(app):
text = (".. c:function:: PyObject* "
"PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems)")

View File

@ -182,9 +182,9 @@ def test_expressions():
expr = i + l + u
exprCheck(expr, 'L' + expr + 'E')
decimalFloats = ['5e42', '5e+42', '5e-42',
'5.', '5.e42', '5.e+42', '5.e-42',
'.5', '.5e42', '.5e+42', '.5e-42',
'5.0', '5.0e42', '5.0e+42', '5.0e-42']
'5.', '5.e42', '5.e+42', '5.e-42',
'.5', '.5e42', '.5e+42', '.5e-42',
'5.0', '5.0e42', '5.0e+42', '5.0e-42']
hexFloats = ['ApF', 'Ap+F', 'Ap-F',
'A.', 'A.pF', 'A.p+F', 'A.p-F',
'.A', '.ApF', '.Ap+F', '.Ap-F',
@ -425,9 +425,9 @@ def test_member_definitions():
check('member', 'int b : 8 = 42', {1: 'b__i', 2: '1b'})
check('member', 'int b : 8{42}', {1: 'b__i', 2: '1b'})
# TODO: enable once the ternary operator is supported
#check('member', 'int b : true ? 8 : a = 42', {1: 'b__i', 2: '1b'})
# check('member', 'int b : true ? 8 : a = 42', {1: 'b__i', 2: '1b'})
# TODO: enable once the ternary operator is supported
#check('member', 'int b : (true ? 8 : a) = 42', {1: 'b__i', 2: '1b'})
# check('member', 'int b : (true ? 8 : a) = 42', {1: 'b__i', 2: '1b'})
check('member', 'int b : 1 || new int{0}', {1: 'b__i', 2: '1b'})
@ -537,8 +537,8 @@ def test_function_definitions():
check('function', 'int foo(const A*...)', {1: "foo__ACPDp", 2: "3fooDpPK1A"})
check('function', 'int foo(const int A::*... a)', {2: "3fooDpM1AKi"})
check('function', 'int foo(const int A::*...)', {2: "3fooDpM1AKi"})
#check('function', 'int foo(int (*a)(A)...)', {1: "foo__ACRDp", 2: "3fooDpPK1A"})
#check('function', 'int foo(int (*)(A)...)', {1: "foo__ACRDp", 2: "3fooDpPK1A"})
# check('function', 'int foo(int (*a)(A)...)', {1: "foo__ACRDp", 2: "3fooDpPK1A"})
# check('function', 'int foo(int (*)(A)...)', {1: "foo__ACRDp", 2: "3fooDpPK1A"})
check('function', 'virtual void f()', {1: "f", 2: "1fv"})
# test for ::nestedName, from issue 1738
check("function", "result(int val, ::std::error_category const &cat)",
@ -707,7 +707,6 @@ def test_class_definitions():
check('class', 'template<class T> {key}has_var<T, std::void_t<decltype(&T::var)>>',
{2: 'I0E7has_varI1TNSt6void_tIDTadN1T3varEEEEE'})
check('class', 'template<typename ...Ts> {key}T<int (*)(Ts)...>',
{2: 'IDpE1TIJPFi2TsEEE'})
check('class', 'template<int... Is> {key}T<(Is)...>',
@ -1001,7 +1000,7 @@ def test_build_domain_cpp_warn_template_param_qualified_name(app, status, warnin
@pytest.mark.sphinx(testroot='domain-cpp', confoverrides={'nitpicky': True})
def test_build_domain_cpp_backslash_ok(app, status, warning):
def test_build_domain_cpp_backslash_ok_true(app, status, warning):
app.builder.build_all()
ws = filter_warnings(warning, "backslash")
assert len(ws) == 0
@ -1016,7 +1015,7 @@ def test_build_domain_cpp_semicolon(app, status, warning):
@pytest.mark.sphinx(testroot='domain-cpp',
confoverrides={'nitpicky': True, 'strip_signature_backslash': True})
def test_build_domain_cpp_backslash_ok(app, status, warning):
def test_build_domain_cpp_backslash_ok_false(app, status, warning):
app.builder.build_all()
ws = filter_warnings(warning, "backslash")
assert len(ws) == 1
@ -1250,4 +1249,4 @@ def test_mix_decl_duplicate(app, warning):
assert "Declaration is '.. cpp:function:: void A()'." in ws[1]
assert "index.rst:3: WARNING: Duplicate C++ declaration, also defined at index:1." in ws[2]
assert "Declaration is '.. cpp:struct:: A'." in ws[3]
assert ws[4] == ""
assert ws[4] == ""

View File

@ -333,7 +333,7 @@ def test_multiple_cmdoptions(app):
def test_productionlist(app, status, warning):
app.builder.build_all()
warnings = warning.getvalue().split("\n");
warnings = warning.getvalue().split("\n")
assert len(warnings) == 2
assert warnings[-1] == ''
assert "Dup2.rst:4: WARNING: duplicate token description of Dup, other instance in Dup1" in warnings[0]

View File

@ -38,8 +38,9 @@ def test_create_single_index(app):
('upgrade', [('', '#index-3')])], None]),
('Python', [[('', '#index-1')], [], None])])
assert index[3] == ('S', [('Sphinx', [[('', '#index-4')], [], None])])
assert index[4] == ('Е', [('ёлка', [[('', '#index-6')], [], None]),
('Ель', [[('', '#index-5')], [], None])])
assert index[4] == ('Е',
[('ёлка', [[('', '#index-6')], [], None]),
('Ель', [[('', '#index-5')], [], None])])
assert index[5] == ('ת', [('‏תירבע‎', [[('', '#index-7')], [], None])])
@ -69,8 +70,9 @@ def test_create_pair_index(app):
('ёлка', [('', '#index-5')]),
('Ель', [('', '#index-4')])],
None])])
assert index[6] == ('Е', [('ёлка', [[], [('Sphinx', [('', '#index-5')])], None]),
('Ель', [[], [('Sphinx', [('', '#index-4')])], None])])
assert index[6] == ('Е',
[('ёлка', [[], [('Sphinx', [('', '#index-5')])], None]),
('Ель', [[], [('Sphinx', [('', '#index-4')])], None])])
@pytest.mark.sphinx('dummy', freshenv=True)

View File

@ -177,7 +177,6 @@ def test_format_signature(app):
for C in (D, E):
assert formatsig('class', 'D', C, None, None) == '()'
class SomeMeta(type):
def __call__(cls, a, b=None):
return type.__call__(cls, a, b)
@ -209,7 +208,6 @@ def test_format_signature(app):
assert formatsig('class', 'C', C, None, None) == '(a, b=None)'
assert formatsig('class', 'C', D, 'a, b', 'X') == '(a, b) -> X'
class ListSubclass(list):
pass
@ -219,7 +217,6 @@ def test_format_signature(app):
else:
assert formatsig('class', 'C', ListSubclass, None, None) == ''
class ExceptionSubclass(Exception):
pass
@ -227,7 +224,6 @@ def test_format_signature(app):
if getattr(Exception, '__text_signature__', None) is None:
assert formatsig('class', 'C', ExceptionSubclass, None, None) == ''
# __init__ have signature at first line of docstring
directive.env.config.autoclass_content = 'both'

View File

@ -700,6 +700,19 @@ def test_autodoc_type_aliases(app):
'.. py:module:: target.annotations',
'',
'',
'.. py:class:: Foo()',
' :module: target.annotations',
'',
' docstring',
'',
'',
' .. py:attribute:: Foo.attr',
' :module: target.annotations',
' :type: int',
'',
' docstring',
'',
'',
'.. py:function:: mult(x: int, y: int) -> int',
' mult(x: float, y: float) -> float',
' :module: target.annotations',
@ -712,6 +725,13 @@ def test_autodoc_type_aliases(app):
'',
' docstring',
'',
'',
'.. py:data:: variable',
' :module: target.annotations',
' :type: int',
'',
' docstring',
'',
]
# define aliases
@ -722,6 +742,19 @@ def test_autodoc_type_aliases(app):
'.. py:module:: target.annotations',
'',
'',
'.. py:class:: Foo()',
' :module: target.annotations',
'',
' docstring',
'',
'',
' .. py:attribute:: Foo.attr',
' :module: target.annotations',
' :type: myint',
'',
' docstring',
'',
'',
'.. py:function:: mult(x: myint, y: myint) -> myint',
' mult(x: float, y: float) -> float',
' :module: target.annotations',
@ -734,6 +767,13 @@ def test_autodoc_type_aliases(app):
'',
' docstring',
'',
'',
'.. py:data:: variable',
' :module: target.annotations',
' :type: myint',
'',
' docstring',
'',
]

View File

@ -11,7 +11,6 @@
import http.server
import os
import unittest
from io import BytesIO
from unittest import mock
import pytest

View File

@ -1070,7 +1070,7 @@ Methods:
description
"""
""" # NOQA
config = Config()
actual = str(GoogleDocstring(docstring, config=config, app=None, what='module',
options={'noindex': True}))
@ -2222,7 +2222,7 @@ definition_after_normal_text : int
["{", "'F'", ", ", "'C'", ", ", "'N or C'", "}", ", ", "default", " ", "'F'"],
["str", ", ", "default", ": ", "'F or C'"],
["int", ", ", "default", ": ", "None"],
["int", ", " , "default", " ", "None"],
["int", ", ", "default", " ", "None"],
["int", ", ", "default", " ", ":obj:`None`"],
['"ma{icious"'],
[r"'with \'quotes\''"],

View File

@ -89,15 +89,6 @@ def assert_count(expected_expr, result, count):
assert len(re.findall(*find_pair)) == count, find_pair
@sphinx_intl
@pytest.mark.sphinx('text')
@pytest.mark.test_params(shared_result='test_intl_basic')
def test_text_toctree(app):
app.build()
result = (app.outdir / 'index.txt').read_text()
assert_startswith(result, "CONTENTS\n********\n\nTABLE OF CONTENTS\n")
@sphinx_intl
@pytest.mark.sphinx('text')
@pytest.mark.test_params(shared_result='test_intl_basic')
@ -436,11 +427,16 @@ def test_text_admonitions(app):
@pytest.mark.test_params(shared_result='test_intl_gettext')
def test_gettext_toctree(app):
app.build()
# --- toctree
# --- toctree (index.rst)
expect = read_po(app.srcdir / 'xx' / 'LC_MESSAGES' / 'index.po')
actual = read_po(app.outdir / 'index.pot')
for expect_msg in [m for m in expect if m.id]:
assert expect_msg.id in [m.id for m in actual if m.id]
# --- toctree (toctree.rst)
expect = read_po(app.srcdir / 'xx' / 'LC_MESSAGES' / 'toctree.po')
actual = read_po(app.outdir / 'toctree.pot')
for expect_msg in [m for m in expect if m.id]:
assert expect_msg.id in [m.id for m in actual if m.id]
@sphinx_intl
@ -467,24 +463,17 @@ def test_text_table(app):
assert expect_msg.string in result
@sphinx_intl
@pytest.mark.sphinx('gettext')
@pytest.mark.test_params(shared_result='test_intl_gettext')
def test_gettext_toctree(app):
app.build()
# --- toctree
expect = read_po(app.srcdir / 'xx' / 'LC_MESSAGES' / 'toctree.po')
actual = read_po(app.outdir / 'toctree.pot')
for expect_msg in [m for m in expect if m.id]:
assert expect_msg.id in [m.id for m in actual if m.id]
@sphinx_intl
@pytest.mark.sphinx('text')
@pytest.mark.test_params(shared_result='test_intl_basic')
def test_text_toctree(app):
app.build()
# --- toctree
# --- toctree (index.rst)
# Note: index.rst contains contents that is not shown in text.
result = (app.outdir / 'index.txt').read_text()
assert 'CONTENTS' in result
assert 'TABLE OF CONTENTS' in result
# --- toctree (toctree.rst)
result = (app.outdir / 'toctree.txt').read_text()
expect = read_po(app.srcdir / 'xx' / 'LC_MESSAGES' / 'toctree.po')
for expect_msg in [m for m in expect if m.id]:

View File

@ -17,7 +17,6 @@ from docutils.parsers.rst import Parser as RstParser
from sphinx import addnodes
from sphinx.builders.html.transforms import KeyboardTransform
from sphinx.builders.latex import LaTeXBuilder
from sphinx.builders.latex.theming import ThemeFactory
from sphinx.roles import XRefRole
from sphinx.testing.util import Struct, assert_node
from sphinx.transforms import SphinxSmartQuotes

View File

@ -13,7 +13,6 @@ from collections import OrderedDict
import pytest
from sphinx.project import Project
from sphinx.testing.comparer import PathComparer
def test_project_discover(rootdir):

View File

@ -19,6 +19,7 @@ from sphinx.pycode import ModuleAnalyzer
SPHINX_MODULE_PATH = os.path.splitext(sphinx.__file__)[0] + '.py'
def test_ModuleAnalyzer_get_module_source():
assert ModuleAnalyzer.get_module_source('sphinx') == (sphinx.__file__, sphinx.__loader__.get_source('sphinx'))

View File

@ -14,8 +14,7 @@ from unittest.mock import patch
import pytest
import sphinx
from sphinx.errors import ExtensionError, PycodeError
from sphinx.errors import ExtensionError
from sphinx.testing.util import strip_escseq
from sphinx.util import (SkipProgressMessage, display_chunk, encode_uri, ensuredir,
import_object, logging, parselinenos, progress_message,

View File

@ -19,7 +19,7 @@ import _testcapi
import pytest
from sphinx.util import inspect
from sphinx.util.inspect import is_builtin_class_method, stringify_signature
from sphinx.util.inspect import stringify_signature
def test_signature():
@ -493,6 +493,28 @@ def test_dict_customtype():
assert "<CustomType(2)>: 2" in description
def test_getslots():
class Foo:
pass
class Bar:
__slots__ = ['attr']
class Baz:
__slots__ = {'attr': 'docstring'}
class Qux:
__slots__ = 'attr'
assert inspect.getslots(Foo) is None
assert inspect.getslots(Bar) == {'attr': None}
assert inspect.getslots(Baz) == {'attr': 'docstring'}
assert inspect.getslots(Qux) == {'attr': None}
with pytest.raises(TypeError):
inspect.getslots(Bar())
@pytest.mark.sphinx(testroot='ext-autodoc')
def test_isclassmethod(app):
from target.methods import Base, Inherited

View File

@ -10,8 +10,7 @@
import sys
from numbers import Integral
from typing import (Any, Callable, Dict, Generator, Generic, List, Optional, Tuple, TypeVar,
Union)
from typing import Any, Callable, Dict, Generator, List, Optional, Tuple, TypeVar, Union
import pytest
@ -25,8 +24,10 @@ class MyClass1:
class MyClass2(MyClass1):
__qualname__ = '<MyClass2>'
T = TypeVar('T')
class MyList(List[T]):
pass
@ -132,8 +133,8 @@ def test_stringify_type_hints_containers():
@pytest.mark.skipif(sys.version_info < (3, 9), reason='python 3.9+ is required.')
def test_stringify_Annotated():
from typing import Annotated
assert stringify(Annotated[str, "foo", "bar"]) == "str"
from typing import Annotated # type: ignore
assert stringify(Annotated[str, "foo", "bar"]) == "str" # NOQA
def test_stringify_type_hints_string():

View File

@ -77,7 +77,7 @@ def f14() -> Any:
pass
def f15(x: "Unknown", y: "int") -> Any:
def f15(x: "Unknown", y: "int") -> Any: # type: ignore # NOQA
pass

View File

@ -1,11 +1,13 @@
import contextlib
import http.server
import os
import pathlib
import ssl
import threading
# Generated with:
# $ openssl req -new -x509 -days 3650 -nodes -out cert.pem -keyout cert.pem
# $ openssl req -new -x509 -days 3650 -nodes -out cert.pem \
# -keyout cert.pem -addext "subjectAltName = DNS:localhost"
CERT_FILE = str(pathlib.Path(__file__).parent / "certs" / "cert.pem")
@ -46,3 +48,18 @@ def create_server(thread_class):
http_server = create_server(HttpServerThread)
https_server = create_server(HttpsServerThread)
@contextlib.contextmanager
def modify_env(**env):
original_env = os.environ.copy()
for k, v in env.items():
os.environ[k] = v
try:
yield
finally:
for k in env:
try:
os.environ[k] = original_env[k]
except KeyError:
os.unsetenv(k)