Merge pull request #5398 from tk0miya/drop_py2_2

Drop branches for sys.version_info < (3, 4)
This commit is contained in:
Takeshi KOMIYA 2018-09-22 17:09:28 +09:00 committed by GitHub
commit bc61764319
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 69 additions and 564 deletions

View File

@ -62,9 +62,7 @@ default_settings = {
# This is increased every time an environment attribute is added
# or changed to properly invalidate pickle files.
#
# NOTE: increase base version by 2 to have distinct numbers for Py2 and 3
ENV_VERSION = 54 + (sys.version_info[0] - 2)
ENV_VERSION = 56
# config status
CONFIG_OK = 1

View File

@ -234,12 +234,6 @@ def get_object_members(subject, objpath, attrgetter, analyzer=None):
# the members directly defined in the class
obj_dict = attrgetter(subject, '__dict__', {})
# Py34 doesn't have enum members in __dict__.
if sys.version_info[:2] == (3, 4) and isenumclass(subject):
obj_dict = dict(obj_dict)
for name, value in subject.__members__.items():
obj_dict[name] = value
members = {} # type: Dict[str, Attribute]
# enum members

View File

@ -9,9 +9,7 @@
:license: BSD, see LICENSE for details.
"""
import sys
from six import PY2, iteritems
from six import iteritems
import sphinx
from sphinx.application import Sphinx
@ -435,34 +433,26 @@ def _skip_member(app, what, name, obj, skip, options):
if name != '__weakref__' and has_doc and is_member:
cls_is_owner = False
if what == 'class' or what == 'exception':
if PY2:
cls = getattr(obj, 'im_class', getattr(obj, '__objclass__',
None))
cls_is_owner = (cls and hasattr(cls, name) and
name in cls.__dict__)
elif sys.version_info >= (3, 3):
qualname = getattr(obj, '__qualname__', '')
cls_path, _, _ = qualname.rpartition('.')
if cls_path:
try:
if '.' in cls_path:
import importlib
import functools
qualname = getattr(obj, '__qualname__', '')
cls_path, _, _ = qualname.rpartition('.')
if cls_path:
try:
if '.' in cls_path:
import importlib
import functools
mod = importlib.import_module(obj.__module__)
mod_path = cls_path.split('.')
cls = functools.reduce(getattr, mod_path, mod)
else:
cls = obj.__globals__[cls_path]
except Exception:
cls_is_owner = False
mod = importlib.import_module(obj.__module__)
mod_path = cls_path.split('.')
cls = functools.reduce(getattr, mod_path, mod)
else:
cls_is_owner = (cls and hasattr(cls, name) and
name in cls.__dict__)
else:
cls = obj.__globals__[cls_path]
except Exception:
cls_is_owner = False
else:
cls_is_owner = (cls and hasattr(cls, name) and # type: ignore
name in cls.__dict__)
else:
cls_is_owner = True
cls_is_owner = False
if what == 'module' or cls_is_owner:
is_init = (name == '__init__')

View File

@ -677,8 +677,7 @@ def xmlname_checker():
[u'\u2C00', u'\u2FEF'], [u'\u3001', u'\uD7FF'], [u'\uF900', u'\uFDCF'],
[u'\uFDF0', u'\uFFFD']]
if sys.version_info.major == 3:
name_start_chars.append([u'\U00010000', u'\U000EFFFF'])
name_start_chars.append([u'\U00010000', u'\U000EFFFF'])
name_chars = [
u"\\-", u"\\.", [u'0', u'9'], u'\u00B7', [u'\u0300', u'\u036F'],

View File

@ -648,108 +648,6 @@ class Signature(object):
return qualname
if sys.version_info >= (3, 5):
_getdoc = inspect.getdoc
else:
# code copied from the inspect.py module of the standard library
# of Python 3.5
def _findclass(func):
# type: (Any) -> Any
cls = sys.modules.get(func.__module__)
if cls is None:
return None
if hasattr(func, 'im_class'):
cls = func.im_class
else:
for name in func.__qualname__.split('.')[:-1]:
cls = getattr(cls, name)
if not inspect.isclass(cls):
return None
return cls
def _finddoc(obj):
# type: (Any) -> unicode
if inspect.isclass(obj):
for base in obj.__mro__:
if base is not object:
try:
doc = base.__doc__
except AttributeError:
continue
if doc is not None:
return doc
return None
if inspect.ismethod(obj) and getattr(obj, '__self__', None):
name = obj.__func__.__name__
self = obj.__self__
if (inspect.isclass(self) and
getattr(getattr(self, name, None), '__func__')
is obj.__func__):
# classmethod
cls = self
else:
cls = self.__class__
elif inspect.isfunction(obj) or inspect.ismethod(obj):
name = obj.__name__
cls = _findclass(obj)
if cls is None or getattr(cls, name) != obj:
return None
elif inspect.isbuiltin(obj):
name = obj.__name__
self = obj.__self__
if (inspect.isclass(self) and
self.__qualname__ + '.' + name == obj.__qualname__):
# classmethod
cls = self
else:
cls = self.__class__
# Should be tested before isdatadescriptor().
elif isinstance(obj, property):
func = obj.fget
name = func.__name__
cls = _findclass(func)
if cls is None or getattr(cls, name) is not obj:
return None
elif inspect.ismethoddescriptor(obj) or inspect.isdatadescriptor(obj):
name = obj.__name__
cls = obj.__objclass__
if getattr(cls, name) is not obj:
return None
else:
return None
for base in cls.__mro__:
try:
doc = getattr(base, name).__doc__
except AttributeError:
continue
if doc is not None:
return doc
return None
def _getdoc(object):
# type: (Any) -> unicode
"""Get the documentation string for an object.
All tabs are expanded to spaces. To clean up docstrings that are
indented to line up with blocks of code, any whitespace than can be
uniformly removed from the second line onwards is removed."""
try:
doc = object.__doc__
except AttributeError:
return None
if doc is None:
try:
doc = _finddoc(object)
except (AttributeError, TypeError):
return None
if not isinstance(doc, str):
return None
return inspect.cleandoc(doc)
def getdoc(obj, attrgetter=safe_getattr, allow_inherited=False):
# type: (Any, Callable, bool) -> unicode
"""Get the docstring for the object.
@ -763,6 +661,6 @@ def getdoc(obj, attrgetter=safe_getattr, allow_inherited=False):
if ispartial(obj) and doc == obj.__class__.__doc__:
return getdoc(obj.func)
elif doc is None and allow_inherited:
doc = _getdoc(obj)
doc = inspect.getdoc(obj)
return doc

View File

@ -9,7 +9,6 @@
import os
import shutil
import sys
import docutils
import pytest
@ -22,13 +21,6 @@ pytest_plugins = 'sphinx.testing.fixtures'
# Exclude 'roots' dirs for pytest test collector
collect_ignore = ['roots']
# Disable Python version-specific
if sys.version_info < (3,):
collect_ignore += ['py3']
if sys.version_info < (3, 5):
collect_ignore += ['py35']
@pytest.fixture(scope='session')
def rootdir():

View File

@ -1,26 +0,0 @@
# -*- coding: utf-8 -*-
"""
py3/test_util_inspect
~~~~~~~~~~~~~~~~~~~~~
Tests util.inspect functions.
:copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
from sphinx.util import inspect
def test_Signature_keyword_only_arguments():
def func1(arg1, arg2, *, arg3=None, arg4=None):
pass
def func2(*, arg3, arg4):
pass
sig = inspect.Signature(func1).format_args()
assert sig == '(arg1, arg2, *, arg3=None, arg4=None)'
sig = inspect.Signature(func2).format_args()
assert sig == '(*, arg3, arg4)'

View File

@ -1,348 +0,0 @@
# -*- coding: utf-8 -*-
"""
test_autodoc
~~~~~~~~~~~~
Test the autodoc extension. This tests mainly the Documenters; the auto
directives are tested in a test source file translated by test_build.
:copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
# "raises" imported for usage by autodoc
import sys
import pytest
import six
from docutils.statemachine import ViewList
from six import StringIO
from sphinx.ext.autodoc import FunctionDocumenter, ALL, Options
from sphinx.testing.util import SphinxTestApp, Struct
from sphinx.util import logging
from sphinx.util import save_traceback # NOQA
app = None
@pytest.fixture(scope='module', autouse=True)
def setup_module(rootdir, sphinx_test_tempdir):
global app
srcdir = sphinx_test_tempdir / 'autodoc-root'
if not srcdir.exists():
(rootdir / 'test-root').copytree(srcdir)
app = SphinxTestApp(srcdir=srcdir)
app.builder.env.app = app
app.builder.env.temp_data['docname'] = 'dummy'
app.connect('autodoc-process-docstring', process_docstring)
app.connect('autodoc-process-signature', process_signature)
app.connect('autodoc-skip-member', skip_member)
yield
app.cleanup()
directive = options = None
@pytest.fixture
def setup_test():
global options, directive
global processed_docstrings, processed_signatures
options = Options(
inherited_members = False,
undoc_members = False,
private_members = False,
special_members = False,
imported_members = False,
show_inheritance = False,
noindex = False,
annotation = None,
synopsis = '',
platform = '',
deprecated = False,
members = [],
member_order = 'alphabetic',
exclude_members = set(),
)
directive = Struct(
env = app.builder.env,
genopt = options,
result = ViewList(),
filename_set = set(),
)
processed_docstrings = []
processed_signatures = []
processed_docstrings = []
processed_signatures = []
def process_docstring(app, what, name, obj, options, lines):
processed_docstrings.append((what, name))
if name == 'bar':
lines.extend(['42', ''])
def process_signature(app, what, name, obj, options, args, retann):
processed_signatures.append((what, name))
if name == 'bar':
return '42', None
def skip_member(app, what, name, obj, skip, options):
if name in ('__special1__', '__special2__'):
return skip
if name.startswith('_'):
return True
if name == 'skipmeth':
return True
@pytest.mark.usefixtures('setup_test')
def test_generate():
logging.setup(app, app._status, app._warning)
def assert_warns(warn_str, objtype, name, **kw):
inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
assert len(directive.result) == 0, directive.result
assert warn_str in app._warning.getvalue()
app._warning.truncate(0)
def assert_works(objtype, name, **kw):
inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
assert directive.result
# print '\n'.join(directive.result)
assert app._warning.getvalue() == ''
del directive.result[:]
def assert_processes(items, objtype, name, **kw):
del processed_docstrings[:]
del processed_signatures[:]
assert_works(objtype, name, **kw)
assert set(processed_docstrings) | set(processed_signatures) == set(items)
def assert_result_contains(item, objtype, name, **kw):
inst = app.registry.documenters[objtype](directive, name)
inst.generate(**kw)
# print '\n'.join(directive.result)
assert app._warning.getvalue() == ''
assert item in directive.result
del directive.result[:]
def assert_order(items, objtype, name, member_order, **kw):
inst = app.registry.documenters[objtype](directive, name)
inst.options.member_order = member_order
inst.generate(**kw)
assert app._warning.getvalue() == ''
items = list(reversed(items))
lineiter = iter(directive.result)
# for line in directive.result:
# if line.strip():
# print repr(line)
while items:
item = items.pop()
for line in lineiter:
if line == item:
break
else: # ran out of items!
assert False, ('item %r not found in result or not in the '
' correct order' % item)
del directive.result[:]
options.members = []
# no module found?
assert_warns("import for autodocumenting 'foobar'",
'function', 'foobar', more_content=None)
# importing
assert_warns("failed to import module 'test_foobar'",
'module', 'test_foobar', more_content=None)
# attributes missing
assert_warns("failed to import function 'foobar' from module 'util'",
'function', 'util.foobar', more_content=None)
# method missing
assert_warns("failed to import method 'Class.foobar' from module 'test_autodoc_py35';",
'method', 'test_autodoc_py35.Class.foobar', more_content=None)
# test auto and given content mixing
directive.env.ref_context['py:module'] = 'test_autodoc_py35'
assert_result_contains(' Function.', 'method', 'Class.meth')
add_content = ViewList()
add_content.append('Content.', '', 0)
assert_result_contains(' Function.', 'method',
'Class.meth', more_content=add_content)
assert_result_contains(' Content.', 'method',
'Class.meth', more_content=add_content)
# test check_module
inst = FunctionDocumenter(directive, 'save_traceback')
inst.generate(check_module=True)
assert len(directive.result) == 0
# assert that exceptions can be documented
assert_works('exception', 'test_autodoc_py35.CustomEx', all_members=True)
assert_works('exception', 'test_autodoc_py35.CustomEx')
# test diverse inclusion settings for members
should = [('class', 'test_autodoc_py35.Class')]
assert_processes(should, 'class', 'Class')
should.extend([('method', 'test_autodoc_py35.Class.meth')])
options.members = ['meth']
options.exclude_members = set(['excludemeth'])
assert_processes(should, 'class', 'Class')
should.extend([('attribute', 'test_autodoc_py35.Class.prop'),
('attribute', 'test_autodoc_py35.Class.descr'),
('attribute', 'test_autodoc_py35.Class.attr'),
('attribute', 'test_autodoc_py35.Class.docattr'),
('attribute', 'test_autodoc_py35.Class.udocattr'),
('attribute', 'test_autodoc_py35.Class.mdocattr'),
('attribute', 'test_autodoc_py35.Class.inst_attr_comment'),
('attribute', 'test_autodoc_py35.Class.inst_attr_inline'),
('attribute', 'test_autodoc_py35.Class.inst_attr_string'),
('method', 'test_autodoc_py35.Class.moore'),
])
if six.PY3 and sys.version_info[:2] >= (3, 5):
should.extend([
('method', 'test_autodoc_py35.Class.do_coroutine'),
])
options.members = ALL
assert_processes(should, 'class', 'Class')
options.undoc_members = True
should.extend((('attribute', 'test_autodoc_py35.Class.skipattr'),
('method', 'test_autodoc_py35.Class.undocmeth'),
('method', 'test_autodoc_py35.Class.roger')))
assert_processes(should, 'class', 'Class')
options.inherited_members = True
should.append(('method', 'test_autodoc_py35.Class.inheritedmeth'))
assert_processes(should, 'class', 'Class')
# test special members
options.special_members = ['__special1__']
should.append(('method', 'test_autodoc_py35.Class.__special1__'))
assert_processes(should, 'class', 'Class')
options.special_members = ALL
should.append(('method', 'test_autodoc_py35.Class.__special2__'))
assert_processes(should, 'class', 'Class')
options.special_members = False
# --- generate fodder ------------
__all__ = ['Class']
#: documentation for the integer
integer = 1
class CustomEx(Exception):
"""My custom exception."""
def f(self):
"""Exception method."""
class CustomDataDescriptor(object):
"""Descriptor class docstring."""
def __init__(self, doc):
self.__doc__ = doc
def __get__(self, obj, type=None):
if obj is None:
return self
return 42
def meth(self):
"""Function."""
return "The Answer"
def _funky_classmethod(name, b, c, d, docstring=None):
"""Generates a classmethod for a class from a template by filling out
some arguments."""
def template(cls, a, b, c, d=4, e=5, f=6):
return a, b, c, d, e, f
from functools import partial
function = partial(template, b=b, c=c, d=d)
function.__name__ = name
function.__doc__ = docstring
return classmethod(function)
class Base(object):
def inheritedmeth(self):
"""Inherited function."""
if six.PY3 and sys.version_info[:2] >= (3, 5):
async def _other_coro_func():
return "run"
class Class(Base):
"""Class to document."""
descr = CustomDataDescriptor("Descriptor instance docstring.")
def meth(self):
"""Function."""
def undocmeth(self):
pass
def skipmeth(self):
"""Method that should be skipped."""
def excludemeth(self):
"""Method that should be excluded."""
# should not be documented
skipattr = 'foo'
#: should be documented -- süß
attr = 'bar'
@property
def prop(self):
"""Property."""
docattr = 'baz'
"""should likewise be documented -- süß"""
udocattr = 'quux'
u"""should be documented as well - süß"""
# initialized to any class imported from another module
mdocattr = StringIO()
"""should be documented as well - süß"""
roger = _funky_classmethod("roger", 2, 3, 4)
moore = _funky_classmethod("moore", 9, 8, 7,
docstring="moore(a, e, f) -> happiness")
def __init__(self, arg):
self.inst_attr_inline = None #: an inline documented instance attr
#: a documented instance attribute
self.inst_attr_comment = None
self.inst_attr_string = None
"""a documented instance attribute"""
def __special1__(self):
"""documented special method"""
def __special2__(self):
# undocumented special method
pass
if six.PY3 and sys.version_info[:2] >= (3, 5):
async def do_coroutine(self):
"""A documented coroutine function"""
attr_coro_result = await _other_coro_func() # NOQA

View File

@ -0,0 +1,8 @@
class AsyncClass:
async def do_coroutine(self):
"""A documented coroutine function"""
attr_coro_result = await _other_coro_func() # NOQA
async def _other_coro_func():
return "run"

View File

@ -1426,8 +1426,24 @@ def test_partialfunction():
assert call_autodoc('module', 'target.partialfunction') == expected
@pytest.mark.skipif(sys.version_info < (3, 4),
reason='functools.partialmethod is available on py34 or above')
@pytest.mark.usefixtures('setup_test')
def test_coroutine():
options = {"members": None}
actual = do_autodoc(app, 'class', 'target.coroutine.AsyncClass', options)
assert list(actual) == [
'',
'.. py:class:: AsyncClass',
' :module: target.coroutine',
'',
' ',
' .. py:method:: AsyncClass.do_coroutine()',
' :module: target.coroutine',
' ',
' A documented coroutine function',
' '
]
@pytest.mark.sphinx('html', testroot='ext-autodoc')
def test_partialmethod(app):
expected = [

View File

@ -33,11 +33,6 @@ def nonascii_srcdir(request, rootdir, sphinx_test_tempdir):
# If supported, build in a non-ASCII source dir
test_name = u'\u65e5\u672c\u8a9e'
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:
srcdir = basedir / test_name
if not srcdir.exists():

View File

@ -10,7 +10,6 @@
"""
import re
import sys
import pytest
@ -82,9 +81,6 @@ def test_texinfo(app, status, warning):
@pytest.mark.sphinx('html', testroot='docutilsconf',
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):
srcdir = path(app.srcdir)
mb_name = u'\u65e5\u672c\u8a9e'

View File

@ -105,13 +105,8 @@ def test_getargspec_bound_methods():
pass
assert expected_unbound == inspect.getargspec(Foo.method)
if PY3 and sys.version_info >= (3, 4, 4):
# On py2, the inspect functions don't properly handle bound
# methods (they include a spurious 'self' argument)
assert expected_bound == inspect.getargspec(bound_method)
# On py2, the inspect functions can't properly handle wrapped
# functions (no __wrapped__ support)
assert expected_bound == inspect.getargspec(wrapped_bound_method)
assert expected_bound == inspect.getargspec(bound_method)
assert expected_bound == inspect.getargspec(wrapped_bound_method)
def test_Signature():
@ -143,10 +138,7 @@ def test_Signature_partial():
p = functools.partial(fun, 10, c=11)
sig = inspect.Signature(p).format_args()
if sys.version_info < (3,):
assert sig == '(b, d=2)'
else:
assert sig == '(b, *, c=11, d=2)'
assert sig == '(b, *, c=11, d=2)'
def test_Signature_methods():
@ -193,16 +185,9 @@ def test_Signature_methods():
# wrapped bound method
sig = inspect.Signature(wrapped_bound_method).format_args()
if sys.version_info < (3,):
assert sig == '(*args, **kwargs)'
elif sys.version_info < (3, 4, 4):
assert sig == '(self, arg1, **kwargs)'
else:
assert sig == '(arg1, **kwargs)'
assert sig == '(arg1, **kwargs)'
@pytest.mark.skipif(sys.version_info < (3, 4),
reason='functools.partialmethod is available on py34 or above')
def test_Signature_partialmethod():
from functools import partialmethod
@ -228,11 +213,9 @@ def test_Signature_partialmethod():
assert sig == '()'
@pytest.mark.skipif(sys.version_info < (3, 4),
reason='type annotation test is available on py34 or above')
def test_Signature_annotations():
from typing_test_data import (
f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, Node)
from typing_test_data import (f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10,
f11, f12, f13, f14, f15, f16, Node)
# Class annotations
sig = inspect.Signature(f0).format_args()
@ -297,6 +280,14 @@ def test_Signature_annotations():
sig = inspect.Signature(f14).format_args()
assert sig == '() -> Any'
# keyword only arguments (1)
sig = inspect.Signature(f15).format_args()
assert sig == '(arg1, arg2, *, arg3=None, arg4=None)'
# keyword only arguments (2)
sig = inspect.Signature(f16).format_args()
assert sig == '(*, arg3, arg4)'
# type hints by string
sig = inspect.Signature(Node.children).format_args()
if (3, 5, 0) <= sys.version_info < (3, 5, 3):
@ -437,10 +428,5 @@ def test_isstaticmethod():
assert inspect.isstaticmethod(Foo.method1, Foo, 'method1') is True
assert inspect.isstaticmethod(Foo.method2, Foo, 'method2') is False
if sys.version_info < (3, 0):
assert inspect.isstaticmethod(Bar.method1, Bar, 'method1') is False
assert inspect.isstaticmethod(Bar.method2, Bar, 'method2') is False
else:
assert inspect.isstaticmethod(Bar.method1, Bar, 'method1') is True
assert inspect.isstaticmethod(Bar.method2, Bar, 'method2') is False
assert inspect.isstaticmethod(Bar.method1, Bar, 'method1') is True
assert inspect.isstaticmethod(Bar.method2, Bar, 'method2') is False

View File

@ -76,6 +76,13 @@ def f14() -> Any:
pass
def f15(arg1, arg2, *, arg3=None, arg4=None):
pass
def f16(*, arg3, arg4):
pass
class Node:
def __init__(self, parent: Optional['Node']) -> None:
pass