Skip to content

Various modifications for ProPlot #91

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 19 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*.so
*.pyd
__pycache__
.vimsession

# Ignore .c files by default to avoid including generated code. If you want to
# add a non-generated .c extension, use `git add -f filename.c`.
Expand Down
9 changes: 9 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ available as a standalone package since it can be used for any other
package. The documentation can be found on
`ReadTheDocs <http://sphinx-automodapi.readthedocs.io/en/latest/>`_.

Proplot modifications
---------------------
I forked this repo for use with my `proplot <https://github.com/lukelbd/proplot>`__ project and added the following features:

* Skip over class methods that are public, but do *not* have their own ``__doc__`` attributes. This prevents inheriting and displaying documentation from external projects, namely matplotlib.
* Include ``__getitem__``, ``__getattr__``, ``__setitem__``, and ``__setattr__`` in the list of builtin methods that are *not* ignored by the documentation generator. I use these to document some dictionary/list subclasses.
* Give class methods and attributes their own stub pages, instead of putting all class methods and attributes on a single page. This also required adding the new files to ``env.found_docs`` and reordering the event hooks in ``automodsumm.py`` so that ``sphinx_automodapi`` is called before ``autosummary``. This way ``autosummary`` sees the new class pages and can build the appropriate stubs.


Running tests
-------------

Expand Down
2 changes: 2 additions & 0 deletions hooks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Git hooks
When you clone this repo, copy the `post-commit` script to `.git/hooks`. This hook tags the commit with a version number if the version string in `__init__.py` was changed.
50 changes: 50 additions & 0 deletions hooks/post-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/usr/bin/env bash
#-----------------------------------------------------------------------------#
# Post commit hook that looks for __version__ variable in __init__.py file,
# compares it to the current tag, and updates the commit if the __version__ was
# also updated. Copy to .git/hooks/post-commit to add this to any project.
# See also: https://stackoverflow.com/a/27332476/4970632
# See also: https://stackoverflow.com/a/46972376/4970632
# This is currently very lightweight and assumes workflow where users
# manually increment tag number, which is probably appropriate
#-----------------------------------------------------------------------------#
# Helper
raise() {
echo "Error: $@"
exit 1
}

# Bail if we are in middle of rebase
base=$(git rev-parse --show-toplevel)
[ $? -ne 0 ] && raise "Not in git repository."
[ -d $base/.git/rebase-merge ] && exit 0

# Get head dir
init=($(git ls-files | grep __init__.py | grep -v 'tests'))
[ ${#init[@]} -eq 0 ] && raise "__init__.py not found."
[ ${#init[@]} -gt 1 ] && raise "Multiple candidates for __init__.py: ${init[@]}"

# Get version string
version=$(cat $init | grep -E '^version|^__version')
[ -z "$version" ] && raise "$init version string not found."
[ $(echo "$version" | wc -l) -gt 1 ] && raise "Ambiguous version string in $init."
version=$(echo "$version" | awk -F"['\"]" '{print $2}') # first string on line

# Prompt user action
# NOTE: Currently git suppresses interactivity but that's fine, just means
# default action of adding tag is taken
tag=$(git describe --tags $(git rev-list --tags --max-count=1))
if [ $? -ne 0 ] || [ "$tag" != "v$version" ]; then
while true; do
read -r -p "Increment tag from $tag --> v$version? ([y]/n) " response
if [ -n "$response" ] && ! [[ "$response" =~ ^[NnYy]$ ]]; then
echo "Invalid response."
continue # invalid, so try again
fi
if ! [[ "$response" =~ ^[Nn]$ ]]; then
git tag "v$version"
[ $? -eq 0 ] && echo "Incremented tag from $tag --> v$version"
fi
break
done
fi
4 changes: 2 additions & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ name = sphinx-automodapi
version = attr:sphinx_automodapi.__version__
description = Sphinx extension for auto-generating API documentation for entire modules
long_description = file:README.rst
author = The Astropy Developers
author_email = [email protected]
author = Repo from The Astropy Developers, forked by Luke Davis
author_email = [email protected], [email protected]
license = BSD 3-Clause License
url = http://astropy.org
classifiers =
Expand Down
2 changes: 1 addition & 1 deletion sphinx_automodapi/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '0.13.dev0'
__version__ = '0.6.proplot-mods'
2 changes: 1 addition & 1 deletion sphinx_automodapi/automodapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,7 @@ def setup(app):
from . import automodsumm
app.setup_extension(automodsumm.__name__)

app.connect('source-read', process_automodapi)
app.connect('source-read', process_automodapi, priority=100)

app.add_config_value('automodapi_inheritance_diagram', True, True)
app.add_config_value('automodapi_toctreedirnm', 'api', True)
Expand Down
55 changes: 42 additions & 13 deletions sphinx_automodapi/automodsumm.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@ class members that are inherited from a base class. This value can be
__all__ = ['Automoddiagram', 'Automodsumm', 'automodsumm_to_autosummary_lines',
'generate_automodsumm_docs', 'process_automodsumm_generation']

api_ignore_methods = []
api_class_methods = [
'__init__', '__call__', # default
'__getitem__', '__setitem__', '__setattr__', '__getattr__', # custom
]

def _str_list_converter(argument):
"""
Expand Down Expand Up @@ -263,13 +268,18 @@ def process_automodsumm_generation(app):
f.write(l)
f.write('\n')

logger = logging.getLogger(__name__)
for sfn, lines in zip(filestosearch, liness):
suffix = os.path.splitext(sfn)[1]
if len(lines) > 0:
generate_automodsumm_docs(
new_files = generate_automodsumm_docs(
lines, sfn, app=app, builder=app.builder,
suffix=suffix, base_path=app.srcdir,
inherited_members=app.config.automodsumm_inherited_members)
# logger.info('New files: ' + ', '.join(os.path.basename(file)
# for file in new_files))
for f in new_files:
env.found_docs.add(env.path2doc(f))


# _automodsummrex = re.compile(r'^(\s*)\.\. automodsumm::\s*([A-Za-z0-9_.]+)\s*'
Expand Down Expand Up @@ -431,10 +441,17 @@ def generate_automodsumm_docs(lines, srcfn, app=None, suffix='.rst',
# Create our own templating environment - here we use Astropy's
# templates rather than the default autosummary templates, in order to
# allow docstrings to be shown for methods.
template_dirs = [os.path.join(os.path.dirname(__file__), 'templates'),
os.path.join(base_path, '_templates')]
local_dir = os.path.join(os.path.dirname(__file__), 'templates')
default_dir = os.path.join(base_path, '_templates')
template_dirs = [local_dir, default_dir]
if builder is not None:
# allow the user to override the templates
# also add to the templates_path
# TODO: messes up usage for some users?
local_dir_full = os.path.join(local_dir, 'autosummary_core')
templates_path = builder.config.templates_path
if local_dir_full not in templates_path:
templates_path.insert(0, local_dir_full)
template_loader = BuiltinTemplateLoader()
template_loader.init(builder, dirs=template_dirs)
else:
Expand Down Expand Up @@ -487,11 +504,11 @@ def generate_automodsumm_docs(lines, srcfn, app=None, suffix='.rst',
fn = os.path.join(path, name + suffix)

# skip it if it exists
# always add to new_files so we can add them to env.found_docs
new_files.append(fn)
if os.path.isfile(fn):
continue

new_files.append(fn)

f = open(fn, 'w')

try:
Expand All @@ -512,7 +529,14 @@ def get_members_mod(obj, typ, include_public=[]):
typ = None -> all
"""
items = []
# Add back? Seems fragile to rely on automodapi to ignore
# random variables you declare or classes you import on the
# top-level module. Better to use the :skip: directive *or*
# omit objects from __all__.
for name in dir(obj):
# docstring = getattr(safe_getattr(obj, name), '__doc__')
# if not docstring and typ in ('function', 'class', 'exception'):
# continue
try:
documenter = get_documenter(app, safe_getattr(obj, name), obj)
except AttributeError:
Expand All @@ -523,7 +547,7 @@ def get_members_mod(obj, typ, include_public=[]):
if x in include_public or not x.startswith('_')]
return public, items

def get_members_class(obj, typ, include_public=[],
def get_members_class(obj, typ, include_public=[], exclude_public=[],
include_base=False):
"""
typ = None -> all
Expand Down Expand Up @@ -552,19 +576,24 @@ def get_members_class(obj, typ, include_public=[],
else:
names = getattr(obj, '__dict__').keys()

# Modification here, only document something if it has a
# docstring! Do not inherit if empty, similar to
# :inherited-members: option
for name in names:
try:
documenter = get_documenter(app, safe_getattr(obj, name), obj)
except AttributeError:
continue
if documenter.objtype == 'method' and not getattr(safe_getattr(obj, name), '__doc__', ''):
continue
if typ is None or documenter.objtype == typ:
items.append(name)
elif typ == 'attribute' and documenter.objtype == 'property':
# In Sphinx 2.0 and above, properties have a separate
# objtype, but we treat them the same here.
items.append(name)
public = [x for x in items
if x in include_public or not x.startswith('_')]
public = [x for x in items if x not in exclude_public and
(x in include_public or not x.startswith('_'))]
return public, items

ns = {}
Expand All @@ -585,17 +614,16 @@ def get_members_class(obj, typ, include_public=[],
# use default value
include_base = inherited_members

api_class_methods = ['__init__', '__call__']
ns['members'] = get_members_class(obj, None,
include_base=include_base)
ns['methods'], ns['all_methods'] = \
get_members_class(obj, 'method', api_class_methods,
get_members_class(obj, 'method', api_class_methods, api_ignore_methods,
include_base=include_base)
ns['attributes'], ns['all_attributes'] = \
get_members_class(obj, 'attribute',
include_base=include_base)
ns['methods'].sort()
ns['attributes'].sort()
ns['methods'] = sorted(ns['methods'])
ns['attributes'] = sorted(ns['attributes'])

parts = name.split('.')
if doc.objtype in ('method', 'attribute'):
Expand Down Expand Up @@ -649,6 +677,7 @@ def get_members_class(obj, typ, include_public=[],
finally:
f.close()

return new_files

def setup(app):

Expand All @@ -663,7 +692,7 @@ def setup(app):

app.add_directive('automod-diagram', Automoddiagram)
app.add_directive('automodsumm', Automodsumm)
app.connect('builder-inited', process_automodsumm_generation)
app.connect('builder-inited', process_automodsumm_generation, priority=100)

app.add_config_value('automodsumm_writereprocessed', False, True)
app.add_config_value('automodsumm_inherited_members', False, 'env')
Expand Down
27 changes: 4 additions & 23 deletions sphinx_automodapi/templates/autosummary_core/class.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
.. rubric:: Attributes Summary

.. autosummary::
:toctree:
:template: base.rst
{% for item in attributes %}
~{{ name }}.{{ item }}
{%- endfor %}
Expand All @@ -33,33 +35,12 @@
.. rubric:: Methods Summary

.. autosummary::
:toctree:
:template: base.rst
{% for item in methods %}
~{{ name }}.{{ item }}
{%- endfor %}

{% endif %}
{% endblock %}

{% block attributes_documentation %}
{% if attributes %}

.. rubric:: Attributes Documentation

{% for item in attributes %}
.. autoattribute:: {{ item }}
{%- endfor %}

{% endif %}
{% endblock %}

{% block methods_documentation %}
{% if methods %}

.. rubric:: Methods Documentation

{% for item in methods %}
.. automethod:: {{ item }}
{%- endfor %}

{% endif %}
{% endblock %}