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:
Takeshi KOMIYA 2018-09-08 22:15:14 +09:00 committed by GitHub
commit 0970619813
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 97 additions and 1 deletions

View File

@ -40,6 +40,7 @@ Bugs fixed
* autodoc: ImportError is replaced by AttributeError for deeper module
* #2720, #4034: Incorrect links with ``:download:``, duplicate names, and
parallel builds
* #5290: autodoc: failed to analyze source code in egg package
Testing
--------

View File

@ -10,6 +10,9 @@
"""
from __future__ import print_function
import re
from zipfile import ZipFile
from six import iteritems, BytesIO, StringIO
from sphinx.errors import PycodeError
@ -42,9 +45,23 @@ class ModuleAnalyzer(object):
obj = cls(f, modname, filename) # type: ignore
cls.cache['file', filename] = obj
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
@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
def for_module(cls, modname):
# type: (str) -> ModuleAnalyzer

View File

@ -314,6 +314,11 @@ 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)
if path.isfile(eggpath):
return 'file', filename
if not path.isfile(filename):
raise PycodeError('source file is not present: %r' % filename)
return 'file', filename

View 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']

View File

@ -0,0 +1,2 @@
test-pycode-egg
===============

Binary file not shown.

View File

@ -0,0 +1,8 @@
# -*- coding: utf-8 -*-
#: constant on sample.py
CONSTANT = 1
def hello(s):
print('Hello %s' % s)

View File

@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
from setuptools import setup
setup(name='sample',
py_modules=['sample'])

View File

@ -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 ' .. py:method:: CustomIter.snafucate()' 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',
''
]

View File

@ -10,6 +10,7 @@
"""
import os
import sys
from six import PY2
@ -47,6 +48,31 @@ def test_ModuleAnalyzer_for_module():
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():
code = ('class Foo(object):\n' # line: 1
' """class Foo!"""\n'