mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Merge pull request #5392 from tk0miya/5290_support_egged_source_code
Fix #5290: autodoc: failed to analyze source code in egg package
This commit is contained in:
commit
0970619813
1
CHANGES
1
CHANGES
@ -40,6 +40,7 @@ Bugs fixed
|
|||||||
* autodoc: ImportError is replaced by AttributeError for deeper module
|
* autodoc: ImportError is replaced by AttributeError for deeper module
|
||||||
* #2720, #4034: Incorrect links with ``:download:``, duplicate names, and
|
* #2720, #4034: Incorrect links with ``:download:``, duplicate names, and
|
||||||
parallel builds
|
parallel builds
|
||||||
|
* #5290: autodoc: failed to analyze source code in egg package
|
||||||
|
|
||||||
Testing
|
Testing
|
||||||
--------
|
--------
|
||||||
|
@ -10,6 +10,9 @@
|
|||||||
"""
|
"""
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
|
import re
|
||||||
|
from zipfile import ZipFile
|
||||||
|
|
||||||
from six import iteritems, BytesIO, StringIO
|
from six import iteritems, BytesIO, StringIO
|
||||||
|
|
||||||
from sphinx.errors import PycodeError
|
from sphinx.errors import PycodeError
|
||||||
@ -42,9 +45,23 @@ class ModuleAnalyzer(object):
|
|||||||
obj = cls(f, modname, filename) # type: ignore
|
obj = cls(f, modname, filename) # type: ignore
|
||||||
cls.cache['file', filename] = obj
|
cls.cache['file', filename] = obj
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
raise PycodeError('error opening %r' % filename, err)
|
if '.egg/' in filename:
|
||||||
|
obj = cls.cache['file', filename] = cls.for_egg(filename, modname)
|
||||||
|
else:
|
||||||
|
raise PycodeError('error opening %r' % filename, err)
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def for_egg(cls, filename, modname):
|
||||||
|
# type: (unicode, unicode) -> ModuleAnalyzer
|
||||||
|
eggpath, relpath = re.split('(?<=\\.egg)/', filename)
|
||||||
|
try:
|
||||||
|
with ZipFile(eggpath) as egg:
|
||||||
|
code = egg.read(relpath).decode('utf-8')
|
||||||
|
return cls.for_string(code, modname, filename)
|
||||||
|
except Exception as exc:
|
||||||
|
raise PycodeError('error opening %r' % filename, exc)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def for_module(cls, modname):
|
def for_module(cls, modname):
|
||||||
# type: (str) -> ModuleAnalyzer
|
# type: (str) -> ModuleAnalyzer
|
||||||
|
@ -314,6 +314,11 @@ def get_module_source(modname):
|
|||||||
filename += 'w'
|
filename += 'w'
|
||||||
elif not (lfilename.endswith('.py') or lfilename.endswith('.pyw')):
|
elif not (lfilename.endswith('.py') or lfilename.endswith('.pyw')):
|
||||||
raise PycodeError('source is not a .py file: %r' % filename)
|
raise PycodeError('source is not a .py file: %r' % filename)
|
||||||
|
elif '.egg' in filename:
|
||||||
|
eggpath, _ = re.split('(?<=\\.egg)/', filename)
|
||||||
|
if path.isfile(eggpath):
|
||||||
|
return 'file', filename
|
||||||
|
|
||||||
if not path.isfile(filename):
|
if not path.isfile(filename):
|
||||||
raise PycodeError('source file is not present: %r' % filename)
|
raise PycodeError('source file is not present: %r' % filename)
|
||||||
return 'file', filename
|
return 'file', filename
|
||||||
|
8
tests/roots/test-pycode-egg/conf.py
Normal file
8
tests/roots/test-pycode-egg/conf.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.abspath('sample-0.0.0-py3.7.egg'))
|
||||||
|
master_doc = 'index'
|
||||||
|
extensions = ['sphinx.ext.autodoc']
|
2
tests/roots/test-pycode-egg/index.rst
Normal file
2
tests/roots/test-pycode-egg/index.rst
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
test-pycode-egg
|
||||||
|
===============
|
BIN
tests/roots/test-pycode-egg/sample-0.0.0-py3.7.egg
Normal file
BIN
tests/roots/test-pycode-egg/sample-0.0.0-py3.7.egg
Normal file
Binary file not shown.
8
tests/roots/test-pycode-egg/src/sample.py
Normal file
8
tests/roots/test-pycode-egg/src/sample.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
#: constant on sample.py
|
||||||
|
CONSTANT = 1
|
||||||
|
|
||||||
|
|
||||||
|
def hello(s):
|
||||||
|
print('Hello %s' % s)
|
6
tests/roots/test-pycode-egg/src/setup.py
Normal file
6
tests/roots/test-pycode-egg/src/setup.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from setuptools import setup
|
||||||
|
|
||||||
|
|
||||||
|
setup(name='sample',
|
||||||
|
py_modules=['sample'])
|
@ -1584,3 +1584,26 @@ def test_autodoc_default_options_with_values(app):
|
|||||||
assert ' list of weak references to the object (if defined)' not in actual
|
assert ' list of weak references to the object (if defined)' not in actual
|
||||||
assert ' .. py:method:: CustomIter.snafucate()' not in actual
|
assert ' .. py:method:: CustomIter.snafucate()' not in actual
|
||||||
assert ' Makes this snafucated.' not in actual
|
assert ' Makes this snafucated.' not in actual
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.sphinx('html', testroot='pycode-egg')
|
||||||
|
def test_autodoc_for_egged_code(app):
|
||||||
|
options = {"members": None,
|
||||||
|
"undoc-members": None}
|
||||||
|
actual = do_autodoc(app, 'module', 'sample', options)
|
||||||
|
assert list(actual) == [
|
||||||
|
'',
|
||||||
|
'.. py:module:: sample',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'.. py:data:: CONSTANT',
|
||||||
|
' :module: sample',
|
||||||
|
' :annotation: = 1',
|
||||||
|
'',
|
||||||
|
' constant on sample.py',
|
||||||
|
' ',
|
||||||
|
'',
|
||||||
|
'.. py:function:: hello(s)',
|
||||||
|
' :module: sample',
|
||||||
|
''
|
||||||
|
]
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
from six import PY2
|
from six import PY2
|
||||||
|
|
||||||
@ -47,6 +48,31 @@ def test_ModuleAnalyzer_for_module():
|
|||||||
assert analyzer.encoding == 'utf-8'
|
assert analyzer.encoding == 'utf-8'
|
||||||
|
|
||||||
|
|
||||||
|
def test_ModuleAnalyzer_for_file_in_egg(rootdir):
|
||||||
|
try:
|
||||||
|
path = rootdir / 'test-pycode-egg' / 'sample-0.0.0-py3.7.egg'
|
||||||
|
sys.path.insert(0, path)
|
||||||
|
|
||||||
|
import sample
|
||||||
|
analyzer = ModuleAnalyzer.for_file(sample.__file__, 'sample')
|
||||||
|
docs = analyzer.find_attr_docs()
|
||||||
|
assert docs == {('', 'CONSTANT'): ['constant on sample.py', '']}
|
||||||
|
finally:
|
||||||
|
sys.path.pop(0)
|
||||||
|
|
||||||
|
|
||||||
|
def test_ModuleAnalyzer_for_module_in_egg(rootdir):
|
||||||
|
try:
|
||||||
|
path = rootdir / 'test-pycode-egg' / 'sample-0.0.0-py3.7.egg'
|
||||||
|
sys.path.insert(0, path)
|
||||||
|
|
||||||
|
analyzer = ModuleAnalyzer.for_module('sample')
|
||||||
|
docs = analyzer.find_attr_docs()
|
||||||
|
assert docs == {('', 'CONSTANT'): ['constant on sample.py', '']}
|
||||||
|
finally:
|
||||||
|
sys.path.pop(0)
|
||||||
|
|
||||||
|
|
||||||
def test_ModuleAnalyzer_find_tags():
|
def test_ModuleAnalyzer_find_tags():
|
||||||
code = ('class Foo(object):\n' # line: 1
|
code = ('class Foo(object):\n' # line: 1
|
||||||
' """class Foo!"""\n'
|
' """class Foo!"""\n'
|
||||||
|
Loading…
Reference in New Issue
Block a user