diff --git a/CHANGES b/CHANGES index 95cc16a5a..9d08f84b4 100644 --- a/CHANGES +++ b/CHANGES @@ -19,6 +19,10 @@ Bugs fixed * #5418: Incorrect default path for sphinx-build -d/doctrees files * #5421: autodoc emits deprecation warning for :confval:`autodoc_default_flags` * #5422: lambda object causes PicklingError on storing environment +* #5417: Sphinx fails to build with syntax error in Python 2.7.5 +* #4911: add latexpdf to make.bat for non make-mode +* #5436: Autodoc does not work with enum subclasses with properties/methods +* #5437: autodoc: crashed on modules importing eggs * #5433: latex: ImportError: cannot import name 'DEFAULT_SETTINGS' Testing @@ -36,6 +40,7 @@ Dependencies ``xelatex/lualatex``), instructs ``make latexpdf`` to use :program:`xindy` for general index. Make sure your LaTeX distribution includes it. (refs: #5134) +* LaTeX: ``latexmk`` is required for ``make latexpdf`` on Windows Incompatible changes -------------------- diff --git a/README.rst b/README.rst index 9d9ffba6e..5a566c300 100644 --- a/README.rst +++ b/README.rst @@ -4,7 +4,7 @@ .. image:: https://img.shields.io/pypi/v/sphinx.svg :target: https://pypi.org/project/Sphinx/ - :alt: Package on PyPi + :alt: Package on PyPI .. image:: https://readthedocs.org/projects/sphinx/badge/?version=master :target: http://www.sphinx-doc.org/ diff --git a/sphinx/ext/autodoc/importer.py b/sphinx/ext/autodoc/importer.py index 041589b58..a2280e82b 100644 --- a/sphinx/ext/autodoc/importer.py +++ b/sphinx/ext/autodoc/importer.py @@ -16,7 +16,7 @@ import warnings from collections import namedtuple from types import FunctionType, MethodType, ModuleType -from six import PY2 +from six import PY2, iteritems from sphinx.util import logging from sphinx.util.inspect import isenumclass, safe_getattr @@ -248,6 +248,11 @@ def get_object_members(subject, objpath, attrgetter, analyzer=None): if name not in members: members[name] = Attribute(name, True, value) + superclass = subject.__mro__[1] + for name, value in iteritems(obj_dict): + if name not in superclass.__dict__: + members[name] = Attribute(name, True, value) + # other members for name in dir(subject): try: diff --git a/sphinx/ext/doctest.py b/sphinx/ext/doctest.py index fa6cbed6d..c35b3d284 100644 --- a/sphinx/ext/doctest.py +++ b/sphinx/ext/doctest.py @@ -153,7 +153,7 @@ class TestDirective(SphinxDirective): if self.name == 'doctest' and 'pyversion' in self.options: try: spec = self.options['pyversion'] - python_version = '.'.join(str(v) for v in sys.version_info[:3]) + python_version = '.'.join([str(v) for v in sys.version_info[:3]]) if not is_allowed_version(spec, python_version): flag = doctest.OPTIONFLAGS_BY_NAME['SKIP'] node['options'][flag] = True # Skip the test diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py index 5cbe4ff66..e3f8dc275 100644 --- a/sphinx/ext/intersphinx.py +++ b/sphinx/ext/intersphinx.py @@ -427,6 +427,6 @@ def inspect_main(argv): if __name__ == '__main__': import logging # type: ignore - logging.basicConfig() + logging.basicConfig() # type: ignore inspect_main(argv=sys.argv[1:]) # type: ignore diff --git a/sphinx/pycode/__init__.py b/sphinx/pycode/__init__.py index a97903a27..3e70e28ce 100644 --- a/sphinx/pycode/__init__.py +++ b/sphinx/pycode/__init__.py @@ -32,7 +32,7 @@ class ModuleAnalyzer(object): def for_string(cls, string, modname, srcname=''): # type: (unicode, unicode, unicode) -> ModuleAnalyzer if isinstance(string, bytes): - return cls(BytesIO(string), modname, srcname) + return cls(BytesIO(string), modname, srcname) # type: ignore return cls(StringIO(string), modname, srcname, decoded=True) # type: ignore @classmethod diff --git a/sphinx/templates/quickstart/make.bat_t b/sphinx/templates/quickstart/make.bat_t index cd96c0736..c162da36d 100644 --- a/sphinx/templates/quickstart/make.bat_t +++ b/sphinx/templates/quickstart/make.bat_t @@ -31,6 +31,7 @@ if "%1" == "help" ( echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. latexpdf to make LaTeX files and run them through platex/dvipdfmx echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files diff --git a/sphinx/testing/path.py b/sphinx/testing/path.py index 585933499..1c9781dea 100644 --- a/sphinx/testing/path.py +++ b/sphinx/testing/path.py @@ -30,7 +30,7 @@ class path(text_type): # type: (unicode, unicode, unicode) -> path if isinstance(s, str): s = s.decode(encoding, errors) - return text_type.__new__(cls, s) + return text_type.__new__(cls, s) # type: ignore return text_type.__new__(cls, s) # type: ignore @property diff --git a/sphinx/util/__init__.py b/sphinx/util/__init__.py index 0c0c22c9c..2da685a97 100644 --- a/sphinx/util/__init__.py +++ b/sphinx/util/__init__.py @@ -314,8 +314,9 @@ def get_module_source(modname): filename += 'w' elif not (lfilename.endswith('.py') or lfilename.endswith('.pyw')): raise PycodeError('source is not a .py file: %r' % filename) - elif '.egg' in filename: - eggpath, _ = re.split('(?<=\\.egg)/', filename) + elif ('.egg' + os.path.sep) in filename: + pat = '(?<=\\.egg)' + re.escape(os.path.sep) + eggpath, _ = re.split(pat, filename, 1) if path.isfile(eggpath): return 'file', filename diff --git a/sphinx/util/images.py b/sphinx/util/images.py index c385c19a9..d5e0db2d7 100644 --- a/sphinx/util/images.py +++ b/sphinx/util/images.py @@ -73,7 +73,7 @@ def get_image_size(filename): def guess_mimetype_for_stream(stream, default=None): # type: (IO, unicode) -> unicode - imgtype = imghdr.what(stream) + imgtype = imghdr.what(stream) # type: ignore if imgtype: return 'image/' + imgtype else: @@ -141,4 +141,4 @@ def test_svg(h, f): # install test_svg() to imghdr # refs: https://docs.python.org/3.6/library/imghdr.html#imghdr.tests -imghdr.tests.append(test_svg) +imghdr.tests.append(test_svg) # type: ignore diff --git a/sphinx/util/osutil.py b/sphinx/util/osutil.py index 986171293..4c75009ee 100644 --- a/sphinx/util/osutil.py +++ b/sphinx/util/osutil.py @@ -235,7 +235,7 @@ def abspath(pathdir): try: pathdir = pathdir.decode(fs_encoding) except UnicodeDecodeError: - raise UnicodeDecodeError('multibyte filename not supported on ' + raise UnicodeDecodeError('multibyte filename not supported on ' # type: ignore 'this filesystem encoding ' '(%r)' % fs_encoding) return pathdir diff --git a/sphinx/util/requests.py b/sphinx/util/requests.py index b6c0d1ab8..65ddaedb7 100644 --- a/sphinx/util/requests.py +++ b/sphinx/util/requests.py @@ -34,7 +34,7 @@ except ImportError: from urllib3.exceptions import InsecureRequestWarning # type: ignore except ImportError: # for requests < 2.4.0 - InsecureRequestWarning = None + InsecureRequestWarning = None # type: ignore try: from requests.packages.urllib3.exceptions import InsecurePlatformWarning @@ -44,7 +44,7 @@ except ImportError: from urllib3.exceptions import InsecurePlatformWarning # type: ignore except ImportError: # for requests < 2.4.0 - InsecurePlatformWarning = None + InsecurePlatformWarning = None # type: ignore # try to load requests[security] (but only if SSL is available) try: diff --git a/tests/roots/test-ext-autodoc/target/__init__.py b/tests/roots/test-ext-autodoc/target/__init__.py index 201e84efd..9bb50bca9 100644 --- a/tests/roots/test-ext-autodoc/target/__init__.py +++ b/tests/roots/test-ext-autodoc/target/__init__.py @@ -223,19 +223,6 @@ class InstAttCls(object): """Docstring for instance attribute InstAttCls.ia2.""" -class EnumCls(enum.Enum): - """ - this is enum class - """ - - #: doc for val1 - val1 = 12 - val2 = 23 #: doc for val2 - val3 = 34 - """doc for val3""" - val4 = 34 - - class CustomIter(object): def __init__(self): """Create a new `CustomIter`.""" diff --git a/tests/roots/test-ext-autodoc/target/enum.py b/tests/roots/test-ext-autodoc/target/enum.py new file mode 100644 index 000000000..31e7c6ccd --- /dev/null +++ b/tests/roots/test-ext-autodoc/target/enum.py @@ -0,0 +1,19 @@ +from __future__ import absolute_import +import enum + + +class EnumCls(enum.Enum): + """ + this is enum class + """ + + #: doc for val1 + val1 = 12 + val2 = 23 #: doc for val2 + val3 = 34 + """doc for val3""" + val4 = 34 + + def say_hello(self): + """a method says hello to you.""" + pass diff --git a/tests/test_autodoc.py b/tests/test_autodoc.py index e2dc37c56..554ad180f 100644 --- a/tests/test_autodoc.py +++ b/tests/test_autodoc.py @@ -829,7 +829,6 @@ def test_autodoc_ignore_module_all(app): '.. py:class:: CustomDataDescriptor2(doc)', '.. py:class:: CustomDataDescriptorMeta', '.. py:class:: CustomDict', - '.. py:class:: EnumCls', '.. py:class:: InstAttCls()', '.. py:class:: Outer', ' .. py:class:: Outer.Inner', @@ -1263,48 +1262,54 @@ def test_instance_attributes(app): def test_enum_class(app): options = {"members": None, "undoc-members": True} - actual = do_autodoc(app, 'class', 'target.EnumCls', options) + actual = do_autodoc(app, 'class', 'target.enum.EnumCls', options) assert list(actual) == [ '', '.. py:class:: EnumCls', - ' :module: target', + ' :module: target.enum', '', ' this is enum class', ' ', ' ', + ' .. py:method:: EnumCls.say_hello()', + ' :module: target.enum', + ' ', + ' a method says hello to you.', + ' ', + ' ', ' .. py:attribute:: EnumCls.val1', - ' :module: target', + ' :module: target.enum', ' :annotation: = 12', ' ', ' doc for val1', ' ', ' ', ' .. py:attribute:: EnumCls.val2', - ' :module: target', + ' :module: target.enum', ' :annotation: = 23', ' ', ' doc for val2', ' ', ' ', ' .. py:attribute:: EnumCls.val3', - ' :module: target', + ' :module: target.enum', ' :annotation: = 34', ' ', ' doc for val3', ' ', ' ', ' .. py:attribute:: EnumCls.val4', - ' :module: target', + ' :module: target.enum', ' :annotation: = 34', ' ' ] # checks for an attribute of EnumClass - actual = do_autodoc(app, 'attribute', 'target.EnumCls.val1') + actual = do_autodoc(app, 'attribute', 'target.enum.EnumCls.val1') assert list(actual) == [ '', '.. py:attribute:: EnumCls.val1', - ' :module: target', + ' :module: target.enum', ' :annotation: = 12', '', ' doc for val1', @@ -1473,7 +1478,7 @@ def test_merge_autodoc_default_flags2(app): @pytest.mark.sphinx('html', testroot='ext-autodoc') def test_autodoc_default_options(app): # no settings - actual = do_autodoc(app, 'class', 'target.EnumCls') + actual = do_autodoc(app, 'class', 'target.enum.EnumCls') assert ' .. py:attribute:: EnumCls.val1' not in actual assert ' .. py:attribute:: EnumCls.val4' not in actual actual = do_autodoc(app, 'class', 'target.CustomIter') @@ -1481,7 +1486,7 @@ def test_autodoc_default_options(app): # with :members: app.config.autodoc_default_options = {'members': None} - actual = do_autodoc(app, 'class', 'target.EnumCls') + actual = do_autodoc(app, 'class', 'target.enum.EnumCls') assert ' .. py:attribute:: EnumCls.val1' in actual assert ' .. py:attribute:: EnumCls.val4' not in actual @@ -1490,7 +1495,7 @@ def test_autodoc_default_options(app): 'members': None, 'undoc-members': None, } - actual = do_autodoc(app, 'class', 'target.EnumCls') + actual = do_autodoc(app, 'class', 'target.enum.EnumCls') assert ' .. py:attribute:: EnumCls.val1' in actual assert ' .. py:attribute:: EnumCls.val4' in actual @@ -1516,7 +1521,7 @@ def test_autodoc_default_options(app): 'members': None, 'exclude-members': None, } - actual = do_autodoc(app, 'class', 'target.EnumCls') + actual = do_autodoc(app, 'class', 'target.enum.EnumCls') assert ' .. py:attribute:: EnumCls.val1' in actual assert ' .. py:attribute:: EnumCls.val4' not in actual app.config.autodoc_default_options = { @@ -1540,7 +1545,7 @@ def test_autodoc_default_options(app): def test_autodoc_default_options_with_values(app): # with :members: app.config.autodoc_default_options = {'members': 'val1,val2'} - actual = do_autodoc(app, 'class', 'target.EnumCls') + actual = do_autodoc(app, 'class', 'target.enum.EnumCls') assert ' .. py:attribute:: EnumCls.val1' in actual assert ' .. py:attribute:: EnumCls.val2' in actual assert ' .. py:attribute:: EnumCls.val3' not in actual @@ -1564,7 +1569,7 @@ def test_autodoc_default_options_with_values(app): 'members': None, 'exclude-members': 'val1' } - actual = do_autodoc(app, 'class', 'target.EnumCls') + actual = do_autodoc(app, 'class', 'target.enum.EnumCls') assert ' .. py:attribute:: EnumCls.val1' not in actual assert ' .. py:attribute:: EnumCls.val2' in actual assert ' .. py:attribute:: EnumCls.val3' in actual