mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch '3.x' into 8222_novalue
This commit is contained in:
commit
3becd56e0e
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
@ -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
|
||||
|
2
CHANGES
2
CHANGES
@ -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
|
||||
--------
|
||||
|
@ -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 =
|
||||
|
@ -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:
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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-----
|
||||
|
@ -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
|
||||
|
@ -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] '
|
||||
|
@ -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",
|
||||
}
|
||||
|
@ -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)")
|
||||
|
@ -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] == ""
|
||||
|
@ -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]
|
||||
|
@ -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)
|
||||
|
@ -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'
|
||||
|
||||
|
@ -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',
|
||||
'',
|
||||
]
|
||||
|
||||
|
||||
|
@ -11,7 +11,6 @@
|
||||
import http.server
|
||||
import os
|
||||
import unittest
|
||||
from io import BytesIO
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
|
@ -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\''"],
|
||||
|
@ -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]:
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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'))
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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():
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user