1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
|
# Licensed under a 3-clause BSD style license - see LICENSE.rst
"""
This extension makes it easy to edit documentation on github.
It adds links associated with each docstring that go to the
corresponding view source page on Github. From there, the user can
push the "Edit" button, edit the docstring, and submit a pull request.
It has the following configuration options (to be set in the project's
``conf.py``):
* ``edit_on_github_project``
The name of the github project, in the form
"username/projectname".
* ``edit_on_github_branch``
The name of the branch to edit. If this is a released version,
this should be a git tag referring to that version. For a
dev version, it often makes sense for it to be "main". It
may also be a git hash.
* ``edit_on_github_source_root``
The location within the source tree of the root of the
Python package. Defaults to "lib".
* ``edit_on_github_doc_root``
The location within the source tree of the root of the
documentation source. Defaults to "doc", but it may make sense to
set it to "doc/source" if the project uses a separate source
directory.
* ``edit_on_github_docstring_message``
The phrase displayed in the links to edit a docstring. Defaults
to "[edit on github]".
* ``edit_on_github_page_message``
The phrase displayed in the links to edit a RST page. Defaults
to "[edit this page on github]".
* ``edit_on_github_help_message``
The phrase displayed as a tooltip on the edit links. Defaults to
"Push the Edit button on the next page"
* ``edit_on_github_skip_regex``
When the path to the .rst file matches this regular expression,
no "edit this page on github" link will be added. Defaults to
``"_.*"``.
"""
import inspect
import os
import re
import sys
from docutils import nodes
from sphinx import addnodes
def import_object(modname, name):
"""
Import the object given by *modname* and *name* and return it.
If not found, or the import fails, returns None.
"""
try:
__import__(modname)
mod = sys.modules[modname]
obj = mod
for part in name.split('.'):
obj = getattr(obj, part)
return obj
except:
return None
def get_url_base(app):
return 'https://github.com/%s/tree/%s/' % (
app.config.edit_on_github_project,
app.config.edit_on_github_branch)
def doctree_read(app, doctree):
# Get the configuration parameters
if app.config.edit_on_github_project == 'REQUIRED':
raise ValueError(
"The edit_on_github_project configuration variable must be "
"provided in the conf.py")
source_root = app.config.edit_on_github_source_root
url = get_url_base(app)
docstring_message = app.config.edit_on_github_docstring_message
# Handle the docstring-editing links
for objnode in doctree.traverse(addnodes.desc):
if objnode.get('domain') != 'py':
continue
names = set()
for signode in objnode:
if not isinstance(signode, addnodes.desc_signature):
continue
modname = signode.get('module')
if not modname:
continue
fullname = signode.get('fullname')
if fullname in names:
# only one link per name, please
continue
names.add(fullname)
obj = import_object(modname, fullname)
anchor = None
if obj is not None:
try:
lines, lineno = inspect.getsourcelines(obj)
except:
pass
else:
anchor = '#L%d' % lineno
if anchor:
real_modname = inspect.getmodule(obj).__name__
path = '%s%s%s.py%s' % (
url, source_root, real_modname.replace('.', '/'), anchor)
onlynode = addnodes.only(expr='html')
onlynode += nodes.reference(
reftitle=app.config.edit_on_github_help_message,
refuri=path)
onlynode[0] += nodes.inline(
'', '', nodes.raw('', ' ', format='html'),
nodes.Text(docstring_message),
classes=['edit-on-github', 'viewcode-link'])
signode += onlynode
def html_page_context(app, pagename, templatename, context, doctree):
if (templatename == 'page.html' and
not re.match(app.config.edit_on_github_skip_regex, pagename)):
doc_root = app.config.edit_on_github_doc_root
if doc_root != '' and not doc_root.endswith('/'):
doc_root += '/'
doc_path = os.path.relpath(doctree.get('source'), app.builder.srcdir)
url = get_url_base(app)
page_message = app.config.edit_on_github_page_message
context['edit_on_github'] = url + doc_root + doc_path
context['edit_on_github_page_message'] = page_message
def setup(app):
app.add_config_value('edit_on_github_project', 'REQUIRED', True)
app.add_config_value('edit_on_github_branch', 'main', True)
app.add_config_value('edit_on_github_source_root', 'lib', True)
app.add_config_value('edit_on_github_doc_root', 'doc', True)
app.add_config_value('edit_on_github_docstring_message',
'[edit on github]', True)
app.add_config_value('edit_on_github_page_message',
'Edit This Page on Github', True)
app.add_config_value('edit_on_github_help_message',
'Push the Edit button on the next page', True)
app.add_config_value('edit_on_github_skip_regex',
'_.*', True)
app.connect('doctree-read', doctree_read)
app.connect('html-page-context', html_page_context)
return {'parallel_read_safe': True,
'parallel_write_safe': True}
|