Skip to content
This repository has been archived by the owner on May 25, 2022. It is now read-only.

Commit

Permalink
Merge pull request #148 from embray/sphinx-1.3-fixes
Browse files Browse the repository at this point in the history
sphinx.ext.automodsumm broken with Sphinx 1.3
  • Loading branch information
embray committed Mar 30, 2015
2 parents 6bdb8a4 + c9d588f commit be5d12d
Show file tree
Hide file tree
Showing 8 changed files with 138 additions and 26 deletions.
3 changes: 3 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ astropy-helpers Changelog
logo and linkout image, falling back to PNGs for browsers that
support it. [#151]

- Various fixes enabling the astropy-helpers Sphinx build command and
Sphinx extensions to work with Sphinx 1.3. [#148]


1.0.1 (2015-03-04)
------------------
Expand Down
9 changes: 9 additions & 0 deletions astropy_helpers/commands/build_sphinx.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@
from distutils import log
from distutils.cmd import DistutilsOptionError

import sphinx
from sphinx.setup_command import BuildDoc as SphinxBuildDoc

from ..utils import minversion


PY3 = sys.version_info[0] >= 3

Expand Down Expand Up @@ -146,6 +149,12 @@ def run(self):
subproccode[i] = repr(val)
subproccode = ''.join(subproccode)

# This is a quick gross hack, but it ensures that the code grabbed from
# SphinxBuildDoc.run will work in Python 2 if it uses the print
# function
if minversion(sphinx, '1.3'):
subproccode = 'from __future__ import print_function' + subproccode

if self.no_intersphinx:
# the confoverrides variable in sphinx.setup_command.BuildDoc can
# be used to override the conf.py ... but this could well break
Expand Down
17 changes: 10 additions & 7 deletions astropy_helpers/sphinx/ext/autodoc_enhancements.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,16 @@ def type_object_attrgetter(obj, attr, *defargs):
of autodoc.
"""

if attr in obj.__dict__ and isinstance(obj.__dict__[attr], property):
# Note, this should only be used for properties--for any other type of
# descriptor (classmethod, for example) this can mess up existing
# expectcations of what getattr(cls, ...) returns
return obj.__dict__[attr]
else:
return getattr(obj, attr, *defargs)
for base in obj.__mro__:
if attr in base.__dict__:
if isinstance(base.__dict__[attr], property):
# Note, this should only be used for properties--for any other
# type of descriptor (classmethod, for example) this can mess
# up existing expectations of what getattr(cls, ...) returns
return base.__dict__[attr]
break

return getattr(obj, attr, *defargs)


def setup(app):
Expand Down
24 changes: 19 additions & 5 deletions astropy_helpers/sphinx/ext/automodapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@

from .utils import find_mod_objs

if sys.version_info[0] == 3:
text_type = str
else:
text_type = unicode


automod_templ_modheader = """
{modname} {pkgormod}
Expand Down Expand Up @@ -296,18 +301,24 @@ def automodapi_replace(sourcestr, app, dotoctree=True, docname=None,
if app.config.automodapi_writereprocessed:
# sometimes they are unicode, sometimes not, depending on how
# sphinx has processed things
if isinstance(newsourcestr, unicode):
if isinstance(newsourcestr, text_type):
ustr = newsourcestr
else:
ustr = newsourcestr.decode(app.config.source_encoding)

if docname is None:
with open(os.path.join(app.srcdir, 'unknown.automodapi'), 'a') as f:
f.write('\n**NEW DOC**\n\n')
f.write(ustr.encode('utf8'))
f.write(ustr)
else:
with open(os.path.join(app.srcdir, docname + '.automodapi'), 'w') as f:
f.write(ustr.encode('utf8'))
env = app.builder.env
# Determine the filename associated with this doc (specifically
# the extension)
filename = docname + os.path.splitext(env.doc2path(docname))[1]
filename += '.automodapi'

with open(os.path.join(app.srcdir, filename), 'w') as f:
f.write(ustr)

return newsourcestr
else:
Expand All @@ -330,8 +341,11 @@ def _mod_info(modname, toskip=[], onlylocals=True):
break

# find_mod_objs has already imported modname
# TODO: There is probably a cleaner way to do this, though this is pretty
# reliable for all Python versions for most cases that we care about.
pkg = sys.modules[modname]
ispkg = '__init__.' in os.path.split(pkg.__name__)[1]
ispkg = (hasattr(pkg, '__file__') and isinstance(pkg.__file__, str) and
os.path.split(pkg.__file__)[1].startswith('__init__.py'))

return ispkg, hascls, hasfunc

Expand Down
33 changes: 23 additions & 10 deletions astropy_helpers/sphinx/ext/automodsumm.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,18 +172,23 @@ def run(self):

self.content = cont

#for some reason, even though ``currentmodule`` is substituted in, sphinx
#doesn't necessarily recognize this fact. So we just force it
#internally, and that seems to fix things
# for some reason, even though ``currentmodule`` is substituted in,
# sphinx doesn't necessarily recognize this fact. So we just force
# it internally, and that seems to fix things
env.temp_data['py:module'] = modname

#can't use super because Sphinx/docutils has trouble
#return super(Autosummary,self).run()
# can't use super because Sphinx/docutils has trouble return
# super(Autosummary,self).run()
nodelist.extend(Autosummary.run(self))

return self.warnings + nodelist
finally: # has_content = False for the Automodsumm
self.content = []

def get_items(self, names):
self.genopt['imported-members'] = True
return Autosummary.get_items(self, names)


#<-------------------automod-diagram stuff------------------------------------>
class Automoddiagram(InheritanceDiagram):
Expand Down Expand Up @@ -220,10 +225,12 @@ def run(self):
#<---------------------automodsumm generation stuff--------------------------->
def process_automodsumm_generation(app):
env = app.builder.env
ext = app.config.source_suffix

filestosearch = [x + ext for x in env.found_docs
if os.path.isfile(env.doc2path(x))]\
filestosearch = []
for docname in env.found_docs:
filename = env.doc2path(docname)
if os.path.isfile(filename):
filestosearch.append(docname + os.path.splitext(filename)[1])

liness = []
for sfn in filestosearch:
Expand All @@ -238,10 +245,11 @@ def process_automodsumm_generation(app):
f.write('\n')

for sfn, lines in zip(filestosearch, liness):
suffix = os.path.splitext(sfn)[1]
if len(lines) > 0:
generate_automodsumm_docs(lines, sfn, builder=app.builder,
warn=app.warn, info=app.info,
suffix=app.config.source_suffix,
suffix=suffix,
base_path=app.srcdir)

#_automodsummrex = re.compile(r'^(\s*)\.\. automodsumm::\s*([A-Za-z0-9_.]+)\s*'
Expand Down Expand Up @@ -281,14 +289,16 @@ def automodsumm_to_autosummary_lines(fn, app):
"""

fullfn = os.path.join(app.builder.env.srcdir, fn)

with open(fullfn) as fr:
if 'astropy_helpers.sphinx.ext.automodapi' in app._extensions:
from astropy_helpers.sphinx.ext.automodapi import automodapi_replace
# Must do the automodapi on the source to get the automodsumm
# that might be in there
filestr = automodapi_replace(fr.read(), app, True, fn, False)
docname = os.path.splitext(fn)[0]
filestr = automodapi_replace(fr.read(), app, True, docname, False)
else:
filestr = fr.read()

Expand Down Expand Up @@ -353,6 +363,9 @@ def automodsumm_to_autosummary_lines(fn, app):
continue
newlines.append(allindent + nm)

# add one newline at the end of the autosummary block
newlines.append('')

return newlines


Expand Down
6 changes: 4 additions & 2 deletions astropy_helpers/sphinx/ext/tests/test_automodsumm.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ def warn(self, msg, loc):
automodsumm_to_autosummary_lines
generate_automodsumm_docs
process_automodsumm_generation
setup"""
setup
"""


def test_ams_to_asmry(tmpdir):
Expand Down Expand Up @@ -97,7 +98,8 @@ def test_ams_to_asmry(tmpdir):
.. autosummary::
:p:
pilot"""
pilot
"""


def test_ams_cython(tmpdir, cython_testpackage):
Expand Down
5 changes: 3 additions & 2 deletions astropy_helpers/sphinx/ext/viewcode.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from sphinx import addnodes
from sphinx.locale import _
from sphinx.pycode import ModuleAnalyzer
from sphinx.util.inspect import safe_getattr
from sphinx.util.nodes import make_refnode

import sys
Expand Down Expand Up @@ -51,12 +52,12 @@ def get_full_modname(modname, attribute):
value = module
for attr in attribute.split('.'):
if attr:
value = getattr(value, attr)
value = safe_getattr(value, attr)
except AttributeError:
app.warn('Didn\'t find %s in %s' % (attribute, module.__name__))
return None
else:
return getattr(value, '__module__', None)
return safe_getattr(value, '__module__', None)


def has_tag(modname, fullname, docname, refname):
Expand Down
67 changes: 67 additions & 0 deletions astropy_helpers/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -592,3 +592,70 @@ def delete(self):
delattr(self, private_name)

return property(get, set, delete)


def minversion(module, version, inclusive=True, version_path='__version__'):
"""
Returns `True` if the specified Python module satisfies a minimum version
requirement, and `False` if not.
By default this uses `pkg_resources.parse_version` to do the version
comparison if available. Otherwise it falls back on
`distutils.version.LooseVersion`.
Parameters
----------
module : module or `str`
An imported module of which to check the version, or the name of
that module (in which case an import of that module is attempted--
if this fails `False` is returned).
version : `str`
The version as a string that this module must have at a minimum (e.g.
``'0.12'``).
inclusive : `bool`
The specified version meets the requirement inclusively (i.e. ``>=``)
as opposed to strictly greater than (default: `True`).
version_path : `str`
A dotted attribute path to follow in the module for the version.
Defaults to just ``'__version__'``, which should work for most Python
modules.
Examples
--------
>>> import astropy
>>> minversion(astropy, '0.4.4')
True
"""

if isinstance(module, types.ModuleType):
module_name = module.__name__
elif isinstance(module, six.string_types):
module_name = module
try:
module = resolve_name(module_name)
except ImportError:
return False
else:
raise ValueError('module argument must be an actual imported '
'module, or the import name of the module; '
'got {0!r}'.format(module))

if '.' not in version_path:
have_version = getattr(module, version_path)
else:
have_version = resolve_name('.'.join([module.__name__, version_path]))

try:
from pkg_resources import parse_version
except ImportError:
from distutils.version import LooseVersion as parse_version

if inclusive:
return parse_version(have_version) >= parse_version(version)
else:
return parse_version(have_version) > parse_version(version)

0 comments on commit be5d12d

Please sign in to comment.