Deprecate and drop internal use of force_decode()

In the Python 3 only code base, this function is no longer necessary.
The type of values is well understood and deliberate. Code should avoid
arbitrary mixing of bytes & str.

By removing force_decode() calls from docstring values, can deprecate
the now unused 'encoding' arguments to various autodoc methods.
This commit is contained in:
Jon Dufresne 2018-11-11 20:48:45 -08:00
parent 6b0e0fa3eb
commit 555960d668
7 changed files with 49 additions and 43 deletions

View File

@ -19,12 +19,17 @@ Incompatible changes
Deprecated
----------
* The ``encoding`` argument of ``autodoc.Documenter.get_doc()``,
``autodoc.DocstringSignatureMixin.get_doc()``,
``autodoc.DocstringSignatureMixin._find_signature()``, and
``autodoc.ClassDocumenter.get_doc()`` are deprecated.
* The ``suffix`` argument of ``env.doc2path()`` is deprecated.
* The string style ``base`` argument of ``env.doc2path()`` is deprecated.
* ``sphinx.application.Sphinx._setting_up_extension``
* ``sphinx.ext.config.check_unicode()``
* ``sphinx.ext.doctest.doctest_encode()``
* ``sphinx.testing.util.remove_unicode_literal()``
* ``sphinx.util.force_decode()``
* ``sphinx.util.get_matching_docs()`` is deprecated
* ``sphinx.util.osutil.walk()``
* ``sphinx.util.pycompat.u``

View File

@ -123,6 +123,14 @@ The following is a list of deprecated interfaces.
- (will be) Removed
- Alternatives
* - ``encoding`` argument of ``autodoc.Documenter.get_doc()``,
``autodoc.DocstringSignatureMixin.get_doc()``,
``autodoc.DocstringSignatureMixin._find_signature()``, and
``autodoc.ClassDocumenter.get_doc()``
- 2.0
- 4.0
- N/A
* - ``suffix`` argument of ``BuildEnvironment.doc2path()``
- 2.0
- 4.0
@ -148,6 +156,11 @@ The following is a list of deprecated interfaces.
- 4.0
- N/A
* - ``sphinx.util.force_decode()``
- 2.0
- 4.0
- N/A
* - ``sphinx.util.get_matching_docs()``
- 2.0
- 4.0

View File

@ -15,7 +15,6 @@ import re
from os import path
from docutils import nodes
from six import text_type
from sphinx import addnodes
from sphinx import package_dir
@ -23,7 +22,7 @@ from sphinx.builders.html import StandaloneHTMLBuilder
from sphinx.config import string_classes
from sphinx.environment.adapters.indexentries import IndexEntries
from sphinx.locale import __
from sphinx.util import force_decode, logging
from sphinx.util import logging
from sphinx.util.osutil import make_filename
from sphinx.util.pycompat import htmlescape
from sphinx.util.template import SphinxRenderer
@ -113,15 +112,7 @@ class QtHelpBuilder(StandaloneHTMLBuilder):
item = section_template % {'title': indexcls.localname,
'ref': '%s.html' % indexname}
sections.append(' ' * 4 * 4 + item)
# sections may be unicode strings or byte strings, we have to make sure
# they are all unicode strings before joining them
new_sections = []
for section in sections:
if not isinstance(section, text_type):
new_sections.append(force_decode(section, None))
else:
new_sections.append(section)
sections = u'\n'.join(new_sections) # type: ignore
sections = '\n'.join(sections) # type: ignore
# keywords
keywords = []
@ -182,7 +173,6 @@ class QtHelpBuilder(StandaloneHTMLBuilder):
def write_toc(self, node, indentlevel=4):
# type: (nodes.Node, int) -> List[unicode]
# XXX this should return a Unicode string, not a bytestring
parts = [] # type: List[unicode]
if self.isdocnode(node):
refnode = node.children[0][0]
@ -202,7 +192,7 @@ class QtHelpBuilder(StandaloneHTMLBuilder):
title = htmlescape(node.astext()).replace('"', '"')
item = section_template % {'title': title, 'ref': link}
item = u' ' * 4 * indentlevel + item
parts.append(item.encode('ascii', 'xmlcharrefreplace'))
parts.append(item.encode('ascii', 'xmlcharrefreplace').decode())
elif isinstance(node, nodes.bullet_list):
for subnode in node:
parts.extend(self.write_toc(subnode, indentlevel))

View File

@ -21,13 +21,13 @@ from docutils.statemachine import ViewList
from six import text_type, string_types
import sphinx
from sphinx.deprecation import RemovedInSphinx30Warning
from sphinx.deprecation import RemovedInSphinx30Warning, RemovedInSphinx40Warning
from sphinx.ext.autodoc.importer import mock, import_object, get_object_members
from sphinx.ext.autodoc.importer import _MockImporter # to keep compatibility # NOQA
from sphinx.locale import _, __
from sphinx.pycode import ModuleAnalyzer, PycodeError
from sphinx.util import logging
from sphinx.util import rpartition, force_decode
from sphinx.util import rpartition
from sphinx.util.docstrings import prepare_docstring
from sphinx.util.inspect import Signature, isdescriptor, safe_getmembers, \
safe_getattr, object_description, is_builtin_class_method, \
@ -438,16 +438,14 @@ class Documenter:
def get_doc(self, encoding=None, ignore=1):
# type: (unicode, int) -> List[List[unicode]]
"""Decode and return lines of the docstring(s) for the object."""
if encoding is not None:
warnings.warn("The 'encoding' argument to autodoc.%s.get_doc() is deprecated."
% self.__class__.__name__,
RemovedInSphinx40Warning)
docstring = getdoc(self.object, self.get_attr,
self.env.config.autodoc_inherit_docstrings)
# make sure we have Unicode docstrings, then sanitize and split
# into lines
if isinstance(docstring, text_type):
if docstring:
return [prepare_docstring(docstring, ignore)]
elif isinstance(docstring, str): # this will not trigger on Py3
return [prepare_docstring(force_decode(docstring, encoding),
ignore)]
# ... else it is something strange, let's ignore it
return []
def process_doc(self, docstrings):
@ -491,8 +489,7 @@ class Documenter:
# add content from docstrings
if not no_docstring:
encoding = self.analyzer and self.analyzer.encoding
docstrings = self.get_doc(encoding)
docstrings = self.get_doc()
if not docstrings:
# append at least a dummy docstring, so that the event
# autodoc-process-docstring is fired and can add some
@ -926,7 +923,11 @@ class DocstringSignatureMixin:
def _find_signature(self, encoding=None):
# type: (unicode) -> Tuple[str, str]
docstrings = self.get_doc(encoding)
if encoding is not None:
warnings.warn("The 'encoding' argument to autodoc.%s._find_signature() is "
"deprecated." % self.__class__.__name__,
RemovedInSphinx40Warning)
docstrings = self.get_doc()
self._new_docstrings = docstrings[:]
result = None
for i, doclines in enumerate(docstrings):
@ -955,10 +956,14 @@ class DocstringSignatureMixin:
def get_doc(self, encoding=None, ignore=1):
# type: (unicode, int) -> List[List[unicode]]
if encoding is not None:
warnings.warn("The 'encoding' argument to autodoc.%s.get_doc() is deprecated."
% self.__class__.__name__,
RemovedInSphinx40Warning)
lines = getattr(self, '_new_docstrings', None)
if lines is not None:
return lines
return Documenter.get_doc(self, encoding, ignore) # type: ignore
return Documenter.get_doc(self, None, ignore) # type: ignore
def format_signature(self):
# type: () -> unicode
@ -1119,6 +1124,10 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
def get_doc(self, encoding=None, ignore=1):
# type: (unicode, int) -> List[List[unicode]]
if encoding is not None:
warnings.warn("The 'encoding' argument to autodoc.%s.get_doc() is deprecated."
% self.__class__.__name__,
RemovedInSphinx40Warning)
lines = getattr(self, '_new_docstrings', None)
if lines is not None:
return lines
@ -1154,14 +1163,7 @@ class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type:
docstrings = [initdocstring]
else:
docstrings.append(initdocstring)
doc = []
for docstring in docstrings:
if isinstance(docstring, text_type):
doc.append(prepare_docstring(docstring, ignore))
elif isinstance(docstring, str): # this will not trigger on Py3
doc.append(prepare_docstring(force_decode(docstring, encoding),
ignore))
return doc
return [prepare_docstring(docstring, ignore) for docstring in docstrings]
def add_content(self, more_content, no_docstring=False):
# type: (Any, bool) -> None

View File

@ -44,13 +44,10 @@ from hashlib import md5
from docutils import nodes
from docutils.parsers.rst import directives
from six import text_type
import sphinx
from sphinx.ext.graphviz import render_dot_html, render_dot_latex, \
render_dot_texinfo, figure_wrapper
from sphinx.pycode import ModuleAnalyzer
from sphinx.util import force_decode
from sphinx.util.docutils import SphinxDirective
if False:
@ -187,10 +184,7 @@ class InheritanceGraph:
tooltip = None
try:
if cls.__doc__:
enc = ModuleAnalyzer.for_module(cls.__module__).encoding
doc = cls.__doc__.strip().split("\n")[0]
if not isinstance(doc, text_type):
doc = force_decode(doc, enc)
if doc:
tooltip = '"%s"' % doc.replace('"', '\\"')
except Exception: # might raise AttributeError for strange classes

View File

@ -258,7 +258,7 @@ def save_traceback(app):
last_msgs = ''
if app is not None:
last_msgs = '\n'.join(
'# %s' % strip_colors(force_decode(s, 'utf-8')).strip() # type: ignore
'# %s' % strip_colors(s).strip()
for s in app.messagelog)
os.write(fd, (_DEBUG_HEADER %
(sphinx.__display_version__,
@ -458,6 +458,8 @@ def parselinenos(spec, total):
def force_decode(string, encoding):
# type: (unicode, unicode) -> unicode
"""Forcibly get a unicode string out of a bytestring."""
warnings.warn('force_decode() is deprecated.',
RemovedInSphinx40Warning, stacklevel=2)
if isinstance(string, bytes):
try:
if encoding:

View File

@ -286,13 +286,13 @@ def test_format_signature():
@pytest.mark.usefixtures('setup_test')
def test_get_doc():
def getdocl(objtype, obj, encoding=None):
def getdocl(objtype, obj):
inst = app.registry.documenters[objtype](directive, 'tmp')
inst.object = obj
inst.objpath = [obj.__name__]
inst.doc_as_attr = False
inst.format_signature() # handle docstring signatures!
ds = inst.get_doc(encoding)
ds = inst.get_doc()
# for testing purposes, concat them and strip the empty line at the end
res = sum(ds, [])[:-1]
print(res)