mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge branch 'stable'
This commit is contained in:
commit
e57fb74ee5
48
.appveyor.yml
Normal file
48
.appveyor.yml
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
environment:
|
||||||
|
global:
|
||||||
|
TEST: -v --durations 25
|
||||||
|
PYTHONFAULTHANDLER: x
|
||||||
|
PYTHONWARNINGS: all
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
- PYTHON: 27
|
||||||
|
DOCUTILS: 0.12
|
||||||
|
TEST_IGNORE: --ignore py35
|
||||||
|
- PYTHON: 27
|
||||||
|
DOCUTILS: 0.13.1
|
||||||
|
TEST_IGNORE: --ignore py35
|
||||||
|
- PYTHON: 36
|
||||||
|
DOCUTILS: 0.13.1
|
||||||
|
- PYTHON: 36-x64
|
||||||
|
DOCUTILS: 0.13.1
|
||||||
|
|
||||||
|
install:
|
||||||
|
- C:\Python%PYTHON%\python.exe -m pip install -U pip setuptools
|
||||||
|
- C:\Python%PYTHON%\python.exe -m pip install docutils==%DOCUTILS%
|
||||||
|
- C:\Python%PYTHON%\python.exe -m pip install -r test-reqs.txt
|
||||||
|
|
||||||
|
# No automatic build, just run python tests
|
||||||
|
build: off
|
||||||
|
|
||||||
|
# Update build information before testing, no warnings during this step
|
||||||
|
before_test:
|
||||||
|
- ps: |
|
||||||
|
$py_warnings = $env:PYTHONWARNINGS
|
||||||
|
$env:PYTHONWARNINGS = 'ignore'
|
||||||
|
Update-AppveyorBuild -Version ((& "C:\Python$($env:PYTHON)\python.exe" -m sphinx --version).Split(' ')[2])
|
||||||
|
$env:PYTHONWARNINGS = $py_warnings
|
||||||
|
|
||||||
|
test_script:
|
||||||
|
- ps: |
|
||||||
|
Push-Location tests
|
||||||
|
$test_ignore = $env:TEST_IGNORE
|
||||||
|
if (-not $test_ignore) { $test_ignore = '' }
|
||||||
|
$tests = $env:TEST
|
||||||
|
if (-not $tests) { $tests = '' }
|
||||||
|
& "C:\Python$($env:PYTHON)\python.exe" run.py $test_ignore.Split(' ') --junitxml .junit.xml $tests.Split(' ')
|
||||||
|
Pop-Location
|
||||||
|
if ($LastExitCode -eq 1) { Write-Host "Test Failures Occurred, leaving for test result parsing" }
|
||||||
|
elseif ($LastExitCode -ne 0) { Write-Host "Other Error Occurred, aborting"; exit $LastExitCode }
|
||||||
|
|
||||||
|
after_test:
|
||||||
|
- ps: (New-Object System.Net.WebClient).UploadFile("https://ci.appveyor.com/api/testresults/junit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path (Join-Path tests .junit.xml)))
|
12
CHANGES
12
CHANGES
@ -85,6 +85,16 @@ Bugs fixed
|
|||||||
name contains spaces
|
name contains spaces
|
||||||
* #3850: Fix color handling in make mode's help command
|
* #3850: Fix color handling in make mode's help command
|
||||||
* #3865: use of self.env.warn in sphinx extension fails
|
* #3865: use of self.env.warn in sphinx extension fails
|
||||||
|
* #3824: production lists apply smart quotes transform since Sphinx 1.6.1
|
||||||
|
* latex: fix ``\sphinxbfcode`` swallows initial space of argument
|
||||||
|
* #3878: Quotes in auto-documented class attributes should be straight quotes
|
||||||
|
in PDF output
|
||||||
|
* #3881: LaTeX figure floated to next page sometimes leaves extra vertical
|
||||||
|
whitespace
|
||||||
|
* #3885: duplicated footnotes raises IndexError
|
||||||
|
* #3873: Failure of deprecation warning mechanism of
|
||||||
|
``sphinx.util.compat.Directive``
|
||||||
|
* #3874: Bogus warnings for "citation not referenced" for cross-file citations
|
||||||
|
|
||||||
Testing
|
Testing
|
||||||
--------
|
--------
|
||||||
@ -300,6 +310,8 @@ Bugs fixed
|
|||||||
* #3661: sphinx-build crashes on parallel build
|
* #3661: sphinx-build crashes on parallel build
|
||||||
* #3669: gettext builder fails with "ValueError: substring not found"
|
* #3669: gettext builder fails with "ValueError: substring not found"
|
||||||
* #3660: Sphinx always depends on sphinxcontrib-websupport and its dependencies
|
* #3660: Sphinx always depends on sphinxcontrib-websupport and its dependencies
|
||||||
|
* #3472: smart quotes getting wrong in latex (at least with list of strings via
|
||||||
|
autoattribute) (refs: #3345, #3666)
|
||||||
|
|
||||||
1.6b3
|
1.6b3
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
.. highlight:: rest
|
.. highlight:: rest
|
||||||
|
|
||||||
|
.. _math-support:
|
||||||
|
|
||||||
Math support in Sphinx
|
Math support in Sphinx
|
||||||
======================
|
======================
|
||||||
|
|
||||||
@ -231,10 +233,14 @@ Sphinx.
|
|||||||
The path to the JavaScript file to include in the HTML files in order to load
|
The path to the JavaScript file to include in the HTML files in order to load
|
||||||
MathJax.
|
MathJax.
|
||||||
|
|
||||||
The default is the ``http://`` URL that loads the JS files from the `MathJax
|
The default is the ``https://`` URL that loads the JS files from the
|
||||||
CDN <http://docs.mathjax.org/en/latest/start.html>`_. If you want MathJax to
|
`cdnjs`__ Content Delivery Network. See the `MathJax Getting Started
|
||||||
be available offline, you have to download it and set this value to a
|
page`__ for details. If you want MathJax to be available offline, you have
|
||||||
different path.
|
to download it and set this value to a different path.
|
||||||
|
|
||||||
|
__ https://cdjns.com
|
||||||
|
|
||||||
|
__ http://docs.mathjax.org/en/latest/start.html
|
||||||
|
|
||||||
The path can be absolute or relative; if it is relative, it is relative to
|
The path can be absolute or relative; if it is relative, it is relative to
|
||||||
the ``_static`` directory of the built docs.
|
the ``_static`` directory of the built docs.
|
||||||
|
@ -210,7 +210,17 @@ Including content based on tags
|
|||||||
Tables
|
Tables
|
||||||
------
|
------
|
||||||
|
|
||||||
Use :ref:`standard reStructuredText tables <rst-tables>`. They work fine in
|
Use :ref:`reStructuredText tables <rst-tables>`, i.e. either
|
||||||
|
|
||||||
|
- grid table syntax (:duref:`ref <grid-tables>`),
|
||||||
|
- simple table syntax (:duref:`ref <simple-tables>`),
|
||||||
|
- :dudir:`csv-table` syntax,
|
||||||
|
- or :dudir:`list-table` syntax.
|
||||||
|
|
||||||
|
The :dudir:`table` directive serves as optional wrapper of the *grid* and
|
||||||
|
*simple* syntaxes.
|
||||||
|
|
||||||
|
They work fine in
|
||||||
HTML output, however there are some gotchas when using tables in LaTeX: the
|
HTML output, however there are some gotchas when using tables in LaTeX: the
|
||||||
column width is hard to determine correctly automatically. For this reason, the
|
column width is hard to determine correctly automatically. For this reason, the
|
||||||
following directive exists:
|
following directive exists:
|
||||||
@ -313,6 +323,11 @@ following directive exists:
|
|||||||
Sphinx's merged cells interact well with ``p{width}``, ``\X{a}{b}``, ``Y{f}``
|
Sphinx's merged cells interact well with ``p{width}``, ``\X{a}{b}``, ``Y{f}``
|
||||||
and tabulary's columns.
|
and tabulary's columns.
|
||||||
|
|
||||||
|
Math
|
||||||
|
----
|
||||||
|
|
||||||
|
See :ref:`math-support`.
|
||||||
|
|
||||||
.. rubric:: Footnotes
|
.. rubric:: Footnotes
|
||||||
|
|
||||||
.. [#] For most builders name and format are the same. At the moment only
|
.. [#] For most builders name and format are the same. At the moment only
|
||||||
|
@ -159,7 +159,7 @@ rendered as "The next paragraph is a code sample:".
|
|||||||
Tables
|
Tables
|
||||||
------
|
------
|
||||||
|
|
||||||
Two forms of tables are supported. For *grid tables* (:duref:`ref
|
For *grid tables* (:duref:`ref
|
||||||
<grid-tables>`), you have to "paint" the cell grid yourself. They look like
|
<grid-tables>`), you have to "paint" the cell grid yourself. They look like
|
||||||
this::
|
this::
|
||||||
|
|
||||||
@ -173,7 +173,7 @@ this::
|
|||||||
+------------------------+------------+----------+----------+
|
+------------------------+------------+----------+----------+
|
||||||
|
|
||||||
*Simple tables* (:duref:`ref <simple-tables>`) are easier to write, but
|
*Simple tables* (:duref:`ref <simple-tables>`) are easier to write, but
|
||||||
limited: they must contain more than one row, and the first column cannot
|
limited: they must contain more than one row, and the first column cells cannot
|
||||||
contain multiple lines. They look like this::
|
contain multiple lines. They look like this::
|
||||||
|
|
||||||
===== ===== =======
|
===== ===== =======
|
||||||
@ -185,6 +185,8 @@ contain multiple lines. They look like this::
|
|||||||
True True True
|
True True True
|
||||||
===== ===== =======
|
===== ===== =======
|
||||||
|
|
||||||
|
Two more syntaxes are supported: *CSV tables* and *List tables*. They use an
|
||||||
|
*explicit markup block*, see `Directives`_ section.
|
||||||
|
|
||||||
Hyperlinks
|
Hyperlinks
|
||||||
----------
|
----------
|
||||||
|
@ -492,8 +492,8 @@ class StandardDomain(Domain):
|
|||||||
initial_data = {
|
initial_data = {
|
||||||
'progoptions': {}, # (program, name) -> docname, labelid
|
'progoptions': {}, # (program, name) -> docname, labelid
|
||||||
'objects': {}, # (type, name) -> docname, labelid
|
'objects': {}, # (type, name) -> docname, labelid
|
||||||
'citations': {}, # name -> docname, labelid, lineno
|
'citations': {}, # citation_name -> docname, labelid, lineno
|
||||||
'citation_refs': {}, # labelid -> list of docnames
|
'citation_refs': {}, # citation_name -> list of docnames
|
||||||
'labels': { # labelname -> docname, labelid, sectionname
|
'labels': { # labelname -> docname, labelid, sectionname
|
||||||
'genindex': ('genindex', '', l_('Index')),
|
'genindex': ('genindex', '', l_('Index')),
|
||||||
'modindex': ('py-modindex', '', l_('Module Index')),
|
'modindex': ('py-modindex', '', l_('Module Index')),
|
||||||
@ -588,12 +588,11 @@ class StandardDomain(Domain):
|
|||||||
|
|
||||||
def note_citation_refs(self, env, docname, document):
|
def note_citation_refs(self, env, docname, document):
|
||||||
# type: (BuildEnvironment, unicode, nodes.Node) -> None
|
# type: (BuildEnvironment, unicode, nodes.Node) -> None
|
||||||
for name, refs in iteritems(document.citation_refs):
|
for node in document.traverse(addnodes.pending_xref):
|
||||||
for ref in refs:
|
if node['refdomain'] == 'std' and node['reftype'] == 'citation':
|
||||||
labelid = ref.get('refid')
|
label = node['reftarget']
|
||||||
if labelid:
|
citation_refs = self.data['citation_refs'].setdefault(label, [])
|
||||||
citation_refs = self.data['citation_refs'].setdefault(labelid, [])
|
citation_refs.append(docname)
|
||||||
citation_refs.append(docname)
|
|
||||||
|
|
||||||
def note_labels(self, env, docname, document):
|
def note_labels(self, env, docname, document):
|
||||||
# type: (BuildEnvironment, unicode, nodes.Node) -> None
|
# type: (BuildEnvironment, unicode, nodes.Node) -> None
|
||||||
@ -638,7 +637,7 @@ class StandardDomain(Domain):
|
|||||||
def check_consistency(self):
|
def check_consistency(self):
|
||||||
# type: () -> None
|
# type: () -> None
|
||||||
for name, (docname, labelid, lineno) in iteritems(self.data['citations']):
|
for name, (docname, labelid, lineno) in iteritems(self.data['citations']):
|
||||||
if labelid not in self.data['citation_refs']:
|
if name not in self.data['citation_refs']:
|
||||||
logger.warning('Citation [%s] is not referenced.', name,
|
logger.warning('Citation [%s] is not referenced.', name,
|
||||||
type='ref', subtype='citation',
|
type='ref', subtype='citation',
|
||||||
location=(docname, lineno))
|
location=(docname, lineno))
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
%
|
%
|
||||||
|
|
||||||
\NeedsTeXFormat{LaTeX2e}[1995/12/01]
|
\NeedsTeXFormat{LaTeX2e}[1995/12/01]
|
||||||
\ProvidesPackage{sphinx}[2017/05/08 v1.6 LaTeX package (Sphinx markup)]
|
\ProvidesPackage{sphinx}[2017/06/17 v1.6.3 LaTeX package (Sphinx markup)]
|
||||||
|
|
||||||
% provides \ltx@ifundefined
|
% provides \ltx@ifundefined
|
||||||
% (many packages load ltxcmds: graphicx does for pdftex and lualatex but
|
% (many packages load ltxcmds: graphicx does for pdftex and lualatex but
|
||||||
@ -1392,7 +1392,7 @@
|
|||||||
\lowercase{\endgroup\def~{\leavevmode\kern\z@\char`#1 }}}
|
\lowercase{\endgroup\def~{\leavevmode\kern\z@\char`#1 }}}
|
||||||
\def\sphinx@literal@nolig@list {\do\`\do\<\do\>\do\'\do\-}%
|
\def\sphinx@literal@nolig@list {\do\`\do\<\do\>\do\'\do\-}%
|
||||||
|
|
||||||
\protected\def\sphinxbfcode#1{\sphinxcode{\bfseries#1}}
|
\protected\def\sphinxbfcode#1{\sphinxcode{\bfseries{}#1}}
|
||||||
\protected\def\sphinxemail#1{\textsf{#1}}
|
\protected\def\sphinxemail#1{\textsf{#1}}
|
||||||
\protected\def\sphinxtablecontinued#1{\textsf{#1}}
|
\protected\def\sphinxtablecontinued#1{\textsf{#1}}
|
||||||
\protected\def\sphinxtitleref#1{\emph{#1}}
|
\protected\def\sphinxtitleref#1{\emph{#1}}
|
||||||
|
@ -283,13 +283,16 @@ class ExtraTranslatableNodes(SphinxTransform):
|
|||||||
|
|
||||||
class UnreferencedFootnotesDetector(SphinxTransform):
|
class UnreferencedFootnotesDetector(SphinxTransform):
|
||||||
"""
|
"""
|
||||||
detect unreferenced footnotes and citations, and emit warnings
|
detect unreferenced footnotes and emit warnings
|
||||||
"""
|
"""
|
||||||
default_priority = 200
|
default_priority = 200
|
||||||
|
|
||||||
def apply(self):
|
def apply(self):
|
||||||
for node in self.document.footnotes:
|
for node in self.document.footnotes:
|
||||||
if node['names'][0] not in self.document.footnote_refs:
|
if node['names'] == []:
|
||||||
|
# footnote having duplicated number. It is already warned at parser.
|
||||||
|
pass
|
||||||
|
elif node['names'][0] not in self.document.footnote_refs:
|
||||||
logger.warning('Footnote [%s] is not referenced.', node['names'][0],
|
logger.warning('Footnote [%s] is not referenced.', node['names'][0],
|
||||||
type='ref', subtype='footnote',
|
type='ref', subtype='footnote',
|
||||||
location=node)
|
location=node)
|
||||||
@ -345,13 +348,17 @@ class SphinxSmartQuotes(SmartQuotes):
|
|||||||
for txtnode in txtnodes:
|
for txtnode in txtnodes:
|
||||||
nodetype = texttype[isinstance(txtnode.parent,
|
nodetype = texttype[isinstance(txtnode.parent,
|
||||||
(nodes.literal,
|
(nodes.literal,
|
||||||
nodes.literal_block,
|
|
||||||
addnodes.literal_emphasis,
|
addnodes.literal_emphasis,
|
||||||
addnodes.literal_strong,
|
addnodes.literal_strong,
|
||||||
addnodes.desc_signature,
|
addnodes.desc_addname,
|
||||||
addnodes.productionlist,
|
addnodes.desc_annotation,
|
||||||
addnodes.desc_optional,
|
|
||||||
addnodes.desc_name,
|
addnodes.desc_name,
|
||||||
|
addnodes.desc_optional,
|
||||||
|
addnodes.desc_parameter,
|
||||||
|
addnodes.desc_parameterlist,
|
||||||
|
addnodes.desc_signature_line,
|
||||||
|
addnodes.desc_type,
|
||||||
|
addnodes.production,
|
||||||
nodes.math,
|
nodes.math,
|
||||||
nodes.image,
|
nodes.image,
|
||||||
nodes.raw,
|
nodes.raw,
|
||||||
|
49
sphinx/util/compat.py
Normal file
49
sphinx/util/compat.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
sphinx.util.compat
|
||||||
|
~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Stuff for docutils compatibility.
|
||||||
|
|
||||||
|
:copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
|
||||||
|
:license: BSD, see LICENSE for details.
|
||||||
|
"""
|
||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import warnings
|
||||||
|
from distutils.version import LooseVersion
|
||||||
|
|
||||||
|
from docutils.parsers.rst import Directive # noqa
|
||||||
|
from docutils import __version__ as _du_version
|
||||||
|
|
||||||
|
from sphinx.deprecation import RemovedInSphinx17Warning
|
||||||
|
|
||||||
|
docutils_version = tuple(LooseVersion(_du_version).version)[:2]
|
||||||
|
|
||||||
|
if False:
|
||||||
|
# For type annotation
|
||||||
|
from typing import Any, Dict # NOQA
|
||||||
|
|
||||||
|
|
||||||
|
class _DeprecationWrapper(object):
|
||||||
|
def __init__(self, mod, deprecated):
|
||||||
|
# type: (Any, Dict) -> None
|
||||||
|
self._mod = mod
|
||||||
|
self._deprecated = deprecated
|
||||||
|
|
||||||
|
def __getattr__(self, attr):
|
||||||
|
# type: (str) -> Any
|
||||||
|
if attr in self._deprecated:
|
||||||
|
warnings.warn("sphinx.util.compat.%s is deprecated and will be "
|
||||||
|
"removed in Sphinx 1.7, please use the standard "
|
||||||
|
"library version instead." % attr,
|
||||||
|
RemovedInSphinx17Warning)
|
||||||
|
return self._deprecated[attr]
|
||||||
|
return getattr(self._mod, attr)
|
||||||
|
|
||||||
|
|
||||||
|
sys.modules[__name__] = _DeprecationWrapper(sys.modules[__name__], dict( # type: ignore
|
||||||
|
docutils_version = docutils_version,
|
||||||
|
Directive = Directive,
|
||||||
|
))
|
@ -1229,7 +1229,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
|||||||
|
|
||||||
def visit_desc_annotation(self, node):
|
def visit_desc_annotation(self, node):
|
||||||
# type: (nodes.Node) -> None
|
# type: (nodes.Node) -> None
|
||||||
self.body.append(r'\sphinxstrong{')
|
self.body.append(r'\sphinxbfcode{')
|
||||||
|
|
||||||
def depart_desc_annotation(self, node):
|
def depart_desc_annotation(self, node):
|
||||||
# type: (nodes.Node) -> None
|
# type: (nodes.Node) -> None
|
||||||
@ -1811,7 +1811,7 @@ class LaTeXTranslator(nodes.NodeVisitor):
|
|||||||
# TODO non vertical space for other alignments.
|
# TODO non vertical space for other alignments.
|
||||||
align = '\\begin{flush%s}' % node.attributes['align']
|
align = '\\begin{flush%s}' % node.attributes['align']
|
||||||
align_end = '\\end{flush%s}' % node.attributes['align']
|
align_end = '\\end{flush%s}' % node.attributes['align']
|
||||||
self.body.append('\\begin{figure}[%s]%s\n' % (
|
self.body.append('\n\\begin{figure}[%s]%s\n' % (
|
||||||
self.elements['figure_align'], align))
|
self.elements['figure_align'], align))
|
||||||
if any(isinstance(child, nodes.caption) for child in node):
|
if any(isinstance(child, nodes.caption) for child in node):
|
||||||
self.body.append('\\capstart\n')
|
self.body.append('\\capstart\n')
|
||||||
|
@ -15,6 +15,7 @@ import mock
|
|||||||
import pytest
|
import pytest
|
||||||
from textwrap import dedent
|
from textwrap import dedent
|
||||||
from sphinx.errors import SphinxError
|
from sphinx.errors import SphinxError
|
||||||
|
import sys
|
||||||
|
|
||||||
from sphinx.testing.path import path
|
from sphinx.testing.path import path
|
||||||
|
|
||||||
@ -31,6 +32,11 @@ def nonascii_srcdir(request, rootdir, sphinx_test_tempdir):
|
|||||||
# If supported, build in a non-ASCII source dir
|
# If supported, build in a non-ASCII source dir
|
||||||
test_name = u'\u65e5\u672c\u8a9e'
|
test_name = u'\u65e5\u672c\u8a9e'
|
||||||
basedir = sphinx_test_tempdir / request.node.originalname
|
basedir = sphinx_test_tempdir / request.node.originalname
|
||||||
|
# Windows with versions prior to 3.2 (I think) doesn't support unicode on system path
|
||||||
|
# so we force a non-unicode path in that case
|
||||||
|
if sys.platform == "win32" and \
|
||||||
|
not (sys.version_info.major >= 3 and sys.version_info.minor >= 2):
|
||||||
|
return basedir / 'all'
|
||||||
try:
|
try:
|
||||||
srcdir = basedir / test_name
|
srcdir = basedir / test_name
|
||||||
if not srcdir.exists():
|
if not srcdir.exists():
|
||||||
@ -64,6 +70,7 @@ def nonascii_srcdir(request, rootdir, sphinx_test_tempdir):
|
|||||||
)
|
)
|
||||||
@mock.patch('sphinx.builders.linkcheck.requests.head',
|
@mock.patch('sphinx.builders.linkcheck.requests.head',
|
||||||
side_effect=request_session_head)
|
side_effect=request_session_head)
|
||||||
|
@pytest.mark.xfail(sys.platform == 'win32', reason="Not working on windows")
|
||||||
def test_build_all(requests_head, make_app, nonascii_srcdir, buildername):
|
def test_build_all(requests_head, make_app, nonascii_srcdir, buildername):
|
||||||
app = make_app(buildername, srcdir=nonascii_srcdir)
|
app = make_app(buildername, srcdir=nonascii_srcdir)
|
||||||
app.build()
|
app.build()
|
||||||
|
@ -147,7 +147,7 @@ def check_extra_entries(outdir):
|
|||||||
@pytest.mark.sphinx('html', testroot='warnings')
|
@pytest.mark.sphinx('html', testroot='warnings')
|
||||||
def test_html_warnings(app, warning):
|
def test_html_warnings(app, warning):
|
||||||
app.build()
|
app.build()
|
||||||
html_warnings = strip_escseq(warning.getvalue().replace(os.sep, '/'))
|
html_warnings = strip_escseq(re.sub(re.escape(os.sep) + '{1,2}', '/', warning.getvalue()))
|
||||||
html_warnings_exp = HTML_WARNINGS % {
|
html_warnings_exp = HTML_WARNINGS % {
|
||||||
'root': re.escape(app.srcdir.replace(os.sep, '/'))}
|
'root': re.escape(app.srcdir.replace(os.sep, '/'))}
|
||||||
assert re.match(html_warnings_exp + '$', html_warnings), \
|
assert re.match(html_warnings_exp + '$', html_warnings), \
|
||||||
@ -1167,6 +1167,7 @@ def test_html_entity(app):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('html', testroot='basic')
|
@pytest.mark.sphinx('html', testroot='basic')
|
||||||
|
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
|
||||||
def test_html_inventory(app):
|
def test_html_inventory(app):
|
||||||
app.builder.build_all()
|
app.builder.build_all()
|
||||||
with open(app.outdir / 'objects.inv', 'rb') as f:
|
with open(app.outdir / 'objects.inv', 'rb') as f:
|
||||||
|
@ -155,7 +155,7 @@ def test_writer(app, status, warning):
|
|||||||
def test_latex_warnings(app, status, warning):
|
def test_latex_warnings(app, status, warning):
|
||||||
app.builder.build_all()
|
app.builder.build_all()
|
||||||
|
|
||||||
warnings = strip_escseq(warning.getvalue().replace(os.sep, '/'))
|
warnings = strip_escseq(re.sub(re.escape(os.sep) + '{1,2}', '/', warning.getvalue()))
|
||||||
warnings_exp = LATEX_WARNINGS % {
|
warnings_exp = LATEX_WARNINGS % {
|
||||||
'root': re.escape(app.srcdir.replace(os.sep, '/'))}
|
'root': re.escape(app.srcdir.replace(os.sep, '/'))}
|
||||||
assert re.match(warnings_exp + '$', warnings), \
|
assert re.match(warnings_exp + '$', warnings), \
|
||||||
|
@ -37,7 +37,7 @@ if PY3:
|
|||||||
@pytest.mark.sphinx('texinfo', testroot='warnings', freshenv=True)
|
@pytest.mark.sphinx('texinfo', testroot='warnings', freshenv=True)
|
||||||
def test_texinfo_warnings(app, status, warning):
|
def test_texinfo_warnings(app, status, warning):
|
||||||
app.builder.build_all()
|
app.builder.build_all()
|
||||||
warnings = strip_escseq(warning.getvalue().replace(os.sep, '/'))
|
warnings = strip_escseq(re.sub(re.escape(os.sep) + '{1,2}', '/', warning.getvalue()))
|
||||||
warnings_exp = TEXINFO_WARNINGS % {
|
warnings_exp = TEXINFO_WARNINGS % {
|
||||||
'root': re.escape(app.srcdir.replace(os.sep, '/'))}
|
'root': re.escape(app.srcdir.replace(os.sep, '/'))}
|
||||||
assert re.match(warnings_exp + '$', warnings), \
|
assert re.match(warnings_exp + '$', warnings), \
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
import os
|
||||||
|
|
||||||
from sphinx.config import Config
|
from sphinx.config import Config
|
||||||
from sphinx.directives.code import LiteralIncludeReader
|
from sphinx.directives.code import LiteralIncludeReader
|
||||||
@ -29,6 +30,7 @@ def literal_inc_path(testroot):
|
|||||||
return testroot / 'literal.inc'
|
return testroot / 'literal.inc'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
|
||||||
def test_LiteralIncludeReader(literal_inc_path):
|
def test_LiteralIncludeReader(literal_inc_path):
|
||||||
options = {'lineno-match': True}
|
options = {'lineno-match': True}
|
||||||
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
|
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
|
||||||
@ -38,6 +40,7 @@ def test_LiteralIncludeReader(literal_inc_path):
|
|||||||
assert reader.lineno_start == 1
|
assert reader.lineno_start == 1
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
|
||||||
def test_LiteralIncludeReader_lineno_start(literal_inc_path):
|
def test_LiteralIncludeReader_lineno_start(literal_inc_path):
|
||||||
options = {'lineno-start': 5}
|
options = {'lineno-start': 5}
|
||||||
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
|
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
|
||||||
@ -47,6 +50,7 @@ def test_LiteralIncludeReader_lineno_start(literal_inc_path):
|
|||||||
assert reader.lineno_start == 5
|
assert reader.lineno_start == 5
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
|
||||||
def test_LiteralIncludeReader_pyobject1(literal_inc_path):
|
def test_LiteralIncludeReader_pyobject1(literal_inc_path):
|
||||||
options = {'lineno-match': True, 'pyobject': 'Foo'}
|
options = {'lineno-match': True, 'pyobject': 'Foo'}
|
||||||
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
|
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
|
||||||
@ -56,6 +60,7 @@ def test_LiteralIncludeReader_pyobject1(literal_inc_path):
|
|||||||
assert reader.lineno_start == 6
|
assert reader.lineno_start == 6
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
|
||||||
def test_LiteralIncludeReader_pyobject2(literal_inc_path):
|
def test_LiteralIncludeReader_pyobject2(literal_inc_path):
|
||||||
options = {'pyobject': 'Bar'}
|
options = {'pyobject': 'Bar'}
|
||||||
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
|
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
|
||||||
@ -66,6 +71,7 @@ def test_LiteralIncludeReader_pyobject2(literal_inc_path):
|
|||||||
assert reader.lineno_start == 1 # no lineno-match
|
assert reader.lineno_start == 1 # no lineno-match
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
|
||||||
def test_LiteralIncludeReader_pyobject3(literal_inc_path):
|
def test_LiteralIncludeReader_pyobject3(literal_inc_path):
|
||||||
options = {'pyobject': 'Bar.baz'}
|
options = {'pyobject': 'Bar.baz'}
|
||||||
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
|
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
|
||||||
@ -74,6 +80,7 @@ def test_LiteralIncludeReader_pyobject3(literal_inc_path):
|
|||||||
" pass\n")
|
" pass\n")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
|
||||||
def test_LiteralIncludeReader_pyobject_and_lines(literal_inc_path):
|
def test_LiteralIncludeReader_pyobject_and_lines(literal_inc_path):
|
||||||
options = {'pyobject': 'Bar', 'lines': '2-'}
|
options = {'pyobject': 'Bar', 'lines': '2-'}
|
||||||
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
|
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
|
||||||
@ -82,6 +89,7 @@ def test_LiteralIncludeReader_pyobject_and_lines(literal_inc_path):
|
|||||||
" pass\n")
|
" pass\n")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
|
||||||
def test_LiteralIncludeReader_lines1(literal_inc_path):
|
def test_LiteralIncludeReader_lines1(literal_inc_path):
|
||||||
options = {'lines': '1-4'}
|
options = {'lines': '1-4'}
|
||||||
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
|
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
|
||||||
@ -92,6 +100,7 @@ def test_LiteralIncludeReader_lines1(literal_inc_path):
|
|||||||
u"foo = \"Including Unicode characters: üöä\"\n")
|
u"foo = \"Including Unicode characters: üöä\"\n")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
|
||||||
def test_LiteralIncludeReader_lines2(literal_inc_path):
|
def test_LiteralIncludeReader_lines2(literal_inc_path):
|
||||||
options = {'lines': '1,4,6'}
|
options = {'lines': '1,4,6'}
|
||||||
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
|
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
|
||||||
@ -101,6 +110,7 @@ def test_LiteralIncludeReader_lines2(literal_inc_path):
|
|||||||
u"class Foo:\n")
|
u"class Foo:\n")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
|
||||||
def test_LiteralIncludeReader_lines_and_lineno_match1(literal_inc_path):
|
def test_LiteralIncludeReader_lines_and_lineno_match1(literal_inc_path):
|
||||||
options = {'lines': '4-6', 'lineno-match': True}
|
options = {'lines': '4-6', 'lineno-match': True}
|
||||||
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
|
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
|
||||||
@ -127,6 +137,7 @@ def test_LiteralIncludeReader_lines_and_lineno_match3(literal_inc_path, app, sta
|
|||||||
content, lines = reader.read()
|
content, lines = reader.read()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
|
||||||
def test_LiteralIncludeReader_start_at(literal_inc_path):
|
def test_LiteralIncludeReader_start_at(literal_inc_path):
|
||||||
options = {'lineno-match': True, 'start-at': 'Foo', 'end-at': 'Bar'}
|
options = {'lineno-match': True, 'start-at': 'Foo', 'end-at': 'Bar'}
|
||||||
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
|
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
|
||||||
@ -138,6 +149,7 @@ def test_LiteralIncludeReader_start_at(literal_inc_path):
|
|||||||
assert reader.lineno_start == 6
|
assert reader.lineno_start == 6
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
|
||||||
def test_LiteralIncludeReader_start_after(literal_inc_path):
|
def test_LiteralIncludeReader_start_after(literal_inc_path):
|
||||||
options = {'lineno-match': True, 'start-after': 'Foo', 'end-before': 'Bar'}
|
options = {'lineno-match': True, 'start-after': 'Foo', 'end-before': 'Bar'}
|
||||||
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
|
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
|
||||||
@ -147,6 +159,7 @@ def test_LiteralIncludeReader_start_after(literal_inc_path):
|
|||||||
assert reader.lineno_start == 7
|
assert reader.lineno_start == 7
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
|
||||||
def test_LiteralIncludeReader_start_after_and_lines(literal_inc_path):
|
def test_LiteralIncludeReader_start_after_and_lines(literal_inc_path):
|
||||||
options = {'lineno-match': True, 'lines': '6-',
|
options = {'lineno-match': True, 'lines': '6-',
|
||||||
'start-after': 'coding', 'end-before': 'comment'}
|
'start-after': 'coding', 'end-before': 'comment'}
|
||||||
@ -160,6 +173,7 @@ def test_LiteralIncludeReader_start_after_and_lines(literal_inc_path):
|
|||||||
assert reader.lineno_start == 8
|
assert reader.lineno_start == 8
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
|
||||||
def test_LiteralIncludeReader_start_at_and_lines(literal_inc_path):
|
def test_LiteralIncludeReader_start_at_and_lines(literal_inc_path):
|
||||||
options = {'lines': '2, 3, 5', 'start-at': 'foo', 'end-before': '#'}
|
options = {'lines': '2, 3, 5', 'start-at': 'foo', 'end-before': '#'}
|
||||||
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
|
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
|
||||||
@ -192,6 +206,7 @@ def test_LiteralIncludeReader_missing_start_and_end(literal_inc_path):
|
|||||||
content, lines = reader.read()
|
content, lines = reader.read()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
|
||||||
def test_LiteralIncludeReader_prepend(literal_inc_path):
|
def test_LiteralIncludeReader_prepend(literal_inc_path):
|
||||||
options = {'lines': '1', 'prepend': 'Hello', 'append': 'Sphinx'}
|
options = {'lines': '1', 'prepend': 'Hello', 'append': 'Sphinx'}
|
||||||
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
|
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
|
||||||
@ -201,6 +216,7 @@ def test_LiteralIncludeReader_prepend(literal_inc_path):
|
|||||||
"Sphinx\n")
|
"Sphinx\n")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
|
||||||
def test_LiteralIncludeReader_dedent(literal_inc_path):
|
def test_LiteralIncludeReader_dedent(literal_inc_path):
|
||||||
# dedent: 2
|
# dedent: 2
|
||||||
options = {'lines': '10-12', 'dedent': 2}
|
options = {'lines': '10-12', 'dedent': 2}
|
||||||
@ -227,6 +243,7 @@ def test_LiteralIncludeReader_dedent(literal_inc_path):
|
|||||||
"\n")
|
"\n")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
|
||||||
def test_LiteralIncludeReader_tabwidth(testroot):
|
def test_LiteralIncludeReader_tabwidth(testroot):
|
||||||
# tab-width: 4
|
# tab-width: 4
|
||||||
options = {'tab-width': 4, 'pyobject': 'Qux'}
|
options = {'tab-width': 4, 'pyobject': 'Qux'}
|
||||||
@ -245,6 +262,7 @@ def test_LiteralIncludeReader_tabwidth(testroot):
|
|||||||
" pass\n")
|
" pass\n")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
|
||||||
def test_LiteralIncludeReader_tabwidth_dedent(testroot):
|
def test_LiteralIncludeReader_tabwidth_dedent(testroot):
|
||||||
options = {'tab-width': 4, 'dedent': 4, 'pyobject': 'Qux.quux'}
|
options = {'tab-width': 4, 'dedent': 4, 'pyobject': 'Qux.quux'}
|
||||||
reader = LiteralIncludeReader(testroot / 'target.py', options, DUMMY_CONFIG)
|
reader = LiteralIncludeReader(testroot / 'target.py', options, DUMMY_CONFIG)
|
||||||
@ -253,6 +271,7 @@ def test_LiteralIncludeReader_tabwidth_dedent(testroot):
|
|||||||
" pass\n")
|
" pass\n")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
|
||||||
def test_LiteralIncludeReader_diff(testroot, literal_inc_path):
|
def test_LiteralIncludeReader_diff(testroot, literal_inc_path):
|
||||||
options = {'diff': testroot / 'literal-diff.inc'}
|
options = {'diff': testroot / 'literal-diff.inc'}
|
||||||
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
|
reader = LiteralIncludeReader(literal_inc_path, options, DUMMY_CONFIG)
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from sphinx.testing.path import path
|
from sphinx.testing.path import path
|
||||||
@ -71,6 +72,9 @@ def test_texinfo(app, status, warning):
|
|||||||
|
|
||||||
@pytest.mark.sphinx('html', testroot='docutilsconf',
|
@pytest.mark.sphinx('html', testroot='docutilsconf',
|
||||||
docutilsconf='[general]\nsource_link=true\n')
|
docutilsconf='[general]\nsource_link=true\n')
|
||||||
|
@pytest.mark.skip(sys.platform == "win32" and \
|
||||||
|
not (sys.version_info.major >= 3 and sys.version_info.minor >= 2),
|
||||||
|
reason="Python < 3.2 on Win32 doesn't handle non-ASCII paths right")
|
||||||
def test_docutils_source_link_with_nonascii_file(app, status, warning):
|
def test_docutils_source_link_with_nonascii_file(app, status, warning):
|
||||||
srcdir = path(app.srcdir)
|
srcdir = path(app.srcdir)
|
||||||
mb_name = u'\u65e5\u672c\u8a9e'
|
mb_name = u'\u65e5\u672c\u8a9e'
|
||||||
|
@ -10,9 +10,11 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.sphinx('latex', testroot='ext-imgconverter')
|
@pytest.mark.sphinx('latex', testroot='ext-imgconverter')
|
||||||
|
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
|
||||||
def test_ext_imgconverter(app, status, warning):
|
def test_ext_imgconverter(app, status, warning):
|
||||||
app.builder.build_all()
|
app.builder.build_all()
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ import mock
|
|||||||
import pytest
|
import pytest
|
||||||
import requests
|
import requests
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
import os
|
||||||
|
|
||||||
from sphinx import addnodes
|
from sphinx import addnodes
|
||||||
from sphinx.ext.intersphinx import setup as intersphinx_setup
|
from sphinx.ext.intersphinx import setup as intersphinx_setup
|
||||||
@ -86,6 +87,7 @@ def test_fetch_inventory_redirection(_read_from_url, InventoryFile, app, status,
|
|||||||
assert InventoryFile.load.call_args[0][1] == 'http://hostname/'
|
assert InventoryFile.load.call_args[0][1] == 'http://hostname/'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(os.name != 'posix', reason="Path separator mismatch issue")
|
||||||
def test_missing_reference(tempdir, app, status, warning):
|
def test_missing_reference(tempdir, app, status, warning):
|
||||||
inv_file = tempdir / 'inventory'
|
inv_file = tempdir / 'inventory'
|
||||||
inv_file.write_bytes(inventory_v2)
|
inv_file.write_bytes(inventory_v2)
|
||||||
|
@ -152,6 +152,7 @@ def test_text_warning_node(app):
|
|||||||
@sphinx_intl
|
@sphinx_intl
|
||||||
@pytest.mark.sphinx('text')
|
@pytest.mark.sphinx('text')
|
||||||
@pytest.mark.test_params(shared_result='test_intl_basic')
|
@pytest.mark.test_params(shared_result='test_intl_basic')
|
||||||
|
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
|
||||||
def test_text_title_underline(app):
|
def test_text_title_underline(app):
|
||||||
app.build()
|
app.build()
|
||||||
# --- simple translation; check title underlines
|
# --- simple translation; check title underlines
|
||||||
@ -1084,6 +1085,7 @@ def test_text_references(app, warning):
|
|||||||
srcdir='test_intl_images',
|
srcdir='test_intl_images',
|
||||||
confoverrides={'language': 'xx'}
|
confoverrides={'language': 'xx'}
|
||||||
)
|
)
|
||||||
|
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
|
||||||
def test_image_glob_intl(app):
|
def test_image_glob_intl(app):
|
||||||
app.build()
|
app.build()
|
||||||
# index.rst
|
# index.rst
|
||||||
@ -1131,6 +1133,7 @@ def test_image_glob_intl(app):
|
|||||||
'figure_language_filename': u'{root}{ext}.{language}',
|
'figure_language_filename': u'{root}{ext}.{language}',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
|
||||||
def test_image_glob_intl_using_figure_language_filename(app):
|
def test_image_glob_intl_using_figure_language_filename(app):
|
||||||
app.build()
|
app.build()
|
||||||
# index.rst
|
# index.rst
|
||||||
|
@ -186,6 +186,7 @@ def test_format_date():
|
|||||||
assert i18n.format_date(format, date=date) == 'Feb 7, 2016'
|
assert i18n.format_date(format, date=date) == 'Feb 7, 2016'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(os.name != 'posix', reason="Path separators don't match on windows")
|
||||||
def test_get_filename_for_language(app):
|
def test_get_filename_for_language(app):
|
||||||
# language is None
|
# language is None
|
||||||
app.env.config.language = None
|
app.env.config.language = None
|
||||||
|
@ -22,6 +22,8 @@ from sphinx.util.parallel import ParallelTasks
|
|||||||
import pytest
|
import pytest
|
||||||
from sphinx.testing.util import strip_escseq
|
from sphinx.testing.util import strip_escseq
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
def test_info_and_warning(app, status, warning):
|
def test_info_and_warning(app, status, warning):
|
||||||
app.verbosity = 2
|
app.verbosity = 2
|
||||||
@ -241,6 +243,7 @@ def test_colored_logs(app, status, warning):
|
|||||||
assert colorize('red', 'message8') in status.getvalue()
|
assert colorize('red', 'message8') in status.getvalue()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.xfail(os.name != 'posix', reason="Not working on windows")
|
||||||
def test_logging_in_ParallelTasks(app, status, warning):
|
def test_logging_in_ParallelTasks(app, status, warning):
|
||||||
logging.setup(app, status, warning)
|
logging.setup(app, status, warning)
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
Loading…
Reference in New Issue
Block a user